oh-my-codex 0.18.3 → 0.18.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +1 -0
  4. package/dist/cli/__tests__/doctor-warning-copy.test.js +37 -3
  5. package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
  6. package/dist/cli/__tests__/explore.test.js +8 -7
  7. package/dist/cli/__tests__/explore.test.js.map +1 -1
  8. package/dist/cli/__tests__/index.test.js +63 -5
  9. package/dist/cli/__tests__/index.test.js.map +1 -1
  10. package/dist/cli/__tests__/setup-install-mode.test.js +56 -17
  11. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  12. package/dist/cli/__tests__/setup-scope.test.js +1 -1
  13. package/dist/cli/__tests__/sparkshell-cli.test.js +2 -2
  14. package/dist/cli/__tests__/sparkshell-cli.test.js.map +1 -1
  15. package/dist/cli/doctor.d.ts.map +1 -1
  16. package/dist/cli/doctor.js +109 -12
  17. package/dist/cli/doctor.js.map +1 -1
  18. package/dist/cli/explore.d.ts +1 -0
  19. package/dist/cli/explore.d.ts.map +1 -1
  20. package/dist/cli/explore.js +6 -0
  21. package/dist/cli/explore.js.map +1 -1
  22. package/dist/cli/index.d.ts +1 -1
  23. package/dist/cli/index.d.ts.map +1 -1
  24. package/dist/cli/index.js +11 -5
  25. package/dist/cli/index.js.map +1 -1
  26. package/dist/cli/question.d.ts.map +1 -1
  27. package/dist/cli/question.js +5 -1
  28. package/dist/cli/question.js.map +1 -1
  29. package/dist/cli/setup.d.ts.map +1 -1
  30. package/dist/cli/setup.js +18 -54
  31. package/dist/cli/setup.js.map +1 -1
  32. package/dist/config/__tests__/generator-idempotent.test.js +5 -5
  33. package/dist/config/generator.d.ts +8 -2
  34. package/dist/config/generator.d.ts.map +1 -1
  35. package/dist/config/generator.js +48 -4
  36. package/dist/config/generator.js.map +1 -1
  37. package/dist/hooks/__tests__/agents-overlay.test.js +9 -9
  38. package/dist/hooks/__tests__/agents-overlay.test.js.map +1 -1
  39. package/dist/hooks/__tests__/autopilot-skill-contract.test.js +10 -1
  40. package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
  41. package/dist/hooks/__tests__/consensus-execution-handoff.test.js +13 -0
  42. package/dist/hooks/__tests__/consensus-execution-handoff.test.js.map +1 -1
  43. package/dist/hooks/__tests__/explore-routing.test.js +10 -13
  44. package/dist/hooks/__tests__/explore-routing.test.js.map +1 -1
  45. package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js +13 -15
  46. package/dist/hooks/__tests__/explore-sparkshell-guidance-contract.test.js.map +1 -1
  47. package/dist/hooks/__tests__/notify-fallback-watcher.test.js +33 -0
  48. package/dist/hooks/__tests__/notify-fallback-watcher.test.js.map +1 -1
  49. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js +60 -0
  50. package/dist/hooks/__tests__/notify-hook-ralph-resume.test.js.map +1 -1
  51. package/dist/hooks/explore-routing.d.ts.map +1 -1
  52. package/dist/hooks/explore-routing.js +8 -14
  53. package/dist/hooks/explore-routing.js.map +1 -1
  54. package/dist/hud/__tests__/hud-tmux-injection.test.js +15 -10
  55. package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
  56. package/dist/hud/__tests__/reconcile.test.js +23 -0
  57. package/dist/hud/__tests__/reconcile.test.js.map +1 -1
  58. package/dist/hud/index.d.ts +1 -1
  59. package/dist/hud/index.d.ts.map +1 -1
  60. package/dist/hud/index.js +24 -2
  61. package/dist/hud/index.js.map +1 -1
  62. package/dist/hud/reconcile.d.ts.map +1 -1
  63. package/dist/hud/reconcile.js +15 -0
  64. package/dist/hud/reconcile.js.map +1 -1
  65. package/dist/question/__tests__/deep-interview.test.js +80 -7
  66. package/dist/question/__tests__/deep-interview.test.js.map +1 -1
  67. package/dist/question/__tests__/policy.test.js +83 -9
  68. package/dist/question/__tests__/policy.test.js.map +1 -1
  69. package/dist/question/autopilot-wait.d.ts +10 -0
  70. package/dist/question/autopilot-wait.d.ts.map +1 -0
  71. package/dist/question/autopilot-wait.js +134 -0
  72. package/dist/question/autopilot-wait.js.map +1 -0
  73. package/dist/question/deep-interview.d.ts.map +1 -1
  74. package/dist/question/deep-interview.js +4 -0
  75. package/dist/question/deep-interview.js.map +1 -1
  76. package/dist/question/policy.d.ts +1 -0
  77. package/dist/question/policy.d.ts.map +1 -1
  78. package/dist/question/policy.js +19 -0
  79. package/dist/question/policy.js.map +1 -1
  80. package/dist/scripts/__tests__/codex-native-hook.test.js +331 -0
  81. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  82. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  83. package/dist/scripts/codex-native-hook.js +45 -3
  84. package/dist/scripts/codex-native-hook.js.map +1 -1
  85. package/dist/scripts/notify-hook.js +13 -0
  86. package/dist/scripts/notify-hook.js.map +1 -1
  87. package/dist/subagents/__tests__/tracker.test.js +69 -0
  88. package/dist/subagents/__tests__/tracker.test.js.map +1 -1
  89. package/dist/subagents/tracker.d.ts +5 -0
  90. package/dist/subagents/tracker.d.ts.map +1 -1
  91. package/dist/subagents/tracker.js +16 -0
  92. package/dist/subagents/tracker.js.map +1 -1
  93. package/dist/ultragoal/__tests__/artifacts.test.js +126 -0
  94. package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
  95. package/dist/ultragoal/artifacts.d.ts.map +1 -1
  96. package/dist/ultragoal/artifacts.js +126 -8
  97. package/dist/ultragoal/artifacts.js.map +1 -1
  98. package/package.json +1 -1
  99. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  100. package/plugins/oh-my-codex/skills/autopilot/SKILL.md +2 -2
  101. package/plugins/oh-my-codex/skills/deep-interview/SKILL.md +1 -1
  102. package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +4 -4
  103. package/plugins/oh-my-codex/skills/plan/SKILL.md +5 -5
  104. package/plugins/oh-my-codex/skills/ralph/SKILL.md +1 -1
  105. package/plugins/oh-my-codex/skills/ralplan/SKILL.md +5 -5
  106. package/prompts/executor.md +1 -1
  107. package/prompts/explore-harness.md +2 -2
  108. package/prompts/explore.md +1 -1
  109. package/prompts/planner.md +1 -1
  110. package/prompts/sisyphus-lite.md +1 -1
  111. package/skills/autopilot/SKILL.md +2 -2
  112. package/skills/deep-interview/SKILL.md +1 -1
  113. package/skills/omx-setup/SKILL.md +4 -4
  114. package/skills/plan/SKILL.md +5 -5
  115. package/skills/ralph/SKILL.md +1 -1
  116. package/skills/ralplan/SKILL.md +5 -5
  117. package/src/scripts/__tests__/codex-native-hook.test.ts +368 -0
  118. package/src/scripts/codex-native-hook.ts +50 -2
  119. package/src/scripts/notify-hook.ts +15 -0
  120. package/templates/AGENTS.md +3 -3
