oh-my-codex 0.18.1 → 0.18.2
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.
- package/Cargo.lock +6 -6
- package/Cargo.toml +1 -1
- package/README.md +4 -2
- package/dist/agents/__tests__/definitions.test.js +14 -0
- package/dist/agents/__tests__/definitions.test.js.map +1 -1
- package/dist/agents/__tests__/native-config.test.js +19 -0
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/definitions.d.ts.map +1 -1
- package/dist/agents/definitions.js +30 -0
- package/dist/agents/definitions.js.map +1 -1
- package/dist/agents/native-config.d.ts +1 -0
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +4 -0
- package/dist/agents/native-config.js.map +1 -1
- package/dist/catalog/__tests__/generator.test.js +4 -0
- package/dist/catalog/__tests__/generator.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-warning-copy.test.js +61 -5
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +161 -21
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/launch-fallback.test.js +51 -3
- package/dist/cli/__tests__/launch-fallback.test.js.map +1 -1
- package/dist/cli/__tests__/question.test.js +2 -2
- package/dist/cli/__tests__/question.test.js.map +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +178 -7
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +7 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +143 -43
- package/dist/cli/index.js.map +1 -1
- package/dist/config/__tests__/codex-hooks.test.js +3 -3
- package/dist/config/__tests__/codex-hooks.test.js.map +1 -1
- package/dist/config/codex-hooks.d.ts +1 -0
- package/dist/config/codex-hooks.d.ts.map +1 -1
- package/dist/config/codex-hooks.js +2 -4
- package/dist/config/codex-hooks.js.map +1 -1
- package/dist/config/generator.d.ts +14 -0
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +100 -1
- package/dist/config/generator.js.map +1 -1
- package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js +21 -0
- package/dist/goal-workflows/__tests__/codex-goal-snapshot.test.js.map +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.d.ts +3 -0
- package/dist/goal-workflows/codex-goal-snapshot.d.ts.map +1 -1
- package/dist/goal-workflows/codex-goal-snapshot.js +45 -2
- package/dist/goal-workflows/codex-goal-snapshot.js.map +1 -1
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js +17 -0
- package/dist/hooks/__tests__/autopilot-skill-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +170 -15
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/prometheus-strict-contract.test.d.ts +2 -0
- package/dist/hooks/__tests__/prometheus-strict-contract.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/prometheus-strict-contract.test.js +320 -0
- package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -0
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js +12 -0
- package/dist/hooks/__tests__/prompt-guidance-wave-two.test.js.map +1 -1
- package/dist/hooks/__tests__/research-workflow-boundaries.test.d.ts +2 -0
- package/dist/hooks/__tests__/research-workflow-boundaries.test.d.ts.map +1 -0
- package/dist/hooks/__tests__/research-workflow-boundaries.test.js +35 -0
- package/dist/hooks/__tests__/research-workflow-boundaries.test.js.map +1 -0
- package/dist/hooks/keyword-detector.d.ts +1 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +28 -6
- package/dist/hooks/keyword-detector.js.map +1 -1
- package/dist/hooks/keyword-registry.d.ts.map +1 -1
- package/dist/hooks/keyword-registry.js +1 -0
- package/dist/hooks/keyword-registry.js.map +1 -1
- package/dist/hooks/prompt-guidance-contract.d.ts.map +1 -1
- package/dist/hooks/prompt-guidance-contract.js +11 -0
- package/dist/hooks/prompt-guidance-contract.js.map +1 -1
- package/dist/hud/__tests__/hud-tmux-injection.test.js +22 -0
- package/dist/hud/__tests__/hud-tmux-injection.test.js.map +1 -1
- package/dist/hud/__tests__/reconcile.test.js +121 -10
- package/dist/hud/__tests__/reconcile.test.js.map +1 -1
- package/dist/hud/__tests__/render.test.js +84 -0
- package/dist/hud/__tests__/render.test.js.map +1 -1
- package/dist/hud/__tests__/state.test.js +51 -1
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/__tests__/tmux.test.js +69 -23
- package/dist/hud/__tests__/tmux.test.js.map +1 -1
- package/dist/hud/index.d.ts +1 -1
- package/dist/hud/index.d.ts.map +1 -1
- package/dist/hud/index.js +8 -3
- package/dist/hud/index.js.map +1 -1
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +6 -3
- package/dist/hud/reconcile.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +26 -0
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/state.d.ts +2 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +62 -1
- package/dist/hud/state.js.map +1 -1
- package/dist/hud/tmux.d.ts +10 -3
- package/dist/hud/tmux.d.ts.map +1 -1
- package/dist/hud/tmux.js +59 -10
- package/dist/hud/tmux.js.map +1 -1
- package/dist/hud/types.d.ts +22 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js.map +1 -1
- package/dist/pipeline/__tests__/orchestrator.test.js +63 -1
- package/dist/pipeline/__tests__/orchestrator.test.js.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +410 -4
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/pipeline/orchestrator.d.ts.map +1 -1
- package/dist/pipeline/orchestrator.js +29 -2
- package/dist/pipeline/orchestrator.js.map +1 -1
- package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
- package/dist/pipeline/stages/ralplan.js +41 -6
- package/dist/pipeline/stages/ralplan.js.map +1 -1
- package/dist/question/__tests__/ui.test.js +43 -10
- package/dist/question/__tests__/ui.test.js.map +1 -1
- package/dist/question/ui.d.ts +12 -0
- package/dist/question/ui.d.ts.map +1 -1
- package/dist/question/ui.js +83 -46
- package/dist/question/ui.js.map +1 -1
- package/dist/ralplan/__tests__/runtime.test.js +200 -10
- package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
- package/dist/ralplan/consensus-gate.d.ts +23 -0
- package/dist/ralplan/consensus-gate.d.ts.map +1 -0
- package/dist/ralplan/consensus-gate.js +212 -0
- package/dist/ralplan/consensus-gate.js.map +1 -0
- package/dist/ralplan/runtime.d.ts +25 -0
- package/dist/ralplan/runtime.d.ts.map +1 -1
- package/dist/ralplan/runtime.js +144 -8
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +626 -7
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/__tests__/docs-site-contract.test.d.ts +2 -0
- package/dist/scripts/__tests__/docs-site-contract.test.d.ts.map +1 -0
- package/dist/scripts/__tests__/docs-site-contract.test.js +42 -0
- package/dist/scripts/__tests__/docs-site-contract.test.js.map +1 -0
- package/dist/scripts/__tests__/notify-dispatcher.test.js +115 -2
- package/dist/scripts/__tests__/notify-dispatcher.test.js.map +1 -1
- package/dist/scripts/__tests__/run-test-files.test.js +57 -0
- package/dist/scripts/__tests__/run-test-files.test.js.map +1 -1
- package/dist/scripts/__tests__/verify-native-agents.test.js +2 -2
- package/dist/scripts/__tests__/verify-native-agents.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +214 -34
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/notify-dispatcher.js +188 -4
- package/dist/scripts/notify-dispatcher.js.map +1 -1
- package/dist/scripts/run-test-files.js +13 -0
- package/dist/scripts/run-test-files.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +6 -0
- package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
- package/dist/state/workflow-transition.d.ts +1 -1
- package/dist/state/workflow-transition.d.ts.map +1 -1
- package/dist/state/workflow-transition.js +7 -0
- package/dist/state/workflow-transition.js.map +1 -1
- package/dist/subagents/tracker.d.ts.map +1 -1
- package/dist/subagents/tracker.js +4 -3
- package/dist/subagents/tracker.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +36 -44
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +58 -18
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +10 -20
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/tmux-session.d.ts.map +1 -1
- package/dist/team/tmux-session.js +15 -6
- package/dist/team/tmux-session.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +50 -0
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +28 -2
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/package.json +1 -1
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/skills/autopilot/SKILL.md +16 -4
- package/plugins/oh-my-codex/skills/autoresearch/SKILL.md +4 -0
- package/plugins/oh-my-codex/skills/autoresearch-goal/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/best-practice-research/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/pipeline/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/plan/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/prometheus-strict/README.md +35 -0
- package/plugins/oh-my-codex/skills/prometheus-strict/SKILL.md +219 -0
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +18 -3
- package/prompts/prometheus-strict-metis.md +274 -0
- package/prompts/prometheus-strict-momus.md +82 -0
- package/prompts/prometheus-strict-oracle.md +107 -0
- package/prompts/researcher.md +22 -3
- package/skills/autopilot/SKILL.md +16 -4
- package/skills/autoresearch/SKILL.md +4 -0
- package/skills/autoresearch-goal/SKILL.md +1 -1
- package/skills/best-practice-research/SKILL.md +1 -1
- package/skills/pipeline/SKILL.md +1 -1
- package/skills/plan/SKILL.md +1 -1
- package/skills/prometheus-strict/README.md +35 -0
- package/skills/prometheus-strict/SKILL.md +219 -0
- package/skills/ralplan/SKILL.md +18 -3
- package/src/scripts/__tests__/codex-native-hook.test.ts +769 -8
- package/src/scripts/__tests__/docs-site-contract.test.ts +47 -0
- package/src/scripts/__tests__/notify-dispatcher.test.ts +132 -3
- package/src/scripts/__tests__/run-test-files.test.ts +67 -0
- package/src/scripts/__tests__/verify-native-agents.test.ts +2 -2
- package/src/scripts/codex-native-hook.ts +237 -30
- package/src/scripts/notify-dispatcher.ts +202 -4
- package/src/scripts/run-test-files.ts +13 -0
- package/templates/catalog-manifest.json +22 -0
|
@@ -97,12 +97,13 @@ describe('RALPLAN Stage', () => {
|
|
|
97
97
|
const stage = createRalplanStage();
|
|
98
98
|
assert.equal(stage.name, 'ralplan');
|
|
99
99
|
});
|
|
100
|
-
it('
|
|
100
|
+
it('fails closed without planning artifacts and consensus evidence', async () => {
|
|
101
101
|
const stage = createRalplanStage();
|
|
102
102
|
const result = await stage.run(makeCtx());
|
|
103
|
-
assert.equal(result.status, '
|
|
103
|
+
assert.equal(result.status, 'failed');
|
|
104
104
|
assert.equal(result.artifacts.stage, 'ralplan');
|
|
105
105
|
assert.ok(result.artifacts.instruction);
|
|
106
|
+
assert.equal(result.error, 'ralplan_planning_artifacts_missing');
|
|
106
107
|
});
|
|
107
108
|
it('canSkip returns false when no plans directory exists', () => {
|
|
108
109
|
const stage = createRalplanStage();
|
|
@@ -121,13 +122,329 @@ describe('RALPLAN Stage', () => {
|
|
|
121
122
|
const stage = createRalplanStage();
|
|
122
123
|
assert.equal(stage.canSkip(makeCtx()), false);
|
|
123
124
|
});
|
|
124
|
-
it('canSkip returns
|
|
125
|
+
it('canSkip returns false when only prd and test spec plan files exist without consensus evidence', async () => {
|
|
126
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
127
|
+
await mkdir(plansDir, { recursive: true });
|
|
128
|
+
await writeFile(join(plansDir, 'prd-my-feature.md'), '# Plan\n');
|
|
129
|
+
await writeFile(join(plansDir, 'test-spec-my-feature.md'), '# Test Spec\n');
|
|
130
|
+
const stage = createRalplanStage();
|
|
131
|
+
assert.equal(stage.canSkip(makeCtx()), false);
|
|
132
|
+
});
|
|
133
|
+
it('run fails with consensus-specific artifact error when consensus exists but planning artifacts are missing', async () => {
|
|
134
|
+
const stage = createRalplanStage();
|
|
135
|
+
const result = await stage.run(makeCtx({
|
|
136
|
+
artifacts: {
|
|
137
|
+
ralplan: {
|
|
138
|
+
ralplanConsensusGate: {
|
|
139
|
+
complete: true,
|
|
140
|
+
sequence: ['architect-review', 'critic-review'],
|
|
141
|
+
ralplan_architect_review: { agent_role: 'architect', verdict: 'approve' },
|
|
142
|
+
ralplan_critic_review: { agent_role: 'critic', verdict: 'approve' },
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
}));
|
|
147
|
+
assert.equal(result.status, 'failed');
|
|
148
|
+
assert.equal(result.error, 'ralplan_planning_artifacts_missing_after_consensus');
|
|
149
|
+
assert.equal(result.artifacts.planningComplete, false);
|
|
150
|
+
});
|
|
151
|
+
it('canSkip returns true only when planning artifacts have sequential Architect and Critic approval evidence', async () => {
|
|
152
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
153
|
+
await mkdir(plansDir, { recursive: true });
|
|
154
|
+
await writeFile(join(plansDir, 'prd-my-feature.md'), '# Plan\n');
|
|
155
|
+
await writeFile(join(plansDir, 'test-spec-my-feature.md'), '# Test Spec\n');
|
|
156
|
+
const stage = createRalplanStage();
|
|
157
|
+
assert.equal(stage.canSkip(makeCtx({
|
|
158
|
+
artifacts: {
|
|
159
|
+
ralplan: {
|
|
160
|
+
ralplanConsensusGate: {
|
|
161
|
+
complete: true,
|
|
162
|
+
ralplan_architect_review: { agent_role: 'architect', verdict: 'approve', summary: 'architect approved' },
|
|
163
|
+
ralplan_critic_review: { agent_role: 'critic', verdict: 'approve', summary: 'critic approved after architect' },
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
},
|
|
167
|
+
})), true);
|
|
168
|
+
});
|
|
169
|
+
it('canSkip honors explicit session-scoped consensus state before root state', async () => {
|
|
170
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
171
|
+
const stateDir = join(tempDir, '.omx', 'state');
|
|
172
|
+
const sessionDir = join(stateDir, 'sessions', 'sess-explicit');
|
|
173
|
+
await mkdir(plansDir, { recursive: true });
|
|
174
|
+
await mkdir(sessionDir, { recursive: true });
|
|
175
|
+
await writeFile(join(plansDir, 'prd-my-feature.md'), '# Plan\n');
|
|
176
|
+
await writeFile(join(plansDir, 'test-spec-my-feature.md'), '# Test Spec\n');
|
|
177
|
+
await writeFile(join(stateDir, 'autopilot-state.json'), JSON.stringify({
|
|
178
|
+
state: {
|
|
179
|
+
handoff_artifacts: {
|
|
180
|
+
ralplan_architect_review: { agent_role: 'architect', verdict: 'reject', approved: true },
|
|
181
|
+
ralplan_critic_review: { agent_role: 'critic', verdict: 'approve' },
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
}));
|
|
185
|
+
await writeFile(join(sessionDir, 'autopilot-state.json'), JSON.stringify({
|
|
186
|
+
state: {
|
|
187
|
+
handoff_artifacts: {
|
|
188
|
+
ralplan_architect_review: { agent_role: 'architect', verdict: 'approve' },
|
|
189
|
+
ralplan_critic_review: { agent_role: 'critic', verdict: 'approve' },
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
}));
|
|
193
|
+
const stage = createRalplanStage();
|
|
194
|
+
assert.equal(stage.canSkip(makeCtx({ sessionId: 'sess-explicit' })), true);
|
|
195
|
+
});
|
|
196
|
+
it('canSkip fails closed when explicit session state is missing despite root consensus', async () => {
|
|
197
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
198
|
+
const stateDir = join(tempDir, '.omx', 'state');
|
|
199
|
+
await mkdir(plansDir, { recursive: true });
|
|
200
|
+
await mkdir(stateDir, { recursive: true });
|
|
201
|
+
await writeFile(join(plansDir, 'prd-my-feature.md'), '# Plan\n');
|
|
202
|
+
await writeFile(join(plansDir, 'test-spec-my-feature.md'), '# Test Spec\n');
|
|
203
|
+
await writeFile(join(stateDir, 'autopilot-state.json'), JSON.stringify({
|
|
204
|
+
state: {
|
|
205
|
+
handoff_artifacts: {
|
|
206
|
+
ralplan_architect_review: { agent_role: 'architect', verdict: 'approve' },
|
|
207
|
+
ralplan_critic_review: { agent_role: 'critic', verdict: 'approve' },
|
|
208
|
+
},
|
|
209
|
+
},
|
|
210
|
+
}));
|
|
211
|
+
const stage = createRalplanStage();
|
|
212
|
+
assert.equal(stage.canSkip(makeCtx({ sessionId: 'sess-missing' })), false);
|
|
213
|
+
});
|
|
214
|
+
it('canSkip fails closed for malformed explicit session ids instead of falling back to root consensus', async () => {
|
|
215
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
216
|
+
const stateDir = join(tempDir, '.omx', 'state');
|
|
217
|
+
await mkdir(plansDir, { recursive: true });
|
|
218
|
+
await mkdir(stateDir, { recursive: true });
|
|
219
|
+
await writeFile(join(plansDir, 'prd-my-feature.md'), '# Plan\n');
|
|
220
|
+
await writeFile(join(plansDir, 'test-spec-my-feature.md'), '# Test Spec\n');
|
|
221
|
+
await writeFile(join(stateDir, 'ralplan-state.json'), JSON.stringify({
|
|
222
|
+
ralplanConsensusGate: {
|
|
223
|
+
complete: true,
|
|
224
|
+
sequence: ['architect-review', 'critic-review'],
|
|
225
|
+
ralplan_architect_review: { agent_role: 'architect', verdict: 'approve' },
|
|
226
|
+
ralplan_critic_review: { agent_role: 'critic', verdict: 'approve' },
|
|
227
|
+
},
|
|
228
|
+
}));
|
|
229
|
+
const stage = createRalplanStage();
|
|
230
|
+
for (const sessionId of ['../bad', 'a'.repeat(65), '']) {
|
|
231
|
+
assert.equal(stage.canSkip(makeCtx({ sessionId })), false);
|
|
232
|
+
}
|
|
233
|
+
});
|
|
234
|
+
it('canSkip rejects blocker aliases even with approval-shaped booleans', async () => {
|
|
235
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
236
|
+
await mkdir(plansDir, { recursive: true });
|
|
237
|
+
await writeFile(join(plansDir, 'prd-my-feature.md'), '# Plan\n');
|
|
238
|
+
await writeFile(join(plansDir, 'test-spec-my-feature.md'), '# Test Spec\n');
|
|
239
|
+
const stage = createRalplanStage();
|
|
240
|
+
for (const blocker of [
|
|
241
|
+
{ blocking: true },
|
|
242
|
+
{ request_changes: true },
|
|
243
|
+
{ requestChanges: true },
|
|
244
|
+
{ status: 'request changes' },
|
|
245
|
+
{ recommendation: 'changes-requested' },
|
|
246
|
+
]) {
|
|
247
|
+
assert.equal(stage.canSkip(makeCtx({
|
|
248
|
+
artifacts: {
|
|
249
|
+
ralplan: {
|
|
250
|
+
ralplanConsensusGate: {
|
|
251
|
+
complete: true,
|
|
252
|
+
ralplan_architect_review: { agent_role: 'architect', verdict: 'approve' },
|
|
253
|
+
ralplan_critic_review: { agent_role: 'critic', approved: true, clean: true, ...blocker },
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
})), false);
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
it('canSkip returns false when Critic evidence is recorded before Architect evidence', async () => {
|
|
261
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
262
|
+
await mkdir(plansDir, { recursive: true });
|
|
263
|
+
await writeFile(join(plansDir, 'prd-my-feature.md'), '# Plan\n');
|
|
264
|
+
await writeFile(join(plansDir, 'test-spec-my-feature.md'), '# Test Spec\n');
|
|
265
|
+
const stage = createRalplanStage();
|
|
266
|
+
assert.equal(stage.canSkip(makeCtx({
|
|
267
|
+
artifacts: {
|
|
268
|
+
ralplan: {
|
|
269
|
+
ralplanConsensusGate: {
|
|
270
|
+
complete: true,
|
|
271
|
+
sequence: ['critic-review', 'architect-review'],
|
|
272
|
+
ralplan_architect_review: { agent_role: 'architect', verdict: 'approve' },
|
|
273
|
+
ralplan_critic_review: { agent_role: 'critic', verdict: 'approve' },
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
})), false);
|
|
278
|
+
});
|
|
279
|
+
it('canSkip returns false when Critic timestamp predates Architect timestamp', async () => {
|
|
280
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
281
|
+
await mkdir(plansDir, { recursive: true });
|
|
282
|
+
await writeFile(join(plansDir, 'prd-my-feature.md'), '# Plan\n');
|
|
283
|
+
await writeFile(join(plansDir, 'test-spec-my-feature.md'), '# Test Spec\n');
|
|
284
|
+
const stage = createRalplanStage();
|
|
285
|
+
assert.equal(stage.canSkip(makeCtx({
|
|
286
|
+
artifacts: {
|
|
287
|
+
ralplan: {
|
|
288
|
+
ralplanConsensusGate: {
|
|
289
|
+
complete: true,
|
|
290
|
+
sequence: ['architect-review', 'critic-review'],
|
|
291
|
+
ralplan_architect_review: {
|
|
292
|
+
agent_role: 'architect',
|
|
293
|
+
verdict: 'approve',
|
|
294
|
+
completed_at: '2026-05-21T10:05:00.000Z',
|
|
295
|
+
},
|
|
296
|
+
ralplan_critic_review: {
|
|
297
|
+
agent_role: 'critic',
|
|
298
|
+
verdict: 'approve',
|
|
299
|
+
completed_at: '2026-05-21T10:00:00.000Z',
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
},
|
|
304
|
+
})), false);
|
|
305
|
+
});
|
|
306
|
+
it('canSkip ignores ambient OMX_ROOT consensus state for local PRD/test-spec-only artifacts', async () => {
|
|
307
|
+
const ambientRoot = await mkdtemp(join(tmpdir(), 'omx-ralplan-ambient-'));
|
|
308
|
+
const previousOmxRoot = process.env.OMX_ROOT;
|
|
309
|
+
try {
|
|
310
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
311
|
+
await mkdir(plansDir, { recursive: true });
|
|
312
|
+
await writeFile(join(plansDir, 'prd-local.md'), '# Plan\n');
|
|
313
|
+
await writeFile(join(plansDir, 'test-spec-local.md'), '# Test Spec\n');
|
|
314
|
+
const ambientStateDir = join(ambientRoot, '.omx', 'state');
|
|
315
|
+
await mkdir(ambientStateDir, { recursive: true });
|
|
316
|
+
await writeFile(join(ambientStateDir, 'ralplan-state.json'), JSON.stringify({
|
|
317
|
+
current_phase: 'complete',
|
|
318
|
+
planning_complete: true,
|
|
319
|
+
ralplan_consensus_gate: {
|
|
320
|
+
complete: true,
|
|
321
|
+
ralplan_architect_review: { agent_role: 'architect', verdict: 'approve', iteration: 1 },
|
|
322
|
+
ralplan_critic_review: { agent_role: 'critic', verdict: 'approve', iteration: 1 },
|
|
323
|
+
},
|
|
324
|
+
}));
|
|
325
|
+
process.env.OMX_ROOT = ambientRoot;
|
|
326
|
+
const stage = createRalplanStage();
|
|
327
|
+
assert.equal(stage.canSkip(makeCtx()), false);
|
|
328
|
+
}
|
|
329
|
+
finally {
|
|
330
|
+
if (previousOmxRoot === undefined)
|
|
331
|
+
delete process.env.OMX_ROOT;
|
|
332
|
+
else
|
|
333
|
+
process.env.OMX_ROOT = previousOmxRoot;
|
|
334
|
+
await rm(ambientRoot, { recursive: true, force: true });
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
it('canSkip returns false for rejected consensus objects with approval-shaped booleans', async () => {
|
|
338
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
339
|
+
await mkdir(plansDir, { recursive: true });
|
|
340
|
+
await writeFile(join(plansDir, 'prd-my-feature.md'), '# Plan\n');
|
|
341
|
+
await writeFile(join(plansDir, 'test-spec-my-feature.md'), '# Test Spec\n');
|
|
342
|
+
const stage = createRalplanStage();
|
|
343
|
+
assert.equal(stage.canSkip(makeCtx({
|
|
344
|
+
artifacts: {
|
|
345
|
+
ralplan: {
|
|
346
|
+
ralplanConsensusGate: {
|
|
347
|
+
complete: true,
|
|
348
|
+
ralplan_architect_review: {
|
|
349
|
+
agent_role: 'architect',
|
|
350
|
+
verdict: 'reject',
|
|
351
|
+
approved: true,
|
|
352
|
+
clean: true,
|
|
353
|
+
},
|
|
354
|
+
ralplan_critic_review: {
|
|
355
|
+
agent_role: 'critic',
|
|
356
|
+
verdict: 'approve',
|
|
357
|
+
approved: true,
|
|
358
|
+
clean: true,
|
|
359
|
+
},
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
},
|
|
363
|
+
})), false);
|
|
364
|
+
});
|
|
365
|
+
it('canSkip returns false when consensus-shaped reviews do not record agent roles', async () => {
|
|
366
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
367
|
+
await mkdir(plansDir, { recursive: true });
|
|
368
|
+
await writeFile(join(plansDir, 'prd-my-feature.md'), '# Plan\n');
|
|
369
|
+
await writeFile(join(plansDir, 'test-spec-my-feature.md'), '# Test Spec\n');
|
|
370
|
+
const stage = createRalplanStage();
|
|
371
|
+
assert.equal(stage.canSkip(makeCtx({
|
|
372
|
+
artifacts: {
|
|
373
|
+
ralplan: {
|
|
374
|
+
ralplanConsensusGate: {
|
|
375
|
+
complete: true,
|
|
376
|
+
ralplan_architect_review: { verdict: 'approve', summary: 'role missing' },
|
|
377
|
+
ralplan_critic_review: { verdict: 'approve', summary: 'role missing' },
|
|
378
|
+
},
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
})), false);
|
|
382
|
+
});
|
|
383
|
+
it('canSkip returns false when review history entries do not record agent roles', async () => {
|
|
384
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
385
|
+
await mkdir(plansDir, { recursive: true });
|
|
386
|
+
await writeFile(join(plansDir, 'prd-my-feature.md'), '# Plan\n');
|
|
387
|
+
await writeFile(join(plansDir, 'test-spec-my-feature.md'), '# Test Spec\n');
|
|
388
|
+
const stage = createRalplanStage();
|
|
389
|
+
assert.equal(stage.canSkip(makeCtx({
|
|
390
|
+
artifacts: {
|
|
391
|
+
ralplan: {
|
|
392
|
+
review_history: [{
|
|
393
|
+
architect_review: { verdict: 'approve', summary: 'role missing' },
|
|
394
|
+
critic_review: { verdict: 'approve', summary: 'role missing' },
|
|
395
|
+
}],
|
|
396
|
+
},
|
|
397
|
+
},
|
|
398
|
+
})), false);
|
|
399
|
+
});
|
|
400
|
+
it('canSkip returns false when review arrays do not record agent roles', async () => {
|
|
401
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
402
|
+
await mkdir(plansDir, { recursive: true });
|
|
403
|
+
await writeFile(join(plansDir, 'prd-my-feature.md'), '# Plan\n');
|
|
404
|
+
await writeFile(join(plansDir, 'test-spec-my-feature.md'), '# Test Spec\n');
|
|
405
|
+
const stage = createRalplanStage();
|
|
406
|
+
assert.equal(stage.canSkip(makeCtx({
|
|
407
|
+
artifacts: {
|
|
408
|
+
ralplan: {
|
|
409
|
+
architectReviews: [{ verdict: 'approve', summary: 'role missing' }],
|
|
410
|
+
criticReviews: [{ verdict: 'approve', summary: 'role missing' }],
|
|
411
|
+
},
|
|
412
|
+
},
|
|
413
|
+
})), false);
|
|
414
|
+
});
|
|
415
|
+
it('canSkip returns false when local state only has latest verdict fields', async () => {
|
|
416
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
417
|
+
const stateDir = join(tempDir, '.omx', 'state');
|
|
418
|
+
await mkdir(plansDir, { recursive: true });
|
|
419
|
+
await mkdir(stateDir, { recursive: true });
|
|
420
|
+
await writeFile(join(plansDir, 'prd-my-feature.md'), '# Plan\n');
|
|
421
|
+
await writeFile(join(plansDir, 'test-spec-my-feature.md'), '# Test Spec\n');
|
|
422
|
+
await writeFile(join(stateDir, 'ralplan-state.json'), JSON.stringify({
|
|
423
|
+
current_phase: 'complete',
|
|
424
|
+
planning_complete: true,
|
|
425
|
+
latest_architect_verdict: 'approve',
|
|
426
|
+
latest_critic_verdict: 'approve',
|
|
427
|
+
}));
|
|
428
|
+
const stage = createRalplanStage();
|
|
429
|
+
assert.equal(stage.canSkip(makeCtx()), false);
|
|
430
|
+
});
|
|
431
|
+
it('canSkip returns false when Architect and Critic roles are swapped', async () => {
|
|
125
432
|
const plansDir = join(tempDir, '.omx', 'plans');
|
|
126
433
|
await mkdir(plansDir, { recursive: true });
|
|
127
434
|
await writeFile(join(plansDir, 'prd-my-feature.md'), '# Plan\n');
|
|
128
435
|
await writeFile(join(plansDir, 'test-spec-my-feature.md'), '# Test Spec\n');
|
|
129
436
|
const stage = createRalplanStage();
|
|
130
|
-
assert.equal(stage.canSkip(makeCtx(
|
|
437
|
+
assert.equal(stage.canSkip(makeCtx({
|
|
438
|
+
artifacts: {
|
|
439
|
+
ralplan: {
|
|
440
|
+
ralplanConsensusGate: {
|
|
441
|
+
complete: true,
|
|
442
|
+
ralplan_architect_review: { agent_role: 'critic', verdict: 'approve' },
|
|
443
|
+
ralplan_critic_review: { agent_role: 'architect', verdict: 'approve' },
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
})), false);
|
|
131
448
|
});
|
|
132
449
|
it('canSkip returns false after non-clean code-review loopback even when plans exist', async () => {
|
|
133
450
|
const plansDir = join(tempDir, '.omx', 'plans');
|
|
@@ -189,11 +506,100 @@ describe('RALPLAN Stage', () => {
|
|
|
189
506
|
const result = await stage.run(makeCtx({ task: 'live ralplan run' }));
|
|
190
507
|
const artifacts = result.artifacts;
|
|
191
508
|
assert.equal(result.status, 'completed');
|
|
509
|
+
assert.equal(result.error, undefined);
|
|
192
510
|
assert.equal(artifacts.runtime, true);
|
|
193
511
|
assert.equal(artifacts.planningComplete, true);
|
|
512
|
+
assert.deepEqual(artifacts.ralplanConsensusGate, {
|
|
513
|
+
complete: true,
|
|
514
|
+
sequence: ['architect-review', 'critic-review'],
|
|
515
|
+
ralplan_architect_review: { agent_role: 'architect', iteration: 1, verdict: 'approve', summary: 'architect ok' },
|
|
516
|
+
ralplan_critic_review: { agent_role: 'critic', iteration: 1, verdict: 'approve', summary: 'critic ok' },
|
|
517
|
+
source: 'runtime-result',
|
|
518
|
+
blockedReason: null,
|
|
519
|
+
});
|
|
194
520
|
assert.equal(artifacts.iteration, 1);
|
|
195
521
|
assert.equal(artifacts.runtimeDrafted, true);
|
|
196
522
|
});
|
|
523
|
+
it('fails runtime handoff when consensus approves but test spec does not match selected PRD', async () => {
|
|
524
|
+
const stage = createRalplanStage({
|
|
525
|
+
executor: {
|
|
526
|
+
async draft() {
|
|
527
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
528
|
+
await mkdir(plansDir, { recursive: true });
|
|
529
|
+
const prdPath = join(plansDir, 'prd-new.md');
|
|
530
|
+
await writeFile(prdPath, '# New runtime plan\n');
|
|
531
|
+
await writeFile(join(plansDir, 'test-spec-old.md'), '# Stale runtime tests\n');
|
|
532
|
+
return { summary: 'drafted mismatched artifacts', planPath: prdPath };
|
|
533
|
+
},
|
|
534
|
+
async architectReview() {
|
|
535
|
+
return { verdict: 'approve', summary: 'architect ok' };
|
|
536
|
+
},
|
|
537
|
+
async criticReview() {
|
|
538
|
+
return { verdict: 'approve', summary: 'critic ok' };
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
});
|
|
542
|
+
const result = await stage.run(makeCtx({ task: 'live ralplan mismatched artifacts' }));
|
|
543
|
+
const artifacts = result.artifacts;
|
|
544
|
+
assert.equal(result.status, 'failed');
|
|
545
|
+
assert.equal(result.error, 'ralplan_planning_artifacts_missing_after_consensus');
|
|
546
|
+
assert.equal(artifacts.planningComplete, false);
|
|
547
|
+
assert.equal(artifacts.ralplanConsensusGate.complete, true);
|
|
548
|
+
});
|
|
549
|
+
it('fails runtime handoff when consensus approves but required planning artifacts are missing', async () => {
|
|
550
|
+
const stage = createRalplanStage({
|
|
551
|
+
executor: {
|
|
552
|
+
async draft() {
|
|
553
|
+
return { summary: 'draft without files' };
|
|
554
|
+
},
|
|
555
|
+
async architectReview() {
|
|
556
|
+
return { verdict: 'approve', summary: 'architect ok' };
|
|
557
|
+
},
|
|
558
|
+
async criticReview() {
|
|
559
|
+
return { verdict: 'approve', summary: 'critic ok' };
|
|
560
|
+
},
|
|
561
|
+
},
|
|
562
|
+
});
|
|
563
|
+
const result = await stage.run(makeCtx({ task: 'live ralplan no artifacts' }));
|
|
564
|
+
const artifacts = result.artifacts;
|
|
565
|
+
assert.equal(result.status, 'failed');
|
|
566
|
+
assert.equal(result.error, 'ralplan_planning_artifacts_missing_after_consensus');
|
|
567
|
+
assert.equal(artifacts.planningComplete, false);
|
|
568
|
+
assert.equal(artifacts.ralplanConsensusGate.complete, true);
|
|
569
|
+
});
|
|
570
|
+
it('fails runtime handoff when Critic has not approved after Architect', async () => {
|
|
571
|
+
const stage = createRalplanStage({
|
|
572
|
+
executor: {
|
|
573
|
+
async draft() {
|
|
574
|
+
const plansDir = join(tempDir, '.omx', 'plans');
|
|
575
|
+
await mkdir(plansDir, { recursive: true });
|
|
576
|
+
const prdPath = join(plansDir, 'prd-runtime.md');
|
|
577
|
+
await writeFile(prdPath, '# Runtime Plan\n');
|
|
578
|
+
await writeFile(join(plansDir, 'test-spec-runtime.md'), '# Runtime Tests\n');
|
|
579
|
+
return { summary: 'drafted', planPath: prdPath };
|
|
580
|
+
},
|
|
581
|
+
async architectReview() {
|
|
582
|
+
return { verdict: 'approve', summary: 'architect ok' };
|
|
583
|
+
},
|
|
584
|
+
async criticReview() {
|
|
585
|
+
return { verdict: 'iterate', summary: 'critic needs changes' };
|
|
586
|
+
},
|
|
587
|
+
},
|
|
588
|
+
maxIterations: 1,
|
|
589
|
+
});
|
|
590
|
+
const result = await stage.run(makeCtx({ task: 'live ralplan run' }));
|
|
591
|
+
const artifacts = result.artifacts;
|
|
592
|
+
assert.equal(result.status, 'failed');
|
|
593
|
+
assert.equal(result.error, 'ralplan_consensus_not_reached_after_1_iterations');
|
|
594
|
+
assert.deepEqual(artifacts.ralplanConsensusGate, {
|
|
595
|
+
complete: false,
|
|
596
|
+
sequence: ['architect-review', 'critic-review'],
|
|
597
|
+
ralplan_architect_review: null,
|
|
598
|
+
ralplan_critic_review: null,
|
|
599
|
+
source: null,
|
|
600
|
+
blockedReason: 'missing_sequential_architect_then_critic_approval',
|
|
601
|
+
});
|
|
602
|
+
});
|
|
197
603
|
it('canSkip returns false for non-prd plan files', async () => {
|
|
198
604
|
const plansDir = join(tempDir, '.omx', 'plans');
|
|
199
605
|
await mkdir(plansDir, { recursive: true });
|