oh-my-codex 0.18.1 → 0.18.3
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 +23 -0
- package/dist/agents/__tests__/definitions.test.js.map +1 -1
- package/dist/agents/__tests__/native-config.test.js +20 -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 +40 -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/auth/__tests__/config-sessions.test.d.ts +2 -0
- package/dist/auth/__tests__/config-sessions.test.d.ts.map +1 -0
- package/dist/auth/__tests__/config-sessions.test.js +48 -0
- package/dist/auth/__tests__/config-sessions.test.js.map +1 -0
- package/dist/auth/__tests__/quota-rotation.test.d.ts +2 -0
- package/dist/auth/__tests__/quota-rotation.test.d.ts.map +1 -0
- package/dist/auth/__tests__/quota-rotation.test.js +33 -0
- package/dist/auth/__tests__/quota-rotation.test.js.map +1 -0
- package/dist/auth/__tests__/redact.test.d.ts +2 -0
- package/dist/auth/__tests__/redact.test.d.ts.map +1 -0
- package/dist/auth/__tests__/redact.test.js +20 -0
- package/dist/auth/__tests__/redact.test.js.map +1 -0
- package/dist/auth/__tests__/storage.test.d.ts +2 -0
- package/dist/auth/__tests__/storage.test.d.ts.map +1 -0
- package/dist/auth/__tests__/storage.test.js +108 -0
- package/dist/auth/__tests__/storage.test.js.map +1 -0
- package/dist/auth/config.d.ts +9 -0
- package/dist/auth/config.d.ts.map +1 -0
- package/dist/auth/config.js +77 -0
- package/dist/auth/config.js.map +1 -0
- package/dist/auth/hotswap.d.ts +36 -0
- package/dist/auth/hotswap.d.ts.map +1 -0
- package/dist/auth/hotswap.js +159 -0
- package/dist/auth/hotswap.js.map +1 -0
- package/dist/auth/index.d.ts +8 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +8 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/paths.d.ts +12 -0
- package/dist/auth/paths.d.ts.map +1 -0
- package/dist/auth/paths.js +78 -0
- package/dist/auth/paths.js.map +1 -0
- package/dist/auth/quota-detector.d.ts +10 -0
- package/dist/auth/quota-detector.d.ts.map +1 -0
- package/dist/auth/quota-detector.js +40 -0
- package/dist/auth/quota-detector.js.map +1 -0
- package/dist/auth/redact.d.ts +2 -0
- package/dist/auth/redact.d.ts.map +1 -0
- package/dist/auth/redact.js +26 -0
- package/dist/auth/redact.js.map +1 -0
- package/dist/auth/rotation.d.ts +9 -0
- package/dist/auth/rotation.d.ts.map +1 -0
- package/dist/auth/rotation.js +26 -0
- package/dist/auth/rotation.js.map +1 -0
- package/dist/auth/sessions.d.ts +15 -0
- package/dist/auth/sessions.d.ts.map +1 -0
- package/dist/auth/sessions.js +62 -0
- package/dist/auth/sessions.js.map +1 -0
- package/dist/auth/storage.d.ts +27 -0
- package/dist/auth/storage.d.ts.map +1 -0
- package/dist/auth/storage.js +111 -0
- package/dist/auth/storage.js.map +1 -0
- package/dist/catalog/__tests__/generator.test.js +4 -0
- package/dist/catalog/__tests__/generator.test.js.map +1 -1
- package/dist/cli/__tests__/auth.test.d.ts +2 -0
- package/dist/cli/__tests__/auth.test.d.ts.map +1 -0
- package/dist/cli/__tests__/auth.test.js +168 -0
- package/dist/cli/__tests__/auth.test.js.map +1 -0
- package/dist/cli/__tests__/doctor-warning-copy.test.js +112 -5
- package/dist/cli/__tests__/doctor-warning-copy.test.js.map +1 -1
- package/dist/cli/__tests__/explore.test.js +20 -0
- package/dist/cli/__tests__/explore.test.js.map +1 -1
- package/dist/cli/__tests__/index.test.js +171 -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__/nested-help-routing.test.js +1 -0
- package/dist/cli/__tests__/nested-help-routing.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/__tests__/setup-agents-overwrite.test.js +30 -1
- package/dist/cli/__tests__/setup-agents-overwrite.test.js.map +1 -1
- package/dist/cli/__tests__/setup-install-mode.test.js +47 -0
- package/dist/cli/__tests__/setup-install-mode.test.js.map +1 -1
- package/dist/cli/auth.d.ts +4 -0
- package/dist/cli/auth.d.ts.map +1 -0
- package/dist/cli/auth.js +89 -0
- package/dist/cli/auth.js.map +1 -0
- package/dist/cli/doctor.d.ts.map +1 -1
- package/dist/cli/doctor.js +190 -7
- package/dist/cli/doctor.js.map +1 -1
- package/dist/cli/explore.d.ts.map +1 -1
- package/dist/cli/explore.js +12 -0
- package/dist/cli/explore.js.map +1 -1
- package/dist/cli/index.d.ts +27 -3
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +245 -47
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +11 -3
- package/dist/cli/setup.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/__tests__/deep-interview.test.d.ts +2 -0
- package/dist/config/__tests__/deep-interview.test.d.ts.map +1 -0
- package/dist/config/__tests__/deep-interview.test.js +239 -0
- package/dist/config/__tests__/deep-interview.test.js.map +1 -0
- package/dist/config/__tests__/generator-idempotent.test.js +123 -0
- package/dist/config/__tests__/generator-idempotent.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/deep-interview.d.ts +22 -0
- package/dist/config/deep-interview.d.ts.map +1 -0
- package/dist/config/deep-interview.js +151 -0
- package/dist/config/deep-interview.js.map +1 -0
- package/dist/config/generator.d.ts +19 -2
- package/dist/config/generator.d.ts.map +1 -1
- package/dist/config/generator.js +198 -29
- 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__/agents-overlay.test.js +2 -0
- package/dist/hooks/__tests__/agents-overlay.test.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__/explore-routing.test.js +1 -0
- package/dist/hooks/__tests__/explore-routing.test.js.map +1 -1
- package/dist/hooks/__tests__/keyword-detector.test.js +471 -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/deep-interview-config-instruction.d.ts +3 -0
- package/dist/hooks/deep-interview-config-instruction.d.ts.map +1 -0
- package/dist/hooks/deep-interview-config-instruction.js +47 -0
- package/dist/hooks/deep-interview-config-instruction.js.map +1 -0
- package/dist/hooks/explore-routing.d.ts.map +1 -1
- package/dist/hooks/explore-routing.js +1 -0
- package/dist/hooks/explore-routing.js.map +1 -1
- package/dist/hooks/keyword-detector.d.ts +6 -1
- package/dist/hooks/keyword-detector.d.ts.map +1 -1
- package/dist/hooks/keyword-detector.js +80 -14
- 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 +213 -17
- 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 +171 -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 +1 -1
- package/dist/hud/reconcile.d.ts.map +1 -1
- package/dist/hud/reconcile.js +14 -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 +17 -3
- package/dist/hud/tmux.d.ts.map +1 -1
- package/dist/hud/tmux.js +96 -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/deep-interview.d.ts +2 -0
- package/dist/question/deep-interview.d.ts.map +1 -1
- package/dist/question/deep-interview.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 +1034 -28
- 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 +238 -36
- 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__/planning-gate.test.d.ts +2 -0
- package/dist/state/__tests__/planning-gate.test.d.ts.map +1 -0
- package/dist/state/__tests__/planning-gate.test.js +219 -0
- package/dist/state/__tests__/planning-gate.test.js.map +1 -0
- 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 +24 -1
- package/dist/state/workflow-transition.d.ts.map +1 -1
- package/dist/state/workflow-transition.js +70 -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 +144 -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 +22 -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/deep-interview/SKILL.md +10 -0
- 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 +24 -5
- 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/prompts/scholastic.md +11 -0
- 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/deep-interview/SKILL.md +10 -0
- 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 +24 -5
- package/src/scripts/__tests__/codex-native-hook.test.ts +1307 -61
- 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 +260 -31
- package/src/scripts/notify-dispatcher.ts +202 -4
- package/src/scripts/run-test-files.ts +13 -0
- package/templates/catalog-manifest.json +27 -0
|
@@ -8,6 +8,21 @@ import { detectKeywords, detectPrimaryKeyword, recordSkillActivation, DEEP_INTER
|
|
|
8
8
|
import { SKILL_ACTIVE_STATE_FILE } from '../../state/skill-active.js';
|
|
9
9
|
import { isUnderspecifiedForExecution, applyRalplanGate } from '../keyword-detector.js';
|
|
10
10
|
import { KEYWORD_TRIGGER_DEFINITIONS } from '../keyword-registry.js';
|
|
11
|
+
async function withIsolatedHome(prefix, run) {
|
|
12
|
+
const homeDir = await mkdtemp(join(tmpdir(), `omx-keyword-home-${prefix}-`));
|
|
13
|
+
const previousHome = process.env.HOME;
|
|
14
|
+
try {
|
|
15
|
+
process.env.HOME = homeDir;
|
|
16
|
+
return await run(homeDir);
|
|
17
|
+
}
|
|
18
|
+
finally {
|
|
19
|
+
if (typeof previousHome === 'string')
|
|
20
|
+
process.env.HOME = previousHome;
|
|
21
|
+
else
|
|
22
|
+
delete process.env.HOME;
|
|
23
|
+
await rm(homeDir, { recursive: true, force: true });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
11
26
|
describe('keyword detector team compatibility', () => {
|
|
12
27
|
it('keeps explicit $skill order in detectKeywords results (left-to-right)', () => {
|
|
13
28
|
const matches = detectKeywords('$analyze $ultraqa $code-review now');
|
|
@@ -194,6 +209,11 @@ describe('keyword detector team compatibility', () => {
|
|
|
194
209
|
assert.equal(detectPrimaryKeyword('cleanup stale deep-interview state after session clear'), null);
|
|
195
210
|
assert.equal(detectPrimaryKeyword('remove the stale deep interview lock from .omx/state'), null);
|
|
196
211
|
});
|
|
212
|
+
it('does not trigger deep-interview from casual discussion mentions', () => {
|
|
213
|
+
assert.equal(detectPrimaryKeyword('the deep interview report is useful context for the next plan'), null);
|
|
214
|
+
assert.equal(detectPrimaryKeyword('we already did a deep interview and should not reactivate it'), null);
|
|
215
|
+
assert.equal(detectPrimaryKeyword('this interview transcript says implementation is ready'), null);
|
|
216
|
+
});
|
|
197
217
|
it('maps "gather requirements" to deep-interview skill', () => {
|
|
198
218
|
const match = detectPrimaryKeyword('let us gather requirements first');
|
|
199
219
|
assert.ok(match);
|
|
@@ -285,6 +305,13 @@ describe('explicit skill-name invocation requirement', () => {
|
|
|
285
305
|
it('does not trigger ralplan from bare skill-name usage', () => {
|
|
286
306
|
assert.equal(detectPrimaryKeyword('please do ralplan first'), null);
|
|
287
307
|
});
|
|
308
|
+
it('detects explicit prometheus-strict invocation only', () => {
|
|
309
|
+
const match = detectPrimaryKeyword('please run $prometheus-strict before implementation');
|
|
310
|
+
assert.ok(match);
|
|
311
|
+
assert.equal(match.skill, 'prometheus-strict');
|
|
312
|
+
assert.equal(match.keyword.toLowerCase(), '$prometheus-strict');
|
|
313
|
+
assert.equal(detectPrimaryKeyword('please use prometheus-strict planning here'), null);
|
|
314
|
+
});
|
|
288
315
|
});
|
|
289
316
|
describe('keyword registry coverage', () => {
|
|
290
317
|
it('includes key team aliases in runtime keyword registry', () => {
|
|
@@ -304,6 +331,7 @@ describe('keyword registry coverage', () => {
|
|
|
304
331
|
assert.ok(registryKeywords.has('wiki lint'));
|
|
305
332
|
assert.ok(registryKeywords.has('$autoresearch'));
|
|
306
333
|
assert.ok(registryKeywords.has('$ultragoal'));
|
|
334
|
+
assert.ok(registryKeywords.has('$prometheus-strict'));
|
|
307
335
|
assert.ok(registryKeywords.has('ultragoal'));
|
|
308
336
|
});
|
|
309
337
|
});
|
|
@@ -398,7 +426,7 @@ describe('keyword detector skill-active-state lifecycle', () => {
|
|
|
398
426
|
await rm(root, { recursive: true, force: true });
|
|
399
427
|
}
|
|
400
428
|
});
|
|
401
|
-
it('writes skill-active-state.json with
|
|
429
|
+
it('writes skill-active-state.json with deep-interview phase when autopilot keyword activates', async () => {
|
|
402
430
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-keyword-state-'));
|
|
403
431
|
const stateDir = join(cwd, '.omx', 'state');
|
|
404
432
|
try {
|
|
@@ -413,11 +441,11 @@ describe('keyword detector skill-active-state lifecycle', () => {
|
|
|
413
441
|
});
|
|
414
442
|
assert.ok(result);
|
|
415
443
|
assert.equal(result.skill, 'autopilot');
|
|
416
|
-
assert.equal(result.phase, '
|
|
444
|
+
assert.equal(result.phase, 'deep-interview');
|
|
417
445
|
assert.equal(result.active, true);
|
|
418
446
|
assert.deepEqual(result.active_skills, [{
|
|
419
447
|
skill: 'autopilot',
|
|
420
|
-
phase: '
|
|
448
|
+
phase: 'deep-interview',
|
|
421
449
|
active: true,
|
|
422
450
|
activated_at: '2026-02-25T00:00:00.000Z',
|
|
423
451
|
updated_at: '2026-02-25T00:00:00.000Z',
|
|
@@ -439,7 +467,27 @@ describe('keyword detector skill-active-state lifecycle', () => {
|
|
|
439
467
|
assert.equal(modeState.review_cycle, 0);
|
|
440
468
|
assert.equal(modeState.max_iterations, 10);
|
|
441
469
|
assert.deepEqual(modeState.state.phase_cycle, ['deep-interview', 'ralplan', 'ultragoal', 'code-review', 'ultraqa']);
|
|
442
|
-
assert.deepEqual(modeState.state.
|
|
470
|
+
assert.deepEqual(modeState.state.deep_interview_gate, {
|
|
471
|
+
status: 'required',
|
|
472
|
+
skip_reason: null,
|
|
473
|
+
rationale: 'Autopilot starts at the deep-interview gate by default; clear bounded tasks may skip only with an explicit persisted skip reason.',
|
|
474
|
+
});
|
|
475
|
+
assert.deepEqual(modeState.state.handoff_artifacts, {
|
|
476
|
+
deep_interview: null,
|
|
477
|
+
ralplan: null,
|
|
478
|
+
ralplan_consensus_gate: {
|
|
479
|
+
required: true,
|
|
480
|
+
sequence: ['architect-review', 'critic-review'],
|
|
481
|
+
planning_artifacts_are_not_consensus: true,
|
|
482
|
+
required_review_roles: ['architect', 'critic'],
|
|
483
|
+
ralplan_architect_review: null,
|
|
484
|
+
ralplan_critic_review: null,
|
|
485
|
+
complete: false,
|
|
486
|
+
},
|
|
487
|
+
ultragoal: null,
|
|
488
|
+
code_review: null,
|
|
489
|
+
ultraqa: null,
|
|
490
|
+
});
|
|
443
491
|
assert.equal(modeState.state.review_verdict, null);
|
|
444
492
|
assert.equal(modeState.state.qa_verdict, null);
|
|
445
493
|
assert.equal(modeState.state.return_to_ralplan_reason, null);
|
|
@@ -792,18 +840,56 @@ describe('keyword detector skill-active-state lifecycle', () => {
|
|
|
792
840
|
}, null, 2));
|
|
793
841
|
const result = await recordSkillActivation({
|
|
794
842
|
stateDir,
|
|
795
|
-
text: '$
|
|
843
|
+
text: '$ultragoal turn the clarified spec into goals',
|
|
796
844
|
sessionId: 'sess-handoff',
|
|
797
845
|
nowIso: '2026-04-10T00:00:00.000Z',
|
|
798
846
|
});
|
|
799
847
|
assert.equal(result?.transition_error, undefined);
|
|
800
|
-
assert.equal(result?.
|
|
848
|
+
assert.equal(result?.skill, 'ultragoal');
|
|
849
|
+
assert.equal(result?.initialized_mode, 'ultragoal');
|
|
850
|
+
assert.equal(result?.initialized_state_path, '.omx/state/sessions/sess-handoff/ultragoal-state.json');
|
|
851
|
+
assert.equal(result?.transition_message, 'mode transiting: deep-interview -> ultragoal');
|
|
801
852
|
const completed = JSON.parse(await readFile(join(stateDir, 'sessions', 'sess-handoff', 'deep-interview-state.json'), 'utf-8'));
|
|
802
853
|
assert.equal(completed.active, false);
|
|
803
854
|
assert.equal(completed.current_phase, 'completed');
|
|
804
855
|
assert.equal(completed.question_enforcement?.status, 'cleared');
|
|
805
856
|
assert.equal(completed.question_enforcement?.clear_reason, 'handoff');
|
|
806
857
|
assert.ok(completed.question_enforcement?.cleared_at);
|
|
858
|
+
const ultragoal = JSON.parse(await readFile(join(stateDir, 'sessions', 'sess-handoff', 'ultragoal-state.json'), 'utf-8'));
|
|
859
|
+
assert.equal(ultragoal.active, true);
|
|
860
|
+
assert.equal(ultragoal.mode, 'ultragoal');
|
|
861
|
+
assert.equal(ultragoal.current_phase, 'planning');
|
|
862
|
+
}
|
|
863
|
+
finally {
|
|
864
|
+
await rm(cwd, { recursive: true, force: true });
|
|
865
|
+
}
|
|
866
|
+
});
|
|
867
|
+
it('keeps ralplan as an allowlisted deep-interview forward handoff', async () => {
|
|
868
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-keyword-ralplan-handoff-'));
|
|
869
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
870
|
+
try {
|
|
871
|
+
await mkdir(join(stateDir, 'sessions', 'sess-ralplan-handoff'), { recursive: true });
|
|
872
|
+
await writeFile(join(stateDir, 'sessions', 'sess-ralplan-handoff', SKILL_ACTIVE_STATE_FILE), JSON.stringify({
|
|
873
|
+
version: 1,
|
|
874
|
+
active: true,
|
|
875
|
+
skill: 'deep-interview',
|
|
876
|
+
phase: 'planning',
|
|
877
|
+
session_id: 'sess-ralplan-handoff',
|
|
878
|
+
active_skills: [{ skill: 'deep-interview', phase: 'planning', active: true, session_id: 'sess-ralplan-handoff' }],
|
|
879
|
+
}, null, 2));
|
|
880
|
+
await writeFile(join(stateDir, 'sessions', 'sess-ralplan-handoff', 'deep-interview-state.json'), JSON.stringify({ active: true, mode: 'deep-interview', current_phase: 'intent-first' }, null, 2));
|
|
881
|
+
const result = await recordSkillActivation({
|
|
882
|
+
stateDir,
|
|
883
|
+
text: '$ralplan implement the approved contract',
|
|
884
|
+
sessionId: 'sess-ralplan-handoff',
|
|
885
|
+
nowIso: '2026-04-10T00:00:00.000Z',
|
|
886
|
+
});
|
|
887
|
+
assert.equal(result?.transition_error, undefined);
|
|
888
|
+
assert.equal(result?.skill, 'ralplan');
|
|
889
|
+
assert.equal(result?.transition_message, 'mode transiting: deep-interview -> ralplan');
|
|
890
|
+
const completed = JSON.parse(await readFile(join(stateDir, 'sessions', 'sess-ralplan-handoff', 'deep-interview-state.json'), 'utf-8'));
|
|
891
|
+
assert.equal(completed.active, false);
|
|
892
|
+
assert.equal(completed.current_phase, 'completed');
|
|
807
893
|
}
|
|
808
894
|
finally {
|
|
809
895
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -1008,6 +1094,292 @@ describe('keyword detector skill-active-state lifecycle', () => {
|
|
|
1008
1094
|
await rm(cwd, { recursive: true, force: true });
|
|
1009
1095
|
}
|
|
1010
1096
|
});
|
|
1097
|
+
it('persists repo-local deep-interview config values into activation and mode state', async () => {
|
|
1098
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-keyword-state-deep-interview-config-'));
|
|
1099
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
1100
|
+
try {
|
|
1101
|
+
await mkdir(join(cwd, '.omx'), { recursive: true });
|
|
1102
|
+
await mkdir(stateDir, { recursive: true });
|
|
1103
|
+
await writeFile(join(cwd, '.omx', 'config.toml'), `[omx.deepInterview]
|
|
1104
|
+
defaultProfile = "standard"
|
|
1105
|
+
standardThreshold = 0.05
|
|
1106
|
+
standardMaxRounds = 15
|
|
1107
|
+
enableChallengeModes = false
|
|
1108
|
+
`);
|
|
1109
|
+
const result = await recordSkillActivation({
|
|
1110
|
+
stateDir,
|
|
1111
|
+
sourceCwd: cwd,
|
|
1112
|
+
text: '$deep-interview clarify runtime config',
|
|
1113
|
+
sessionId: 'sess-deep-interview-config',
|
|
1114
|
+
nowIso: '2026-02-25T00:00:00.000Z',
|
|
1115
|
+
});
|
|
1116
|
+
assert.ok(result);
|
|
1117
|
+
assert.equal(result.skill, 'deep-interview');
|
|
1118
|
+
assert.equal(result.deep_interview_config?.profile, 'standard');
|
|
1119
|
+
assert.equal(result.deep_interview_config?.threshold, 0.05);
|
|
1120
|
+
assert.equal(result.deep_interview_config?.maxRounds, 15);
|
|
1121
|
+
assert.equal(result.initialized_state_path, '.omx/state/sessions/sess-deep-interview-config/deep-interview-state.json');
|
|
1122
|
+
const modeState = JSON.parse(await readFile(join(stateDir, 'sessions', 'sess-deep-interview-config', DEEP_INTERVIEW_STATE_FILE), 'utf-8'));
|
|
1123
|
+
assert.equal(modeState.profile, 'standard');
|
|
1124
|
+
assert.equal(modeState.threshold, 0.05);
|
|
1125
|
+
assert.equal(modeState.max_rounds, 15);
|
|
1126
|
+
assert.equal(modeState.enable_challenge_modes, false);
|
|
1127
|
+
assert.equal(modeState.config_source, join(cwd, '.omx', 'config.toml'));
|
|
1128
|
+
assert.equal(modeState.deep_interview_config?.sourcePath, join(cwd, '.omx', 'config.toml'));
|
|
1129
|
+
}
|
|
1130
|
+
finally {
|
|
1131
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1132
|
+
}
|
|
1133
|
+
});
|
|
1134
|
+
it('persists deep-interview config when mixed workflow prompts defer execution modes', async () => {
|
|
1135
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-keyword-state-deep-interview-config-mixed-'));
|
|
1136
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
1137
|
+
const sessionId = 'sess-deep-interview-config-mixed';
|
|
1138
|
+
try {
|
|
1139
|
+
await mkdir(join(cwd, '.omx'), { recursive: true });
|
|
1140
|
+
await mkdir(stateDir, { recursive: true });
|
|
1141
|
+
await writeFile(join(cwd, '.omx', 'config.toml'), `[omx.deepInterview]
|
|
1142
|
+
defaultProfile = "deep"
|
|
1143
|
+
deepThreshold = 0.13
|
|
1144
|
+
deepMaxRounds = 21
|
|
1145
|
+
enableChallengeModes = false
|
|
1146
|
+
`);
|
|
1147
|
+
const result = await recordSkillActivation({
|
|
1148
|
+
stateDir,
|
|
1149
|
+
sourceCwd: cwd,
|
|
1150
|
+
text: '$autopilot $deep-interview prove mixed workflow config',
|
|
1151
|
+
sessionId,
|
|
1152
|
+
nowIso: '2026-02-25T00:00:00.000Z',
|
|
1153
|
+
});
|
|
1154
|
+
assert.ok(result);
|
|
1155
|
+
assert.equal(result.skill, 'deep-interview');
|
|
1156
|
+
assert.deepEqual(result.deferred_skills, ['autopilot']);
|
|
1157
|
+
assert.equal(result.input_lock?.active, true);
|
|
1158
|
+
assert.equal(result.deep_interview_config?.profile, 'deep');
|
|
1159
|
+
assert.equal(result.deep_interview_config?.threshold, 0.13);
|
|
1160
|
+
assert.equal(result.deep_interview_config?.maxRounds, 21);
|
|
1161
|
+
assert.equal(result.deep_interview_config?.enableChallengeModes, false);
|
|
1162
|
+
const modeState = JSON.parse(await readFile(join(stateDir, 'sessions', sessionId, DEEP_INTERVIEW_STATE_FILE), 'utf-8'));
|
|
1163
|
+
assert.equal(modeState.profile, 'deep');
|
|
1164
|
+
assert.equal(modeState.threshold, 0.13);
|
|
1165
|
+
assert.equal(modeState.max_rounds, 21);
|
|
1166
|
+
assert.equal(modeState.enable_challenge_modes, false);
|
|
1167
|
+
assert.equal(modeState.config_source, join(cwd, '.omx', 'config.toml'));
|
|
1168
|
+
assert.equal(modeState.deep_interview_config?.profile, 'deep');
|
|
1169
|
+
assert.equal(modeState.input_lock?.active, true);
|
|
1170
|
+
}
|
|
1171
|
+
finally {
|
|
1172
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1173
|
+
}
|
|
1174
|
+
});
|
|
1175
|
+
it('shows before-after state change when deep-interview config is added at runtime', async () => {
|
|
1176
|
+
await withIsolatedHome('deep-interview-config-before-after', async () => {
|
|
1177
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-keyword-state-deep-interview-config-before-after-'));
|
|
1178
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
1179
|
+
const sessionId = 'sess-deep-interview-config-before-after';
|
|
1180
|
+
const statePath = join(stateDir, 'sessions', sessionId, DEEP_INTERVIEW_STATE_FILE);
|
|
1181
|
+
try {
|
|
1182
|
+
await mkdir(join(cwd, '.omx'), { recursive: true });
|
|
1183
|
+
await mkdir(stateDir, { recursive: true });
|
|
1184
|
+
const before = await recordSkillActivation({
|
|
1185
|
+
stateDir,
|
|
1186
|
+
sourceCwd: cwd,
|
|
1187
|
+
text: '$deep-interview prove config before state',
|
|
1188
|
+
sessionId,
|
|
1189
|
+
nowIso: '2026-02-25T00:00:00.000Z',
|
|
1190
|
+
});
|
|
1191
|
+
const beforeModeState = JSON.parse(await readFile(statePath, 'utf-8'));
|
|
1192
|
+
assert.ok(before);
|
|
1193
|
+
assert.equal(before.deep_interview_config, undefined);
|
|
1194
|
+
assert.equal(beforeModeState.deep_interview_config, undefined);
|
|
1195
|
+
assert.equal(beforeModeState.profile, undefined);
|
|
1196
|
+
assert.equal(beforeModeState.threshold, undefined);
|
|
1197
|
+
assert.equal(beforeModeState.max_rounds, undefined);
|
|
1198
|
+
assert.equal(beforeModeState.config_source, undefined);
|
|
1199
|
+
await writeFile(join(cwd, '.omx', 'config.toml'), `[omx.deepInterview]
|
|
1200
|
+
defaultProfile = "standard"
|
|
1201
|
+
standardThreshold = 0.05
|
|
1202
|
+
standardMaxRounds = 15
|
|
1203
|
+
`);
|
|
1204
|
+
const after = await recordSkillActivation({
|
|
1205
|
+
stateDir,
|
|
1206
|
+
sourceCwd: cwd,
|
|
1207
|
+
text: '$deep-interview prove config after state',
|
|
1208
|
+
sessionId,
|
|
1209
|
+
nowIso: '2026-02-25T00:00:01.000Z',
|
|
1210
|
+
});
|
|
1211
|
+
const afterModeState = JSON.parse(await readFile(statePath, 'utf-8'));
|
|
1212
|
+
assert.ok(after);
|
|
1213
|
+
assert.equal(after.deep_interview_config?.profile, 'standard');
|
|
1214
|
+
assert.equal(after.deep_interview_config?.threshold, 0.05);
|
|
1215
|
+
assert.equal(after.deep_interview_config?.maxRounds, 15);
|
|
1216
|
+
assert.equal(afterModeState.deep_interview_config?.profile, 'standard');
|
|
1217
|
+
assert.equal(afterModeState.profile, 'standard');
|
|
1218
|
+
assert.equal(afterModeState.threshold, 0.05);
|
|
1219
|
+
assert.equal(afterModeState.max_rounds, 15);
|
|
1220
|
+
assert.equal(afterModeState.config_source, join(cwd, '.omx', 'config.toml'));
|
|
1221
|
+
}
|
|
1222
|
+
finally {
|
|
1223
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1224
|
+
}
|
|
1225
|
+
});
|
|
1226
|
+
});
|
|
1227
|
+
it('preserves deep-interview config values during continuation prompts', async () => {
|
|
1228
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-keyword-state-deep-interview-config-continuation-'));
|
|
1229
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
1230
|
+
const sessionId = 'sess-deep-interview-config-continuation';
|
|
1231
|
+
const statePath = join(stateDir, 'sessions', sessionId, DEEP_INTERVIEW_STATE_FILE);
|
|
1232
|
+
try {
|
|
1233
|
+
await mkdir(join(cwd, '.omx'), { recursive: true });
|
|
1234
|
+
await mkdir(stateDir, { recursive: true });
|
|
1235
|
+
await writeFile(join(cwd, '.omx', 'config.toml'), `[omx.deepInterview]
|
|
1236
|
+
defaultProfile = "standard"
|
|
1237
|
+
standardThreshold = 0.05
|
|
1238
|
+
standardMaxRounds = 15
|
|
1239
|
+
`);
|
|
1240
|
+
await recordSkillActivation({
|
|
1241
|
+
stateDir,
|
|
1242
|
+
sourceCwd: cwd,
|
|
1243
|
+
text: '$deep-interview prove config continuation',
|
|
1244
|
+
sessionId,
|
|
1245
|
+
nowIso: '2026-02-25T00:00:00.000Z',
|
|
1246
|
+
});
|
|
1247
|
+
const continued = await recordSkillActivation({
|
|
1248
|
+
stateDir,
|
|
1249
|
+
sourceCwd: cwd,
|
|
1250
|
+
text: 'continue',
|
|
1251
|
+
sessionId,
|
|
1252
|
+
nowIso: '2026-02-25T00:00:01.000Z',
|
|
1253
|
+
});
|
|
1254
|
+
const modeState = JSON.parse(await readFile(statePath, 'utf-8'));
|
|
1255
|
+
assert.equal(continued?.skill, 'deep-interview');
|
|
1256
|
+
assert.equal(continued?.deep_interview_config?.profile, 'standard');
|
|
1257
|
+
assert.equal(continued?.deep_interview_config?.threshold, 0.05);
|
|
1258
|
+
assert.equal(continued?.deep_interview_config?.maxRounds, 15);
|
|
1259
|
+
assert.equal(modeState.deep_interview_config?.profile, 'standard');
|
|
1260
|
+
assert.equal(modeState.profile, 'standard');
|
|
1261
|
+
assert.equal(modeState.threshold, 0.05);
|
|
1262
|
+
assert.equal(modeState.max_rounds, 15);
|
|
1263
|
+
}
|
|
1264
|
+
finally {
|
|
1265
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1266
|
+
}
|
|
1267
|
+
});
|
|
1268
|
+
it('preserves explicit deep-interview profile flags during continuation prompts', async () => {
|
|
1269
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-keyword-state-deep-interview-config-profile-continuation-'));
|
|
1270
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
1271
|
+
const sessionId = 'sess-deep-interview-config-profile-continuation';
|
|
1272
|
+
const statePath = join(stateDir, 'sessions', sessionId, DEEP_INTERVIEW_STATE_FILE);
|
|
1273
|
+
try {
|
|
1274
|
+
await mkdir(join(cwd, '.omx'), { recursive: true });
|
|
1275
|
+
await mkdir(stateDir, { recursive: true });
|
|
1276
|
+
await writeFile(join(cwd, '.omx', 'config.toml'), `[omx.deepInterview]
|
|
1277
|
+
defaultProfile = "standard"
|
|
1278
|
+
standardThreshold = 0.22
|
|
1279
|
+
standardMaxRounds = 13
|
|
1280
|
+
deepThreshold = 0.13
|
|
1281
|
+
deepMaxRounds = 21
|
|
1282
|
+
`);
|
|
1283
|
+
const started = await recordSkillActivation({
|
|
1284
|
+
stateDir,
|
|
1285
|
+
sourceCwd: cwd,
|
|
1286
|
+
text: '$deep-interview --deep prove explicit profile continuation',
|
|
1287
|
+
sessionId,
|
|
1288
|
+
nowIso: '2026-02-25T00:00:00.000Z',
|
|
1289
|
+
});
|
|
1290
|
+
const continued = await recordSkillActivation({
|
|
1291
|
+
stateDir,
|
|
1292
|
+
sourceCwd: cwd,
|
|
1293
|
+
text: 'continue',
|
|
1294
|
+
sessionId,
|
|
1295
|
+
nowIso: '2026-02-25T00:00:01.000Z',
|
|
1296
|
+
});
|
|
1297
|
+
const modeState = JSON.parse(await readFile(statePath, 'utf-8'));
|
|
1298
|
+
assert.equal(started?.deep_interview_config?.profile, 'deep');
|
|
1299
|
+
assert.equal(continued?.deep_interview_config?.profile, 'deep');
|
|
1300
|
+
assert.equal(continued?.deep_interview_config?.threshold, 0.13);
|
|
1301
|
+
assert.equal(continued?.deep_interview_config?.maxRounds, 21);
|
|
1302
|
+
assert.equal(modeState.deep_interview_config?.profile, 'deep');
|
|
1303
|
+
assert.equal(modeState.profile, 'deep');
|
|
1304
|
+
assert.equal(modeState.threshold, 0.13);
|
|
1305
|
+
assert.equal(modeState.max_rounds, 21);
|
|
1306
|
+
}
|
|
1307
|
+
finally {
|
|
1308
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1309
|
+
}
|
|
1310
|
+
});
|
|
1311
|
+
it('keeps the documented deep-interview Suggested Config executable through activation state', async () => {
|
|
1312
|
+
const skillDoc = await readFile(join(process.cwd(), 'skills', 'deep-interview', 'SKILL.md'), 'utf-8');
|
|
1313
|
+
const markerIndex = skillDoc.indexOf('## Suggested Config (optional)');
|
|
1314
|
+
assert.notEqual(markerIndex, -1);
|
|
1315
|
+
const configMatch = skillDoc.slice(markerIndex).match(/```toml\n([\s\S]*?)\n```/);
|
|
1316
|
+
assert.ok(configMatch);
|
|
1317
|
+
const documentedConfig = configMatch[1]?.trimEnd();
|
|
1318
|
+
assert.ok(documentedConfig);
|
|
1319
|
+
assert.match(documentedConfig, /standardThreshold = 0\.20/);
|
|
1320
|
+
assert.match(documentedConfig, /standardMaxRounds = 12/);
|
|
1321
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-keyword-state-deep-interview-doc-config-'));
|
|
1322
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
1323
|
+
const sessionId = 'sess-deep-interview-doc-config';
|
|
1324
|
+
const statePath = join(stateDir, 'sessions', sessionId, DEEP_INTERVIEW_STATE_FILE);
|
|
1325
|
+
try {
|
|
1326
|
+
await mkdir(join(cwd, '.omx'), { recursive: true });
|
|
1327
|
+
await mkdir(stateDir, { recursive: true });
|
|
1328
|
+
await writeFile(join(cwd, '.omx', 'config.toml'), `${documentedConfig}\n`);
|
|
1329
|
+
const result = await recordSkillActivation({
|
|
1330
|
+
stateDir,
|
|
1331
|
+
sourceCwd: cwd,
|
|
1332
|
+
text: '$deep-interview prove documented config runtime contract',
|
|
1333
|
+
sessionId,
|
|
1334
|
+
nowIso: '2026-02-25T00:00:00.000Z',
|
|
1335
|
+
});
|
|
1336
|
+
const modeState = JSON.parse(await readFile(statePath, 'utf-8'));
|
|
1337
|
+
assert.ok(result);
|
|
1338
|
+
assert.equal(result.deep_interview_config?.profile, 'standard');
|
|
1339
|
+
assert.equal(result.deep_interview_config?.threshold, 0.2);
|
|
1340
|
+
assert.equal(result.deep_interview_config?.maxRounds, 12);
|
|
1341
|
+
assert.equal(modeState.deep_interview_config?.profile, 'standard');
|
|
1342
|
+
assert.equal(modeState.profile, 'standard');
|
|
1343
|
+
assert.equal(modeState.threshold, 0.2);
|
|
1344
|
+
assert.equal(modeState.max_rounds, 12);
|
|
1345
|
+
assert.equal(modeState.config_source, join(cwd, '.omx', 'config.toml'));
|
|
1346
|
+
}
|
|
1347
|
+
finally {
|
|
1348
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1349
|
+
}
|
|
1350
|
+
});
|
|
1351
|
+
it('keeps deep-interview activation alive when repo config TOML is malformed', async () => {
|
|
1352
|
+
await withIsolatedHome('deep-interview-malformed-config', async () => {
|
|
1353
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-keyword-state-deep-interview-malformed-config-'));
|
|
1354
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
1355
|
+
const originalWarn = console.warn;
|
|
1356
|
+
try {
|
|
1357
|
+
console.warn = () => { };
|
|
1358
|
+
await mkdir(join(cwd, '.omx'), { recursive: true });
|
|
1359
|
+
await mkdir(stateDir, { recursive: true });
|
|
1360
|
+
await writeFile(join(cwd, '.omx', 'config.toml'), '[omx.deepInterview\nstandardThreshold = 0.05\n');
|
|
1361
|
+
const result = await recordSkillActivation({
|
|
1362
|
+
stateDir,
|
|
1363
|
+
sourceCwd: cwd,
|
|
1364
|
+
text: '$deep-interview clarify despite malformed config',
|
|
1365
|
+
sessionId: 'sess-deep-interview-malformed-config',
|
|
1366
|
+
nowIso: '2026-02-25T00:00:00.000Z',
|
|
1367
|
+
});
|
|
1368
|
+
assert.ok(result);
|
|
1369
|
+
assert.equal(result.skill, 'deep-interview');
|
|
1370
|
+
assert.equal(result.active, true);
|
|
1371
|
+
assert.equal(result.deep_interview_config, undefined);
|
|
1372
|
+
const modeState = JSON.parse(await readFile(join(stateDir, 'sessions', 'sess-deep-interview-malformed-config', DEEP_INTERVIEW_STATE_FILE), 'utf-8'));
|
|
1373
|
+
assert.equal(modeState.mode, 'deep-interview');
|
|
1374
|
+
assert.equal(modeState.active, true);
|
|
1375
|
+
assert.equal(modeState.deep_interview_config, undefined);
|
|
1376
|
+
}
|
|
1377
|
+
finally {
|
|
1378
|
+
console.warn = originalWarn;
|
|
1379
|
+
await rm(cwd, { recursive: true, force: true });
|
|
1380
|
+
}
|
|
1381
|
+
});
|
|
1382
|
+
});
|
|
1011
1383
|
it('creates the session-scoped deep-interview state directory before persisting mode state', async () => {
|
|
1012
1384
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-keyword-state-deep-interview-session-dir-'));
|
|
1013
1385
|
const stateDir = join(cwd, '.omx', 'state');
|
|
@@ -1151,7 +1523,7 @@ describe('keyword detector skill-active-state lifecycle', () => {
|
|
|
1151
1523
|
await rm(cwd, { recursive: true, force: true });
|
|
1152
1524
|
}
|
|
1153
1525
|
});
|
|
1154
|
-
it('records ultragoal as a prompt skill
|
|
1526
|
+
it('records ultragoal as a prompt skill with first-class mode state', async () => {
|
|
1155
1527
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-keyword-state-ultragoal-'));
|
|
1156
1528
|
const stateDir = join(cwd, '.omx', 'state');
|
|
1157
1529
|
try {
|
|
@@ -1163,9 +1535,12 @@ describe('keyword detector skill-active-state lifecycle', () => {
|
|
|
1163
1535
|
assert.ok(result);
|
|
1164
1536
|
assert.equal(result.skill, 'ultragoal');
|
|
1165
1537
|
assert.equal(result.keyword, '$ultragoal');
|
|
1166
|
-
assert.equal(result.initialized_mode,
|
|
1167
|
-
assert.equal(result.initialized_state_path,
|
|
1168
|
-
|
|
1538
|
+
assert.equal(result.initialized_mode, 'ultragoal');
|
|
1539
|
+
assert.equal(result.initialized_state_path, '.omx/state/ultragoal-state.json');
|
|
1540
|
+
const modeState = JSON.parse(await readFile(join(stateDir, 'ultragoal-state.json'), 'utf-8'));
|
|
1541
|
+
assert.equal(modeState.active, true);
|
|
1542
|
+
assert.equal(modeState.mode, 'ultragoal');
|
|
1543
|
+
assert.equal(modeState.current_phase, 'planning');
|
|
1169
1544
|
}
|
|
1170
1545
|
finally {
|
|
1171
1546
|
await rm(cwd, { recursive: true, force: true });
|
|
@@ -1696,7 +2071,7 @@ describe('isUnderspecifiedForExecution', () => {
|
|
|
1696
2071
|
});
|
|
1697
2072
|
});
|
|
1698
2073
|
describe('applyRalplanGate', () => {
|
|
1699
|
-
it('
|
|
2074
|
+
it('gates short team follow-up when only PRD/test-spec artifacts exist', async () => {
|
|
1700
2075
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-keyword-gate-followup-'));
|
|
1701
2076
|
try {
|
|
1702
2077
|
const plansDir = join(cwd, '.omx', 'plans');
|
|
@@ -1704,20 +2079,32 @@ describe('applyRalplanGate', () => {
|
|
|
1704
2079
|
await writeFile(join(plansDir, 'prd-issue-831.md'), '# Approved plan\n\nLaunch hint: omx team 3:executor "Execute approved issue 831 plan"\n');
|
|
1705
2080
|
await writeFile(join(plansDir, 'test-spec-issue-831.md'), '# Test spec\n');
|
|
1706
2081
|
const result = applyRalplanGate(['team'], 'team', { cwd });
|
|
1707
|
-
assert.equal(result.gateApplied,
|
|
1708
|
-
assert.deepEqual(result.keywords, ['
|
|
2082
|
+
assert.equal(result.gateApplied, true);
|
|
2083
|
+
assert.deepEqual(result.keywords, ['ralplan']);
|
|
1709
2084
|
}
|
|
1710
2085
|
finally {
|
|
1711
2086
|
await rm(cwd, { recursive: true, force: true });
|
|
1712
2087
|
}
|
|
1713
2088
|
});
|
|
1714
|
-
it('does not re-enter ralplan for a short approved
|
|
2089
|
+
it('does not re-enter ralplan for a short approved team follow-up with durable consensus', async () => {
|
|
1715
2090
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-keyword-gate-followup-ko-'));
|
|
1716
2091
|
try {
|
|
1717
2092
|
const plansDir = join(cwd, '.omx', 'plans');
|
|
2093
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
1718
2094
|
await mkdir(plansDir, { recursive: true });
|
|
2095
|
+
await mkdir(stateDir, { recursive: true });
|
|
1719
2096
|
await writeFile(join(plansDir, 'prd-issue-831.md'), '# Approved plan\n\nLaunch hint: omx team 3:executor "Execute approved issue 831 plan"\n');
|
|
1720
2097
|
await writeFile(join(plansDir, 'test-spec-issue-831.md'), '# Test spec\n');
|
|
2098
|
+
await writeFile(join(stateDir, 'ralplan-state.json'), JSON.stringify({
|
|
2099
|
+
current_phase: 'complete',
|
|
2100
|
+
planning_complete: true,
|
|
2101
|
+
ralplan_consensus_gate: {
|
|
2102
|
+
complete: true,
|
|
2103
|
+
sequence: ['architect-review', 'critic-review'],
|
|
2104
|
+
ralplan_architect_review: { agent_role: 'architect', verdict: 'approve', iteration: 1 },
|
|
2105
|
+
ralplan_critic_review: { agent_role: 'critic', verdict: 'approve', iteration: 1 },
|
|
2106
|
+
},
|
|
2107
|
+
}));
|
|
1721
2108
|
const result = applyRalplanGate(['team'], 'team으로 해줘', { cwd });
|
|
1722
2109
|
assert.equal(result.gateApplied, false);
|
|
1723
2110
|
assert.deepEqual(result.keywords, ['team']);
|
|
@@ -1726,13 +2113,25 @@ describe('applyRalplanGate', () => {
|
|
|
1726
2113
|
await rm(cwd, { recursive: true, force: true });
|
|
1727
2114
|
}
|
|
1728
2115
|
});
|
|
1729
|
-
it('does not re-enter ralplan for a short approved ralph follow-up', async () => {
|
|
2116
|
+
it('does not re-enter ralplan for a short approved ralph follow-up with durable consensus', async () => {
|
|
1730
2117
|
const cwd = await mkdtemp(join(tmpdir(), 'omx-keyword-gate-followup-ralph-'));
|
|
1731
2118
|
try {
|
|
1732
2119
|
const plansDir = join(cwd, '.omx', 'plans');
|
|
2120
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
1733
2121
|
await mkdir(plansDir, { recursive: true });
|
|
2122
|
+
await mkdir(stateDir, { recursive: true });
|
|
1734
2123
|
await writeFile(join(plansDir, 'prd-issue-832.md'), '# Approved plan\n\nLaunch hint: omx ralph "Execute approved issue 832 plan"\n');
|
|
1735
2124
|
await writeFile(join(plansDir, 'test-spec-issue-832.md'), '# Test spec\n');
|
|
2125
|
+
await writeFile(join(stateDir, 'ralplan-state.json'), JSON.stringify({
|
|
2126
|
+
current_phase: 'complete',
|
|
2127
|
+
planning_complete: true,
|
|
2128
|
+
ralplan_consensus_gate: {
|
|
2129
|
+
complete: true,
|
|
2130
|
+
sequence: ['architect-review', 'critic-review'],
|
|
2131
|
+
ralplan_architect_review: { agent_role: 'architect', verdict: 'approve', iteration: 1 },
|
|
2132
|
+
ralplan_critic_review: { agent_role: 'critic', verdict: 'approve', iteration: 1 },
|
|
2133
|
+
},
|
|
2134
|
+
}));
|
|
1736
2135
|
const result = applyRalplanGate(['ralph'], 'ralph please', { cwd, priorSkill: 'ralplan' });
|
|
1737
2136
|
assert.equal(result.gateApplied, false);
|
|
1738
2137
|
assert.deepEqual(result.keywords, ['ralph']);
|
|
@@ -1741,6 +2140,63 @@ describe('applyRalplanGate', () => {
|
|
|
1741
2140
|
await rm(cwd, { recursive: true, force: true });
|
|
1742
2141
|
}
|
|
1743
2142
|
});
|
|
2143
|
+
it('ignores ambient OMX_ROOT consensus state for local PRD/test-spec-only follow-up gating', async () => {
|
|
2144
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-keyword-gate-local-'));
|
|
2145
|
+
const ambientRoot = await mkdtemp(join(tmpdir(), 'omx-keyword-gate-ambient-'));
|
|
2146
|
+
const previousOmxRoot = process.env.OMX_ROOT;
|
|
2147
|
+
try {
|
|
2148
|
+
const plansDir = join(cwd, '.omx', 'plans');
|
|
2149
|
+
await mkdir(plansDir, { recursive: true });
|
|
2150
|
+
await writeFile(join(plansDir, 'prd-local.md'), '# Plan\n');
|
|
2151
|
+
await writeFile(join(plansDir, 'test-spec-local.md'), '# Test spec\n');
|
|
2152
|
+
const ambientStateDir = join(ambientRoot, '.omx', 'state');
|
|
2153
|
+
await mkdir(ambientStateDir, { recursive: true });
|
|
2154
|
+
await writeFile(join(ambientStateDir, 'ralplan-state.json'), JSON.stringify({
|
|
2155
|
+
current_phase: 'complete',
|
|
2156
|
+
planning_complete: true,
|
|
2157
|
+
ralplan_consensus_gate: {
|
|
2158
|
+
complete: true,
|
|
2159
|
+
ralplan_architect_review: { agent_role: 'architect', verdict: 'approve', iteration: 1 },
|
|
2160
|
+
ralplan_critic_review: { agent_role: 'critic', verdict: 'approve', iteration: 1 },
|
|
2161
|
+
},
|
|
2162
|
+
}));
|
|
2163
|
+
process.env.OMX_ROOT = ambientRoot;
|
|
2164
|
+
const result = applyRalplanGate(['team'], 'team', { cwd });
|
|
2165
|
+
assert.equal(result.gateApplied, true);
|
|
2166
|
+
assert.deepEqual(result.keywords, ['ralplan']);
|
|
2167
|
+
}
|
|
2168
|
+
finally {
|
|
2169
|
+
if (previousOmxRoot === undefined)
|
|
2170
|
+
delete process.env.OMX_ROOT;
|
|
2171
|
+
else
|
|
2172
|
+
process.env.OMX_ROOT = previousOmxRoot;
|
|
2173
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2174
|
+
await rm(ambientRoot, { recursive: true, force: true });
|
|
2175
|
+
}
|
|
2176
|
+
});
|
|
2177
|
+
it('gates short follow-up when local state only has latest verdict fields', async () => {
|
|
2178
|
+
const cwd = await mkdtemp(join(tmpdir(), 'omx-keyword-gate-latest-only-'));
|
|
2179
|
+
try {
|
|
2180
|
+
const plansDir = join(cwd, '.omx', 'plans');
|
|
2181
|
+
const stateDir = join(cwd, '.omx', 'state');
|
|
2182
|
+
await mkdir(plansDir, { recursive: true });
|
|
2183
|
+
await mkdir(stateDir, { recursive: true });
|
|
2184
|
+
await writeFile(join(plansDir, 'prd-local.md'), '# Plan\n\nLaunch hint: omx team 3:executor "Execute approved local plan"\n');
|
|
2185
|
+
await writeFile(join(plansDir, 'test-spec-local.md'), '# Test spec\n');
|
|
2186
|
+
await writeFile(join(stateDir, 'ralplan-state.json'), JSON.stringify({
|
|
2187
|
+
current_phase: 'complete',
|
|
2188
|
+
planning_complete: true,
|
|
2189
|
+
latest_architect_verdict: 'approve',
|
|
2190
|
+
latest_critic_verdict: 'approve',
|
|
2191
|
+
}));
|
|
2192
|
+
const result = applyRalplanGate(['team'], 'team', { cwd });
|
|
2193
|
+
assert.equal(result.gateApplied, true);
|
|
2194
|
+
assert.deepEqual(result.keywords, ['ralplan']);
|
|
2195
|
+
}
|
|
2196
|
+
finally {
|
|
2197
|
+
await rm(cwd, { recursive: true, force: true });
|
|
2198
|
+
}
|
|
2199
|
+
});
|
|
1744
2200
|
it('redirects underspecified execution keywords to ralplan', () => {
|
|
1745
2201
|
const result = applyRalplanGate(['ralph'], 'ralph fix this');
|
|
1746
2202
|
assert.equal(result.gateApplied, true);
|