@@ -30,7 +30,7 @@ Jumping into code without understanding requirements leads to rework, scope cree
30
30
  - Auto-detect interview vs direct mode based on request specificity
31
31
  - Ask one question at a time during interviews -- never batch multiple interview rounds into one question form
32
32
  - Gather codebase facts via `explore` agent before asking the user about them
33
- - When session guidance enables `USE_OMX_EXPLORE_CMD`, prefer `omx explore` for simple read-only repository lookups during planning; keep prompts narrow and concrete, and keep prompt-heavy or ambiguous planning work on the richer normal path and fall back normally if `omx explore` is unavailable.
33
+ - `omx explore` is deprecated. Use normal repository inspection tools/subagents for simple read-only repository lookups during planning; use `omx sparkshell` only for explicit shell-native read-only evidence, and keep prompt-heavy or ambiguous planning work on the richer normal path.
34
34
  - Plans must meet quality standards: 80%+ claims cite file/line, 90%+ criteria are testable
35
35
  - Implementation step count must be right-sized to task scope; avoid defaulting to exactly five steps when the work is clearly smaller or larger
36
36
  - Consensus mode outputs the final plan by default; add `--interactive` to enable execution handoff
@@ -80,8 +80,8 @@ Jumping into code without understanding requirements leads to rework, scope cree
80
80
  - **Request changes** — return to step 1 with user feedback incorporated
81
81
  - **Skip review** — go directly to final approval (step 7)
82
82
  If NOT running with `--interactive`, automatically proceed to review (step 3).
83
- 3. **Architect** reviews for architectural soundness using `ask_codex` with `agent_role: "architect"`. Architect review **MUST** include: strongest steelman counterargument (antithesis) against the favored option, at least one meaningful tradeoff tension, and (when possible) a synthesis path. In deliberate mode, Architect should explicitly flag principle violations. **Wait for this step to complete before proceeding to step 4.** Do NOT run steps 3 and 4 in parallel.
84
- 4. **Critic** evaluates against quality criteria using `ask_codex` with `agent_role: "critic"`. Critic **MUST** verify principle-option consistency, fair alternative exploration, risk mitigation clarity, testable acceptance criteria, and concrete verification steps. Critic **MUST** explicitly reject shallow alternatives, driver contradictions, vague risks, or weak verification. In deliberate mode, Critic **MUST** reject missing/weak pre-mortem or missing/weak expanded test plan. Run only after step 3 is complete.
83
+ 3. **Architect** reviews for architectural soundness as a dedicated subsequent `Architect` subagent with the full task, current plan text/path, RALPLAN-DR summary, and relevant artifact context. Architect review **MUST** include: strongest steelman counterargument (antithesis) against the favored option, at least one meaningful tradeoff tension, and (when possible) a synthesis path. In deliberate mode, Architect should explicitly flag principle violations. **Wait for this step to complete before proceeding to step 4.** Do NOT run steps 3 and 4 in parallel. Do NOT substitute a default/improvised subagent prompt for the role-specific `Architect` prompt.
84
+ 4. **Critic** evaluates against quality criteria as a dedicated subsequent `Critic` subagent with the full task, current plan text/path, RALPLAN-DR summary, artifact context, and the completed `Architect` result. Critic **MUST** verify principle-option consistency, fair alternative exploration, risk mitigation clarity, testable acceptance criteria, and concrete verification steps. Critic **MUST** explicitly reject shallow alternatives, driver contradictions, vague risks, or weak verification. In deliberate mode, Critic **MUST** reject missing/weak pre-mortem or missing/weak expanded test plan. Run only after step 3 is complete. Do NOT let the `Architect` response self-approve the Critic gate.
85
85
  5. **Re-review loop** (max 5 iterations): If Critic rejects or iterates, execute this closed loop:
86
86
  a. Collect all feedback from Architect + Critic
87
87
  b. Pass feedback to Planner to produce a revised plan
@@ -142,9 +142,9 @@ Plans are saved to `.omx/plans/`. Drafts go to `.omx/drafts/`.
142
142
  - Use the `explore` agent (LOW tier, bounded quick pass) to gather codebase facts before asking the user
143
143
  - Use `ask_codex` with `agent_role: "planner"` for planning validation on large-scope plans
144
144
  - Use `ask_codex` with `agent_role: "analyst"` for requirements analysis
145
- - Use `ask_codex` with `agent_role: "critic"` for plan review in consensus and review modes
145
+ - Use `ask_codex` with `agent_role: "critic"` for standalone review mode. In consensus mode, use the dedicated sequential role-specific `Architect` and `Critic` subagents described in steps 3-4 instead of a single critic-only review call.
146
146
  - If optional MCP compatibility tools or Codex consultation are unavailable, fall back to equivalent OMX prompt/native agents -- never block on external tools
147
- - **CRITICAL — Consensus mode agent calls MUST be sequential, never parallel.** Always await the Architect result before issuing the Critic call.
147
+ - **CRITICAL — Consensus mode agent calls MUST be sequential, never parallel.** Always await the subsequent role-specific `Architect` result before issuing the subsequent role-specific `Critic` call.
148
148
  - In consensus mode, default to RALPLAN-DR short mode; enable deliberate mode on `--deliberate` or explicit high-risk signals (auth/security, migrations, destructive changes, production incidents, compliance/PII, public API breakage)
