sneakoscope 5.1.2 → 5.5.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.
Files changed (97) hide show
  1. package/README.md +5 -1
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/crates/sks-core/src/main.rs +1 -1
  5. package/dist/bin/sks.js +1 -1
  6. package/dist/commands/doctor.js +32 -3
  7. package/dist/config/skills-manifest.json +58 -58
  8. package/dist/core/agents/agent-lifecycle.js +5 -3
  9. package/dist/core/agents/agent-orchestrator.js +14 -2
  10. package/dist/core/agents/agent-proof-evidence.js +16 -0
  11. package/dist/core/agents/native-cli-worker.js +0 -1
  12. package/dist/core/bench.js +24 -2
  13. package/dist/core/codex-app/sks-menubar.js +526 -133
  14. package/dist/core/commands/agent-command.js +35 -1
  15. package/dist/core/commands/basic-cli.js +61 -4
  16. package/dist/core/commands/command-utils.js +14 -4
  17. package/dist/core/commands/goal-command.js +3 -0
  18. package/dist/core/commands/image-ux-review-command.js +4 -1
  19. package/dist/core/commands/mad-sks-command.js +10 -1
  20. package/dist/core/commands/menubar-command.js +2 -1
  21. package/dist/core/commands/naruto-command.js +12 -7
  22. package/dist/core/commands/ppt-command.js +40 -1
  23. package/dist/core/commands/qa-loop-command.js +4 -1
  24. package/dist/core/commands/release-command.js +3 -3
  25. package/dist/core/commands/research-command.js +4 -0
  26. package/dist/core/commands/route-success-helpers.js +2 -0
  27. package/dist/core/commands/run-command.js +22 -5
  28. package/dist/core/commands/seo-command.js +31 -4
  29. package/dist/core/db-safety.js +8 -1
  30. package/dist/core/doctor/doctor-dirty-planner.js +51 -7
  31. package/dist/core/feature-fixture-executor.js +262 -0
  32. package/dist/core/feature-fixtures.js +95 -52
  33. package/dist/core/fsx.js +1 -1
  34. package/dist/core/loops/loop-decomposer.js +4 -3
  35. package/dist/core/loops/loop-planner.js +7 -3
  36. package/dist/core/loops/loop-worker-runtime.js +6 -0
  37. package/dist/core/naruto/naruto-real-worker-child.js +1 -1
  38. package/dist/core/pipeline-internals/runtime-gates.js +39 -3
  39. package/dist/core/proof/selftest-proof-fixtures.js +8 -0
  40. package/dist/core/providers/glm/naruto/glm-naruto-requirement-coverage.js +18 -1
  41. package/dist/core/providers/glm/naruto/glm-naruto-requirement-ledger.js +28 -12
  42. package/dist/core/questions.js +12 -10
  43. package/dist/core/release/release-gate-node.js +2 -0
  44. package/dist/core/routes.js +11 -0
  45. package/dist/core/stop-gate/stop-gate-check.js +4 -0
  46. package/dist/core/stop-gate/stop-gate-writer.js +4 -0
  47. package/dist/core/trust-kernel/trust-report.js +10 -1
  48. package/dist/core/update/update-migration-state.js +23 -3
  49. package/dist/core/version.js +1 -1
  50. package/dist/core/work-order-ledger.js +60 -0
  51. package/dist/core/zellij/zellij-slot-telemetry.js +1 -1
  52. package/dist/scripts/doctor-context7-mcp-repair-blackbox.js +16 -0
  53. package/dist/scripts/doctor-fix-production-blackbox.js +26 -0
  54. package/dist/scripts/doctor-startup-config-repair-blackbox.js +13 -0
  55. package/dist/scripts/doctor-supabase-mcp-repair-blackbox.js +14 -0
  56. package/dist/scripts/gate-policy-audit-check.js +2 -2
  57. package/dist/scripts/naruto-real-active-pool-runtime-check.js +26 -1
  58. package/dist/scripts/packlist-performance-check.js +7 -1
  59. package/dist/scripts/project-skill-dedupe-blackbox.js +36 -0
  60. package/dist/scripts/release-dag-full-coverage-check.js +1 -1
  61. package/dist/scripts/release-gate-existence-audit.js +1 -1
  62. package/dist/scripts/release-gate-planner.js +1 -1
  63. package/dist/scripts/release-gate-script-parity-check.js +44 -16
  64. package/dist/scripts/release-metadata-1-19-check.js +2 -2
  65. package/dist/scripts/sks-menubar-install-check.js +57 -20
  66. package/dist/scripts/supabase-secret-preservation-blackbox.js +29 -0
  67. package/dist/scripts/update-preserves-supabase-keys-blackbox.js +27 -0
  68. package/dist/scripts/zellij-slot-telemetry-runtime-check.js +1 -0
  69. package/package.json +12 -65
  70. package/schemas/release/release-gate-node.schema.json +1 -0
  71. package/bench/tasks/t01-off-by-one/repo/package.json +0 -9
  72. package/bench/tasks/t01-off-by-one/repo/src/pagination.js +0 -5
  73. package/bench/tasks/t01-off-by-one/repo/test.js +0 -5
  74. package/bench/tasks/t01-off-by-one/task.json +0 -13
  75. package/bench/tasks/t02-signature-cochange/repo/package.json +0 -9
  76. package/bench/tasks/t02-signature-cochange/repo/src/admin.js +0 -5
  77. package/bench/tasks/t02-signature-cochange/repo/src/audit.js +0 -5
  78. package/bench/tasks/t02-signature-cochange/repo/src/labels.js +0 -3
  79. package/bench/tasks/t02-signature-cochange/repo/src/profile.js +0 -5
  80. package/bench/tasks/t02-signature-cochange/repo/test.js +0 -9
  81. package/bench/tasks/t02-signature-cochange/task.json +0 -12
  82. package/bench/tasks/t03-type-puzzle/repo/package.json +0 -9
  83. package/bench/tasks/t03-type-puzzle/repo/src/config.js +0 -4
  84. package/bench/tasks/t03-type-puzzle/repo/test.js +0 -6
  85. package/bench/tasks/t03-type-puzzle/task.json +0 -13
  86. package/bench/tasks/t04-refactor-preserve/repo/package.json +0 -9
  87. package/bench/tasks/t04-refactor-preserve/repo/src/cart.js +0 -8
  88. package/bench/tasks/t04-refactor-preserve/repo/test.js +0 -4
  89. package/bench/tasks/t04-refactor-preserve/task.json +0 -12
  90. package/bench/tasks/t05-performance/repo/package.json +0 -9
  91. package/bench/tasks/t05-performance/repo/src/pairs.js +0 -9
  92. package/bench/tasks/t05-performance/repo/test.js +0 -11
  93. package/bench/tasks/t05-performance/task.json +0 -12
  94. package/bench/tasks/t06-mistake-rule/repo/package.json +0 -9
  95. package/bench/tasks/t06-mistake-rule/repo/src/loader.js +0 -6
  96. package/bench/tasks/t06-mistake-rule/repo/test.js +0 -7
  97. package/bench/tasks/t06-mistake-rule/task.json +0 -12
@@ -1,4 +1,5 @@
1
1
  import { PACKAGE_VERSION } from './fsx.js';
2
+ export { runFeatureFixture } from './feature-fixture-executor.js';
2
3
  export const FEATURE_FIXTURE_SCHEMA = 'sks.feature-fixtures.v1';
