oh-my-codex 0.19.0 → 0.20.0
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 +8 -1
- package/crates/omx-api/src/lib.rs +3 -3
- package/crates/omx-api/tests/cli.rs +4 -4
- package/crates/omx-explore/src/main.rs +4 -4
- package/crates/omx-sparkshell/src/codex_bridge.rs +2 -2
- package/dist/agents/__tests__/definitions.test.js +3 -3
- package/dist/agents/__tests__/definitions.test.js.map +1 -1
- package/dist/agents/__tests__/native-config.test.js +71 -42
- package/dist/agents/__tests__/native-config.test.js.map +1 -1
- package/dist/agents/definitions.d.ts +1 -1
- package/dist/agents/definitions.d.ts.map +1 -1
- package/dist/agents/definitions.js +3 -3
- package/dist/agents/definitions.js.map +1 -1
- package/dist/agents/native-config.d.ts +2 -2
- package/dist/agents/native-config.d.ts.map +1 -1
- package/dist/agents/native-config.js +7 -5
- package/dist/agents/native-config.js.map +1 -1
- package/dist/autopilot/__tests__/planner-routing.test.js +19 -12
- package/dist/autopilot/__tests__/planner-routing.test.js.map +1 -1
- package/dist/autopilot/__tests__/ralplan-gate.test.js +151 -0
- package/dist/autopilot/__tests__/ralplan-gate.test.js.map +1 -1
- package/dist/autopilot/planner-routing.d.ts.map +1 -1
- package/dist/autopilot/planner-routing.js +4 -2
- package/dist/autopilot/planner-routing.js.map +1 -1
- package/dist/autopilot/ralplan-gate.d.ts +1 -0
- package/dist/autopilot/ralplan-gate.d.ts.map +1 -1
- package/dist/autopilot/ralplan-gate.js +38 -2
- package/dist/autopilot/ralplan-gate.js.map +1 -1
- package/dist/autoresearch/runtime.d.ts +2 -0
- package/dist/autoresearch/runtime.d.ts.map +1 -1
- package/dist/autoresearch/runtime.js +8 -0
- package/dist/autoresearch/runtime.js.map +1 -1
- package/dist/capabilities/__tests__/lockfile.test.d.ts +2 -0
- package/dist/capabilities/__tests__/lockfile.test.d.ts.map +1 -0
- package/dist/capabilities/__tests__/lockfile.test.js +115 -0
- package/dist/capabilities/__tests__/lockfile.test.js.map +1 -0
- package/dist/capabilities/lockfile.d.ts +102 -0
- package/dist/capabilities/lockfile.d.ts.map +1 -0
- package/dist/capabilities/lockfile.js +334 -0
- package/dist/capabilities/lockfile.js.map +1 -0
- package/dist/cli/__tests__/agents.test.js +5 -5
- package/dist/cli/__tests__/agents.test.js.map +1 -1
- package/dist/cli/__tests__/capabilities.test.d.ts +2 -0
- package/dist/cli/__tests__/capabilities.test.d.ts.map +1 -0
- package/dist/cli/__tests__/capabilities.test.js +135 -0
- package/dist/cli/__tests__/capabilities.test.js.map +1 -0
- package/dist/cli/__tests__/codex-plugin-layout.test.js +237 -6
- package/dist/cli/__tests__/codex-plugin-layout.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-context-window-warning.test.js +9 -9
- package/dist/cli/__tests__/doctor-context-window-warning.test.js.map +1 -1
- package/dist/cli/__tests__/doctor-invalid-config.test.js +1 -1
- package/dist/cli/__tests__/doctor-spark-routing.test.js +22 -6
- package/dist/cli/__tests__/doctor-spark-routing.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +53 -18
- package/dist/cli/__tests__/index.test.js.map +1 -1
- package/dist/cli/__tests__/mission.test.d.ts +2 -0
- package/dist/cli/__tests__/mission.test.d.ts.map +1 -0
- package/dist/cli/__tests__/mission.test.js +239 -0
- package/dist/cli/__tests__/mission.test.js.map +1 -0
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js +3 -3
- package/dist/cli/__tests__/plugin-marketplace-idempotent.test.js.map +1 -1
- package/dist/cli/__tests__/resume.test.js +95 -6
- package/dist/cli/__tests__/resume.test.js.map +1 -1
- package/dist/cli/__tests__/session-scoped-runtime.test.js +67 -0
- package/dist/cli/__tests__/session-scoped-runtime.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +46 -18
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/__tests__/setup-prompts-overwrite.test.js +3 -3
- package/dist/cli/__tests__/setup-refresh.test.js +29 -7
- package/dist/cli/__tests__/setup-refresh.test.js.map +1 -1
- package/dist/cli/__tests__/ultragoal.test.js +1 -1
- package/dist/cli/__tests__/uninstall.test.js +4 -4
- package/dist/cli/__tests__/uninstall.test.js.map +1 -1
- package/dist/cli/agents.js +1 -1
- package/dist/cli/agents.js.map +1 -1
- package/dist/cli/capabilities.d.ts +3 -0
- package/dist/cli/capabilities.d.ts.map +1 -0
- package/dist/cli/capabilities.js +118 -0
- package/dist/cli/capabilities.js.map +1 -0
- package/dist/cli/doctor.d.ts +1 -1
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +9 -4
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/index.d.ts +7 -4
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +107 -23
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/mission.d.ts +36 -0
- package/dist/cli/mission.d.ts.map +1 -0
- package/dist/cli/mission.js +443 -0
- package/dist/cli/mission.js.map +1 -0
- package/dist/cli/plugin-marketplace.d.ts +1 -0
- package/dist/cli/plugin-marketplace.d.ts.map +1 -1
- package/dist/cli/plugin-marketplace.js +31 -0
- package/dist/cli/plugin-marketplace.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +7 -4
- package/dist/cli/setup.js.map +1 -1
- package/dist/config/__tests__/generator-idempotent.test.js +23 -23
- package/dist/config/__tests__/generator-idempotent.test.js.map +1 -1
- package/dist/config/__tests__/generator-notify.test.js +9 -9
- package/dist/config/__tests__/generator-notify.test.js.map +1 -1
- package/dist/config/__tests__/models.test.js +51 -26
- package/dist/config/__tests__/models.test.js.map +1 -1
- package/dist/config/generator.d.ts +1 -1
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/models.d.ts +16 -6
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +19 -8
- package/dist/config/models.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +12 -4
- package/dist/hooks/__tests__/keyword-detector.test.js.map +1 -1
- package/dist/hooks/__tests__/notify-hook-auto-nudge.test.js +1 -1
- package/dist/hooks/__tests__/prometheus-strict-contract.test.js +2 -2
- package/dist/hooks/__tests__/prometheus-strict-contract.test.js.map +1 -1
- package/dist/hooks/__tests__/tmux-hook-engine.test.js +2 -2
- package/dist/hud/__tests__/state.test.js +31 -6
- package/dist/hud/__tests__/state.test.js.map +1 -1
- package/dist/hud/state.d.ts.map +1 -1
- package/dist/hud/state.js +7 -3
- package/dist/hud/state.js.map +1 -1
- package/dist/leader/__tests__/contract.test.js +108 -1
- package/dist/leader/__tests__/contract.test.js.map +1 -1
- package/dist/leader/contract.d.ts +26 -0
- package/dist/leader/contract.d.ts.map +1 -1
- package/dist/leader/contract.js +170 -0
- package/dist/leader/contract.js.map +1 -1
- package/dist/mcp/__tests__/state-paths.test.js +32 -1
- package/dist/mcp/__tests__/state-paths.test.js.map +1 -1
- package/dist/mcp/state-paths.d.ts +4 -0
- package/dist/mcp/state-paths.d.ts.map +1 -1
- package/dist/mcp/state-paths.js +6 -15
- package/dist/mcp/state-paths.js.map +1 -1
- package/dist/modes/base.d.ts +1 -1
- package/dist/modes/base.d.ts.map +1 -1
- package/dist/pipeline/__tests__/stages.test.js +27 -1
- package/dist/pipeline/__tests__/stages.test.js.map +1 -1
- package/dist/pipeline/stages/ralplan.d.ts +2 -1
- package/dist/pipeline/stages/ralplan.d.ts.map +1 -1
- package/dist/pipeline/stages/ralplan.js +1 -0
- package/dist/pipeline/stages/ralplan.js.map +1 -1
- package/dist/pipeline/stages/ultragoal.d.ts +5 -1
- package/dist/pipeline/stages/ultragoal.d.ts.map +1 -1
- package/dist/pipeline/stages/ultragoal.js +14 -4
- package/dist/pipeline/stages/ultragoal.js.map +1 -1
- package/dist/ralplan/__tests__/consensus-gate.test.js +193 -0
- package/dist/ralplan/__tests__/consensus-gate.test.js.map +1 -1
- package/dist/ralplan/__tests__/runtime.test.js +132 -0
- package/dist/ralplan/__tests__/runtime.test.js.map +1 -1
- package/dist/ralplan/consensus-gate.js +24 -3
- package/dist/ralplan/consensus-gate.js.map +1 -1
- package/dist/ralplan/runtime.d.ts +17 -0
- package/dist/ralplan/runtime.d.ts.map +1 -1
- package/dist/ralplan/runtime.js +97 -1
- package/dist/ralplan/runtime.js.map +1 -1
- package/dist/scripts/__tests__/codex-native-hook.test.js +762 -20
- package/dist/scripts/__tests__/codex-native-hook.test.js.map +1 -1
- package/dist/scripts/codex-native-hook.d.ts.map +1 -1
- package/dist/scripts/codex-native-hook.js +306 -22
- package/dist/scripts/codex-native-hook.js.map +1 -1
- package/dist/scripts/generate-catalog-docs.js +1 -0
- package/dist/scripts/generate-catalog-docs.js.map +1 -1
- package/dist/state/__tests__/operations.test.js +737 -0
- package/dist/state/__tests__/operations.test.js.map +1 -1
- package/dist/state/__tests__/skill-active.test.js +59 -0
- package/dist/state/__tests__/skill-active.test.js.map +1 -1
- package/dist/state/__tests__/workflow-transition.test.js +14 -0
- package/dist/state/__tests__/workflow-transition.test.js.map +1 -1
- package/dist/state/operations.d.ts.map +1 -1
- package/dist/state/operations.js +80 -17
- package/dist/state/operations.js.map +1 -1
- package/dist/state/skill-active.js +3 -3
- package/dist/state/skill-active.js.map +1 -1
- package/dist/state/terminal-normalization.d.ts +8 -0
- package/dist/state/terminal-normalization.d.ts.map +1 -0
- package/dist/state/terminal-normalization.js +127 -0
- package/dist/state/terminal-normalization.js.map +1 -0
- package/dist/state/workflow-transition-reconcile.d.ts.map +1 -1
- package/dist/state/workflow-transition-reconcile.js +3 -1
- package/dist/state/workflow-transition-reconcile.js.map +1 -1
- package/dist/team/__tests__/delegation-policy.test.js +2 -2
- package/dist/team/__tests__/delegation-policy.test.js.map +1 -1
- package/dist/team/__tests__/model-contract.test.js +25 -25
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/runtime.test.js +28 -28
- package/dist/team/__tests__/runtime.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +7 -7
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/tmux-claude-workers-demo.test.js +5 -5
- package/dist/team/__tests__/tmux-claude-workers-demo.test.js.map +1 -1
- package/dist/team/__tests__/tmux-session.test.js +12 -12
- package/dist/team/__tests__/tmux-session.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +57 -9
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/__tests__/worker-runtime-identity.test.js +3 -3
- package/dist/team/__tests__/worker-runtime-identity.test.js.map +1 -1
- package/dist/team/model-contract.d.ts +1 -1
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/runtime.d.ts.map +1 -1
- package/dist/team/runtime.js +11 -0
- package/dist/team/runtime.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts +2 -0
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +6 -1
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/ultragoal/__tests__/artifacts.test.js +82 -5
- package/dist/ultragoal/__tests__/artifacts.test.js.map +1 -1
- package/dist/ultragoal/artifacts.d.ts +5 -1
- package/dist/ultragoal/artifacts.d.ts.map +1 -1
- package/dist/ultragoal/artifacts.js +111 -14
- package/dist/ultragoal/artifacts.js.map +1 -1
- package/dist/utils/__tests__/agents-model-table.test.js +7 -7
- package/dist/utils/__tests__/agents-model-table.test.js.map +1 -1
- package/dist/utils/__tests__/worktree-tool-context.test.d.ts +2 -0
- package/dist/utils/__tests__/worktree-tool-context.test.d.ts.map +1 -0
- package/dist/utils/__tests__/worktree-tool-context.test.js +118 -0
- package/dist/utils/__tests__/worktree-tool-context.test.js.map +1 -0
- package/dist/utils/worktree-tool-context.d.ts +25 -0
- package/dist/utils/worktree-tool-context.d.ts.map +1 -0
- package/dist/utils/worktree-tool-context.js +116 -0
- package/dist/utils/worktree-tool-context.js.map +1 -0
- package/package.json +1 -1
- package/plugins/oh-my-codex/.codex-plugin/plugin.json +1 -1
- package/plugins/oh-my-codex/hooks/codex-native-hook.mjs +96 -10
- package/plugins/oh-my-codex/skills/ai-slop-cleaner/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/code-review/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/omx-setup/SKILL.md +5 -5
- package/plugins/oh-my-codex/skills/prometheus-strict/SKILL.md +3 -3
- package/plugins/oh-my-codex/skills/ralplan/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/team/SKILL.md +1 -1
- package/plugins/oh-my-codex/skills/ultrawork/references/agent-tiers.md +2 -2
- package/prompts/prometheus-strict-metis.md +1 -1
- package/skills/ai-slop-cleaner/SKILL.md +1 -1
- package/skills/code-review/SKILL.md +1 -1
- package/skills/ecomode/references/agent-tiers.md +1 -1
- package/skills/omx-setup/SKILL.md +5 -5
- package/skills/prometheus-strict/SKILL.md +3 -3
- package/skills/ralplan/SKILL.md +1 -1
- package/skills/team/SKILL.md +1 -1
- package/skills/ultrawork/references/agent-tiers.md +2 -2
- package/src/scripts/__tests__/codex-native-hook.test.ts +824 -7
- package/src/scripts/codex-native-hook.ts +347 -20
- package/src/scripts/generate-catalog-docs.ts +1 -0
|
@@ -66,6 +66,45 @@ async function withOmxRootEnv(root, run) {
|
|
|
66
66
|
delete process.env.OMX_TEAM_STATE_ROOT;
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
+
async function withStateRootEnv(env, run) {
|
|
70
|
+
const previousOmxRoot = process.env.OMX_ROOT;
|
|
71
|
+
const previousOmxStateRoot = process.env.OMX_STATE_ROOT;
|
|
72
|
+
const previousTeamStateRoot = process.env.OMX_TEAM_STATE_ROOT;
|
|
73
|
+
if (typeof env.OMX_ROOT === 'string')
|
|
74
|
+
process.env.OMX_ROOT = env.OMX_ROOT;
|
|
75
|
+
else
|
|
76
|
+
delete process.env.OMX_ROOT;
|
|
77
|
+
if (typeof env.OMX_STATE_ROOT === 'string')
|
|
78
|
+
process.env.OMX_STATE_ROOT = env.OMX_STATE_ROOT;
|
|
79
|
+
else
|
|
80
|
+
delete process.env.OMX_STATE_ROOT;
|
|
81
|
+
if (typeof env.OMX_TEAM_STATE_ROOT === 'string')
|
|
82
|
+
process.env.OMX_TEAM_STATE_ROOT = env.OMX_TEAM_STATE_ROOT;
|
|
83
|
+
else
|
|
84
|
+
delete process.env.OMX_TEAM_STATE_ROOT;
|
|
85
|
+
try {
|
|
86
|
+
return await run();
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
if (typeof previousOmxRoot === 'string')
|
|
90
|
+
process.env.OMX_ROOT = previousOmxRoot;
|
|
91
|
+
else
|
|
92
|
+
delete process.env.OMX_ROOT;
|
|
93
|
+
if (typeof previousOmxStateRoot === 'string')
|
|
94
|
+
process.env.OMX_STATE_ROOT = previousOmxStateRoot;
|
|
95
|
+
else
|
|
96
|
+
delete process.env.OMX_STATE_ROOT;
|
|
97
|
+
if (typeof previousTeamStateRoot === 'string')
|
|
98
|
+
process.env.OMX_TEAM_STATE_ROOT = previousTeamStateRoot;
|
|
99
|
+
else
|
|
100
|
+
delete process.env.OMX_TEAM_STATE_ROOT;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
function responsePayload(response) {
|
|
104
|
+
assert.equal(response.isError, undefined);
|
|
105
|
+
assert.ok(response.payload && typeof response.payload === 'object' && !Array.isArray(response.payload));
|
|
106
|
+
return response.payload;
|
|
107
|
+
}
|
|
69
108
|
function validExecutionContract(stride) {
|
|
70
109
|
const perStride = {
|
|
71
110
|
task: {
|
|
@@ -235,6 +274,123 @@ describe('state operations directory initialization', () => {
|
|
|
235
274
|
await rm(wd, { recursive: true, force: true });
|
|
236
275
|
}
|
|
237
276
|
});
|
|
277
|
+
it('writes and clears session state under OMX_TEAM_STATE_ROOT without creating cwd .omx', async () => {
|
|
278
|
+
const root = await mkdtemp(join(tmpdir(), 'omx-state-ops-team-root-'));
|
|
279
|
+
try {
|
|
280
|
+
const wd = join(root, 'workspace');
|
|
281
|
+
const teamStateRoot = join(root, 'team-state');
|
|
282
|
+
await mkdir(wd, { recursive: true });
|
|
283
|
+
await withStateRootEnv({ OMX_TEAM_STATE_ROOT: teamStateRoot }, async () => {
|
|
284
|
+
const writeResponse = await executeStateOperation('state_write', {
|
|
285
|
+
workingDirectory: wd,
|
|
286
|
+
session_id: 'sess-team-write',
|
|
287
|
+
mode: 'autoresearch',
|
|
288
|
+
active: true,
|
|
289
|
+
current_phase: 'running',
|
|
290
|
+
});
|
|
291
|
+
const writePayload = responsePayload(writeResponse);
|
|
292
|
+
assert.equal(writePayload.path, join(teamStateRoot, 'sessions', 'sess-team-write', 'autoresearch-state.json'));
|
|
293
|
+
assert.equal(existsSync(writePayload.path), true);
|
|
294
|
+
assert.equal(existsSync(join(teamStateRoot, 'sessions', 'sess-team-write', 'skill-active-state.json')), true);
|
|
295
|
+
assert.equal(existsSync(join(wd, '.omx')), false);
|
|
296
|
+
const clearResponse = await executeStateOperation('state_clear', {
|
|
297
|
+
workingDirectory: wd,
|
|
298
|
+
session_id: 'sess-team-write',
|
|
299
|
+
mode: 'autoresearch',
|
|
300
|
+
});
|
|
301
|
+
const clearPayload = responsePayload(clearResponse);
|
|
302
|
+
assert.equal(clearPayload.path, writePayload.path);
|
|
303
|
+
assert.equal(existsSync(writePayload.path), false);
|
|
304
|
+
assert.equal(existsSync(join(teamStateRoot, 'sessions', 'sess-team-write', 'skill-active-state.json')), true);
|
|
305
|
+
assert.equal(existsSync(join(wd, '.omx')), false);
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
finally {
|
|
309
|
+
await rm(root, { recursive: true, force: true });
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
it('writes and clears session state under OMX_ROOT when cwd is filesystem root', async () => {
|
|
313
|
+
const boxRoot = await mkdtemp(join(tmpdir(), 'omx-state-ops-omx-root-'));
|
|
314
|
+
try {
|
|
315
|
+
await withStateRootEnv({ OMX_ROOT: boxRoot }, async () => {
|
|
316
|
+
const writeResponse = await executeStateOperation('state_write', {
|
|
317
|
+
workingDirectory: '/',
|
|
318
|
+
session_id: 'sess-omx-root',
|
|
319
|
+
mode: 'autoresearch',
|
|
320
|
+
active: true,
|
|
321
|
+
current_phase: 'running',
|
|
322
|
+
});
|
|
323
|
+
const writePayload = responsePayload(writeResponse);
|
|
324
|
+
const expectedPath = join(boxRoot, '.omx', 'state', 'sessions', 'sess-omx-root', 'autoresearch-state.json');
|
|
325
|
+
assert.equal(writePayload.path, expectedPath);
|
|
326
|
+
assert.equal(existsSync(expectedPath), true);
|
|
327
|
+
const clearResponse = await executeStateOperation('state_clear', {
|
|
328
|
+
workingDirectory: '/',
|
|
329
|
+
session_id: 'sess-omx-root',
|
|
330
|
+
mode: 'autoresearch',
|
|
331
|
+
});
|
|
332
|
+
const clearPayload = responsePayload(clearResponse);
|
|
333
|
+
assert.equal(clearPayload.path, expectedPath);
|
|
334
|
+
assert.equal(existsSync(expectedPath), false);
|
|
335
|
+
const workspace = join(boxRoot, 'workspace');
|
|
336
|
+
await mkdir(workspace, { recursive: true });
|
|
337
|
+
const workspaceResponse = await executeStateOperation('state_write', {
|
|
338
|
+
workingDirectory: workspace,
|
|
339
|
+
session_id: 'sess-omx-root-workspace',
|
|
340
|
+
mode: 'autoresearch',
|
|
341
|
+
active: true,
|
|
342
|
+
current_phase: 'running',
|
|
343
|
+
});
|
|
344
|
+
const workspacePayload = responsePayload(workspaceResponse);
|
|
345
|
+
assert.equal(workspacePayload.path, join(boxRoot, '.omx', 'state', 'sessions', 'sess-omx-root-workspace', 'autoresearch-state.json'));
|
|
346
|
+
assert.equal(existsSync(join(workspace, '.omx')), false);
|
|
347
|
+
});
|
|
348
|
+
}
|
|
349
|
+
finally {
|
|
350
|
+
await rm(boxRoot, { recursive: true, force: true });
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
it('writes and clears session state under OMX_STATE_ROOT when cwd is filesystem root', async () => {
|
|
354
|
+
const stateRoot = await mkdtemp(join(tmpdir(), 'omx-state-ops-state-root-'));
|
|
355
|
+
try {
|
|
356
|
+
await withStateRootEnv({ OMX_STATE_ROOT: stateRoot }, async () => {
|
|
357
|
+
const writeResponse = await executeStateOperation('state_write', {
|
|
358
|
+
workingDirectory: '/',
|
|
359
|
+
session_id: 'sess-state-root',
|
|
360
|
+
mode: 'autoresearch',
|
|
361
|
+
active: true,
|
|
362
|
+
current_phase: 'running',
|
|
363
|
+
});
|
|
364
|
+
const writePayload = responsePayload(writeResponse);
|
|
365
|
+
const expectedPath = join(stateRoot, '.omx', 'state', 'sessions', 'sess-state-root', 'autoresearch-state.json');
|
|
366
|
+
assert.equal(writePayload.path, expectedPath);
|
|
367
|
+
assert.equal(existsSync(expectedPath), true);
|
|
368
|
+
const clearResponse = await executeStateOperation('state_clear', {
|
|
369
|
+
workingDirectory: '/',
|
|
370
|
+
session_id: 'sess-state-root',
|
|
371
|
+
mode: 'autoresearch',
|
|
372
|
+
});
|
|
373
|
+
const clearPayload = responsePayload(clearResponse);
|
|
374
|
+
assert.equal(clearPayload.path, expectedPath);
|
|
375
|
+
assert.equal(existsSync(expectedPath), false);
|
|
376
|
+
const workspace = join(stateRoot, 'workspace');
|
|
377
|
+
await mkdir(workspace, { recursive: true });
|
|
378
|
+
const workspaceResponse = await executeStateOperation('state_write', {
|
|
379
|
+
workingDirectory: workspace,
|
|
380
|
+
session_id: 'sess-state-root-workspace',
|
|
381
|
+
mode: 'autoresearch',
|
|
382
|
+
active: true,
|
|
383
|
+
current_phase: 'running',
|
|
384
|
+
});
|
|
385
|
+
const workspacePayload = responsePayload(workspaceResponse);
|
|
386
|
+
assert.equal(workspacePayload.path, join(stateRoot, '.omx', 'state', 'sessions', 'sess-state-root-workspace', 'autoresearch-state.json'));
|
|
387
|
+
assert.equal(existsSync(join(workspace, '.omx')), false);
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
finally {
|
|
391
|
+
await rm(stateRoot, { recursive: true, force: true });
|
|
392
|
+
}
|
|
393
|
+
});
|
|
238
394
|
it('surfaces active ultragoal artifacts in list-active without mode state files', async () => {
|
|
239
395
|
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-ultragoal-artifact-'));
|
|
240
396
|
try {
|
|
@@ -266,6 +422,57 @@ describe('state operations directory initialization', () => {
|
|
|
266
422
|
await rm(wd, { recursive: true, force: true });
|
|
267
423
|
}
|
|
268
424
|
});
|
|
425
|
+
it('reports reconciled task-scoped aggregate ultragoal artifacts as inactive in get-status', async () => {
|
|
426
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-ultragoal-reconciled-'));
|
|
427
|
+
try {
|
|
428
|
+
await mkdir(join(wd, '.omx', 'ultragoal'), { recursive: true });
|
|
429
|
+
await writeFile(join(wd, '.omx', 'ultragoal', 'goals.json'), JSON.stringify({
|
|
430
|
+
aggregateCompletion: {
|
|
431
|
+
status: 'complete',
|
|
432
|
+
completedAt: '2026-06-01T12:00:00.000Z',
|
|
433
|
+
evidence: 'task-scoped Codex aggregate completed and active microgoal row was reconciled',
|
|
434
|
+
},
|
|
435
|
+
activeGoalId: 'G002',
|
|
436
|
+
goals: [{
|
|
437
|
+
id: 'G001',
|
|
438
|
+
title: 'Fix duplicate HUD panes',
|
|
439
|
+
objective: 'Keep one HUD renderer per leader.',
|
|
440
|
+
status: 'complete',
|
|
441
|
+
completedAt: '2026-06-01T12:00:00.000Z',
|
|
442
|
+
}, {
|
|
443
|
+
id: 'G002',
|
|
444
|
+
title: 'Still marked running',
|
|
445
|
+
objective: 'Progress-only row left running by the old aggregate path.',
|
|
446
|
+
status: 'in_progress',
|
|
447
|
+
}, {
|
|
448
|
+
id: 'G003',
|
|
449
|
+
title: 'Still marked pending',
|
|
450
|
+
objective: 'Progress-only row left pending by the old aggregate path.',
|
|
451
|
+
status: 'pending',
|
|
452
|
+
}],
|
|
453
|
+
}, null, 2));
|
|
454
|
+
const activeResponse = await executeStateOperation('state_list_active', {
|
|
455
|
+
workingDirectory: wd,
|
|
456
|
+
});
|
|
457
|
+
assert.deepEqual(activeResponse.payload, { active_modes: [] });
|
|
458
|
+
const statusResponse = await executeStateOperation('state_get_status', {
|
|
459
|
+
workingDirectory: wd,
|
|
460
|
+
mode: 'ultragoal',
|
|
461
|
+
});
|
|
462
|
+
const statuses = statusResponse.payload.statuses || {};
|
|
463
|
+
assert.equal(statuses.ultragoal?.active, false);
|
|
464
|
+
assert.equal(statuses.ultragoal?.phase, 'complete');
|
|
465
|
+
assert.equal(statuses.ultragoal?.path, join(wd, '.omx', 'ultragoal', 'goals.json'));
|
|
466
|
+
assert.equal(statuses.ultragoal?.source, 'ultragoal-artifacts');
|
|
467
|
+
assert.equal(statuses.ultragoal?.data?.activeGoal, undefined);
|
|
468
|
+
assert.equal(statuses.ultragoal?.data?.complete, 1);
|
|
469
|
+
assert.equal(statuses.ultragoal?.data?.inProgress, 1);
|
|
470
|
+
assert.equal(statuses.ultragoal?.data?.pending, 1);
|
|
471
|
+
}
|
|
472
|
+
finally {
|
|
473
|
+
await rm(wd, { recursive: true, force: true });
|
|
474
|
+
}
|
|
475
|
+
});
|
|
269
476
|
it('prefers active ultragoal artifacts over stale inactive mode state', async () => {
|
|
270
477
|
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-ultragoal-stale-state-'));
|
|
271
478
|
try {
|
|
@@ -403,6 +610,58 @@ describe('state operations directory initialization', () => {
|
|
|
403
610
|
await rm(wd, { recursive: true, force: true });
|
|
404
611
|
}
|
|
405
612
|
});
|
|
613
|
+
it('normalizes terminal deep-interview snapshots by releasing stale locks', async () => {
|
|
614
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-di-terminal-normalize-'));
|
|
615
|
+
try {
|
|
616
|
+
const completedAt = '2026-07-09T00:00:00.000Z';
|
|
617
|
+
const writeResponse = await executeStateOperation('state_write', {
|
|
618
|
+
workingDirectory: wd,
|
|
619
|
+
mode: 'deep-interview',
|
|
620
|
+
active: false,
|
|
621
|
+
current_phase: 'cancelled',
|
|
622
|
+
completed_at: completedAt,
|
|
623
|
+
input_lock: {
|
|
624
|
+
active: true,
|
|
625
|
+
owner: 'question-round',
|
|
626
|
+
},
|
|
627
|
+
approval_lock: {
|
|
628
|
+
status: 'pending',
|
|
629
|
+
reviewer: 'user',
|
|
630
|
+
},
|
|
631
|
+
question_enforcement: {
|
|
632
|
+
obligation_id: 'obligation-stale',
|
|
633
|
+
source: 'omx-question',
|
|
634
|
+
status: 'pending',
|
|
635
|
+
lifecycle_outcome: 'askuserQuestion',
|
|
636
|
+
requested_at: '2026-07-08T23:59:00.000Z',
|
|
637
|
+
},
|
|
638
|
+
});
|
|
639
|
+
assert.equal(writeResponse.isError, undefined);
|
|
640
|
+
const readResponse = await executeStateOperation('state_read', {
|
|
641
|
+
workingDirectory: wd,
|
|
642
|
+
mode: 'deep-interview',
|
|
643
|
+
});
|
|
644
|
+
const readBody = readResponse.payload;
|
|
645
|
+
const inputLock = readBody.input_lock;
|
|
646
|
+
const approvalLock = readBody.approval_lock;
|
|
647
|
+
const questionEnforcement = readBody.question_enforcement;
|
|
648
|
+
assert.equal(readBody.active, false);
|
|
649
|
+
assert.equal(readBody.current_phase, 'cancelled');
|
|
650
|
+
assert.equal(readBody.completed_at, completedAt);
|
|
651
|
+
assert.equal(readBody.run_outcome, 'cancelled');
|
|
652
|
+
assert.equal(inputLock.active, false);
|
|
653
|
+
assert.equal(inputLock.status, 'released');
|
|
654
|
+
assert.equal(inputLock.released_at, completedAt);
|
|
655
|
+
assert.equal(approvalLock.active, false);
|
|
656
|
+
assert.equal(approvalLock.status, 'released');
|
|
657
|
+
assert.equal(inputLock.release_reason, 'terminal_state_normalization');
|
|
658
|
+
assert.equal(questionEnforcement.status, 'cleared');
|
|
659
|
+
assert.equal(questionEnforcement.clear_reason, 'abort');
|
|
660
|
+
}
|
|
661
|
+
finally {
|
|
662
|
+
await rm(wd, { recursive: true, force: true });
|
|
663
|
+
}
|
|
664
|
+
});
|
|
406
665
|
it('writes and reads autoresearch state', async () => {
|
|
407
666
|
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-autoresearch-'));
|
|
408
667
|
try {
|
|
@@ -772,6 +1031,86 @@ describe('state operations directory initialization', () => {
|
|
|
772
1031
|
await rm(wd, { recursive: true, force: true });
|
|
773
1032
|
}
|
|
774
1033
|
});
|
|
1034
|
+
it('finalizes ralplan when runtime tracker lags but workspace tracker has completed native reviews', async () => {
|
|
1035
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-ralplan-runtime-lag-complete-'));
|
|
1036
|
+
const stateRoot = await mkdtemp(join(tmpdir(), 'omx-state-ops-ralplan-runtime-lag-root-'));
|
|
1037
|
+
try {
|
|
1038
|
+
await withStateRootEnv({ OMX_STATE_ROOT: stateRoot }, async () => {
|
|
1039
|
+
const sessionId = 'sess-ralplan-runtime-lag-complete';
|
|
1040
|
+
const runtimeStateDir = join(stateRoot, '.omx', 'state');
|
|
1041
|
+
const sessionDir = join(runtimeStateDir, 'sessions', sessionId);
|
|
1042
|
+
const workspaceStateDir = join(wd, '.omx', 'state');
|
|
1043
|
+
await mkdir(sessionDir, { recursive: true });
|
|
1044
|
+
await mkdir(workspaceStateDir, { recursive: true });
|
|
1045
|
+
await writeFile(join(runtimeStateDir, 'session.json'), JSON.stringify({
|
|
1046
|
+
session_id: sessionId,
|
|
1047
|
+
native_session_id: 'thread-leader',
|
|
1048
|
+
cwd: wd,
|
|
1049
|
+
}, null, 2));
|
|
1050
|
+
const consensusGate = ralplanConsensusGate(sessionId, 'native_subagent');
|
|
1051
|
+
consensusGate.ralplan_architect_review.completed_at = '2026-07-07T04:30:00.000Z';
|
|
1052
|
+
consensusGate.ralplan_critic_review.completed_at = '2026-07-07T04:31:00.000Z';
|
|
1053
|
+
await writeFile(subagentTrackingPath(wd), JSON.stringify({
|
|
1054
|
+
schemaVersion: 1,
|
|
1055
|
+
sessions: {
|
|
1056
|
+
[sessionId]: {
|
|
1057
|
+
session_id: sessionId,
|
|
1058
|
+
leader_thread_id: 'thread-leader',
|
|
1059
|
+
updated_at: '2026-07-07T04:31:00.000Z',
|
|
1060
|
+
threads: {
|
|
1061
|
+
'thread-leader': { thread_id: 'thread-leader', kind: 'leader', first_seen_at: '2026-07-07T04:29:00.000Z', last_seen_at: '2026-07-07T04:29:00.000Z', turn_count: 1 },
|
|
1062
|
+
'thread-architect': { thread_id: 'thread-architect', kind: 'subagent', first_seen_at: '2026-07-07T04:30:00.000Z', last_seen_at: '2026-07-07T04:30:00.000Z', turn_count: 1 },
|
|
1063
|
+
'thread-critic': { thread_id: 'thread-critic', kind: 'subagent', first_seen_at: '2026-07-07T04:31:00.000Z', last_seen_at: '2026-07-07T04:31:00.000Z', turn_count: 1 },
|
|
1064
|
+
},
|
|
1065
|
+
},
|
|
1066
|
+
},
|
|
1067
|
+
}, null, 2));
|
|
1068
|
+
await writeFile(join(workspaceStateDir, 'subagent-tracking.json'), JSON.stringify({
|
|
1069
|
+
schemaVersion: 1,
|
|
1070
|
+
sessions: {
|
|
1071
|
+
[sessionId]: {
|
|
1072
|
+
session_id: sessionId,
|
|
1073
|
+
leader_thread_id: 'thread-leader',
|
|
1074
|
+
updated_at: '2026-07-07T04:31:00.000Z',
|
|
1075
|
+
threads: {
|
|
1076
|
+
'thread-leader': { thread_id: 'thread-leader', kind: 'leader', first_seen_at: '2026-07-07T04:29:00.000Z', last_seen_at: '2026-07-07T04:29:00.000Z', turn_count: 1 },
|
|
1077
|
+
'thread-architect': { thread_id: 'thread-architect', kind: 'subagent', first_seen_at: '2026-07-07T04:30:00.000Z', last_seen_at: '2026-07-07T04:30:00.000Z', completed_at: '2026-07-07T04:30:00.000Z', turn_count: 1 },
|
|
1078
|
+
'thread-critic': { thread_id: 'thread-critic', kind: 'subagent', first_seen_at: '2026-07-07T04:31:00.000Z', last_seen_at: '2026-07-07T04:31:00.000Z', completed_at: '2026-07-07T04:31:00.000Z', turn_count: 1 },
|
|
1079
|
+
},
|
|
1080
|
+
},
|
|
1081
|
+
},
|
|
1082
|
+
}, null, 2));
|
|
1083
|
+
await writeFile(join(sessionDir, 'ralplan-state.json'), JSON.stringify({
|
|
1084
|
+
mode: 'ralplan',
|
|
1085
|
+
active: true,
|
|
1086
|
+
current_phase: 'planning',
|
|
1087
|
+
session_id: sessionId,
|
|
1088
|
+
}, null, 2));
|
|
1089
|
+
const response = await executeStateOperation('state_write', {
|
|
1090
|
+
workingDirectory: wd,
|
|
1091
|
+
session_id: sessionId,
|
|
1092
|
+
mode: 'ralplan',
|
|
1093
|
+
active: false,
|
|
1094
|
+
current_phase: 'complete',
|
|
1095
|
+
planning_complete: true,
|
|
1096
|
+
latest_plan_path: '.omx/plans/prd-clickstack-otel-consumer-20260707T043000Z.md',
|
|
1097
|
+
terminal_reason: 'consensus approved despite runtime tracker lag',
|
|
1098
|
+
ralplan_consensus_gate: consensusGate,
|
|
1099
|
+
});
|
|
1100
|
+
assert.equal(response.isError, undefined);
|
|
1101
|
+
const sessionRalplan = JSON.parse(await readFile(join(sessionDir, 'ralplan-state.json'), 'utf-8'));
|
|
1102
|
+
assert.equal(sessionRalplan.active, false);
|
|
1103
|
+
assert.equal(sessionRalplan.current_phase, 'complete');
|
|
1104
|
+
assert.equal(sessionRalplan.status, 'complete');
|
|
1105
|
+
assert.equal(sessionRalplan.terminal_reason, 'consensus approved despite runtime tracker lag');
|
|
1106
|
+
assert.equal(sessionRalplan.ralplan_consensus_gate.complete, true);
|
|
1107
|
+
});
|
|
1108
|
+
}
|
|
1109
|
+
finally {
|
|
1110
|
+
await rm(wd, { recursive: true, force: true });
|
|
1111
|
+
await rm(stateRoot, { recursive: true, force: true });
|
|
1112
|
+
}
|
|
1113
|
+
});
|
|
775
1114
|
it('rejects forged ralplan complete gates before mutating active session state', async () => {
|
|
776
1115
|
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-ralplan-forged-complete-'));
|
|
777
1116
|
try {
|
|
@@ -809,6 +1148,285 @@ describe('state operations directory initialization', () => {
|
|
|
809
1148
|
await rm(wd, { recursive: true, force: true });
|
|
810
1149
|
}
|
|
811
1150
|
});
|
|
1151
|
+
it('allows ralplan unsupported native non-clean recovery without tracker-backed consensus', async () => {
|
|
1152
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-ralplan-unsupported-recovery-'));
|
|
1153
|
+
try {
|
|
1154
|
+
const sessionId = 'sess-ralplan-unsupported-recovery';
|
|
1155
|
+
const stateDir = join(wd, '.omx', 'state');
|
|
1156
|
+
const sessionDir = join(stateDir, 'sessions', sessionId);
|
|
1157
|
+
await mkdir(sessionDir, { recursive: true });
|
|
1158
|
+
await writeFile(join(stateDir, 'session.json'), JSON.stringify({ session_id: sessionId, cwd: wd }, null, 2));
|
|
1159
|
+
await writeFile(join(sessionDir, 'ralplan-state.json'), JSON.stringify({
|
|
1160
|
+
mode: 'ralplan',
|
|
1161
|
+
active: true,
|
|
1162
|
+
current_phase: 'planning',
|
|
1163
|
+
session_id: sessionId,
|
|
1164
|
+
}, null, 2));
|
|
1165
|
+
const response = await executeStateOperation('state_write', {
|
|
1166
|
+
workingDirectory: wd,
|
|
1167
|
+
session_id: sessionId,
|
|
1168
|
+
mode: 'ralplan',
|
|
1169
|
+
active: false,
|
|
1170
|
+
current_phase: 'blocked',
|
|
1171
|
+
native_subagent_support: {
|
|
1172
|
+
status: 'unsupported',
|
|
1173
|
+
reason: 'multi_agent_v1_unavailable',
|
|
1174
|
+
source: 'post_tool_failure',
|
|
1175
|
+
},
|
|
1176
|
+
});
|
|
1177
|
+
assert.equal(response.isError, undefined);
|
|
1178
|
+
const state = JSON.parse(await readFile(join(sessionDir, 'ralplan-state.json'), 'utf-8'));
|
|
1179
|
+
assert.equal(state.active, false);
|
|
1180
|
+
assert.equal(state.current_phase, 'blocked');
|
|
1181
|
+
assert.equal(state.ralplan_consensus_gate, undefined);
|
|
1182
|
+
}
|
|
1183
|
+
finally {
|
|
1184
|
+
await rm(wd, { recursive: true, force: true });
|
|
1185
|
+
}
|
|
1186
|
+
});
|
|
1187
|
+
it('rejects ralplan clean complete without tracker-backed native consensus after unsupported recovery support', async () => {
|
|
1188
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-ralplan-clean-still-strict-'));
|
|
1189
|
+
try {
|
|
1190
|
+
const sessionId = 'sess-ralplan-clean-still-strict';
|
|
1191
|
+
const stateDir = join(wd, '.omx', 'state');
|
|
1192
|
+
const sessionDir = join(stateDir, 'sessions', sessionId);
|
|
1193
|
+
await mkdir(sessionDir, { recursive: true });
|
|
1194
|
+
await writeFile(join(stateDir, 'session.json'), JSON.stringify({ session_id: sessionId, cwd: wd }, null, 2));
|
|
1195
|
+
await writeFile(join(sessionDir, 'ralplan-state.json'), JSON.stringify({
|
|
1196
|
+
mode: 'ralplan',
|
|
1197
|
+
active: true,
|
|
1198
|
+
current_phase: 'planning',
|
|
1199
|
+
session_id: sessionId,
|
|
1200
|
+
native_subagent_support: {
|
|
1201
|
+
status: 'unsupported',
|
|
1202
|
+
reason: 'multi_agent_v1_unavailable',
|
|
1203
|
+
source: 'post_tool_failure',
|
|
1204
|
+
},
|
|
1205
|
+
}, null, 2));
|
|
1206
|
+
const response = await executeStateOperation('state_write', {
|
|
1207
|
+
workingDirectory: wd,
|
|
1208
|
+
session_id: sessionId,
|
|
1209
|
+
mode: 'ralplan',
|
|
1210
|
+
active: false,
|
|
1211
|
+
current_phase: 'complete',
|
|
1212
|
+
status: 'complete',
|
|
1213
|
+
});
|
|
1214
|
+
assert.equal(response.isError, true);
|
|
1215
|
+
assert.match(String(response.payload.error ?? ''), /Cannot complete ralplan cleanly while native subagent support is unavailable/);
|
|
1216
|
+
const state = JSON.parse(await readFile(join(sessionDir, 'ralplan-state.json'), 'utf-8'));
|
|
1217
|
+
assert.equal(state.active, true);
|
|
1218
|
+
assert.equal(state.current_phase, 'planning');
|
|
1219
|
+
}
|
|
1220
|
+
finally {
|
|
1221
|
+
await rm(wd, { recursive: true, force: true });
|
|
1222
|
+
}
|
|
1223
|
+
});
|
|
1224
|
+
it('rejects ralplan clean complete with unsupported evidence even when native consensus is valid', async () => {
|
|
1225
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-ralplan-clean-unsupported-valid-consensus-deny-'));
|
|
1226
|
+
try {
|
|
1227
|
+
const sessionId = 'sess-ralplan-clean-unsupported-valid-consensus-deny';
|
|
1228
|
+
await writeNativeSubagentTracking(wd, sessionId);
|
|
1229
|
+
const stateDir = join(wd, '.omx', 'state');
|
|
1230
|
+
const sessionDir = join(stateDir, 'sessions', sessionId);
|
|
1231
|
+
await mkdir(sessionDir, { recursive: true });
|
|
1232
|
+
await writeFile(join(stateDir, 'session.json'), JSON.stringify({ session_id: sessionId, cwd: wd }, null, 2));
|
|
1233
|
+
await writeFile(join(sessionDir, 'ralplan-state.json'), JSON.stringify({
|
|
1234
|
+
mode: 'ralplan',
|
|
1235
|
+
active: true,
|
|
1236
|
+
current_phase: 'planning',
|
|
1237
|
+
session_id: sessionId,
|
|
1238
|
+
}, null, 2));
|
|
1239
|
+
const response = await executeStateOperation('state_write', {
|
|
1240
|
+
workingDirectory: wd,
|
|
1241
|
+
session_id: sessionId,
|
|
1242
|
+
mode: 'ralplan',
|
|
1243
|
+
active: false,
|
|
1244
|
+
current_phase: 'complete',
|
|
1245
|
+
status: 'complete',
|
|
1246
|
+
native_subagent_support: {
|
|
1247
|
+
status: 'unsupported',
|
|
1248
|
+
reason: 'multi_agent_v1_unavailable',
|
|
1249
|
+
source: 'post_tool_failure',
|
|
1250
|
+
},
|
|
1251
|
+
ralplan_consensus_gate: ralplanConsensusGate(sessionId, 'native_subagent'),
|
|
1252
|
+
});
|
|
1253
|
+
assert.equal(response.isError, true);
|
|
1254
|
+
assert.match(String(response.payload.error ?? ''), /Cannot complete ralplan cleanly while native subagent support is unavailable/);
|
|
1255
|
+
const state = JSON.parse(await readFile(join(sessionDir, 'ralplan-state.json'), 'utf-8'));
|
|
1256
|
+
assert.equal(state.active, true);
|
|
1257
|
+
assert.equal(state.current_phase, 'planning');
|
|
1258
|
+
}
|
|
1259
|
+
finally {
|
|
1260
|
+
await rm(wd, { recursive: true, force: true });
|
|
1261
|
+
}
|
|
1262
|
+
});
|
|
1263
|
+
it('rejects ralplan clean complete with handoff-artifact unsupported evidence', async () => {
|
|
1264
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-ralplan-clean-handoff-unsupported-deny-'));
|
|
1265
|
+
try {
|
|
1266
|
+
const sessionId = 'sess-ralplan-clean-handoff-unsupported-deny';
|
|
1267
|
+
await writeNativeSubagentTracking(wd, sessionId);
|
|
1268
|
+
const stateDir = join(wd, '.omx', 'state');
|
|
1269
|
+
const sessionDir = join(stateDir, 'sessions', sessionId);
|
|
1270
|
+
await mkdir(sessionDir, { recursive: true });
|
|
1271
|
+
await writeFile(join(stateDir, 'session.json'), JSON.stringify({ session_id: sessionId, cwd: wd }, null, 2));
|
|
1272
|
+
await writeFile(join(sessionDir, 'ralplan-state.json'), JSON.stringify({
|
|
1273
|
+
mode: 'ralplan',
|
|
1274
|
+
active: true,
|
|
1275
|
+
current_phase: 'planning',
|
|
1276
|
+
session_id: sessionId,
|
|
1277
|
+
}, null, 2));
|
|
1278
|
+
const response = await executeStateOperation('state_write', {
|
|
1279
|
+
workingDirectory: wd,
|
|
1280
|
+
session_id: sessionId,
|
|
1281
|
+
mode: 'ralplan',
|
|
1282
|
+
active: false,
|
|
1283
|
+
current_phase: 'complete',
|
|
1284
|
+
status: 'complete',
|
|
1285
|
+
handoff_artifacts: {
|
|
1286
|
+
ralplan: {
|
|
1287
|
+
native_subagent_support: {
|
|
1288
|
+
status: 'unsupported',
|
|
1289
|
+
reason: 'multi_agent_v1_unavailable',
|
|
1290
|
+
source: 'post_tool_failure',
|
|
1291
|
+
},
|
|
1292
|
+
},
|
|
1293
|
+
},
|
|
1294
|
+
ralplan_consensus_gate: ralplanConsensusGate(sessionId, 'native_subagent'),
|
|
1295
|
+
});
|
|
1296
|
+
assert.equal(response.isError, true);
|
|
1297
|
+
assert.match(String(response.payload.error ?? ''), /Cannot complete ralplan cleanly while native subagent support is unavailable/);
|
|
1298
|
+
const state = JSON.parse(await readFile(join(sessionDir, 'ralplan-state.json'), 'utf-8'));
|
|
1299
|
+
assert.equal(state.active, true);
|
|
1300
|
+
assert.equal(state.current_phase, 'planning');
|
|
1301
|
+
}
|
|
1302
|
+
finally {
|
|
1303
|
+
await rm(wd, { recursive: true, force: true });
|
|
1304
|
+
}
|
|
1305
|
+
});
|
|
1306
|
+
it('rejects ralplan clean complete with nested handoff-artifact unsupported evidence', async () => {
|
|
1307
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-ralplan-clean-nested-handoff-unsupported-deny-'));
|
|
1308
|
+
try {
|
|
1309
|
+
const sessionId = 'sess-ralplan-clean-nested-handoff-unsupported-deny';
|
|
1310
|
+
await writeNativeSubagentTracking(wd, sessionId);
|
|
1311
|
+
const stateDir = join(wd, '.omx', 'state');
|
|
1312
|
+
const sessionDir = join(stateDir, 'sessions', sessionId);
|
|
1313
|
+
await mkdir(sessionDir, { recursive: true });
|
|
1314
|
+
await writeFile(join(stateDir, 'session.json'), JSON.stringify({ session_id: sessionId, cwd: wd }, null, 2));
|
|
1315
|
+
await writeFile(join(sessionDir, 'ralplan-state.json'), JSON.stringify({
|
|
1316
|
+
mode: 'ralplan',
|
|
1317
|
+
active: true,
|
|
1318
|
+
current_phase: 'planning',
|
|
1319
|
+
session_id: sessionId,
|
|
1320
|
+
}, null, 2));
|
|
1321
|
+
const response = await executeStateOperation('state_write', {
|
|
1322
|
+
workingDirectory: wd,
|
|
1323
|
+
session_id: sessionId,
|
|
1324
|
+
mode: 'ralplan',
|
|
1325
|
+
active: false,
|
|
1326
|
+
current_phase: 'complete',
|
|
1327
|
+
status: 'complete',
|
|
1328
|
+
state: {
|
|
1329
|
+
handoff_artifacts: {
|
|
1330
|
+
ralplan: {
|
|
1331
|
+
native_subagent_support: {
|
|
1332
|
+
status: 'unsupported',
|
|
1333
|
+
reason: 'multi_agent_v1_unavailable',
|
|
1334
|
+
source: 'post_tool_failure',
|
|
1335
|
+
},
|
|
1336
|
+
},
|
|
1337
|
+
},
|
|
1338
|
+
},
|
|
1339
|
+
ralplan_consensus_gate: ralplanConsensusGate(sessionId, 'native_subagent'),
|
|
1340
|
+
});
|
|
1341
|
+
assert.equal(response.isError, true);
|
|
1342
|
+
assert.match(String(response.payload.error ?? ''), /Cannot complete ralplan cleanly while native subagent support is unavailable/);
|
|
1343
|
+
const state = JSON.parse(await readFile(join(sessionDir, 'ralplan-state.json'), 'utf-8'));
|
|
1344
|
+
assert.equal(state.active, true);
|
|
1345
|
+
assert.equal(state.current_phase, 'planning');
|
|
1346
|
+
}
|
|
1347
|
+
finally {
|
|
1348
|
+
await rm(wd, { recursive: true, force: true });
|
|
1349
|
+
}
|
|
1350
|
+
});
|
|
1351
|
+
it('rejects ralplan clean complete with handoff-root unsupported evidence', async () => {
|
|
1352
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-ralplan-clean-handoff-root-unsupported-deny-'));
|
|
1353
|
+
try {
|
|
1354
|
+
const sessionId = 'sess-ralplan-clean-handoff-root-unsupported-deny';
|
|
1355
|
+
await writeNativeSubagentTracking(wd, sessionId);
|
|
1356
|
+
const stateDir = join(wd, '.omx', 'state');
|
|
1357
|
+
const sessionDir = join(stateDir, 'sessions', sessionId);
|
|
1358
|
+
await mkdir(sessionDir, { recursive: true });
|
|
1359
|
+
await writeFile(join(stateDir, 'session.json'), JSON.stringify({ session_id: sessionId, cwd: wd }, null, 2));
|
|
1360
|
+
await writeFile(join(sessionDir, 'ralplan-state.json'), JSON.stringify({
|
|
1361
|
+
mode: 'ralplan',
|
|
1362
|
+
active: true,
|
|
1363
|
+
current_phase: 'planning',
|
|
1364
|
+
session_id: sessionId,
|
|
1365
|
+
}, null, 2));
|
|
1366
|
+
const response = await executeStateOperation('state_write', {
|
|
1367
|
+
workingDirectory: wd,
|
|
1368
|
+
session_id: sessionId,
|
|
1369
|
+
mode: 'ralplan',
|
|
1370
|
+
active: false,
|
|
1371
|
+
current_phase: 'complete',
|
|
1372
|
+
status: 'complete',
|
|
1373
|
+
handoff_artifacts: {
|
|
1374
|
+
native_subagent_support: {
|
|
1375
|
+
status: 'unsupported',
|
|
1376
|
+
reason: 'multi_agent_v1_unavailable',
|
|
1377
|
+
source: 'post_tool_failure',
|
|
1378
|
+
},
|
|
1379
|
+
ralplan_consensus_gate: ralplanConsensusGate(sessionId, 'native_subagent'),
|
|
1380
|
+
},
|
|
1381
|
+
});
|
|
1382
|
+
assert.equal(response.isError, true);
|
|
1383
|
+
assert.match(String(response.payload.error ?? ''), /Cannot complete ralplan cleanly while native subagent support is unavailable/);
|
|
1384
|
+
const state = JSON.parse(await readFile(join(sessionDir, 'ralplan-state.json'), 'utf-8'));
|
|
1385
|
+
assert.equal(state.active, true);
|
|
1386
|
+
assert.equal(state.current_phase, 'planning');
|
|
1387
|
+
}
|
|
1388
|
+
finally {
|
|
1389
|
+
await rm(wd, { recursive: true, force: true });
|
|
1390
|
+
}
|
|
1391
|
+
});
|
|
1392
|
+
it('rejects ralplan clean status alias with unsupported evidence even when native consensus is valid', async () => {
|
|
1393
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-ralplan-status-alias-unsupported-deny-'));
|
|
1394
|
+
try {
|
|
1395
|
+
const sessionId = 'sess-ralplan-status-alias-unsupported-deny';
|
|
1396
|
+
await writeNativeSubagentTracking(wd, sessionId);
|
|
1397
|
+
const stateDir = join(wd, '.omx', 'state');
|
|
1398
|
+
const sessionDir = join(stateDir, 'sessions', sessionId);
|
|
1399
|
+
await mkdir(sessionDir, { recursive: true });
|
|
1400
|
+
await writeFile(join(stateDir, 'session.json'), JSON.stringify({ session_id: sessionId, cwd: wd }, null, 2));
|
|
1401
|
+
await writeFile(join(sessionDir, 'ralplan-state.json'), JSON.stringify({
|
|
1402
|
+
mode: 'ralplan',
|
|
1403
|
+
active: true,
|
|
1404
|
+
current_phase: 'planning',
|
|
1405
|
+
session_id: sessionId,
|
|
1406
|
+
}, null, 2));
|
|
1407
|
+
const response = await executeStateOperation('state_write', {
|
|
1408
|
+
workingDirectory: wd,
|
|
1409
|
+
session_id: sessionId,
|
|
1410
|
+
mode: 'ralplan',
|
|
1411
|
+
active: false,
|
|
1412
|
+
status: 'complete',
|
|
1413
|
+
native_subagent_support: {
|
|
1414
|
+
status: 'unsupported',
|
|
1415
|
+
reason: 'multi_agent_v1_unavailable',
|
|
1416
|
+
source: 'post_tool_failure',
|
|
1417
|
+
},
|
|
1418
|
+
ralplan_consensus_gate: ralplanConsensusGate(sessionId, 'native_subagent'),
|
|
1419
|
+
});
|
|
1420
|
+
assert.equal(response.isError, true);
|
|
1421
|
+
assert.match(String(response.payload.error ?? ''), /Cannot complete ralplan cleanly while native subagent support is unavailable/);
|
|
1422
|
+
const state = JSON.parse(await readFile(join(sessionDir, 'ralplan-state.json'), 'utf-8'));
|
|
1423
|
+
assert.equal(state.active, true);
|
|
1424
|
+
assert.equal(state.current_phase, 'planning');
|
|
1425
|
+
}
|
|
1426
|
+
finally {
|
|
1427
|
+
await rm(wd, { recursive: true, force: true });
|
|
1428
|
+
}
|
|
1429
|
+
});
|
|
812
1430
|
it('normalizes currentPhase before gating ralplan terminal writes', async () => {
|
|
813
1431
|
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-ralplan-current-phase-alias-'));
|
|
814
1432
|
try {
|
|
@@ -3042,6 +3660,40 @@ describe('state operations directory initialization', () => {
|
|
|
3042
3660
|
await rm(wd, { recursive: true, force: true });
|
|
3043
3661
|
}
|
|
3044
3662
|
});
|
|
3663
|
+
it('allows Autopilot ralplan unsupported native non-clean recovery', async () => {
|
|
3664
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-autopilot-ralplan-unsupported-recovery-'));
|
|
3665
|
+
try {
|
|
3666
|
+
await withOmxRootEnv(wd, async () => {
|
|
3667
|
+
const sessionId = 'sess-autopilot-ralplan-unsupported-recovery';
|
|
3668
|
+
const sessionDir = join(wd, '.omx', 'state', 'sessions', sessionId);
|
|
3669
|
+
await mkdir(sessionDir, { recursive: true });
|
|
3670
|
+
await writeFile(join(sessionDir, 'autopilot-state.json'), JSON.stringify({
|
|
3671
|
+
active: true,
|
|
3672
|
+
mode: 'autopilot',
|
|
3673
|
+
current_phase: 'ralplan',
|
|
3674
|
+
}, null, 2));
|
|
3675
|
+
const response = await executeStateOperation('state_write', {
|
|
3676
|
+
workingDirectory: wd,
|
|
3677
|
+
session_id: sessionId,
|
|
3678
|
+
mode: 'autopilot',
|
|
3679
|
+
active: false,
|
|
3680
|
+
current_phase: 'blocked',
|
|
3681
|
+
native_subagent_support: {
|
|
3682
|
+
status: 'unsupported',
|
|
3683
|
+
reason: 'multi_agent_v1_unavailable',
|
|
3684
|
+
source: 'post_tool_failure',
|
|
3685
|
+
},
|
|
3686
|
+
});
|
|
3687
|
+
assert.equal(response.isError, undefined);
|
|
3688
|
+
const state = JSON.parse(await readFile(join(sessionDir, 'autopilot-state.json'), 'utf-8'));
|
|
3689
|
+
assert.equal(state.active, false);
|
|
3690
|
+
assert.equal(state.current_phase, 'blocked');
|
|
3691
|
+
});
|
|
3692
|
+
}
|
|
3693
|
+
finally {
|
|
3694
|
+
await rm(wd, { recursive: true, force: true });
|
|
3695
|
+
}
|
|
3696
|
+
});
|
|
3045
3697
|
it('denies Autopilot ralplan to ultragoal self-write with codex_exec consensus evidence', async () => {
|
|
3046
3698
|
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-autopilot-ralplan-native-deny-'));
|
|
3047
3699
|
try {
|
|
@@ -3080,6 +3732,91 @@ describe('state operations directory initialization', () => {
|
|
|
3080
3732
|
await rm(wd, { recursive: true, force: true });
|
|
3081
3733
|
}
|
|
3082
3734
|
});
|
|
3735
|
+
it('denies Autopilot ralplan to ultragoal when unsupported native evidence is present', async () => {
|
|
3736
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-autopilot-ralplan-unsupported-ultragoal-deny-'));
|
|
3737
|
+
try {
|
|
3738
|
+
await withOmxRootEnv(wd, async () => {
|
|
3739
|
+
const sessionId = 'sess-autopilot-ralplan-unsupported-ultragoal-deny';
|
|
3740
|
+
const sessionDir = join(wd, '.omx', 'state', 'sessions', sessionId);
|
|
3741
|
+
await mkdir(sessionDir, { recursive: true });
|
|
3742
|
+
await writeFile(join(sessionDir, 'autopilot-state.json'), JSON.stringify({
|
|
3743
|
+
active: true,
|
|
3744
|
+
mode: 'autopilot',
|
|
3745
|
+
current_phase: 'ralplan',
|
|
3746
|
+
native_subagent_support: {
|
|
3747
|
+
status: 'unsupported',
|
|
3748
|
+
reason: 'native_subagents_unsupported',
|
|
3749
|
+
source: 'post_tool_failure',
|
|
3750
|
+
},
|
|
3751
|
+
}, null, 2));
|
|
3752
|
+
const response = await executeStateOperation('state_write', {
|
|
3753
|
+
workingDirectory: wd,
|
|
3754
|
+
session_id: sessionId,
|
|
3755
|
+
mode: 'autopilot',
|
|
3756
|
+
active: true,
|
|
3757
|
+
current_phase: 'ultragoal',
|
|
3758
|
+
native_subagent_support: {
|
|
3759
|
+
status: 'unsupported',
|
|
3760
|
+
reason: 'native_subagents_unsupported',
|
|
3761
|
+
source: 'post_tool_failure',
|
|
3762
|
+
},
|
|
3763
|
+
});
|
|
3764
|
+
assert.equal(response.isError, true);
|
|
3765
|
+
assert.match(String(response.payload.error || ''), /Cannot transition ralplan -> ultragoal/i);
|
|
3766
|
+
const state = JSON.parse(await readFile(join(sessionDir, 'autopilot-state.json'), 'utf-8'));
|
|
3767
|
+
assert.equal(state.current_phase, 'ralplan');
|
|
3768
|
+
});
|
|
3769
|
+
}
|
|
3770
|
+
finally {
|
|
3771
|
+
await rm(wd, { recursive: true, force: true });
|
|
3772
|
+
}
|
|
3773
|
+
});
|
|
3774
|
+
it('denies Autopilot ralplan to ultragoal with unsupported evidence even when native consensus is valid', async () => {
|
|
3775
|
+
const wd = await mkdtemp(join(tmpdir(), 'omx-state-ops-autopilot-ralplan-unsupported-valid-consensus-deny-'));
|
|
3776
|
+
try {
|
|
3777
|
+
await withOmxRootEnv(wd, async () => {
|
|
3778
|
+
const sessionId = 'sess-autopilot-ralplan-unsupported-valid-consensus-deny';
|
|
3779
|
+
const sessionDir = join(wd, '.omx', 'state', 'sessions', sessionId);
|
|
3780
|
+
await mkdir(sessionDir, { recursive: true });
|
|
3781
|
+
await writeNativeSubagentTracking(wd, sessionId);
|
|
3782
|
+
await writeFile(join(sessionDir, 'autopilot-state.json'), JSON.stringify({
|
|
3783
|
+
active: true,
|
|
3784
|
+
mode: 'autopilot',
|
|
3785
|
+
current_phase: 'ralplan',
|
|
3786
|
+
native_subagent_support: {
|
|
3787
|
+
status: 'unsupported',
|
|
3788
|
+
reason: 'multi_agent_v1_unavailable',
|
|
3789
|
+
source: 'post_tool_failure',
|
|
3790
|
+
},
|
|
3791
|
+
state: {
|
|
3792
|
+
handoff_artifacts: {
|
|
3793
|
+
ralplan: {
|
|
3794
|
+
plan_path: '.omx/plans/prd.md',
|
|
3795
|
+
test_spec_path: '.omx/plans/test-spec.md',
|
|
3796
|
+
},
|
|
3797
|
+
ralplan_consensus_gate: ralplanConsensusGate(sessionId, 'native_subagent'),
|
|
3798
|
+
},
|
|
3799
|
+
},
|
|
3800
|
+
}, null, 2));
|
|
3801
|
+
const response = await executeStateOperation('state_write', {
|
|
3802
|
+
workingDirectory: wd,
|
|
3803
|
+
session_id: sessionId,
|
|
3804
|
+
mode: 'autopilot',
|
|
3805
|
+
active: true,
|
|
3806
|
+
current_phase: 'ultragoal',
|
|
3807
|
+
});
|
|
3808
|
+
assert.equal(response.isError, true);
|
|
3809
|
+
const error = String(response.payload.error || '');
|
|
3810
|
+
assert.match(error, /terminalize non-clean/);
|
|
3811
|
+
assert.match(error, /blocked\/cancelled\/failed/);
|
|
3812
|
+
const state = JSON.parse(await readFile(join(sessionDir, 'autopilot-state.json'), 'utf-8'));
|
|
3813
|
+
assert.equal(state.current_phase, 'ralplan');
|
|
3814
|
+
});
|
|
3815
|
+
}
|
|
3816
|
+
finally {
|
|
3817
|
+
await rm(wd, { recursive: true, force: true });
|
|
3818
|
+
}
|
|
3819
|
+
});
|
|
3083
3820
|
for (const { lane, architectVerdict, criticVerdict } of [
|
|
3084
3821
|
{ lane: 'architect', architectVerdict: 'iterate', criticVerdict: 'approve' },
|
|
3085
3822
|
{ lane: 'critic', architectVerdict: 'approve', criticVerdict: 'iterate' },
|