149
149
  - In consensus mode with `--interactive`: use `AskUserQuestion` / the structured question UI for the user feedback step (step 2) and the final approval step (step 7) -- never ask for approval in plain text when a structured surface is available. Without `--interactive`, auto-proceed through planning steps without pausing. Output the final plan without execution.
150
150
  - In consensus mode with `--interactive`, on user approval **MUST** invoke the selected follow-up lane from step 9 (`$ultragoal`, `$team`, `$autoresearch-goal`, `$performance-goal`, or explicit `$ralph` fallback) -- never implement directly in the planning agent
@@ -50,7 +50,7 @@ Complex tasks often fail silently: partial implementations get declared "done",
50
50
  - unknowns/open questions
51
51
  - likely codebase touchpoints
52
52
  - If an existing relevant snapshot is available, reuse it and record the path in Ralph state.
53
- - If request ambiguity is high, gather brownfield facts first. When session guidance enables `USE_OMX_EXPLORE_CMD`, prefer `omx explore` for simple read-only repository lookups with narrow, concrete prompts; otherwise use the richer normal explore path. Then run `$deep-interview --quick <task>` to close critical gaps.
53
+ - If request ambiguity is high, gather brownfield facts first. `omx explore` is deprecated; use normal repository inspection tools/subagents for simple read-only repository lookups and `omx sparkshell` only for explicit shell-native read-only evidence. Then run `$deep-interview --quick <task>` to close critical gaps.
54
54
  - Do not begin Ralph execution work (delegation, implementation, or verification loops) until snapshot grounding exists. If forced to proceed quickly, note explicit risk tradeoffs.
55
55
  1. **Review progress**: Check TODO list and any prior iteration state
56
56
  2. **Continue from where you left off**: Pick up incomplete tasks
@@ -20,7 +20,7 @@ $ralplan "task description"
20
20
 
21
21
  ## Ontology-heavy review
22
22
 
23
- For requirements semantics, taxonomy, prompt/spec design, policy distinctions, or category-risk architecture, Scholastic may be cited as an available advisory ontology reviewer/persona. Its findings can inform the plan or follow-up evidence when explicitly used, but `$ralplan` itself remains the Planner → Architect → Critic consensus workflow and the durable gate remains Architect→Critic only.
23
+ For requirements semantics, taxonomy, prompt/spec design, policy distinctions, or category-risk architecture, subagent `Scholastic` may be cited as an available advisory ontology reviewer/persona. Its findings can inform the plan or follow-up evidence when explicitly used, but `$ralplan` itself remains the Planner → Architect → Critic consensus workflow and the durable gate remains Architect→Critic only.
24
24
 
25
25
  ## Usage with interactive mode
26
26
 
@@ -49,8 +49,8 @@ The consensus workflow:
49
49
  - If only one viable option remains, explicit invalidation rationale for alternatives
50
50
  - Deliberate mode only: pre-mortem (3 scenarios) + expanded test plan (unit/integration/e2e/observability)
51
51
  2. **User feedback** *(--interactive only)*: If `--interactive` is set, use the structured question UI (`omx question` in attached tmux; native structured input outside tmux when available) to present the draft plan **plus the Principles / Drivers / Options summary** before review (Proceed to review / Request changes / Skip review). Otherwise, automatically proceed to review.
52
- 3. **Architect** reviews for architectural soundness and must provide the strongest steelman antithesis, at least one real tradeoff tension, and (when possible) synthesis — **await completion before step 4**. In deliberate mode, Architect should explicitly flag principle violations.
53
- 4. **Critic** evaluates against quality criteria — run only after step 3 completes. Critic must enforce principle-option consistency, fair alternatives, risk mitigation clarity, testable acceptance criteria, and concrete verification steps. In deliberate mode, Critic must reject missing/weak pre-mortem or expanded test plan.
52
+ 3. **Architect** reviews for architectural soundness and must provide the strongest steelman antithesis, at least one real tradeoff tension, and (when possible) synthesis — **await completion before step 4**. Launch this as a subsequent `Architect` subagent (`agent_type: "architect"`) and pass the full task statement, context snapshot, PRD/test-spec paths, and relevant prior findings; do not use a default subagent with only a short improvised reviewer prompt. In deliberate mode, Architect should explicitly flag principle violations.
53
+ 4. **Critic** evaluates against quality criteria — run only after step 3 completes. Launch this as a subsequent `Critic` subagent (`agent_type: "critic"`) with the full task statement, context snapshot, PRD/test-spec paths, and the completed Architect review; do not ask the Architect subagent to perform the Critic gate and do not substitute a default subagent fantasy prompt for the packaged Critic role. Critic must enforce principle-option consistency, fair alternatives, risk mitigation clarity, testable acceptance criteria, and concrete verification steps. In deliberate mode, Critic must reject missing/weak pre-mortem or expanded test plan.
54
54
  5. **Re-review loop** (max 5 iterations): Any non-`APPROVE` Critic verdict (`ITERATE` or `REJECT`) MUST run the same full closed loop:
55
55
  a. Collect Architect and Critic feedback
56
56
  b. Revise the plan with Planner
@@ -62,7 +62,7 @@ The consensus workflow:
62
62
  7. *(--interactive only)* User chooses: Approve (`$ultragoal` durable goal execution, `$team`, explicit `$ralph` fallback, or a specialized goal-mode follow-up), Request changes, or Reject
63
63
  8. *(--interactive only)* On approval: invoke `$ultragoal` for default durable sequential execution, `$team` for parallel team execution, the selected specialized goal-mode follow-up (`$autoresearch-goal` or `$performance-goal`), or `$ralph` only when the user explicitly selects that fallback with the approved plan and matching success/evaluator context -- never implement directly. Preserve the explicit available-agent-types roster, reasoning-by-lane guidance, role/staffing allocation guidance, launch hints, and verification-path guidance from the approved plan for Ultragoal/team paths and any explicit Ralph fallback.
64
64
 
65
- > **Important:** Steps 3 and 4 MUST run sequentially. Do NOT issue both agent calls in the same parallel batch. Always await the Architect result before invoking Critic.
65
+ > **Important:** Steps 3 and 4 MUST run sequentially as role-specific subagents. Do NOT issue both agent calls in the same parallel batch. Always await the subsequent `Architect` result before invoking the subsequent `Critic`; only a completed, role-specific `Critic` approval can satisfy the durable gate.
66
66
 
