oh-my-codex 0.18.12 → 0.18.13

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 (95) hide show
  1. package/Cargo.lock +6 -6
  2. package/Cargo.toml +1 -1
  3. package/README.md +7 -0
  4. package/dist/autopilot/__tests__/ralplan-gate.test.js +621 -0
  5. package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
  6. package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
  7. package/dist/autopilot/ralplan-gate.js +32 -18
  8. package/dist/autopilot/ralplan-gate.js.map +1 -1
  9. package/dist/cli/__tests__/doctor-invalid-config.test.js +35 -0
  10. package/dist/cli/__tests__/doctor-invalid-config.test.js.map +1 -1
  11. package/dist/cli/__tests__/index.test.js +54 -1
  12. package/dist/cli/__tests__/index.test.js.map +1 -1
  13. package/dist/cli/__tests__/resume.test.js +217 -1
  14. package/dist/cli/__tests__/resume.test.js.map +1 -1
  15. package/dist/cli/__tests__/session-search-help.test.js +3 -2
  16. package/dist/cli/__tests__/session-search-help.test.js.map +1 -1
  17. package/dist/cli/__tests__/session-search.test.js +64 -2
  18. package/dist/cli/__tests__/session-search.test.js.map +1 -1
  19. package/dist/cli/__tests__/setup-install-mode.test.js +6 -5
  20. package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
  21. package/dist/cli/__tests__/setup-prompts-overwrite.test.js +74 -0
  22. package/dist/cli/__tests__/setup-prompts-overwrite.test.js.map +1 -1
  23. package/dist/cli/__tests__/setup-scope.test.js +45 -0
  24. package/dist/cli/__tests__/setup-scope.test.js.map +1 -1
  25. package/dist/cli/__tests__/update.test.js +5 -2
  26. package/dist/cli/__tests__/update.test.js.map +1 -1
  27. package/dist/cli/doctor.d.ts.map +1 -1
  28. package/dist/cli/doctor.js +9 -1
  29. package/dist/cli/doctor.js.map +1 -1
  30. package/dist/cli/index.d.ts +9 -1
  31. package/dist/cli/index.d.ts.map +1 -1
  32. package/dist/cli/index.js +209 -7
  33. package/dist/cli/index.js.map +1 -1
  34. package/dist/cli/project-runtime-codex-homes.d.ts +6 -0
  35. package/dist/cli/project-runtime-codex-homes.d.ts.map +1 -0
  36. package/dist/cli/project-runtime-codex-homes.js +27 -0
  37. package/dist/cli/project-runtime-codex-homes.js.map +1 -0
  38. package/dist/cli/session-search.d.ts.map +1 -1
  39. package/dist/cli/session-search.js +8 -1
  40. package/dist/cli/session-search.js.map +1 -1
  41. package/dist/cli/setup.d.ts +1 -0
  42. package/dist/cli/setup.d.ts.map +1 -1
  43. package/dist/cli/setup.js +168 -4
  44. package/dist/cli/setup.js.map +1 -1
  45. package/dist/cli/update.d.ts.map +1 -1
  46. package/dist/cli/update.js +2 -0
  47. package/dist/cli/update.js.map +1 -1
  48. package/dist/config/__tests__/codex-hooks.test.js +38 -24
  49. package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
  50. package/dist/config/codex-hooks.d.ts +6 -0
  51. package/dist/config/codex-hooks.d.ts.map +1 -1
  52. package/dist/config/codex-hooks.js +34 -49
  53. package/dist/config/codex-hooks.js.map +1 -1
  54. package/dist/pipeline/__tests__/orchestrator.test.js +125 -0
  55. package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
  56. package/dist/pipeline/__tests__/stages.test.js +109 -0
  57. package/dist/pipeline/__tests__/stages.test.js.map +1 -1
  58. package/dist/pipeline/orchestrator.d.ts.map +1 -1
  59. package/dist/pipeline/orchestrator.js +7 -0
  60. package/dist/pipeline/orchestrator.js.map +1 -1
  61. package/dist/ralplan/__tests__/consensus-gate.test.js +440 -1
  62. package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
  63. package/dist/ralplan/consensus-gate.d.ts +2 -0
  64. package/dist/ralplan/consensus-gate.d.ts.map +1 -1
  65. package/dist/ralplan/consensus-gate.js +173 -71
  66. package/dist/ralplan/consensus-gate.js.map +1 -1
  67. package/dist/scripts/__tests__/codex-native-hook.test.js +273 -0
  68. package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
  69. package/dist/scripts/codex-native-hook.d.ts.map +1 -1
  70. package/dist/scripts/codex-native-hook.js +56 -12
  71. package/dist/scripts/codex-native-hook.js.map +1 -1
  72. package/dist/scripts/codex-native-pre-post.d.ts +1 -0
  73. package/dist/scripts/codex-native-pre-post.d.ts.map +1 -1
  74. package/dist/scripts/codex-native-pre-post.js +130 -0
  75. package/dist/scripts/codex-native-pre-post.js.map +1 -1
  76. package/dist/session-history/__tests__/search.test.js +166 -0
  77. package/dist/session-history/__tests__/search.test.js.map +1 -1
  78. package/dist/session-history/search.d.ts +7 -0
  79. package/dist/session-history/search.d.ts.map +1 -1
  80. package/dist/session-history/search.js +83 -24
  81. package/dist/session-history/search.js.map +1 -1
  82. package/dist/sidecar/__tests__/collector.test.js +60 -0
  83. package/dist/sidecar/__tests__/collector.test.js.map +1 -1
  84. package/dist/sidecar/collector.d.ts.map +1 -1
  85. package/dist/sidecar/collector.js +3 -6
  86. package/dist/sidecar/collector.js.map +1 -1
  87. package/dist/verification/__tests__/ci-rust-gates.test.js +4 -2
  88. package/dist/verification/__tests__/ci-rust-gates.test.js.map +1 -1
  89. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js +71 -3
  90. package/dist/verification/__tests__/dev-merge-issue-close-workflow.test.js.map +1 -1
  91. package/package.json +1 -1
  92. package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
  93. package/src/scripts/__tests__/codex-native-hook.test.ts +363 -0
  94. package/src/scripts/codex-native-hook.ts +68 -18
  95. package/src/scripts/codex-native-pre-post.ts +137 -0