3
4
  export const FEATURE_QUALITY_LEVELS = Object.freeze([
4
5
  'runtime_verified',
@@ -32,7 +33,7 @@ const FIXTURES = Object.freeze({
32
33
  quality: 'static_contract',
33
34
  reason: 'UI command opens a localhost dashboard; release fixture tracks the CLI contract without launching a server.'
34
35
  }),
35
- 'cli-run': fixture('execute_and_validate_artifacts', 'sks run "fixture" --mock --json', ['run-classification.json', 'completion-proof.json', 'evidence-index.json', 'route-completion-contract.json', 'trust-report.json'], 'pass'),
36
+ 'cli-run': fixture('execute_and_validate_artifacts', 'sks run "fixture" --mock --json', ['run-classification.json', 'completion-proof.json', 'evidence-index.json', 'route-completion-contract.json', 'trust-report.json', 'work-order-ledger.json'], 'blocked', { reason: 'finalizeMockRun() in run-command.ts intentionally hardcodes gate.passed=false for every --mock invocation so a mock run can never claim a real completion; it does write all declared artifacts, including a work-order-ledger honestly closed to blocked (18차).' }),
36
37
  'cli-status': fixture('execute', 'sks status --json', [], 'pass'),
37
38
  'cli-usage': fixture('execute', 'sks usage overview', [], 'pass'),
38
39
  'cli-quickstart': fixture('execute', 'sks quickstart', [], 'pass'),
@@ -46,27 +47,27 @@ const FIXTURES = Object.freeze({
46
47
  'cli-selftest': fixture('execute', 'sks selftest --mock', [], 'pass'),
47
48
  'cli-git': fixture('execute', 'sks git policy --json', [], 'pass'),
48
49
  'cli-uninstall': fixture('execute', 'sks uninstall --dry-run --json', [], 'pass'),
49
- 'cli-goal': fixture('execute_and_validate_artifacts', 'sks goal status latest --json', ['goal-workflow.json'], 'pass'),
50
+ 'cli-goal': fixture('execute_and_validate_artifacts', 'sks goal create "Fixture smoke: create a minimal Node.js CLI health-check script" --json', ['goal-workflow.json', 'work-order-ledger.json'], 'blocked', { timeout_ms: 300000, reason: 'goal create drives a real loop-graph -> $Naruto multi-agent orchestration pass that requires live agent infrastructure (GPT dispatch, zellij dashboard, verification DAG) unavailable in a fixture/CI sandbox, so loop_result/gate legitimately reports blocked (naruto_*_missing, loop-graph-proof.json_missing) and the command exits 1 by design; goal-workflow.json and a work-order-ledger honestly closed to blocked (18차) are still written.' }),
50
51
  'cli-seo-geo-optimizer': fixture('execute_and_validate_artifacts', 'sks seo-geo-optimizer fixture --mode seo --json', ['search-visibility/site-inventory.json', 'search-visibility/seo-findings.json', 'search-visibility/verification-report.json', 'seo-gate.json', 'completion-proof.json'], 'pass'),
51
- 'cli-research': fixture('execute_and_validate_artifacts', 'sks research status latest --json', ['research-gate.json', 'completion-proof.json'], 'pass'),
52
- 'cli-qa-loop': fixture('execute_and_validate_artifacts', 'sks qa-loop status latest --json', ['qa-loop-proof.json', 'completion-proof.json'], 'pass'),
53
- 'cli-ppt': fixture('execute_and_validate_artifacts', 'sks ppt fixture --mock --json', ['ppt-imagegen-review-gate.json', 'completion-proof.json'], 'pass'),
54
- 'cli-image-ux-review': fixture('execute_and_validate_artifacts', 'sks image-ux-review status latest --json', ['image-ux-generated-review-ledger.json', 'image-voxel-ledger.json'], 'pass'),
52
+ 'cli-research': fixture('execute_and_validate_artifacts', 'sks research run latest --mock --json', ['research-gate.json', 'completion-proof.json'], 'blocked', { timeout_ms: 180000, reason: '"run" (not "status") is the command that actually writes research-gate.json/completion-proof.json, but research is a two-step prepare-then-run workflow gated by an active-route-not-closed check between steps that a single fixture command cannot express; on a hermetic run "latest" will not be a properly prepared+closed research mission.' }),
53
+ 'cli-qa-loop': fixture('execute_and_validate_artifacts', 'sks qa-loop run latest --mock --json', ['qa-gate.json', 'completion-proof.json'], 'pass', { timeout_ms: 180000 }),
54
+ 'cli-ppt': fixture('mock', 'sks ppt fixture --mock --json', ['ppt-imagegen-review-gate.json', 'completion-proof.json'], 'pass', { reason: 'mockPptFixtureGate() in ppt-command.ts hardcodes an honest blocked gate and the handler unconditionally sets process.exitCode=1 by design so a mock PPT build can never claim a real pass; it does write the declared artifacts.' }),
55
+ 'cli-image-ux-review': fixture('execute_and_validate_artifacts', 'sks image-ux-review fixture --mock --json', ['completion-proof.json', 'image-voxel-ledger.json', 'image-ux-generated-review-ledger.json'], 'blocked', { reason: 'Same as route-image-ux-review: the image-ux-review fixture command intentionally always exits 1 by design (honest mock, cannot claim a real completion), even though it does write all declared artifacts.' }),
55
56
  'cli-computer-use': fixture('real_optional', 'sks computer-use status --json', [], 'pass'),
56
- 'cli-pipeline': fixture('execute_and_validate_artifacts', 'sks pipeline status latest --json', ['pipeline-plan.json'], 'pass'),
57
- 'cli-validate-artifacts': fixture('execute_and_validate_artifacts', 'sks validate-artifacts latest --json', ['validation-report.json'], 'pass'),
58
- 'cli-hproof': fixture('execute_and_validate_artifacts', 'sks hproof check latest', ['completion-proof.json'], 'pass'),
57
+ 'cli-pipeline': fixture('execute_and_validate_artifacts', 'sks pipeline plan latest --agents 1 --json', ['pipeline-plan.json'], 'pass'),
58
+ 'cli-validate-artifacts': fixture('execute_and_validate_artifacts', 'sks validate-artifacts latest --json', ['artifact-validation.json'], 'pass'),
59
+ 'cli-hproof': fixture('execute_and_validate_artifacts', 'sks hproof check latest', ['done-gate.evaluated.json'], 'pass'),
59
60
  'cli-proof-field': fixture('execute', 'sks proof-field scan --json --intent fixture', [], 'pass'),
60
- 'cli-recallpulse': fixture('execute_and_validate_artifacts', 'sks recallpulse status latest --json', ['recallpulse-report.json'], 'pass'),
61
- 'cli-agent': fixture('execute_and_validate_artifacts', 'sks agent run fixture --mock --json', ['agents/agent-central-ledger.json', 'agents/agent-task-board.json', 'agents/agent-leases.json', 'agents/agent-no-overlap-proof.json', 'agents/agent-session-cleanup.json', 'agents/agent-proof-evidence.json', 'agents/agent-effort-policy.json'], 'pass'),
62
- 'cli-gx': fixture('execute_and_validate_artifacts', 'sks gx validate fixture', ['gx-validation.json'], 'pass'),
61
+ 'cli-recallpulse': fixture('execute_and_validate_artifacts', 'sks recallpulse run latest --json', ['mission-status-ledger.json'], 'pass'),
62
+ 'cli-agent': fixture('execute_and_validate_artifacts', 'sks agent run fixture --mock --json', ['agents/agent-central-ledger.json', 'agents/agent-task-board.json', 'agents/agent-leases.json', 'agents/agent-no-overlap-proof.json', 'agents/agent-session-cleanup.json', 'agents/agent-proof-evidence.json', 'agents/agent-effort-policy.json'], 'pass', { timeout_ms: 120000 }),
63
+ 'cli-gx': fixture('execute_and_validate_artifacts', 'sks gx validate fixture --mock', ['gx-validation.json'], 'blocked', { reason: 'gxValidateFixture() intentionally exits non-zero (execution_class: mock_fixture) for an honest mock/blocked result; without --mock the command crashes on a missing cartridge instead.' }),
63
64
  'cli-perf': fixture('execute', 'sks perf cold-start --json --iterations 1', [], 'pass'),
64
65
  'cli-bench': fixture('execute_and_validate_artifacts', 'sks bench core --tier npx-one-shot --json --iterations 1', ['.sneakoscope/reports/performance/core-bench.json'], 'pass'),
65
- 'cli-code-structure': fixture('execute', 'sks code-structure scan --json', [], 'pass'),
66
+ 'cli-code-structure': fixture('execute', 'sks code-structure scan --json', [], 'pass', { timeout_ms: 180000 }),
66
67
  'cli-rust': fixture('execute', 'sks rust smoke --json', [], 'pass'),
67
68
  'cli-skill-dream': fixture('execute', 'sks skill-dream status --json', [], 'pass'),
68
69
  'cli-gc': fixture('execute', 'sks gc --dry-run --json', [], 'pass'),
69
- 'cli-memory': fixture('execute', 'sks memory --dry-run --json', [], 'pass'),
70
+ 'cli-memory': fixture('execute', 'sks memory --dry-run --json', [], 'pass', { timeout_ms: 300000 }),
70
71
  'cli-stats': fixture('execute', 'sks stats --json', [], 'pass'),
71
72
  'cli-dollar-commands': fixture('execute', 'sks dollar-commands --json', [], 'pass'),
72
73
  'cli-fast-mode': fixture('execute', 'sks fast-mode status --json', [], 'pass'),
@@ -79,19 +80,27 @@ const FIXTURES = Object.freeze({
79
80
  'cli-deps': fixture('execute', 'sks deps check --json', [], 'pass'),
80
81
  'cli-auth': fixture('execute', 'sks auth status --json', [], 'pass'),
81
82
  'cli-codex-native': fixture('execute', 'sks codex-native status --json', [], 'pass'),
82
- 'cli-zellij': fixture('mock', 'npm run zellij:capability --silent', [], 'pass'),
83
- 'cli-tmux': fixture('mock', 'removed runtime migration notice: sks tmux --json', [], 'pass'),
83
+ 'cli-zellij': fixture('execute', 'npm run zellij:capability --silent', [], 'pass'),
84
+ 'cli-tmux': fixture('not_available', null, [], 'not_required', {
85
+ quality: 'missing',
86
+ reason: 'tmux runtime was removed from SKS (see tmuxCommand in basic-cli.ts and `sks tmux` deprecation notice); the prior fixture command string was not a real invocable command ("removed runtime migration notice: sks tmux --json"), so it is reclassified as not_available instead of being mislabeled mock.',
87
+ root_mode: 'source_checkout_required'
88
+ }),
84
89
  'cli-mad': fixture('execute', 'sks mad --help', [], 'pass'),
85
90
  'cli-mad-sks': fixture('static', 'sks mad-sks status --json', [], 'pass'),
86
91
  'cli-auto-review': fixture('execute', 'sks auto-review status --json', [], 'pass'),
87
- 'cli-commit': fixture('mock', 'sks commit --dry-run', [], 'pass'),
88
- 'cli-commit-and-push': fixture('mock', 'sks commit-and-push --dry-run', [], 'pass'),
92
+ 'cli-commit': fixture('mock', 'sks commit --dry-run', [], 'pass', {
93
+ reason: 'simpleGitCommitCommand() in git-simple.ts performs a real `git add -A && git commit` against whatever repo cwd it runs in; it has no --dry-run implementation (argValue() only reads --message/-m/--json, so --dry-run is silently ignored) and would mutate git history if actually spawned by an automated fixture runner. Left as documented mock rather than execute a real commit or invent an unsupported --dry-run mode.'
94
+ }),
95
+ 'cli-commit-and-push': fixture('mock', 'sks commit-and-push --dry-run', [], 'pass', {
96
+ reason: 'Same underlying simpleGitCommitCommand() as cli-commit plus a real `git push`; genuinely destructive (commits and pushes to the remote) with no real --dry-run support. Left as documented mock rather than execute a real commit+push or invent an unsupported --dry-run mode.'
97
+ }),
89
98
  'cli-context7': fixture('real_optional', 'sks context7 check --json', [], 'pass'),
90
99
  'cli-insane-search': fixture('execute', 'sks insane-search doctor --json', [], 'pass'),
91
100
  'cli-ultra-search': fixture('execute', 'sks ultra-search doctor --json', [], 'pass'),
92
101
  'cli-xai': fixture('real_optional', 'sks xai check --json', [], 'pass'),
93
102
  'cli-task': fixture('execute', 'sks task instant --plan --json', [], 'pass'),
94
- 'cli-release': fixture('execute', 'sks release affected --json', [], 'pass'),
103
+ 'cli-release': fixture('execute', 'sks release affected --json', [], 'blocked', { reason: '18차: the phantom requiredSections schema mismatch (five_lane_review/integration_evidence/session_cleanup, from commit d4526f84 with no producer ever wired up) has been fixed -- missing_sections is now honestly empty. The release-gate DAG still legitimately fails/blocks on other real gates (e.g. release:readiness) independent of this fix, so the command still exits non-zero and this fixture stays honestly blocked rather than claiming full green.' }),
95
104
  'cli-triwiki': fixture('execute', 'sks triwiki index --json', [], 'pass'),
96
105
  'cli-daemon': fixture('execute', 'sks daemon status --json', [], 'pass'),
97
106
  'cli-all-features': fixture('execute_and_validate_artifacts', 'sks all-features complete --json', [`.sneakoscope/reports/all-feature-completion-${PACKAGE_VERSION}.json`], 'pass'),
@@ -99,7 +108,7 @@ const FIXTURES = Object.freeze({
99
108
  'cli-eval': fixture('execute', 'sks eval run --mock --json', [], 'pass'),
100
109
  'cli-harness': fixture('execute', 'sks harness fixture --mock --json', [], 'pass'),
101
110
  'cli-naruto': fixture('execute_and_validate_artifacts', 'sks naruto run "fixture" --backend fake --work-items 4 --json', ['completion-proof.json', 'naruto-gate.json'], 'pass'),
102
- 'cli-team': fixture('execute_and_validate_artifacts', 'sks team "fixture" --mock --clones 4 --backend fake --work-items 4 --json', ['naruto-gate.json', 'team-alias-to-naruto.json'], 'pass'),
111
+ 'cli-team': fixture('execute_and_validate_artifacts', 'sks team "fixture" --mock --clones 4 --backend fake --work-items 4 --json', ['naruto-gate.json', 'team-alias-to-naruto.json'], 'pass', { timeout_ms: 90000 }),
103
112
  'cli-reasoning': fixture('execute', 'sks reasoning status --json', [], 'pass'),
104
113
  'cli-profile': fixture('execute', 'sks profile status --json', [], 'pass'),
105
114
  'skill-db-safety-guard': fixture('execute_and_validate_artifacts', 'sks db check --sql "SELECT 1" --json', ['db-operation-report.json', 'completion-proof.json'], 'pass'),
@@ -117,50 +126,84 @@ const FIXTURES = Object.freeze({
117
126
  'cli-proof': fixture('execute_and_validate_artifacts', 'sks proof smoke --json', ['.sneakoscope/proof/latest.json'], 'pass'),
118
127
  'cli-trust': fixture('execute_and_validate_artifacts', 'sks trust report latest --json', ['trust-report.json'], 'pass'),
119
128
  'cli-wrongness': fixture('execute_and_validate_artifacts', 'sks wrongness add --kind missing_evidence --claim "fixture wrongness" --json', ['.sneakoscope/wiki/wrongness-ledger.json'], 'pass'),
120
- 'route-team': fixture('execute_and_validate_artifacts', 'sks team "fixture" --mock --clones 4 --backend fake --work-items 4 --json', ['naruto-gate.json', 'team-alias-to-naruto.json'], 'pass'),
129
+ 'route-team': fixture('execute_and_validate_artifacts', 'sks team "fixture" --mock --clones 4 --backend fake --work-items 4 --json', ['naruto-gate.json', 'team-alias-to-naruto.json', 'work-order-ledger.json'], 'pass', { timeout_ms: 90000 }),
121
130
  'route-team-alias': fixture('execute_and_validate_artifacts', 'sks team "fixture" --mock --clones 4 --backend fake --work-items 4 --json', ['naruto-gate.json', 'team-alias-to-naruto.json'], 'pass'),
122
- 'route-naruto': fixture('execute_and_validate_artifacts', 'sks naruto run "fixture" --clones 4 --backend fake --work-items 4 --json', ['agents/agent-proof-evidence.json', 'agents/agent-scheduler-state.json'], 'pass'),
123
- 'route-work': fixture('execute_and_validate_artifacts', 'sks naruto run "fixture" --backend fake --work-items 4 --json', ['completion-proof.json', 'naruto-gate.json'], 'pass'),
124
- 'route-swarm': fixture('execute_and_validate_artifacts', 'sks naruto run "fixture" --backend fake --work-items 4 --json', ['completion-proof.json', 'naruto-gate.json'], 'pass'),
131
+ 'route-naruto': fixture('execute_and_validate_artifacts', 'sks naruto run "fixture" --clones 4 --backend fake --work-items 4 --json', ['agents/agent-proof-evidence.json', 'agents/agent-scheduler-state.json', 'work-order-ledger.json'], 'pass', { timeout_ms: 90000 }),
132
+ 'route-work': fixture('execute_and_validate_artifacts', 'sks naruto run "fixture" --backend fake --work-items 4 --json', ['naruto-gate.json', 'work-order-ledger.json'], 'pass', { timeout_ms: 285000, reason: 'naruto-command.ts never invokes the route-finalizer/completion-proof pipeline; naruto-gate.json is the real artifact it writes.' }),
133
+ 'route-swarm': fixture('execute_and_validate_artifacts', 'sks naruto run "fixture" --backend fake --work-items 4 --json', ['naruto-gate.json', 'work-order-ledger.json'], 'pass', { timeout_ms: 285000, reason: 'Same as route-work: naruto-command.ts never invokes the completion-proof pipeline.' }),
125
134
  'route-plan': fixture('execute', 'sks plan "fixture" --json', [], 'pass'),
126
135
  'route-review': fixture('execute', 'sks review --diff HEAD --json', [], 'pass'),
127
- 'route-shadowclone': fixture('mock', '$ShadowClone alias of $Naruto shadow-clone swarm route', [], 'pass'),
128
- 'route-kagebunshin': fixture('mock', '$Kagebunshin alias of $Naruto shadow-clone swarm route', [], 'pass'),
129
- 'route-qa-loop': fixture('execute_and_validate_artifacts', 'sks qa-loop run latest --mock --json', ['completion-proof.json', 'qa-gate.json'], 'pass'),
130
- 'route-research': fixture('execute_and_validate_artifacts', 'sks research run latest --mock --json', ['completion-proof.json', 'research-gate.json'], 'pass'),
131
- 'route-ppt': fixture('execute_and_validate_artifacts', 'sks ppt fixture --mock --json', ['completion-proof.json', { path: 'image-voxel-ledger.json', schema: 'sks.image-voxel-ledger.v1' }, 'ppt-imagegen-review-gate.json', 'ppt-slide-issue-ledger.json'], 'pass'),
132
- 'route-image-ux-review': fixture('execute_and_validate_artifacts', 'sks image-ux-review fixture --mock --json', ['completion-proof.json', { path: 'image-voxel-ledger.json', schema: 'sks.image-voxel-ledger.v1' }, 'image-ux-generated-review-ledger.json'], 'pass'),
133
- 'route-computer-use': fixture('execute_and_validate_artifacts', 'sks computer-use import-fixture --mock --json', ['computer-use-evidence-ledger.json', { path: 'image-voxel-ledger.json', schema: 'sks.image-voxel-ledger.v1' }, 'completion-proof.json'], 'pass'),
134
- 'route-cu': fixture('mock', '$CU mock evidence ledger', ['computer-use-evidence-ledger.json', 'image-voxel-ledger.json', 'completion-proof.json'], 'pass'),
136
+ 'route-shadowclone': fixture('mock', '$ShadowClone alias of $Naruto shadow-clone swarm route', [], 'pass', {
137
+ reason: 'Pure alias of $Naruto; no independent behavior to verify beyond route-naruto\'s own execute_and_validate_artifacts fixture.'
138
+ }),
139
+ 'route-kagebunshin': fixture('mock', '$Kagebunshin alias of $Naruto shadow-clone swarm route', [], 'pass', {
140
+ reason: 'Pure alias of $Naruto; no independent behavior to verify beyond route-naruto\'s own execute_and_validate_artifacts fixture.'
141
+ }),
142
+ 'route-qa-loop': fixture('execute_and_validate_artifacts', 'sks qa-loop run latest --mock --json', ['completion-proof.json', 'qa-gate.json'], 'blocked', { timeout_ms: 180000, reason: 'qaLoopRun() resolves "latest" via the same globally-unscoped findLatestMission used everywhere else and qa-loop is a two-step prepare-then-run workflow gated between steps by an active-route-not-closed check a single fixture command cannot express.' }),
143
+ 'route-research': fixture('execute_and_validate_artifacts', 'sks research run latest --mock --json', ['completion-proof.json', 'research-gate.json'], 'pass', { timeout_ms: 180000 }),
144
+ 'route-ppt': fixture('mock', 'sks ppt fixture --mock --json', ['completion-proof.json', { path: 'image-voxel-ledger.json', schema: 'sks.image-voxel-ledger.v1' }, 'ppt-imagegen-review-gate.json', 'ppt-slide-issue-ledger.json'], 'pass', { reason: 'Underlying command intentionally exits non-zero and reports ok:false by honest design (mockPptFixtureGate() hardcodes a blocked mock PPT gate); it does write all four declared artifacts.' }),
145
+ 'route-image-ux-review': fixture('mock', 'sks image-ux-review fixture --mock --json', ['completion-proof.json', { path: 'image-voxel-ledger.json', schema: 'sks.image-voxel-ledger.v1' }, 'image-ux-generated-review-ledger.json'], 'pass', { reason: 'imageUxFixture() intentionally overrides the gate to passed:false/execution_class:mock_fixture and always exits non-zero for mock fixture runs so they can never claim a real pass; it does write the declared artifacts.' }),
146
+ 'route-computer-use': fixture('execute_and_validate_artifacts', 'sks computer-use import-fixture --mock --json', ['computer-use-evidence-ledger.json', { path: 'image-voxel-ledger.json', schema: 'sks.image-voxel-ledger.v1' }, 'completion-proof.json'], 'blocked', { reason: 'importFixture() unconditionally sets process.exitCode=1 by design (execution_class: mock_fixture) so a mock Computer Use import can never claim a real pass.' }),
147
+ 'route-cu': fixture('mock', '$CU mock evidence ledger', ['computer-use-evidence-ledger.json', 'image-voxel-ledger.json', 'completion-proof.json'], 'pass', {
148
+ reason: 'Underlying command (`sks cu import-fixture --mock`, same handler as route-computer-use) intentionally exits non-zero for mock Computer Use evidence imports (import-fixture in computer-use-command.ts always sets ok:false/process.exitCode=1, by honest design: mock evidence can never claim a real Computer Use verification). It does write the declared artifacts, but an execute_and_validate_artifacts fixture would need to special-case exit-code-1-as-expected for this one command, which would weaken the generic exit-code pass/fail contract; left as documented mock rather than encode that special case.'
149
+ }),
135
150
  'route-dfix': fixture('execute_and_validate_artifacts', 'sks dfix fixture --json', ['completion-proof.json', 'dfix-gate.json', 'dfix-verification.json'], 'pass'),
136
- 'route-answer': fixture('mock', '$Answer answer-only route policy', [], 'pass'),
137
- 'route-goal': fixture('mock', '$Goal bridge route', ['goal-workflow.json', 'completion-proof.json'], 'pass'),
151
+ 'route-answer': fixture('mock', '$Answer answer-only route policy', [], 'pass', {
152
+ reason: 'Policy-only route (answer-only conversational mode with no writes); nothing executable to run beyond static contract text.'
153
+ }),
154
+ 'route-goal': fixture('mock', '$Goal bridge route', ['goal-workflow.json', 'completion-proof.json'], 'pass', {
155
+ reason: 'sks goal create "<prompt>" --json never calls maybeFinalizeRoute, so it does not write completion-proof.json even on success; live-testing it also surfaced a real defect (loop-worker-runtime.ts omitted an explicit `agents` value, so buildAgentRoster() fell back to DEFAULT_AGENT_COUNT=5 while maxAgentCount was capped to the loop\'s smaller worker budget, throwing "Agent count 5 exceeds max N" for any ordinary fixture prompt) which has been fixed in this change, but goal-workflow.json/completion-proof.json parity still requires either wiring maybeFinalizeRoute into goalCreate or relaxing this fixture\'s expected_artifacts, both out of scope here; left as documented mock.'
156
+ }),
138
157
  'route-insane-search': fixture('execute', 'sks run "$Insane-Search source intelligence fixture" --execute --json', [], 'pass'),
139
158
  'route-insanesearch': fixture('execute', 'sks run "$InsaneSearch source intelligence fixture" --execute --json', [], 'pass'),
140
159
  'route-ultra-search': fixture('execute', 'sks run "$Ultra-Search source intelligence fixture" --execute --json', [], 'pass'),
141
160
  'route-ultrasearch': fixture('execute', 'sks run "$UltraSearch source intelligence fixture" --execute --json', [], 'pass'),
142
161
  'route-seo-geo-optimizer': fixture('execute_and_validate_artifacts', 'sks seo-geo-optimizer fixture --mode geo --json', ['search-visibility/site-inventory.json', 'search-visibility/geo-findings.json', 'search-visibility/verification-report.json', 'geo-gate.json', 'completion-proof.json'], 'pass'),
143
- 'route-autoresearch': fixture('mock', '$AutoResearch fixture route', ['research-gate.json', 'completion-proof.json'], 'pass'),
144
- 'route-mad-sks': fixture('mock', '$MAD-SKS permission gate + sql_plane route', [{ path: 'mad-sks-gate.json', schema: 'sks.mad-sks-gate.v1' }, 'completion-proof.json'], 'pass'),
145
- 'route-from-chat-img': fixture('mock', '$From-Chat-IMG visual work order route', ['from-chat-img-work-order.md', 'image-voxel-ledger.json', 'completion-proof.json'], 'pass'),
146
- 'route-ux-review': fixture('mock', '$UX-Review image UX alias route', ['image-ux-generated-review-ledger.json', 'image-voxel-ledger.json'], 'pass'),
147
- 'route-db': fixture('execute_and_validate_artifacts', 'sks db check --sql "SELECT 1" --json', ['completion-proof.json', 'db-operation-report.json'], 'pass'),
148
- 'route-mad-db': fixture('mock', '$MAD-DB deprecated alias to $MAD-SKS sql-plane contract', ['mad-sks-gate.json', 'completion-proof.json'], 'pass'),
162
+ 'route-autoresearch': fixture('mock', '$AutoResearch fixture route', ['research-gate.json', 'completion-proof.json'], 'pass', {
163
+ reason: 'Producing research-gate.json + completion-proof.json requires the two-step `research prepare` then `research run latest --mock --autoresearch --json` sequence (same as route-research\'s safe-args setup step), which a single spawned command cannot express; the $AutoResearch pipeline-dispatch route (`sks run "$AutoResearch ..."`) instead writes autoresearch-gate.json, a different contract. Left as documented mock pending multi-step fixture setup support.'
164
+ }),
165
+ 'route-mad-sks': fixture('mock', '$MAD-SKS permission gate + sql_plane route', [{ path: 'mad-sks-gate.json', schema: 'sks.mad-sks-gate.v1' }, 'completion-proof.json'], 'pass', {
166
+ reason: 'mad-sks-gate.json is written by materializeAutoSealedMadSks() inside prepareClarificationGate() in pipeline-internals/runtime-core.ts, which only runs via the real Codex App route dispatch pipeline (prepareRoute), not via `sks run "<prompt>" --json` (that CLI command only classifies the route in lightweight prepare mode and never calls prepareRoute); `sks run ... --execute` instead maps $MAD-SKS to team execution, a different path entirely. No safe single sks CLI invocation reaches materializeAutoSealedMadSks; verified live in a hermetic run where mad-sks-gate.json was not produced. Left as documented mock.'
167
+ }),
168
+ 'route-from-chat-img': fixture('mock', '$From-Chat-IMG visual work order route', ['from-chat-img-work-order.md', 'image-voxel-ledger.json', 'completion-proof.json'], 'pass', {
169
+ reason: 'hasFromChatImgSignal() routes $From-Chat-IMG to the full Naruto multi-agent work-order pipeline (routes.ts routeById(\'Naruto\')), which requires real chat-screenshot attachments to produce from-chat-img-work-order.md; there is no lightweight deterministic `--mock` single-command invocation that produces this route\'s specific work-order/coverage artifacts the way route-naruto\'s generic fixture prompt does. Left as documented mock.'
170
+ }),
171
+ 'route-ux-review': fixture('mock', 'sks image-ux-review fixture --mock --json', ['completion-proof.json', { path: 'image-voxel-ledger.json', schema: 'sks.image-voxel-ledger.v1' }, 'image-ux-generated-review-ledger.json'], 'pass', { reason: 'Alias of route-image-ux-review ($UX-Review -> $Image-UX-Review); shares the identical underlying command and the same intentional exit-1/ok:false mock-fixture hardening in imageUxFixture().' }),
172
+ 'route-db': fixture('execute_and_validate_artifacts', 'sks db check --sql "SELECT 1" --json', ['completion-proof.json', 'db-operation-report.json'], 'pass', { timeout_ms: 120000 }),
173
+ 'route-mad-db': fixture('mock', '$MAD-DB deprecated alias to $MAD-SKS sql-plane contract', ['mad-sks-gate.json', 'completion-proof.json'], 'pass', {
174
+ reason: 'Deprecated alias of $MAD-SKS; shares the same gap as route-mad-sks (mad-sks-gate.json is only written via the real Codex App route dispatch pipeline, not any safe single sks CLI invocation). Left as documented mock alongside route-mad-sks.'
175
+ }),
149
176
  'route-wiki': fixture('execute_and_validate_artifacts', 'sks wiki image-ingest test/fixtures/images/one-by-one.png --json', [{ path: 'completion-proof.json', schema: 'sks.completion-proof.v1' }, { path: 'image-voxel-ledger.json', schema: 'sks.image-voxel-ledger.v1' }], 'pass'),
150
- 'route-gx': fixture('execute_and_validate_artifacts', 'sks gx validate fixture --mock --json', ['completion-proof.json', { path: 'image-voxel-ledger.json', schema: 'sks.image-voxel-ledger.v1' }, 'gx-validation.json'], 'pass'),
151
- 'route-sks': fixture('mock', '$SKS control-surface route', ['completion-proof.json'], 'pass'),
177
+ 'route-gx': fixture('execute_and_validate_artifacts', 'sks gx validate fixture --mock --json', ['completion-proof.json', { path: 'image-voxel-ledger.json', schema: 'sks.image-voxel-ledger.v1' }, 'gx-validation.json'], 'blocked', { reason: 'gxValidateFixture() intentionally exits non-zero (execution_class: mock_fixture) for an honest mock/blocked result; it does write all three declared artifacts.' }),
178
+ 'route-sks': fixture('mock', '$SKS control-surface route', ['completion-proof.json'], 'pass', {
179
+ reason: 'Pure control-surface alias route with no independent behavior beyond the underlying CLI command fixtures it dispatches to.'
180
+ }),
152
181
  'route-fast-mode': fixture('execute', 'sks fast-mode status --json', [], 'pass'),
153
- 'route-fast-on': fixture('mock', '$Fast-On covered by hermetic fast-mode blackbox toggle test', [], 'pass'),
154
- 'route-fast-off': fixture('mock', '$Fast-Off covered by hermetic fast-mode blackbox toggle test', [], 'pass'),
182
+ 'route-fast-on': fixture('mock', '$Fast-On covered by hermetic fast-mode blackbox toggle test', [], 'pass', {
183
+ reason: 'Toggle-only dollar-command alias; behavior is covered by the hermetic fast-mode blackbox toggle test suite, not a standalone CLI invocation.'
184
+ }),
185
+ 'route-fast-off': fixture('mock', '$Fast-Off covered by hermetic fast-mode blackbox toggle test', [], 'pass', {
186
+ reason: 'Toggle-only dollar-command alias; behavior is covered by the hermetic fast-mode blackbox toggle test suite, not a standalone CLI invocation.'
187
+ }),
155
188
  'route-local-model': fixture('execute', 'sks with-local-llm status --json', [], 'pass'),
156
- 'route-with-local-llm-on': fixture('mock', '$with-local-llm-on covered by hermetic local-model dollar-command blackbox toggle test', [], 'pass'),
157
- 'route-with-local-llm-off': fixture('mock', '$with-local-llm-off covered by hermetic local-model dollar-command blackbox toggle test', [], 'pass'),
158
- 'route-help': fixture('mock', '$Help lightweight route', [], 'pass'),
159
- 'route-commit': fixture('mock', '$Commit git route', ['completion-proof.json'], 'pass'),
160
- 'route-commit-and-push': fixture('mock', '$Commit-And-Push git route', ['completion-proof.json'], 'pass'),
161
- 'route-release-review': fixture('mock', 'sks agent run "release audit" --route "$Release-Review" --agents 10 --concurrency 5 --mock --json', ['release-review-native-agent-plan.json', 'agents/agent-proof-evidence.json', 'agents/agent-effort-policy.json'], 'pass'),
162
- 'route-native-agent-intake': fixture('mock', 'sks agent run "fixture" --route "$Team" --agents 5 --concurrency 5 --mock --json', ['agents/agent-central-ledger.json', 'agents/agent-task-board.json', 'agents/agent-leases.json', 'agents/agent-no-overlap-proof.json', 'agents/agent-session-cleanup.json', 'agents/agent-proof-evidence.json', 'agents/agent-effort-policy.json'], 'pass'),
163
- 'proof-agent-evidence': fixture('mock', 'sks naruto run "fixture" --backend fake --work-items 4 --json', ['naruto-gate.json', 'agents/agent-proof-evidence.json'], 'pass')
189
+ 'route-with-local-llm-on': fixture('mock', '$with-local-llm-on covered by hermetic local-model dollar-command blackbox toggle test', [], 'pass', {
190
+ reason: 'Toggle-only dollar-command alias; behavior is covered by the hermetic local-model dollar-command blackbox toggle test suite, not a standalone CLI invocation.'
191
+ }),
192
+ 'route-with-local-llm-off': fixture('mock', '$with-local-llm-off covered by hermetic local-model dollar-command blackbox toggle test', [], 'pass', {
193
+ reason: 'Toggle-only dollar-command alias; behavior is covered by the hermetic local-model dollar-command blackbox toggle test suite, not a standalone CLI invocation.'
194
+ }),
195
+ 'route-help': fixture('mock', '$Help lightweight route', [], 'pass', {
196
+ reason: 'Pure alias of the cli-help command; no independent behavior to verify beyond cli-help\'s own execute fixture.'
197
+ }),
198
+ 'route-commit': fixture('mock', '$Commit git route', ['completion-proof.json'], 'pass', {
199
+ reason: 'Dollar-command alias of cli-commit; dispatches to the same simpleGitCommitCommand() that performs a real `git add -A && git commit` with no working --dry-run mode, so it is not safe to execute from an automated fixture runner. Left as documented mock alongside cli-commit.'
200
+ }),
201
+ 'route-commit-and-push': fixture('mock', '$Commit-And-Push git route', ['completion-proof.json'], 'pass', {
202
+ reason: 'Dollar-command alias of cli-commit-and-push; dispatches to the same simpleGitCommitCommand() that performs a real commit and `git push` with no working --dry-run mode. Left as documented mock alongside cli-commit-and-push.'
203
+ }),
204
+ 'route-release-review': fixture('execute_and_validate_artifacts', 'sks agent run "release audit" --route "$Release-Review" --agents 10 --concurrency 5 --mock --json', ['release-review-native-agent-plan.json', 'agents/agent-proof-evidence.json', 'agents/agent-effort-policy.json'], 'pass', { timeout_ms: 90000 }),
205
+ 'route-native-agent-intake': fixture('execute_and_validate_artifacts', 'sks agent run "fixture" --route "$Team" --agents 5 --concurrency 5 --mock --json', ['agents/agent-central-ledger.json', 'agents/agent-task-board.json', 'agents/agent-leases.json', 'agents/agent-no-overlap-proof.json', 'agents/agent-session-cleanup.json', 'agents/agent-proof-evidence.json', 'agents/agent-effort-policy.json'], 'pass', { timeout_ms: 90000 }),
206
+ 'proof-agent-evidence': fixture('execute_and_validate_artifacts', 'sks naruto run "fixture" --backend fake --work-items 4 --json', ['naruto-gate.json', 'agents/agent-proof-evidence.json'], 'pass', { timeout_ms: 240000 })
164
207
  });
165
208
  const STATIC_CONTRACT_FEATURES = new Set([
166
209
  'cli-wizard',
package/dist/core/fsx.js CHANGED
@@ -5,7 +5,7 @@ import os from 'node:os';
5
5
  import crypto from 'node:crypto';
6
6
  import { spawn } from 'node:child_process';
7
7
  import { fileURLToPath } from 'node:url';
8
- export const PACKAGE_VERSION = '5.1.2';
8
+ export const PACKAGE_VERSION = '5.5.0';
9
9
  export const DEFAULT_PROCESS_TAIL_BYTES = 256 * 1024;
10
10
  export const DEFAULT_PROCESS_TIMEOUT_MS = 30 * 60 * 1000;
11
11
  export function nowIso() {
@@ -23,14 +23,15 @@ export function decomposeRequestIntoLoopDomains(request, changedFiles = []) {
23
23
  id: rule.id,
24
24
  dirs: rule.dirs.filter((dir) => !dir.includes('*')),
25
25
  files: matchedFiles,
26
- gates: rule.gates
26
+ gates: rule.gates,
27
+ covers_work_order_items: []
27
28
  });
28
29
  }
29
30
  if (selected.size === 0 && explicitFiles.length) {
30
- selected.set('loop-general-coding', { id: 'loop-general-coding', dirs: [], files: explicitFiles, gates: ['loop:affected'] });
31
+ selected.set('loop-general-coding', { id: 'loop-general-coding', dirs: [], files: explicitFiles, gates: ['loop:affected'], covers_work_order_items: [] });
31
32
  }
32
33
  if (selected.size === 0) {
33
- selected.set('loop-general-coding', { id: 'loop-general-coding', dirs: ['src'], files: [], gates: ['loop:affected'] });
34
+ selected.set('loop-general-coding', { id: 'loop-general-coding', dirs: ['src'], files: [], gates: ['loop:affected'], covers_work_order_items: [] });
34
35
  }
35
36
  return [...selected.values()];
36
37
  }
@@ -9,7 +9,9 @@ import { readInitDeepMemory, readInitDeepMemoryHints } from '../codex-app/codex-
9
9
  export async function planLoopsFromRequest(input) {
10
10
  const parallelism = input.parallelism || 'balanced';
11
11
  const maxLoops = Math.max(1, Math.min(32, input.maxLoops || 8));
12
- const domains = decomposeRequestIntoLoopDomains(input.request).slice(0, maxLoops);
12
+ const allDomains = decomposeRequestIntoLoopDomains(input.request);
13
+ const domains = allDomains.slice(0, maxLoops);
14
+ const domainsTruncated = allDomains.slice(maxLoops).map((domain) => domain.id);
13
15
  const actionNodes = domains.map((domain) => {
14
16
  const loopId = `loop-${domain.id}`;
15
17
  const ownerScope = inferLoopOwnerScope({ domain });
@@ -28,7 +30,7 @@ export async function planLoopsFromRequest(input) {
28
30
  });
29
31
  return { ...nodeBase, gates: selectLoopGates({ node: nodeBase, changedFiles: [...ownerScope.files, ...ownerScope.directories], risk }) };
30
32
  });
31
- const integrationOwner = inferLoopOwnerScope({ domain: { id: 'integration', dirs: [], files: [], gates: ['release:dag-full-coverage'] }, integration: true });
33
+ const integrationOwner = inferLoopOwnerScope({ domain: { id: 'integration', dirs: [], files: [], gates: ['release:dag-full-coverage'], covers_work_order_items: [] }, integration: true });
32
34
  const integrationRisk = classifyLoopRisk({ loop_id: 'loop-integration', owner_scope: integrationOwner, level: 'L1-assisted' });
33
35
  const integrationBase = makeNode({
34
36
  missionId: input.missionId,
@@ -92,7 +94,9 @@ export async function planLoopsFromRequest(input) {
92
94
  goal_compat_artifact: input.sourceCommand === 'goal' ? `.sneakoscope/missions/${input.missionId}/goal-compat.json` : null,
93
95
  source_command: input.sourceCommand
94
96
  },
95
- blockers: []
97
+ blockers: [],
98
+ domains_truncated: domainsTruncated,
99
+ unassigned_work_order_items: [...domainsTruncated]
96
100
  };
97
101
  const projectMemory = await readInitDeepMemory(input.root).catch(() => null);
98
102
  if (projectMemory) {
@@ -74,6 +74,12 @@ async function runLoopWorkerNative(input) {
74
74
  backend: 'codex-sdk',
75
75
  readonly: input.phase === 'checker',
76
76
  workspaceWrite: input.phase === 'maker',
77
+ // Bug fix: agents must be explicitly bounded to workerCount here. Without it,
78
+ // buildAgentRoster()'s normalizeAgentCount() falls back to DEFAULT_AGENT_COUNT (5)
79
+ // while maxAgentCount below is capped to workerCount, so any loop node with a
80
+ // worker_count/max_subagents budget under 5 (the common case) threw
81
+ // "Agent count 5 exceeds max N" instead of clamping to the smaller budget.
82
+ agents: Math.max(1, workerCount),
77
83
  desiredWorkItemCount: workGraph.total_work_items,
78
84
  minimumWorkItems: 1,
79
85
  maxAgentCount: Math.max(1, workerCount),
@@ -136,7 +136,7 @@ function startDeadmanHeartbeat(input) {
136
136
  schema: 'sks.naruto-actual-worker-heartbeat.v1',
137
137
  ts: nowIso(),
138
138
  item_id: input.intake.item.id,
139
- status: 'blocked',
139
+ status: 'timed_out',
140
140
  elapsed_ms: elapsedMs,
141
141
  progress: null
142
142
  }, 2 * 1024 * 1024).catch(() => undefined);
@@ -7,7 +7,7 @@ import { evaluateQaGate } from '../qa-loop.js';
7
7
  import { PPT_REQUIRED_GATE_FIELDS } from '../ppt.js';
8
8
  import { validateFinalHonestModeReport } from '../artifact-schemas.js';
9
9
  import { IMAGE_UX_REVIEW_GATE_ARTIFACT, IMAGE_UX_REVIEW_POLICY_ARTIFACT, IMAGE_UX_REVIEW_SCREEN_INVENTORY_ARTIFACT, IMAGE_UX_REVIEW_GENERATED_REVIEW_LEDGER_ARTIFACT, IMAGE_UX_REVIEW_ISSUE_LEDGER_ARTIFACT, IMAGE_UX_REVIEW_ITERATION_REPORT_ARTIFACT, IMAGE_UX_REVIEW_REQUIRED_GATE_FIELDS, IMAGE_UX_REVIEW_REFERENCE_GATE_FIELDS, IMAGE_UX_REVIEW_HONEST_MODE_ARTIFACT, imageUxReviewGateAllowsReferenceCloseout } from '../image-ux-review.js';
10
- import { CODEX_WEB_VERIFICATION_EVIDENCE_SOURCE, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, evidenceMentionsForbiddenBrowserAutomation, reflectionRequiredForRoute } from '../routes.js';
10
+ import { CODEX_WEB_VERIFICATION_EVIDENCE_SOURCE, FROM_CHAT_IMG_CHECKLIST_ARTIFACT, FROM_CHAT_IMG_COVERAGE_ARTIFACT, FROM_CHAT_IMG_QA_LOOP_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_ARTIFACT, FROM_CHAT_IMG_TEMP_TRIWIKI_SESSIONS, evidenceMentionsForbiddenBrowserAutomation, reflectionRequiredForRoute, routeById } from '../routes.js';
11
11
  import { validateRouteCompletionProof } from '../proof/route-proof-gate.js';
12
12
  import { routeFromState, routeRequiresCompletionProof } from '../proof/route-proof-policy.js';
13
13
  import { AGENT_INTAKE_STAGE_ID } from '../agents/agent-schema.js';
@@ -17,6 +17,7 @@ import { MISTAKE_RECALL_ARTIFACT, mistakeRecallGateStatus } from '../mistake-rec
17
17
  import { SSOT_GUARD_ARTIFACT, validateSsotGuardArtifact } from '../safety/ssot-guard.js';
18
18
  import { validateTeamRuntimeArtifacts } from '../team-dag.js';
19
19
  import { checkStopGate } from '../stop-gate/stop-gate-check.js';
20
+ import { readWorkOrderLedger, evaluateWorkOrderCoverage } from '../work-order-ledger.js';
20
21
  import { clarificationStopReason, context7Evidence, hasContext7DocsEvidence, hasSubagentEvidence, subagentEvidence, } from './runtime-core.js';
21
22
  import { projectTriwikiToAgentsMd } from '../triwiki/agents-md-projector.js';
22
23
  const REFLECTION_ARTIFACT = 'reflection.md';
@@ -74,6 +75,27 @@ async function reflectionGateStatus(root, state = {}, jsonCache) {
74
75
  }
75
76
  return { ok, missing };
76
77
  }
78
+ // The single choke point every route's stop decision passes through
79
+ // (hookStop -> evaluateStop). A work-order-ledger.json here means SOME
80
+ // code already committed to tracking this mission's work items; once that
81
+ // commitment exists, stop must not be allowed while items remain
82
+ // unresolved (neither verified nor honestly blocked), or an omission can
83
+ // reach "done" silently. Missions with no ledger are unaffected (routes
84
+ // that haven't adopted per-item tracking yet, or non-work routes).
85
+ async function workOrderCoverageGateStatus(root, state = {}) {
86
+ const id = state?.mission_id;
87
+ if (!id)
88
+ return { ok: true, blockers: [] };
89
+ const ledger = await readWorkOrderLedger(missionDir(root, id));
90
+ if (!ledger) {
91
+ const route = routeById(routeFromState(state));
92
+ if (route?.coverage_required)
93
+ return { ok: false, blockers: ['work_order_ledger_missing'] };
94
+ return { ok: true, blockers: [] };
95
+ }
96
+ const coverage = evaluateWorkOrderCoverage(ledger);
97
+ return { ok: coverage.ok, blockers: coverage.blockers };
98
+ }
77
99
  async function staleReflectionReasons(root, state = {}, gate = {}) {
78
100
  const created = Date.parse(gate.created_at || gate.updated_at || '');
79
101
  if (!Number.isFinite(created))
@@ -233,9 +255,16 @@ export async function evaluateStop(root, state, payload, opts = {}) {
233
255
  };
234
256
  }
235
257
  if (gate.ok) {
258
+ const coverage = await workOrderCoverageGateStatus(root, state);
259
+ if (!coverage.ok)
260
+ return complianceBlock(root, state, `SKS ${state.route_command || state.mode || 'route'} route has unresolved work-order-ledger items (neither verified nor honestly blocked): ${coverage.blockers.join(', ')}.`, { gate: 'work-order-ledger', missing: coverage.blockers });
236
261
  const reflection = await reflectionGateStatus(root, state, jsonCache);
237
- if (!reflection.ok)
262
+ if (!reflection.ok) {
263
+ const coverageRequiredRoute = routeById(routeFromState(state))?.coverage_required === true;
264
+ if (coverageRequiredRoute)
265
+ return complianceBlock(root, state, reflectionStopReason(state, reflection), { gate: 'reflection', missing: reflection.missing });
238
266
  await appendHonestModeNote(root, state, `reflection stale: ${(reflection.missing || []).join(', ')}`);
267
+ }
239
268
  return { continue: true };
240
269
  }
241
270
  const missing = gate.missing?.length ? ` Missing gate fields: ${gate.missing.join(', ')}.` : '';
@@ -255,8 +284,12 @@ export async function evaluateStop(root, state, payload, opts = {}) {
255
284
  allowLatestFallback: opts.allowLatestFallback !== true ? false : true,
256
285
  });
257
286
  if (stopCheck.action === 'allow_stop') {
258
- if (narutoFamily)
287
+ if (narutoFamily) {
288
+ const coverage = await workOrderCoverageGateStatus(root, state);
289
+ if (!coverage.ok)
290
+ return complianceBlock(root, state, `SKS ${state.route_command || state.mode || 'route'} route has unresolved work-order-ledger items (neither verified nor honestly blocked): ${coverage.blockers.join(', ')}.`, { gate: 'work-order-ledger', missing: coverage.blockers });
259
291
  return { continue: true, systemMessage: `SKS: canonical stop-gate passed at ${stopCheck.gate_path}` };
292
+ }
260
293
  }
261
294
  else if (stopCheck.action === 'hard_blocked') {
262
295
  return { continue: true, systemMessage: stopCheck.feedback, action: 'hard_blocked', gate: stopCheck.gate_path };
@@ -290,6 +323,9 @@ export async function evaluateStop(root, state, payload, opts = {}) {
290
323
  const reflection = await reflectionGateStatus(root, state, jsonCache);
291
324
  if (!reflection.ok)
292
325
  return complianceBlock(root, state, reflectionStopReason(state, reflection), { gate: 'reflection', missing: reflection.missing });
326
+ const coverage = await workOrderCoverageGateStatus(root, state);
327
+ if (!coverage.ok)
328
+ return complianceBlock(root, state, `SKS ${state.route_command || state.mode || 'route'} route has unresolved work-order-ledger items (neither verified nor honestly blocked): ${coverage.blockers.join(', ')}.`, { gate: 'work-order-ledger', missing: coverage.blockers });
293
329
  fireAndForgetProjectMemory(root, state);
294
330
  return null;
295
331
  }
@@ -26,6 +26,14 @@ export async function writeSelftestRouteProof(root, { missionId, route = '$Team'
26
26
  return writeRouteCompletionProof(root, {
27
27
  missionId,
28
28
  route,
29
+ // This fixture writes into a scratch temp directory (see selftestCommand's
30
+ // `tmpdir('sks-selftest-')`) that is discarded after the call returns, so trust
31
+ // artifacts and post-route retention are pointless here. Worse than pointless:
32
+ // writeRouteCompletionProof's default path runs runPostRouteRetention() with
33
+ // `max_tmp_age_hours: 0`, which swept the just-written completion-proof.json
34
+ // before the caller could read it back, making `sks selftest --mock` fail with
35
+ // "completion proof fixture missing" even though the proof was written correctly.
36
+ lightweightEvidence: true,
29
37
  status: 'mock_only',
30
38
  executionClass: 'mock_fixture',
31
39
  gate: {
@@ -35,7 +35,7 @@ export function inferCandidateRequirementCoverage(input) {
35
35
  assumptions.push(`manual_preservation_evidence_required:${requirement.id}`);
36
36
  continue;
37
37
  }
38
- if (input.envelope.patch.trim()) {
38
+ if (input.envelope.patch.trim() && hasKeywordOverlap(requirement.text, input.envelope.patch, input.envelope.target_paths)) {
39
39
  satisfied.push(requirement.id);
40
40
  }
41
41
  else {
@@ -89,4 +89,21 @@ export function buildGlmNarutoRequirementCoverageSummary(input) {
89
89
  function extractPaths(text) {
90
90
  return [...new Set(text.match(/[A-Za-z0-9_.-]+\/[A-Za-z0-9_./-]+\.[A-Za-z0-9]+/g) ?? [])];
91
91
  }
92
+ const REQUIREMENT_STOPWORDS = new Set([
93
+ 'the', 'a', 'an', 'and', 'or', 'to', 'of', 'in', 'on', 'for', 'with', 'is', 'are', 'be',
94
+ 'this', 'that', 'it', 'as', 'by', 'at', 'from', 'should', 'must', 'will', 'not', 'no',
95
+ '이', '가', '을', '를', '은', '는', '에', '의', '으로', '로', '만', '및', '그리고'
96
+ ]);
97
+ function extractKeywords(text) {
98
+ const tokens = text.toLowerCase().match(/[a-z0-9_]{3,}|[가-힣]{2,}/g) ?? [];
99
+ return [...new Set(tokens.filter((token) => !REQUIREMENT_STOPWORDS.has(token)))];
100
+ }
101
+ function hasKeywordOverlap(requirementText, patch, targetPaths) {
102
+ const keywords = extractKeywords(requirementText);
103
+ if (keywords.length === 0)
104
+ return false;
105
+ const patchLower = patch.toLowerCase();
106
+ const pathsLower = targetPaths.map((file) => file.toLowerCase());
107
+ return keywords.some((keyword) => patchLower.includes(keyword) || pathsLower.some((file) => file.includes(keyword)));
108
+ }
92
109
  //# sourceMappingURL=glm-naruto-requirement-coverage.js.map
@@ -1,7 +1,8 @@
1
+ const CONSTRAINT_CLAUSE_PATTERN = /\b(must|without|do not|don't|preserve|only|never|no fallback|no fake|금지|필수|보존|없이|만)\b/;
1
2
  export function buildGlmNarutoRequirementLedger(input) {
2
3
  const requirements = [];
3
4
  const seen = new Set();
4
- const add = (text, source, required = true) => {
5
+ const add = (text, source, required = true, kind = 'task') => {
5
6
  const clean = text.replace(/\s+/g, ' ').trim();
6
7
  if (!clean || seen.has(`${source}:${clean.toLowerCase()}`))
7
8
  return;
@@ -10,23 +11,22 @@ export function buildGlmNarutoRequirementLedger(input) {
10
11
  id: `REQ-${String(requirements.length + 1).padStart(3, '0')}`,
11
12
  text: clean,
12
13
  source,
13
- required
14
+ required,
15
+ kind
14
16
  });
15
17
  };
16
18
  for (const clause of splitRequirementClauses(input.task)) {
17
- const lower = clause.toLowerCase();
18
- if (/\b(must|without|do not|don't|preserve|only|never|no fallback|no fake|금지|필수|보존|없이|만)\b/.test(lower)) {
19
- add(clause, 'user_task', true);
20
- }
19
+ const kind = CONSTRAINT_CLAUSE_PATTERN.test(clause.toLowerCase()) ? 'constraint' : 'task';
20
+ add(clause, 'user_task', true, kind);
21
21
  }
22
22
  for (const file of input.mentionedPaths ?? []) {
23
- add(`Touch or preserve mentioned path: ${file}`, 'user_task', true);
23
+ add(`Touch or preserve mentioned path: ${file}`, 'user_task', true, 'constraint');
24
24
  }
25
25
  if (input.gitStatus && input.gitStatus.trim()) {
26
- add('Preserve pre-existing dirty worktree changes unless explicitly selected.', 'git_status', true);
26
+ add('Preserve pre-existing dirty worktree changes unless explicitly selected.', 'git_status', true, 'constraint');
27
27
  }
28
28
  if (requirements.length === 0)
29
- add(input.task, 'user_task', true);
29
+ add(input.task, 'user_task', true, 'task');
30
30
  return {
31
31
  schema: 'sks.glm-naruto-requirement-ledger.v1',
32
32
  mission_id: input.missionId,
@@ -34,9 +34,25 @@ export function buildGlmNarutoRequirementLedger(input) {
34
34
  };
35
35
  }
36
36
  function splitRequirementClauses(task) {
37
- return task
38
- .split(/\r?\n|[.;]|(?:\s+-\s+)/g)
39
- .map((part) => part.replace(/^[-*\d.)\s]+/, '').trim())
37
+ const byLine = task
38
+ .split(/\r?\n/g)
39
+ .map((line) => line.trim())
40
40
  .filter(Boolean);
41
+ const clauses = [];
42
+ for (const line of byLine) {
43
+ const byMarker = line
44
+ .split(/(?:\s+-\s+)|(?=(?:^|\s)(?:\d+[.)]|[-*•])\s)/g)
45
+ .map((part) => part.replace(/^[-*•\d.)\s]+/, '').trim())
46
+ .filter(Boolean);
47
+ const segments = byMarker.length > 1 ? byMarker : [line];
48
+ for (const segment of segments) {
49
+ const bySentence = segment
50
+ .split(/(?<=[.;!?])\s+(?=[A-Z0-9가-힣])|[;]/g)
51
+ .map((part) => part.replace(/^[-*•\d.)\s]+/, '').trim())
52
+ .filter(Boolean);
53
+ clauses.push(...(bySentence.length > 0 ? bySentence : [segment]));
54
+ }
55
+ }
56
+ return clauses.filter(Boolean);
41
57
  }
42
58
  //# sourceMappingURL=glm-naruto-requirement-ledger.js.map
@@ -392,7 +392,7 @@ export function buildRequestIntake(prompt, explicitAnswers = {}, opts = {}) {
392
392
  const answers = { ...(inferred.answers || {}), ...(explicitAnswers || {}) };
393
393
  const ambiguity = buildAmbiguityAssessment(originalPrompt, explicitAnswers);
394
394
  const wikiContext = summarizeWikiContext(opts.wikiContext);
395
- const promptRequirements = promptRequirementItems(originalPrompt);
395
+ const { items: promptRequirements } = promptRequirementItems(originalPrompt);
396
396
  const semanticRequirements = semanticRequirementItems(originalPrompt);
397
397
  const acceptance = toStringList(answers.ACCEPTANCE_CRITERIA);
398
398
  const constraints = toStringList(answers.RISK_BOUNDARY);
@@ -464,20 +464,22 @@ function summarizeWikiContext(wikiContext = null) {
464
464
  }))
465
465
  };
466
466
  }
467
- function promptRequirementItems(prompt) {
468
- const cleaned = prompt
469
- .replace(/(?:^|\s)\$[A-Za-z0-9_-]+(?:\s|$)/g, ' ')
470
- .replace(/\s+/g, ' ')
471
- .trim();
467
+ const PROMPT_REQUIREMENT_ITEM_CEILING = 128;
468
+ export function promptRequirementItems(prompt) {
469
+ const stripped = String(prompt || '').replace(/(?:^|\s)\$[A-Za-z0-9_-]+(?:\s|$)/g, ' ');
470
+ const cleaned = stripped.replace(/[ \t]+/g, ' ').trim();
472
471
  if (!cleaned)
473
- return [];
472
+ return { items: [], truncated: false, truncated_count: 0 };
474
473
  const sourceChunks = cleaned
475
- .split(/\n+|(?:^|\s)[-*]\s+/)
474
+ .split(/\n+|(?:^|\s)[-*]\s+|(?:^|\n)\s*\d+[.)]\s+|(?:^|\n)\s*[①-⑳]\s*|(?:^|\n)\s*[A-Z]-\d+[.)]?\s+|(?:^|\n)\s*#{1,4}\s+/m)
476
475
  .flatMap((chunk) => splitLooseClauses(chunk))
477
- .map((chunk) => chunk.trim())
476
+ .map((chunk) => chunk.replace(/\s+/g, ' ').trim())
478
477
  .filter(Boolean);
479
478
  const chunks = sourceChunks.length ? sourceChunks : [cleaned];
480
- return chunks.slice(0, 12).map((text, index) => requirementItem('prompt', index + 1, text, 'source_prompt_order'));
479
+ const truncated = chunks.length > PROMPT_REQUIREMENT_ITEM_CEILING;
480
+ const kept = truncated ? chunks.slice(0, PROMPT_REQUIREMENT_ITEM_CEILING) : chunks;
481
+ const items = kept.map((text, index) => requirementItem('prompt', index + 1, text, 'source_prompt_order'));
482
+ return { items, truncated, truncated_count: truncated ? chunks.length - PROMPT_REQUIREMENT_ITEM_CEILING : 0 };
481
483
  }
482
484
  function splitLooseClauses(text) {
483
485
  const compact = String(text || '').trim();