67
67
  ## Durable Consensus Handoff Contract
68
68
 
@@ -102,7 +102,7 @@ Before consensus planning or execution handoff, ensure a grounded context snapsh
102
102
  - constraints
103
103
  - unknowns/open questions
104
104
  - likely codebase touchpoints
105
- 4. If ambiguity remains high, gather brownfield facts first. When session guidance enables `USE_OMX_EXPLORE_CMD`, prefer `omx explore` for simple read-only repository lookups with narrow, concrete prompts; otherwise use the richer normal explore path. Then run `$deep-interview --quick <task>` before continuing.
105
+ 4. If ambiguity remains high, gather brownfield facts first. `omx explore` is deprecated; use normal repository inspection tools/subagents for simple read-only repository lookups and `omx sparkshell` only for explicit shell-native read-only evidence. Then run `$deep-interview --quick <task>` before continuing.
106
106
  5. If the plan depends on official docs, version-aware framework guidance, best practices, or external dependency behavior, use `$best-practice-research` as the bounded evidence wrapper and auto-delegate `researcher` for the official/upstream lookup before finalizing the planning handoff so execution does not start from repo-local recall alone.
107
107
  6. If a prior `$autoresearch` or `$autoresearch-goal` run exists, treat its approved artifact as evidence for the plan. Do not include Autoresearch as a final architecture or runtime component unless the user explicitly requested ongoing research automation; otherwise synthesize the evidence into the `$ralplan` ADR, risks, and verification steps.
108
108
 
@@ -66,6 +66,26 @@ async function writeJson(path: string, value: unknown): Promise<void> {
66
66
  await writeFile(path, JSON.stringify(value, null, 2));
67
67
  }
68
68
 
69
+ async function setTeamPaneIds(
70
+ cwd: string,
71
+ teamName: string,
72
+ paneIds: { leaderPaneId: string; workerPaneIds: Record<string, string> },
73
+ ): Promise<void> {
74
+ for (const fileName of ["config.json", "manifest.v2.json"]) {
75
+ const filePath = join(cwd, ".omx", "state", "team", teamName, fileName);
76
+ const parsed = JSON.parse(await readFile(filePath, "utf-8")) as {
77
+ leader_pane_id?: string | null;
78
+ workers?: Array<{ name?: string; pane_id?: string | null }>;
79
+ };
80
+ parsed.leader_pane_id = paneIds.leaderPaneId;
81
+ parsed.workers = (parsed.workers ?? []).map((worker) => ({
82
+ ...worker,
83
+ pane_id: worker.name ? paneIds.workerPaneIds[worker.name] ?? worker.pane_id ?? null : worker.pane_id ?? null,
84
+ }));
85
+ await writeJson(filePath, parsed);
86
+ }
87
+ }
88
+
69
89
  async function withIsolatedHome<T>(prefix: string, run: (homeDir: string) => Promise<T>): Promise<T> {
70
90
  const homeDir = await mkdtemp(join(tmpdir(), `omx-native-hook-home-${prefix}-`));
71
91
  const previousHome = process.env.HOME;
@@ -243,6 +263,7 @@ const DEFAULT_AUTO_NUDGE_RESPONSE =
243
263
 
244
264
  const TEAM_ENV_KEYS = [
245
265
  "OMX_TEAM_WORKER",
266
+ "OMX_TEAM_INTERNAL_WORKER",
246
267
  "OMX_TEAM_STATE_ROOT",
247
268
  "OMX_TEAM_LEADER_CWD",
248
269
  "OMX_SESSION_ID",
@@ -2362,6 +2383,38 @@ standardMaxRounds = 15
2362
2383
  }
2363
2384
  });
2364
2385
 
2386
+ it("does not repeat ultragoal Stop recovery after a safe completed-aggregate microgoal blocker is recorded", async () => {
2387
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-aggregate-blocked-stop-"));
2388
+ try {
2389
+ await writeJson(join(cwd, ".omx", "ultragoal", "goals.json"), {
2390
+ version: 1,
2391
+ codexGoalMode: "aggregate",
2392
+ activeGoalId: "G001-demo",
2393
+ goals: [{
2394
+ id: "G001-demo",
2395
+ status: "in_progress",
2396
+ objective: "Demo goal",
2397
+ failureReason: "aggregate Codex goal already complete and unreconcilable while repo-native .omx/ultragoal/goals.json still has an in-progress microgoal; stop the recovery loop",
2398
+ }],
2399
+ });
2400
+
2401
+ const result = await dispatchCodexNativeHook({
2402
+ hook_event_name: "Stop",
2403
+ cwd,
2404
+ session_id: "sess-ultragoal-aggregate-blocked-stop",
2405
+ thread_id: "thread-ultragoal-aggregate-blocked-stop",
2406
+ stop_hook_active: true,
2407
+ last_assistant_message: "Goal complete.",
2408
+ }, { cwd });
2409
+
2410
+ assert.notEqual(result.outputJson?.decision, "block");
2411
+ assert.notEqual(result.outputJson?.stopReason, "ultragoal_codex_goal_snapshot_required");
2412
+ assert.doesNotMatch(JSON.stringify(result.outputJson), /omx ultragoal checkpoint --goal-id G001-demo --status complete/);
2413
+ } finally {
2414
+ await rm(cwd, { recursive: true, force: true });
2415
+ }
2416
+ });
2417
+
2365
2418
 
2366
2419
  it("does not block ultragoal Stop after task-scoped reconciliation finishes exploded bookkeeping", async () => {
2367
2420
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-ultragoal-reconciled-stop-"));
@@ -3852,6 +3905,198 @@ export async function onHookEvent(event) {
3852
3905
  }
3853
3906
  });
3854
3907
 