@@ -6,6 +6,219 @@ import { tmpdir } from 'node:os';
6
6
  import { subagentTrackingPath } from '../../subagents/tracker.js';
7
7
  import { buildAutopilotRalplanUltragoalGateError, canAdvanceAutopilotRalplanToUltragoal, } from '../ralplan-gate.js';
8
8
  describe('autopilot ralplan gate', () => {
9
+ it('rejects invalid next-state complete consensus before falling back to older valid current state', async () => {
10
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-autopilot-ralplan-next-invalid-terminal-'));
11
+ const sessionId = 'sess-autopilot-next-invalid-terminal';
12
+ const trackingPath = subagentTrackingPath(cwd);
13
+ try {
14
+ await mkdir(join(trackingPath, '..'), { recursive: true });
15
+ await writeFile(trackingPath, JSON.stringify({
16
+ schemaVersion: 1,
17
+ sessions: {
18
+ [sessionId]: {
19
+ session_id: sessionId,
20
+ leader_thread_id: 'thread-leader',
21
+ updated_at: '2026-06-12T10:00:00.000Z',
22
+ threads: {
23
+ 'thread-leader': { thread_id: 'thread-leader', kind: 'leader', first_seen_at: '2026-06-12T09:59:00.000Z', last_seen_at: '2026-06-12T09:59:00.000Z', turn_count: 1 },
24
+ 'thread-architect-old': { thread_id: 'thread-architect-old', kind: 'subagent', first_seen_at: '2026-06-12T09:59:30.000Z', last_seen_at: '2026-06-12T09:59:30.000Z', completed_at: '2026-06-12T09:59:30.000Z', turn_count: 1 },
25
+ 'thread-critic-old': { thread_id: 'thread-critic-old', kind: 'subagent', first_seen_at: '2026-06-12T10:00:00.000Z', last_seen_at: '2026-06-12T10:00:00.000Z', completed_at: '2026-06-12T10:00:00.000Z', turn_count: 1 },
26
+ },
27
+ },
28
+ },
29
+ }, null, 2));
30
+ const nextState = {
31
+ current_phase: 'ralplan',
32
+ return_to_ralplan_reason: 'Code review requested a plan update.',
33
+ handoff_artifacts: {
34
+ ralplan_consensus_gate: {
35
+ complete: true,
36
+ sequence: ['architect-review', 'critic-review'],
37
+ ralplan_architect_review: {
38
+ agent_role: 'architect',
39
+ provenance_kind: 'native_subagent',
40
+ verdict: 'iterate',
41
+ session_id: sessionId,
42
+ thread_id: 'thread-architect-new',
43
+ artifact_path: '.omx/artifacts/architect-new.md',
44
+ tracker_path: '.omx/state/subagent-tracking.json',
45
+ completed_at: '2026-06-12T10:01:00.000Z',
46
+ },
47
+ ralplan_critic_review: {
48
+ agent_role: 'critic',
49
+ provenance_kind: 'native_subagent',
50
+ verdict: 'approve',
51
+ session_id: sessionId,
52
+ thread_id: 'thread-critic-new',
53
+ artifact_path: '.omx/artifacts/critic-new.md',
54
+ tracker_path: '.omx/state/subagent-tracking.json',
55
+ completed_at: '2026-06-12T10:02:00.000Z',
56
+ },
57
+ },
58
+ },
59
+ };
60
+ const currentState = {
61
+ current_phase: 'ralplan',
62
+ handoff_artifacts: {
63
+ ralplan_consensus_gate: {
64
+ complete: true,
65
+ sequence: ['architect-review', 'critic-review'],
66
+ ralplan_architect_review: {
67
+ agent_role: 'architect',
68
+ provenance_kind: 'native_subagent',
69
+ verdict: 'approve',
70
+ session_id: sessionId,
71
+ thread_id: 'thread-architect-old',
72
+ artifact_path: '.omx/artifacts/architect-old.md',
73
+ tracker_path: '.omx/state/subagent-tracking.json',
74
+ completed_at: '2026-06-12T09:59:30.000Z',
75
+ },
76
+ ralplan_critic_review: {
77
+ agent_role: 'critic',
78
+ provenance_kind: 'native_subagent',
79
+ verdict: 'approve',
80
+ session_id: sessionId,
81
+ thread_id: 'thread-critic-old',
82
+ artifact_path: '.omx/artifacts/critic-old.md',
83
+ tracker_path: '.omx/state/subagent-tracking.json',
84
+ completed_at: '2026-06-12T10:00:00.000Z',
85
+ },
86
+ },
87
+ },
88
+ };
89
+ const decision = canAdvanceAutopilotRalplanToUltragoal({ cwd, sessionId, nextState, currentState });
90
+ assert.equal(decision.allowed, false);
91
+ assert.equal(decision.evidence?.source, 'next-autopilot-state:handoff_artifacts');
92
+ assert.equal(decision.evidence?.blockedReason, 'non_approving_ralplan_consensus_review');
93
+ assert.match(buildAutopilotRalplanUltragoalGateError(decision), /architect.*verdict=iterate/i);
94
+ }
95
+ finally {
96
+ await rm(cwd, { recursive: true, force: true });
97
+ }
98
+ });
99
+ it('rejects stale nested ralplan handoff consensus when parent state returned to ralplan', async () => {
100
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-autopilot-ralplan-stale-nested-'));
101
+ const sessionId = 'sess-autopilot-stale-nested';
102
+ const trackingPath = subagentTrackingPath(cwd);
103
+ try {
104
+ await mkdir(join(trackingPath, '..'), { recursive: true });
105
+ await writeFile(trackingPath, JSON.stringify({
106
+ schemaVersion: 1,
107
+ sessions: {
108
+ [sessionId]: {
109
+ session_id: sessionId,
110
+ leader_thread_id: 'thread-leader',
111
+ updated_at: '2026-06-12T10:00:00.000Z',
112
+ threads: {
113
+ 'thread-leader': { thread_id: 'thread-leader', kind: 'leader', first_seen_at: '2026-06-12T09:59:00.000Z', last_seen_at: '2026-06-12T09:59:00.000Z', turn_count: 1 },
114
+ 'thread-architect-stale': { thread_id: 'thread-architect-stale', kind: 'subagent', first_seen_at: '2026-06-12T09:59:30.000Z', last_seen_at: '2026-06-12T09:59:30.000Z', completed_at: '2026-06-12T09:59:30.000Z', turn_count: 1 },
115
+ 'thread-critic-stale': { thread_id: 'thread-critic-stale', kind: 'subagent', first_seen_at: '2026-06-12T10:00:00.000Z', last_seen_at: '2026-06-12T10:00:00.000Z', completed_at: '2026-06-12T10:00:00.000Z', turn_count: 1 },
116
+ },
117
+ },
118
+ },
119
+ }, null, 2));
120
+ const nextState = {
121
+ current_phase: 'ralplan',
122
+ return_to_ralplan_reason: 'Code review requested a plan update.',
123
+ review_cycle: 2,
124
+ handoff_artifacts: {
125
+ ralplan: {
126
+ review_cycle: 2,
127
+ ralplanConsensusGate: {
128
+ complete: true,
129
+ sequence: ['architect-review', 'critic-review'],
130
+ ralplan_architect_review: {
131
+ agent_role: 'architect',
132
+ provenance_kind: 'native_subagent',
133
+ verdict: 'approve',
134
+ session_id: sessionId,
135
+ thread_id: 'thread-architect-stale',
136
+ artifact_path: '.omx/artifacts/architect-stale.md',
137
+ tracker_path: '.omx/state/subagent-tracking.json',
138
+ completed_at: '2026-06-12T09:59:30.000Z',
139
+ },
140
+ ralplan_critic_review: {
141
+ agent_role: 'critic',
142
+ provenance_kind: 'native_subagent',
143
+ verdict: 'approve',
144
+ session_id: sessionId,
145
+ thread_id: 'thread-critic-stale',
146
+ artifact_path: '.omx/artifacts/critic-stale.md',
147
+ tracker_path: '.omx/state/subagent-tracking.json',
148
+ completed_at: '2026-06-12T10:00:00.000Z',
149
+ },
150
+ },
151
+ },
152
+ },
153
+ };
154
+ const decision = canAdvanceAutopilotRalplanToUltragoal({ cwd, sessionId, nextState });
155
+ assert.equal(decision.allowed, false);
156
+ assert.equal(decision.evidence?.blockedReason, 'missing_sequential_architect_then_critic_approval');
157
+ }
158
+ finally {
159
+ await rm(cwd, { recursive: true, force: true });
160
+ }
161
+ });
162
+ it('rejects stale raw handoff consensus when parent state returned to ralplan', async () => {
163
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-autopilot-ralplan-stale-raw-handoff-'));
164
+ const sessionId = 'sess-autopilot-stale-raw-handoff';
165
+ const trackingPath = subagentTrackingPath(cwd);
166
+ try {
167
+ await mkdir(join(trackingPath, '..'), { recursive: true });
168
+ await writeFile(trackingPath, JSON.stringify({
169
+ schemaVersion: 1,
170
+ sessions: {
171
+ [sessionId]: {
172
+ session_id: sessionId,
173
+ leader_thread_id: 'thread-leader',
174
+ updated_at: '2026-06-12T10:00:00.000Z',
175
+ threads: {
176
+ 'thread-leader': { thread_id: 'thread-leader', kind: 'leader', first_seen_at: '2026-06-12T09:59:00.000Z', last_seen_at: '2026-06-12T09:59:00.000Z', turn_count: 1 },
177
+ 'thread-architect-stale': { thread_id: 'thread-architect-stale', kind: 'subagent', first_seen_at: '2026-06-12T09:59:30.000Z', last_seen_at: '2026-06-12T09:59:30.000Z', completed_at: '2026-06-12T09:59:30.000Z', turn_count: 1 },
178
+ 'thread-critic-stale': { thread_id: 'thread-critic-stale', kind: 'subagent', first_seen_at: '2026-06-12T10:00:00.000Z', last_seen_at: '2026-06-12T10:00:00.000Z', completed_at: '2026-06-12T10:00:00.000Z', turn_count: 1 },
179
+ },
180
+ },
181
+ },
182
+ }, null, 2));
183
+ const currentState = {
184
+ current_phase: 'ralplan',
185
+ return_to_ralplan_reason: 'Code review requested a plan update.',
186
+ review_cycle: 1,
187
+ handoff_artifacts: {
188
+ ralplan_consensus_gate: {
189
+ complete: true,
190
+ sequence: ['architect-review', 'critic-review'],
191
+ ralplan_architect_review: {
192
+ agent_role: 'architect',
193
+ provenance_kind: 'native_subagent',
194
+ verdict: 'approve',
195
+ session_id: sessionId,
196
+ thread_id: 'thread-architect-stale',
197
+ artifact_path: '.omx/artifacts/architect-stale.md',
198
+ tracker_path: '.omx/state/subagent-tracking.json',
199
+ completed_at: '2026-06-12T09:59:30.000Z',
200
+ },
201
+ ralplan_critic_review: {
202
+ agent_role: 'critic',
203
+ provenance_kind: 'native_subagent',
204
+ verdict: 'approve',
205
+ session_id: sessionId,
206
+ thread_id: 'thread-critic-stale',
207
+ artifact_path: '.omx/artifacts/critic-stale.md',
208
+ tracker_path: '.omx/state/subagent-tracking.json',
209
+ completed_at: '2026-06-12T10:00:00.000Z',
210
+ },
211
+ },
212
+ },
213
+ };
214
+ const decision = canAdvanceAutopilotRalplanToUltragoal({ cwd, sessionId, currentState });
215
+ assert.equal(decision.allowed, false);
216
+ assert.equal(decision.evidence?.blockedReason, 'missing_sequential_architect_then_critic_approval');
217
+ }
218
+ finally {
219
+ await rm(cwd, { recursive: true, force: true });
220
+ }
221
+ });
9
222
  for (const { lane, architectVerdict, criticVerdict } of [
10
223
  { lane: 'architect', architectVerdict: 'iterate', criticVerdict: 'approve' },
11
224
  { lane: 'critic', architectVerdict: 'approve', criticVerdict: 'iterate' },
@@ -53,6 +266,412 @@ describe('autopilot ralplan gate', () => {
53
266
  assert.match(error, new RegExp(`${lane}.*verdict=iterate`, 'i'));
54
267
  });
55
268
  }
269
+ it('accepts fresh next-state consensus over stale invalid current-state consensus', async () => {
270
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-autopilot-ralplan-fresh-next-valid-'));
271
+ const sessionId = 'sess-autopilot-fresh-next-valid';
272
+ const trackingPath = subagentTrackingPath(cwd);
273
+ try {
274
+ await mkdir(join(trackingPath, '..'), { recursive: true });
275
+ await writeFile(trackingPath, JSON.stringify({
276
+ schemaVersion: 1,
277
+ sessions: {
278
+ [sessionId]: {
279
+ session_id: sessionId,
280
+ leader_thread_id: 'thread-leader',
281
+ updated_at: '2026-06-12T10:03:00.000Z',
282
+ threads: {
283
+ 'thread-leader': { thread_id: 'thread-leader', kind: 'leader', first_seen_at: '2026-06-12T09:59:00.000Z', last_seen_at: '2026-06-12T09:59:00.000Z', turn_count: 1 },
284
+ 'thread-architect-fresh': { thread_id: 'thread-architect-fresh', kind: 'subagent', first_seen_at: '2026-06-12T10:02:00.000Z', last_seen_at: '2026-06-12T10:02:00.000Z', completed_at: '2026-06-12T10:02:00.000Z', turn_count: 1 },
285
+ 'thread-critic-fresh': { thread_id: 'thread-critic-fresh', kind: 'subagent', first_seen_at: '2026-06-12T10:03:00.000Z', last_seen_at: '2026-06-12T10:03:00.000Z', completed_at: '2026-06-12T10:03:00.000Z', turn_count: 1 },
286
+ },
287
+ },
288
+ },
289
+ }, null, 2));
290
+ const nextState = {
291
+ current_phase: 'ralplan',
292
+ review_cycle: 2,
293
+ handoff_artifacts: {
294
+ ralplan_consensus_gate: {
295
+ complete: true,
296
+ sequence: ['architect-review', 'critic-review'],
297
+ ralplan_architect_review: {
298
+ agent_role: 'architect',
299
+ provenance_kind: 'native_subagent',
300
+ verdict: 'approve',
301
+ review_cycle: 2,
302
+ session_id: sessionId,
303
+ thread_id: 'thread-architect-fresh',
304
+ artifact_path: '.omx/artifacts/architect-fresh.md',
305
+ tracker_path: '.omx/state/subagent-tracking.json',
306
+ completed_at: '2026-06-12T10:02:00.000Z',
307
+ },
308
+ ralplan_critic_review: {
309
+ agent_role: 'critic',
310
+ provenance_kind: 'native_subagent',
311
+ verdict: 'approve',
312
+ review_cycle: 2,
313
+ session_id: sessionId,
314
+ thread_id: 'thread-critic-fresh',
315
+ artifact_path: '.omx/artifacts/critic-fresh.md',
316
+ tracker_path: '.omx/state/subagent-tracking.json',
317
+ completed_at: '2026-06-12T10:03:00.000Z',
318
+ },
319
+ },
320
+ },
321
+ };
322
+ const currentState = {
323
+ current_phase: 'ralplan',
324
+ return_to_ralplan_reason: 'Code review requested a plan update.',
325
+ review_cycle: 1,
326
+ handoff_artifacts: {
327
+ ralplan_consensus_gate: {
328
+ complete: true,
329
+ sequence: ['architect-review', 'critic-review'],
330
+ ralplan_architect_review: {
331
+ agent_role: 'architect',
332
+ provenance_kind: 'native_subagent',
333
+ verdict: 'block',
334
+ review_cycle: 1,
335
+ session_id: sessionId,
336
+ thread_id: 'thread-architect-stale',
337
+ artifact_path: '.omx/artifacts/architect-stale.md',
338
+ tracker_path: '.omx/state/subagent-tracking.json',
339
+ completed_at: '2026-06-12T10:00:00.000Z',
340
+ },
341
+ ralplan_critic_review: {
342
+ agent_role: 'critic',
343
+ provenance_kind: 'native_subagent',
344
+ verdict: 'approve',
345
+ review_cycle: 1,
346
+ session_id: sessionId,
347
+ thread_id: 'thread-critic-stale',
348
+ artifact_path: '.omx/artifacts/critic-stale.md',
349
+ tracker_path: '.omx/state/subagent-tracking.json',
350
+ completed_at: '2026-06-12T10:01:00.000Z',
351
+ },
352
+ },
353
+ },
354
+ };
355
+ const decision = canAdvanceAutopilotRalplanToUltragoal({ cwd, sessionId, nextState, currentState });
356
+ assert.equal(decision.allowed, true);
357
+ assert.equal(decision.evidence?.source, 'next-autopilot-state');
358
+ assert.equal(decision.evidence?.blockedReason, null);
359
+ }
360
+ finally {
361
+ await rm(cwd, { recursive: true, force: true });
362
+ }
363
+ });
364
+ it('accepts tracker-backed native reviews without duplicated session, tracker, or artifact fields', async () => {
365
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-autopilot-ralplan-tracker-resolved-'));
366
+ const sessionId = 'sess-autopilot-tracker-resolved';
367
+ const trackingPath = subagentTrackingPath(cwd);
368
+ try {
369
+ await mkdir(join(trackingPath, '..'), { recursive: true });
370
+ await writeFile(trackingPath, JSON.stringify({
371
+ schemaVersion: 1,
372
+ sessions: {
373
+ [sessionId]: {
374
+ session_id: sessionId,
375
+ leader_thread_id: 'thread-leader',
376
+ updated_at: '2026-06-12T10:03:00.000Z',
377
+ threads: {
378
+ 'thread-leader': { thread_id: 'thread-leader', kind: 'leader', first_seen_at: '2026-06-12T09:59:00.000Z', last_seen_at: '2026-06-12T09:59:00.000Z', turn_count: 1 },
379
+ 'thread-architect': { thread_id: 'thread-architect', kind: 'subagent', first_seen_at: '2026-06-12T10:02:00.000Z', last_seen_at: '2026-06-12T10:02:00.000Z', completed_at: '2026-06-12T10:02:00.000Z', turn_count: 1 },
380
+ 'thread-critic': { thread_id: 'thread-critic', kind: 'subagent', first_seen_at: '2026-06-12T10:03:00.000Z', last_seen_at: '2026-06-12T10:03:00.000Z', completed_at: '2026-06-12T10:03:00.000Z', turn_count: 1 },
381
+ },
382
+ },
383
+ },
384
+ }, null, 2));
385
+ const state = {
386
+ current_phase: 'ralplan',
387
+ handoff_artifacts: {
388
+ ralplan_consensus_gate: {
389
+ complete: true,
390
+ sequence: ['architect-review', 'critic-review'],
391
+ ralplan_architect_review: {
392
+ agent_role: 'architect',
393
+ provenance_kind: 'native_subagent',
394
+ verdict: 'approve',
395
+ thread_id: 'thread-architect',
396
+ completed_at: '2026-06-12T10:02:00.000Z',
397
+ },
398
+ ralplan_critic_review: {
399
+ agent_role: 'critic',
400
+ provenance_kind: 'native_subagent',
401
+ verdict: 'approve',
402
+ thread_id: 'thread-critic',
403
+ completed_at: '2026-06-12T10:03:00.000Z',
404
+ },
405
+ },
406
+ },
407
+ };
408
+ const decision = canAdvanceAutopilotRalplanToUltragoal({ cwd, sessionId, currentState: state });
409
+ assert.equal(decision.allowed, true);
410
+ assert.equal(decision.evidence?.blockedReason, null);
411
+ }
412
+ finally {
413
+ await rm(cwd, { recursive: true, force: true });
414
+ }
415
+ });
416
+ it('rejects omitted review session ids when no transition session context exists', async () => {
417
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-autopilot-ralplan-no-session-context-'));
418
+ const trackingPath = subagentTrackingPath(cwd);
419
+ try {
420
+ await mkdir(join(trackingPath, '..'), { recursive: true });
421
+ await writeFile(trackingPath, JSON.stringify({
422
+ schemaVersion: 1,
423
+ sessions: {},
424
+ }, null, 2));
425
+ const state = {
426
+ current_phase: 'ralplan',
427
+ handoff_artifacts: {
428
+ ralplan_consensus_gate: {
429
+ complete: true,
430
+ sequence: ['architect-review', 'critic-review'],
431
+ ralplan_architect_review: {
432
+ agent_role: 'architect',
433
+ provenance_kind: 'native_subagent',
434
+ verdict: 'approve',
435
+ thread_id: 'thread-architect',
436
+ },
437
+ ralplan_critic_review: {
438
+ agent_role: 'critic',
439
+ provenance_kind: 'native_subagent',
440
+ verdict: 'approve',
441
+ thread_id: 'thread-critic',
442
+ },
443
+ },
444
+ },
445
+ };
446
+ const decision = canAdvanceAutopilotRalplanToUltragoal({ cwd, currentState: state });
447
+ assert.equal(decision.allowed, false);
448
+ assert.match(buildAutopilotRalplanUltragoalGateError(decision), /cannot resolve session_id/);
449
+ }
450
+ finally {
451
+ await rm(cwd, { recursive: true, force: true });
452
+ }
453
+ });
454
+ it('rejects architect and critic reviews that reuse the same native tracker thread', async () => {
455
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-autopilot-ralplan-duplicate-thread-'));
456
+ const sessionId = 'sess-autopilot-duplicate-thread';
457
+ const trackingPath = subagentTrackingPath(cwd);
458
+ try {
459
+ await mkdir(join(trackingPath, '..'), { recursive: true });
460
+ await writeFile(trackingPath, JSON.stringify({
461
+ schemaVersion: 1,
462
+ sessions: {
463
+ [sessionId]: {
464
+ session_id: sessionId,
465
+ leader_thread_id: 'thread-leader',
466
+ updated_at: '2026-06-12T10:03:00.000Z',
467
+ threads: {
468
+ 'thread-leader': { thread_id: 'thread-leader', kind: 'leader', first_seen_at: '2026-06-12T09:59:00.000Z', last_seen_at: '2026-06-12T09:59:00.000Z', turn_count: 1 },
469
+ 'thread-reviewer': { thread_id: 'thread-reviewer', kind: 'subagent', first_seen_at: '2026-06-12T10:02:00.000Z', last_seen_at: '2026-06-12T10:03:00.000Z', completed_at: '2026-06-12T10:03:00.000Z', turn_count: 1 },
470
+ },
471
+ },
472
+ },
473
+ }, null, 2));
474
+ const state = {
475
+ current_phase: 'ralplan',
476
+ handoff_artifacts: {
477
+ ralplan_consensus_gate: {
478
+ complete: true,
479
+ sequence: ['architect-review', 'critic-review'],
480
+ ralplan_architect_review: {
481
+ agent_role: 'architect',
482
+ provenance_kind: 'native_subagent',
483
+ verdict: 'approve',
484
+ session_id: sessionId,
485
+ thread_id: 'thread-reviewer',
486
+ completed_at: '2026-06-12T10:02:00.000Z',
487
+ },
488
+ ralplan_critic_review: {
489
+ agent_role: 'critic',
490
+ provenance_kind: 'native_subagent',
491
+ verdict: 'approve',
492
+ session_id: sessionId,
493
+ thread_id: 'thread-reviewer',
494
+ completed_at: '2026-06-12T10:03:00.000Z',
495
+ },
496
+ },
497
+ },
498
+ };
499
+ const decision = canAdvanceAutopilotRalplanToUltragoal({ cwd, sessionId, currentState: state });
500
+ assert.equal(decision.allowed, false);
501
+ assert.match(buildAutopilotRalplanUltragoalGateError(decision), /distinct native subagent tracker threads/);
502
+ }
503
+ finally {
504
+ await rm(cwd, { recursive: true, force: true });
505
+ }
506
+ });
507
+ it('rejects tracker-backed native reviews composed from different sessions', async () => {
508
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-autopilot-ralplan-cross-session-'));
509
+ const trackingPath = subagentTrackingPath(cwd);
510
+ try {
511
+ await mkdir(join(trackingPath, '..'), { recursive: true });
512
+ await writeFile(trackingPath, JSON.stringify({
513
+ schemaVersion: 1,
514
+ sessions: {
515
+ 'sess-architect': {
516
+ session_id: 'sess-architect',
517
+ leader_thread_id: 'thread-leader-a',
518
+ updated_at: '2026-06-12T10:02:00.000Z',
519
+ threads: {
520
+ 'thread-leader-a': { thread_id: 'thread-leader-a', kind: 'leader', first_seen_at: '2026-06-12T09:59:00.000Z', last_seen_at: '2026-06-12T09:59:00.000Z', turn_count: 1 },
521
+ 'thread-architect': { thread_id: 'thread-architect', kind: 'subagent', first_seen_at: '2026-06-12T10:02:00.000Z', last_seen_at: '2026-06-12T10:02:00.000Z', completed_at: '2026-06-12T10:02:00.000Z', turn_count: 1 },
522
+ },
523
+ },
524
+ 'sess-critic': {
525
+ session_id: 'sess-critic',
526
+ leader_thread_id: 'thread-leader-c',
527
+ updated_at: '2026-06-12T10:03:00.000Z',
528
+ threads: {
529
+ 'thread-leader-c': { thread_id: 'thread-leader-c', kind: 'leader', first_seen_at: '2026-06-12T09:59:00.000Z', last_seen_at: '2026-06-12T09:59:00.000Z', turn_count: 1 },
530
+ 'thread-critic': { thread_id: 'thread-critic', kind: 'subagent', first_seen_at: '2026-06-12T10:03:00.000Z', last_seen_at: '2026-06-12T10:03:00.000Z', completed_at: '2026-06-12T10:03:00.000Z', turn_count: 1 },
531
+ },
532
+ },
533
+ },
534
+ }, null, 2));
535
+ const state = {
536
+ current_phase: 'ralplan',
537
+ handoff_artifacts: {
538
+ ralplan_consensus_gate: {
539
+ complete: true,
540
+ sequence: ['architect-review', 'critic-review'],
541
+ ralplan_architect_review: {
542
+ agent_role: 'architect',
543
+ provenance_kind: 'native_subagent',
544
+ verdict: 'approve',
545
+ session_id: 'sess-architect',
546
+ thread_id: 'thread-architect',
547
+ completed_at: '2026-06-12T10:02:00.000Z',
548
+ },
549
+ ralplan_critic_review: {
550
+ agent_role: 'critic',
551
+ provenance_kind: 'native_subagent',
552
+ verdict: 'approve',
553
+ session_id: 'sess-critic',
554
+ thread_id: 'thread-critic',
555
+ completed_at: '2026-06-12T10:03:00.000Z',
556
+ },
557
+ },
558
+ },
559
+ };
560
+ const decision = canAdvanceAutopilotRalplanToUltragoal({ cwd, currentState: state });
561
+ assert.equal(decision.allowed, false);
562
+ assert.match(buildAutopilotRalplanUltragoalGateError(decision), /same native subagent tracker session/);
563
+ }
564
+ finally {
565
+ await rm(cwd, { recursive: true, force: true });
566
+ }
567
+ });
568
+ it('rejects stale review session ids even when transition session context exists', async () => {
569
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-autopilot-ralplan-live-session-mismatch-'));
570
+ const sessionId = 'sess-autopilot-live-session';
571
+ const trackingPath = subagentTrackingPath(cwd);
572
+ try {
573
+ await mkdir(join(trackingPath, '..'), { recursive: true });
574
+ await writeFile(trackingPath, JSON.stringify({
575
+ schemaVersion: 1,
576
+ sessions: {
577
+ [sessionId]: {
578
+ session_id: sessionId,
579
+ leader_thread_id: 'thread-leader',
580
+ updated_at: '2026-06-12T10:03:00.000Z',
581
+ threads: {
582
+ 'thread-leader': { thread_id: 'thread-leader', kind: 'leader', first_seen_at: '2026-06-12T09:59:00.000Z', last_seen_at: '2026-06-12T09:59:00.000Z', turn_count: 1 },
583
+ 'thread-architect': { thread_id: 'thread-architect', kind: 'subagent', first_seen_at: '2026-06-12T10:02:00.000Z', last_seen_at: '2026-06-12T10:02:00.000Z', completed_at: '2026-06-12T10:02:00.000Z', turn_count: 1 },
584
+ 'thread-critic': { thread_id: 'thread-critic', kind: 'subagent', first_seen_at: '2026-06-12T10:03:00.000Z', last_seen_at: '2026-06-12T10:03:00.000Z', completed_at: '2026-06-12T10:03:00.000Z', turn_count: 1 },
585
+ },
586
+ },
587
+ },
588
+ }, null, 2));
589
+ const state = {
590
+ current_phase: 'ralplan',
591
+ handoff_artifacts: {
592
+ ralplan_consensus_gate: {
593
+ complete: true,
594
+ sequence: ['architect-review', 'critic-review'],
595
+ ralplan_architect_review: {
596
+ agent_role: 'architect',
597
+ provenance_kind: 'native_subagent',
598
+ verdict: 'approve',
599
+ thread_id: 'thread-architect',
600
+ completed_at: '2026-06-12T10:02:00.000Z',
601
+ },
602
+ ralplan_critic_review: {
603
+ agent_role: 'critic',
604
+ provenance_kind: 'native_subagent',
605
+ verdict: 'approve',
606
+ session_id: 'sess-stale-critic',
607
+ thread_id: 'thread-critic',
608
+ completed_at: '2026-06-12T10:03:00.000Z',
609
+ },
610
+ },
611
+ },
612
+ };
613
+ const decision = canAdvanceAutopilotRalplanToUltragoal({ cwd, sessionId, currentState: state });
614
+ assert.equal(decision.allowed, false);
615
+ assert.match(buildAutopilotRalplanUltragoalGateError(decision), /critic review session_id=sess-stale-critic does not match/);
616
+ }
617
+ finally {
618
+ await rm(cwd, { recursive: true, force: true });
619
+ }
620
+ });
621
+ it('rejects tracker-backed native reviews whose subagent threads are not completed', async () => {
622
+ const cwd = await mkdtemp(join(tmpdir(), 'omx-autopilot-ralplan-incomplete-thread-'));
623
+ const sessionId = 'sess-autopilot-incomplete-thread';
624
+ const trackingPath = subagentTrackingPath(cwd);
625
+ try {
626
+ await mkdir(join(trackingPath, '..'), { recursive: true });
627
+ await writeFile(trackingPath, JSON.stringify({
628
+ schemaVersion: 1,
629
+ sessions: {
630
+ [sessionId]: {
631
+ session_id: sessionId,
632
+ leader_thread_id: 'thread-leader',
633
+ updated_at: '2026-06-12T10:03:00.000Z',
634
+ threads: {
635
+ 'thread-leader': { thread_id: 'thread-leader', kind: 'leader', first_seen_at: '2026-06-12T09:59:00.000Z', last_seen_at: '2026-06-12T09:59:00.000Z', turn_count: 1 },
636
+ 'thread-architect': { thread_id: 'thread-architect', kind: 'subagent', first_seen_at: '2026-06-12T10:02:00.000Z', last_seen_at: '2026-06-12T10:02:00.000Z', turn_count: 1 },
637
+ 'thread-critic': { thread_id: 'thread-critic', kind: 'subagent', first_seen_at: '2026-06-12T10:03:00.000Z', last_seen_at: '2026-06-12T10:03:00.000Z', completed_at: '2026-06-12T10:03:00.000Z', turn_count: 1 },
638
+ },
639
+ },
640
+ },
641
+ }, null, 2));
642
+ const state = {
643
+ current_phase: 'ralplan',
644
+ handoff_artifacts: {
645
+ ralplan_consensus_gate: {
646
+ complete: true,
647
+ sequence: ['architect-review', 'critic-review'],
648
+ ralplan_architect_review: {
649
+ agent_role: 'architect',
650
+ provenance_kind: 'native_subagent',
651
+ verdict: 'approve',
652
+ session_id: sessionId,
653
+ thread_id: 'thread-architect',
654
+ completed_at: '2026-06-12T10:02:00.000Z',
655
+ },
656
+ ralplan_critic_review: {
657
+ agent_role: 'critic',
658
+ provenance_kind: 'native_subagent',
659
+ verdict: 'approve',
660
+ session_id: sessionId,
661
+ thread_id: 'thread-critic',
662
+ completed_at: '2026-06-12T10:03:00.000Z',
663
+ },
664
+ },
665
+ },
666
+ };
667
+ const decision = canAdvanceAutopilotRalplanToUltragoal({ cwd, sessionId, currentState: state });
668
+ assert.equal(decision.allowed, false);
669
+ assert.match(buildAutopilotRalplanUltragoalGateError(decision), /architect tracker thread thread-architect is not completed/);
670
+ }
671
+ finally {
672
+ await rm(cwd, { recursive: true, force: true });
673
+ }
674
+ });
56
675
  it('rejects native review evidence from the session leader even when malformed tracking marks it as subagent', async () => {
57
676
  const cwd = await mkdtemp(join(tmpdir(), 'omx-autopilot-ralplan-leader-spoof-'));
58
677
  const sessionId = 'sess-autopilot-leader-spoof';
@@ -145,6 +764,7 @@ describe('autopilot ralplan gate', () => {
145
764
  kind: 'subagent',
146
765
  first_seen_at: '2026-05-28T18:34:51.000Z',
147
766
  last_seen_at: '2026-05-28T18:34:51.000Z',
767
+ completed_at: '2026-05-28T18:34:51.000Z',
148
768
  turn_count: 1,
149
769
  mode: 'architect',
150
770
  },
@@ -153,6 +773,7 @@ describe('autopilot ralplan gate', () => {
153
773
  kind: 'subagent',
154
774
  first_seen_at: '2026-05-28T18:35:10.000Z',
155
775
  last_seen_at: '2026-05-28T18:35:10.000Z',
776
+ completed_at: '2026-05-28T18:35:10.000Z',
156
777
  turn_count: 1,
157
778
  mode: 'critic',
158
779
  },