3908
+ it("skips prompt-submit HUD reconciliation for confirmed team worker panes", async () => {
3909
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-team-worker-skip-"));
3910
+ try {
3911
+ const teamName = "hud-worker-skip";
3912
+ await initTeamState(teamName, "skip worker HUD reconcile", "executor", 1, cwd);
3913
+ await setTeamPaneIds(cwd, teamName, {
3914
+ leaderPaneId: "%42",
3915
+ workerPaneIds: { "worker-1": "%10" },
3916
+ });
3917
+ process.env.TMUX = "1";
3918
+ process.env.TMUX_PANE = "%10";
3919
+ process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/worker-1`;
3920
+ process.env.OMX_TEAM_WORKER = `${teamName}/worker-1`;
3921
+ process.env[OMX_TMUX_HUD_OWNER_ENV] = "1";
3922
+
3923
+ let reconcileCalls = 0;
3924
+ const result = await dispatchCodexNativeHook(
3925
+ {
3926
+ hook_event_name: "UserPromptSubmit",
3927
+ cwd,
3928
+ session_id: "sess-hud-team-worker",
3929
+ prompt: "$ralplan prepare plan",
3930
+ },
3931
+ {
3932
+ cwd,
3933
+ reconcileHudForPromptSubmitFn: async () => {
3934
+ reconcileCalls += 1;
3935
+ return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
3936
+ },
3937
+ },
3938
+ );
3939
+
3940
+ assert.equal(result.omxEventName, "keyword-detector");
3941
+ assert.equal(reconcileCalls, 0);
3942
+ } finally {
3943
+ await rm(cwd, { recursive: true, force: true });
3944
+ }
3945
+ });
3946
+
3947
+ it("preserves prompt-submit HUD reconciliation for team leader panes", async () => {
3948
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-team-leader-preserve-"));
3949
+ try {
3950
+ const teamName = "hud-leader-keep";
3951
+ await initTeamState(teamName, "preserve leader HUD reconcile", "executor", 1, cwd);
3952
+ await setTeamPaneIds(cwd, teamName, {
3953
+ leaderPaneId: "%42",
3954
+ workerPaneIds: { "worker-1": "%10" },
3955
+ });
3956
+ process.env.TMUX = "1";
3957
+ process.env.TMUX_PANE = "%42";
3958
+ process.env[OMX_TMUX_HUD_OWNER_ENV] = "1";
3959
+
3960
+ let reconcileCall: { cwd: string; sessionId?: string } | null = null;
3961
+ const result = await dispatchCodexNativeHook(
3962
+ {
3963
+ hook_event_name: "UserPromptSubmit",
3964
+ cwd,
3965
+ session_id: "sess-hud-team-leader",
3966
+ prompt: "$ralplan prepare plan",
3967
+ },
3968
+ {
3969
+ cwd,
3970
+ reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => {
3971
+ reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId };
3972
+ return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
3973
+ },
3974
+ },
3975
+ );
3976
+
3977
+ assert.equal(result.omxEventName, "keyword-detector");
3978
+ assert.deepEqual(reconcileCall, { cwd, sessionId: "sess-hud-team-leader" });
3979
+ } finally {
3980
+ await rm(cwd, { recursive: true, force: true });
3981
+ }
3982
+ });
3983
+
3984
+ it("preserves prompt-submit HUD reconciliation when worker pane detection is ambiguous", async () => {
3985
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-team-worker-ambiguous-"));
3986
+ try {
3987
+ const teamName = "hud-worker-ambiguous";
3988
+ await initTeamState(teamName, "fail closed for ambiguous worker HUD reconcile", "executor", 1, cwd);
3989
+ await setTeamPaneIds(cwd, teamName, {
3990
+ leaderPaneId: "%42",
3991
+ workerPaneIds: { "worker-1": "%10" },
3992
+ });
3993
+ process.env.TMUX = "1";
3994
+ process.env.TMUX_PANE = "%99";
3995
+ process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/worker-1`;
3996
+ process.env.OMX_TEAM_WORKER = `${teamName}/worker-1`;
3997
+ process.env[OMX_TMUX_HUD_OWNER_ENV] = "1";
3998
+
3999
+ let reconcileCalls = 0;
4000
+ const result = await dispatchCodexNativeHook(
4001
+ {
4002
+ hook_event_name: "UserPromptSubmit",
4003
+ cwd,
4004
+ session_id: "sess-hud-team-worker-ambiguous",
4005
+ prompt: "$ralplan prepare plan",
4006
+ },
4007
+ {
4008
+ cwd,
4009
+ reconcileHudForPromptSubmitFn: async () => {
4010
+ reconcileCalls += 1;
4011
+ return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
4012
+ },
4013
+ },
4014
+ );
4015
+
4016
+ assert.equal(result.omxEventName, "keyword-detector");
4017
+ assert.equal(reconcileCalls, 1);
4018
+ } finally {
4019
+ await rm(cwd, { recursive: true, force: true });
4020
+ }
4021
+ });
4022
+
4023
+ it("preserves prompt-submit HUD reconciliation for native subagents even with worker pane env", async () => {
4024
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-subagent-worker-preserve-"));
4025
+ try {
4026
+ const teamName = "hud-subagent-keep";
4027
+ await initTeamState(teamName, "preserve subagent HUD reconcile", "executor", 1, cwd);
4028
+ await setTeamPaneIds(cwd, teamName, {
4029
+ leaderPaneId: "%42",
4030
+ workerPaneIds: { "worker-1": "%10" },
4031
+ });
4032
+ const stateDir = join(cwd, ".omx", "state");
4033
+ const canonicalSessionId = "sess-subagent-hud-parent";
4034
+ const leaderNativeSessionId = "native-subagent-hud-parent";
4035
+ const childNativeSessionId = "native-subagent-hud-child";
4036
+ const nowIso = new Date().toISOString();
4037
+ await writeJson(join(stateDir, "session.json"), {
4038
+ session_id: canonicalSessionId,
4039
+ native_session_id: leaderNativeSessionId,
4040
+ });
4041
+ await writeJson(join(stateDir, "subagent-tracking.json"), {
4042
+ schemaVersion: 1,
4043
+ sessions: {
4044
+ [canonicalSessionId]: {
4045
+ session_id: canonicalSessionId,
4046
+ leader_thread_id: leaderNativeSessionId,
4047
+ updated_at: nowIso,
4048
+ threads: {
4049
+ [leaderNativeSessionId]: {
4050
+ thread_id: leaderNativeSessionId,
4051
+ kind: "leader",
4052
+ first_seen_at: nowIso,
4053
+ last_seen_at: nowIso,
4054
+ turn_count: 1,
4055
+ },
4056
+ [childNativeSessionId]: {
4057
+ thread_id: childNativeSessionId,
4058
+ kind: "subagent",
4059
+ first_seen_at: nowIso,
4060
+ last_seen_at: nowIso,
4061
+ turn_count: 1,
4062
+ mode: "verifier",
4063
+ },
4064
+ },
4065
+ },
4066
+ },
4067
+ });
4068
+ process.env.TMUX = "1";
4069
+ process.env.TMUX_PANE = "%10";
4070
+ process.env.OMX_TEAM_INTERNAL_WORKER = `${teamName}/worker-1`;
4071
+ process.env.OMX_TEAM_WORKER = `${teamName}/worker-1`;
4072
+ process.env[OMX_TMUX_HUD_OWNER_ENV] = "1";
4073
+
4074
+ let reconcileCall: { cwd: string; sessionId?: string } | null = null;
4075
+ const result = await dispatchCodexNativeHook(
4076
+ {
4077
+ hook_event_name: "UserPromptSubmit",
4078
+ cwd,
4079
+ session_id: childNativeSessionId,
4080
+ thread_id: childNativeSessionId,
4081
+ turn_id: "turn-subagent-hud-child",
4082
+ prompt: "Review the worker patch literally; do not activate $ralplan.",
4083
+ },
4084
+ {
4085
+ cwd,
4086
+ reconcileHudForPromptSubmitFn: async (hookCwd, deps = {}) => {
4087
+ reconcileCall = { cwd: hookCwd, sessionId: deps.sessionId };
4088
+ return { status: "recreated", paneId: "%9", desiredHeight: 3, duplicateCount: 0 };
4089
+ },
4090
+ },
4091
+ );
4092
+
4093
+ assert.equal(result.outputJson, null);
4094
+ assert.deepEqual(reconcileCall, { cwd, sessionId: canonicalSessionId });
4095
+ } finally {
4096
+ await rm(cwd, { recursive: true, force: true });
4097
+ }
4098
+ });
4099
+
3855
4100
  it("runs prompt-submit HUD reconciliation as a best-effort tmux-only side effect", async () => {
3856
4101
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-hud-reconcile-"));
3857
4102
  const originalTmux = process.env.TMUX;
@@ -9264,6 +9509,70 @@ exit 0
9264
9509
  }
9265
9510
  });
9266
9511
 
9512
+ it("does not report ralplan subagent waiting when notify-fallback already recorded completion", async () => {
9513
+ const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-skill-subagent-complete-"));
9514
+ try {
9515
+ const stateDir = join(cwd, ".omx", "state");
9516
+ const now = new Date().toISOString();
9517
+ await mkdir(join(stateDir, "sessions", "sess-stop-skill-subagent-complete"), { recursive: true });
9518
+ await writeJson(join(stateDir, "session.json"), { session_id: "sess-stop-skill-subagent-complete" });
9519
+ await writeJson(join(stateDir, "sessions", "sess-stop-skill-subagent-complete", "skill-active-state.json"), {
9520
+ active: true,
9521
+ skill: "ralplan",
9522
+ phase: "planning",
9523
+ });
9524
+ await writeJson(join(stateDir, "sessions", "sess-stop-skill-subagent-complete", "ralplan-state.json"), {
9525
+ active: true,
9526
+ current_phase: "planning",
9527
+ });
9528
+ await writeJson(join(stateDir, "subagent-tracking.json"), {
9529
+ schemaVersion: 1,
9530
+ sessions: {
9531
+ "sess-stop-skill-subagent-complete": {
9532
+ session_id: "sess-stop-skill-subagent-complete",
9533
+ leader_thread_id: "leader-1",
9534
+ updated_at: now,
9535
+ threads: {
9536
+ "leader-1": {
9537
+ thread_id: "leader-1",
9538
+ kind: "leader",
9539
+ first_seen_at: now,
9540
+ last_seen_at: now,
9541
+ turn_count: 1,
9542
+ },
9543
+ "sub-1": {
9544
+ thread_id: "sub-1",
9545
+ kind: "subagent",
9546
+ first_seen_at: now,
9547
+ last_seen_at: now,
9548
+ completed_at: now,
9549
+ last_completed_turn_id: "turn-complete-1",
9550
+ completion_source: "notify-fallback-watcher",
9551
+ turn_count: 2,
9552
+ },
9553
+ },
9554
+ },
9555
+ },
9556
+ });
9557
+
9558
+ const result = await dispatchCodexNativeHook(
9559
+ {
9560
+ hook_event_name: "Stop",
9561
+ cwd,
9562
+ session_id: "sess-stop-skill-subagent-complete",
9563
+ },
9564
+ { cwd },
9565
+ );
9566
+
9567
+ assert.equal(result.omxEventName, "stop");
9568
+ assert.equal(result.outputJson?.decision, "block");
9569
+ assert.doesNotMatch(String(result.outputJson?.reason ?? ""), /waiting for 1 active native subagent thread/);
9570
+ assert.equal(result.outputJson?.stopReason, "skill_ralplan_planning_continue_artifact");
9571
+ } finally {
9572
+ await rm(cwd, { recursive: true, force: true });
9573
+ }
9574
+ });
9575
+
9267
9576
  it("does not block on stale root ralplan skill when the explicit session-scoped canonical skill state is absent", async () => {
9268
9577
  const cwd = await mkdtemp(join(tmpdir(), "omx-native-hook-stop-stale-root-skill-"));
9269
9578
  try {
@@ -13833,3 +14142,62 @@ describe("codex native hook triage integration", () => {
13833
14142
  }
13834
14143
  });
13835
14144
  });
14145
+
14146
+ describe('native Stop autopilot deep-interview wait', () => {
14147
+ it('does not force continued execution while autopilot is waiting on a deep-interview omx question', async () => {
14148
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-native-hook-autopilot-question-wait-'));
14149
+ try {
14150
+ const sessionId = 'sess-autopilot-wait';
14151
+ const sessionDir = join(cwd, '.omx', 'state', 'sessions', sessionId);
14152
+ await writeJson(join(cwd, '.omx', 'state', 'session.json'), { session_id: sessionId });
14153
+ await writeJson(join(sessionDir, 'autopilot-state.json'), {
14154
+ mode: 'autopilot',
14155
+ active: true,
14156
+ current_phase: 'waiting-for-user',
14157
+ run_outcome: 'blocked_on_user',
14158
+ lifecycle_outcome: 'askuserQuestion',
14159
+ session_id: sessionId,
14160
+ state: {
14161
+ deep_interview_question: {
14162
+ status: 'waiting_for_user',
14163
+ source: 'omx-question',
14164
+ obligation_id: 'obligation-stop-1',
14165
+ previous_phase: 'deep-interview',
14166
+ },
14167
+ },
14168
+ });
14169
+ await writeJson(join(sessionDir, 'deep-interview-state.json'), {
14170
+ mode: 'deep-interview',
14171
+ active: false,
14172
+ current_phase: 'intent-first',
14173
+ lifecycle_outcome: 'askuserQuestion',
14174
+ run_outcome: 'blocked_on_user',
14175
+ session_id: sessionId,
14176
+ question_enforcement: {
14177
+ obligation_id: 'obligation-stop-1',
14178
+ source: 'omx-question',
14179
+ status: 'pending',
14180
+ lifecycle_outcome: 'askuserQuestion',
14181
+ requested_at: '2026-04-19T00:00:00.000Z',
14182
+ },
14183
+ });
14184
+ await writeJson(join(sessionDir, 'skill-active-state.json'), {
14185
+ active: true,
14186
+ skill: 'autopilot',
14187
+ phase: 'deep-interview',
14188
+ session_id: sessionId,
14189
+ active_skills: [{ skill: 'autopilot', phase: 'deep-interview', active: true, session_id: sessionId }],
14190
+ });
14191
+
14192
+ const result = await dispatchCodexNativeHook({
14193
+ hook_event_name: 'Stop',
14194
+ session_id: sessionId,
14195
+ thread_id: 'thread-autopilot-wait',
14196
+ }, { cwd });
14197
+
14198
+ assert.equal(result.outputJson, null);
14199
+ } finally {
14200
+ await rm(cwd, { recursive: true, force: true });
14201
+ }
14202
+ });
14203
+ });
@@ -30,6 +30,7 @@ import {
30
30
  import {
31
31
  appendTeamEvent,
32
32
  readTeamLeaderAttention,
33
+ readTeamConfig,
33
34
  readTeamManifestV2,
34
35
  readTeamPhase,
35
36
  writeTeamLeaderAttention,
@@ -100,6 +101,7 @@ import {
100
101
  isPendingDeepInterviewQuestionEnforcement,
101
102
  reconcileDeepInterviewQuestionEnforcementFromAnsweredRecords,
102
103
  } from "../question/deep-interview.js";
104
+ import { readAutopilotDeepInterviewQuestionWaitState } from "../question/autopilot-wait.js";
103
105
  import {
104
106
  buildDocumentRefreshAdvisoryOutput,
105
107
  evaluateFinalHandoffDocumentRefresh,
@@ -1873,6 +1875,27 @@ async function resolveTeamStateDirForWorkerContext(
1873
1875
  return null;
1874
1876
  }
1875
1877
 
1878
+ async function isConfirmedTeamWorkerPromptSubmitPane(cwd: string): Promise<boolean> {
1879
+ const workerContext =
1880
+ parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_INTERNAL_WORKER))
1881
+ || parseTeamWorkerEnv(safeString(process.env.OMX_TEAM_WORKER));
1882
+ if (!workerContext) return false;
1883
+
1884
+ const currentPaneId = safeString(process.env.TMUX_PANE).trim();
1885
+ if (!currentPaneId) return false;
1886
+
1887
+ const config = await readTeamConfig(workerContext.teamName, cwd).catch(() => null);
1888
+ if (!config) return false;
1889
+
1890
+ const leaderPaneId = safeString(config.leader_pane_id).trim();
1891
+ if (leaderPaneId && leaderPaneId === currentPaneId) return false;
1892
+
1893
+ const workerPaneId = safeString(
1894
+ config.workers.find((worker) => worker.name === workerContext.workerName)?.pane_id,
1895
+ ).trim();
1896
+ return workerPaneId !== "" && workerPaneId === currentPaneId;
1897
+ }
1898
+
1876
1899
 
1877
1900
  type TeamWorkerStopDecision =
1878
1901
  | {
@@ -2033,6 +2056,9 @@ async function buildModeBasedStopOutput(
2033
2056
  if (await readCanonicalTerminalRunStateForStop(cwd, sessionId, mode)) {
2034
2057
  return null;
2035
2058
  }
2059
+ if (mode === "autopilot" && await readAutopilotDeepInterviewQuestionWaitState(cwd, sessionId)) {
2060
+ return null;
2061
+ }
2036
2062
  const state = await readModeStateForActiveDecision(mode, sessionId?.trim() || undefined, cwd);
2037
2063
  if (!state || !shouldContinueRun(state)) return null;
2038
2064
  const phase = formatPhase(state.current_phase);
@@ -2077,6 +2103,18 @@ function reportsBlockedPerformanceGoalObjectiveMismatch(state: unknown): boolean
2077
2103
  return /objective mismatch/i.test(evidence);
2078
2104
  }
2079
2105
 
2106
+ function reportsBlockedUltragoalCompletedAggregateMicrogoalLoop(goal: Record<string, unknown>): boolean {
2107
+ const evidence = [
2108
+ safeString(goal.failureReason),
2109
+ safeString(goal.blockedReason),
2110
+ safeString(goal.evidence),
2111
+ ].join(" ");
2112
+ return /aggregate codex goal/i.test(evidence)
2113
+ && /\bcomplete(?:d)?\b/i.test(evidence)
2114
+ && /microgoal/i.test(evidence)
2115
+ && /\b(?:unreconcilable|mismatch|loop|already complete|already completed|blocks?)\b/i.test(evidence);
2116
+ }
2117
+
2080
2118
  async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Promise<{ workflow: string; command: string; remediation?: string } | null> {
2081
2119
  const ultragoal = await readJsonIfExists(join(cwd, ".omx", "ultragoal", "goals.json"));
2082
2120
  const aggregateCompletion = safeObject(ultragoal?.aggregateCompletion);
@@ -2085,6 +2123,9 @@ async function findActiveGoalWorkflowReconciliationRequirement(cwd: string): Pro
2085
2123
  const activeUltragoal = aggregateProductComplete
2086
2124
  ? undefined
2087
2125
  : ultragoals.find((goal) => safeString(goal.status) === "in_progress" || safeString(goal.id) === safeString(ultragoal?.activeGoalId));
2126
+ if (activeUltragoal && reportsBlockedUltragoalCompletedAggregateMicrogoalLoop(activeUltragoal)) {
2127
+ return null;
2128
+ }
2088
2129
  if (activeUltragoal) {
2089
2130
  const goalId = safeString(activeUltragoal.id) || "<goal-id>";
2090
2131
  return {
@@ -2847,6 +2888,9 @@ async function buildDeepInterviewQuestionStopOutput(
2847
2888
  threadId: string,
2848
2889
  ): Promise<{ output: Record<string, unknown>; obligationId: string } | null> {
2849
2890
  await reconcileDeepInterviewQuestionEnforcementFromAnsweredRecords(cwd, sessionId);
2891
+ if (await readAutopilotDeepInterviewQuestionWaitState(cwd, sessionId)) {
2892
+ return null;
2893
+ }
2850
2894
  const modeState = await readStopSessionPinnedState("deep-interview-state.json", cwd, sessionId, stateDir);
2851
2895
  if (!modeState) return null;
2852
2896
 
@@ -3878,8 +3922,12 @@ export async function dispatchCodexNativeHook(
3878
3922
  triageAdditionalContext = null;
3879
3923
  }
3880
3924
  }
3881
- const reconcileHudForPromptSubmitFn = options.reconcileHudForPromptSubmitFn ?? reconcileHudForPromptSubmit;
3882
- await reconcileHudForPromptSubmitFn(cwd, { sessionId: canonicalSessionId || sessionIdForState || undefined }).catch(() => {});
3925
+ const skipHudReconcileForTeamWorkerPane = !isSubagentPromptSubmit
3926
+ && await isConfirmedTeamWorkerPromptSubmitPane(cwd).catch(() => false);
3927
+ if (!skipHudReconcileForTeamWorkerPane) {
3928
+ const reconcileHudForPromptSubmitFn = options.reconcileHudForPromptSubmitFn ?? reconcileHudForPromptSubmit;
3929
+ await reconcileHudForPromptSubmitFn(cwd, { sessionId: canonicalSessionId || sessionIdForState || undefined }).catch(() => {});
3930
+ }
3883
3931
  }
3884
3932
 
3885
3933
  if (omxEventName && !skipCanonicalSessionStartContext && !suppressNoisySubagentLifecycleDispatch) {
@@ -272,6 +272,14 @@ function isTurnCompletePayload(payload: Record<string, unknown>): boolean {
272
272
  return type === '' || type === 'agent-turn-complete' || type === 'turn-complete';
273
273
  }
274
274
 
275
+ function isNotifyFallbackTaskCompletePayload(payload: Record<string, unknown>): boolean {
276
+ const source = safeString(payload.source || '').trim();
277
+ if (source !== 'notify-fallback-watcher') return false;
278
+ return normalizeInputMessages(payload).some((message) => (
279
+ message.includes('[notify-fallback] synthesized from rollout task_complete')
280
+ ));
281
+ }
282
+
275
283
  async function main() {
276
284
  const rawPayload = process.argv[process.argv.length - 1];
277
285
  if (!rawPayload || rawPayload.startsWith('-')) {
@@ -294,6 +302,7 @@ async function main() {
294
302
  const inputMessages = normalizeInputMessages(payload);
295
303
  const latestUserInput = safeString(inputMessages.length > 0 ? inputMessages[inputMessages.length - 1] : '');
296
304
  const isTurnComplete = isTurnCompletePayload(payload);
305
+ const isNotifyFallbackTaskComplete = isNotifyFallbackTaskCompletePayload(payload);
297
306
 
298
307
  // Team worker detection via environment variable
299
308
  const teamWorkerEnv = process.env.OMX_TEAM_INTERNAL_WORKER || process.env.OMX_TEAM_WORKER; // e.g., "fix-ts/worker-1"
@@ -358,6 +367,12 @@ async function main() {
358
367
  ...(turnId ? { turnId } : {}),
359
368
  timestamp: new Date().toISOString(),
360
369
  mode: safeString(payload.mode || ''),
370
+ ...(isNotifyFallbackTaskComplete
371
+ ? {
372
+ completed: true,
373
+ completionSource: 'notify-fallback-watcher',
374
+ }
375
+ : {}),
361
376
  });
362
377
  }
363
378
  } catch {
@@ -274,10 +274,10 @@ Verification loop: define the claim and success criteria, run the smallest valid
274
274
  Mode selection: use `$deep-interview` for unclear intent/boundaries; `$ralplan` for consensus on architecture, tradeoffs, or tests; `$team` for approved multi-lane work; `$ralph` for persistent single-owner completion/verification loops; otherwise execute directly in solo mode. Switch modes only when evidence shows the current lane is mismatched or blocked.
275
275
 
276
276
  Command routing:
277
- - When `USE_OMX_EXPLORE_CMD` enables advisory routing, strongly prefer `omx explore` as the default surface for simple read-only repository lookup tasks (files, symbols, patterns, relationships).
278
- - For simple file/symbol lookups, use `omx explore` FIRST before attempting full code analysis.
277
+ - `omx explore` is deprecated and MUST NOT be recommended as the default surface for simple read-only repository lookup tasks. Use normal Codex repository inspection tools/subagents for file, symbol, pattern, relationship, and implementation discovery.
278
+ - `USE_OMX_EXPLORE_CMD` is compatibility-only for legacy callers; it does not make `omx explore` preferred for new work.
279
279
 
280
- Use `omx explore --prompt ...` for simple read-only lookups through the shell-only, allowlisted, read-only path. Use `omx sparkshell` for noisy read-only shell commands, bounded verification, repo-wide listing/search, or explicit `omx sparkshell --tmux-pane` summaries. Treat sparkshell as explicit opt-in. When to use what: keep ambiguous, implementation-heavy, edit-heavy, diagnostics, tests, MCP/web, and complex shell work on the normal path; if `omx explore` or `omx sparkshell` is incomplete, retry narrower or gracefully fall back to the normal path.
280
+ Use `omx sparkshell` for explicit shell-native read-only commands, bounded verification, repo-wide listing/search, or explicit `omx sparkshell --tmux-pane` summaries. Treat sparkshell as explicit opt-in. When to use what: keep ambiguous, implementation-heavy, edit-heavy, diagnostics, tests, MCP/web, and complex shell work on the normal path; if `omx sparkshell` is incomplete, retry narrower or gracefully fall back to the normal path.
281
281
 
282
282
  Leader vs worker:
283
283
  - The leader chooses the mode, keeps the brief current, delegates bounded work, and owns verification plus stop/escalate calls.