sneakoscope 4.8.7 → 5.3.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 (261) hide show
  1. package/README.md +51 -726
  2. package/config/bench-baseline.json +7 -0
  3. package/crates/sks-core/Cargo.lock +1 -1
  4. package/crates/sks-core/Cargo.toml +1 -1
  5. package/crates/sks-core/src/main.rs +1 -1
  6. package/dist/bin/install.js +35 -0
  7. package/dist/bin/sks.js +1 -1
  8. package/dist/cli/cli-theme.js +51 -0
  9. package/dist/cli/command-registry.js +15 -4
  10. package/dist/cli/help-fast.js +18 -8
  11. package/dist/cli/insane-search-command.js +36 -8
  12. package/dist/cli/install-helpers.js +32 -0
  13. package/dist/commands/codex-lb.js +2 -0
  14. package/dist/commands/doctor.js +94 -9
  15. package/dist/commands/proof.js +6 -3
  16. package/dist/config/skills-manifest.json +82 -54
  17. package/dist/core/agents/agent-conflict-graph.js +5 -0
  18. package/dist/core/agents/agent-lease.js +5 -0
  19. package/dist/core/agents/agent-ledger-schemas.js +1 -0
  20. package/dist/core/agents/agent-lifecycle.js +5 -3
  21. package/dist/core/agents/agent-orchestrator.js +268 -62
  22. package/dist/core/agents/agent-output-validator.js +14 -3
  23. package/dist/core/agents/agent-patch-queue-store.js +95 -2
  24. package/dist/core/agents/agent-patch-queue.js +6 -3
  25. package/dist/core/agents/agent-patch-schema.js +13 -0
  26. package/dist/core/agents/agent-proof-evidence.js +6 -1
  27. package/dist/core/agents/agent-runner-fake.js +40 -0
  28. package/dist/core/agents/agent-worker-pipeline.js +36 -2
  29. package/dist/core/agents/codex-exec-worker-adapter.js +2 -0
  30. package/dist/core/agents/native-cli-worker.js +0 -1
  31. package/dist/core/agents/native-worker-backend-router.js +58 -1
  32. package/dist/core/codex-app/sks-menubar.js +477 -132
  33. package/dist/core/codex-app.js +1 -0
  34. package/dist/core/codex-control/codex-sdk-config-policy.js +2 -1
  35. package/dist/core/codex-control/codex-sdk-env-policy.js +1 -1
  36. package/dist/core/codex-control/schemas/agent-worker-result.schema.js +12 -3
  37. package/dist/core/codex-hooks/codex-hook-managed-install.js +5 -0
  38. package/dist/core/codex-hooks/codex-hook-state-writer.js +70 -11
  39. package/dist/core/codex-hooks/codex-hook-trust-doctor.js +2 -2
  40. package/dist/core/codex-lb/codex-lb-env.js +20 -1
  41. package/dist/core/commands/basic-cli.js +18 -1
  42. package/dist/core/commands/check-command.js +37 -5
  43. package/dist/core/commands/computer-use-command.js +62 -7
  44. package/dist/core/commands/db-command.js +31 -5
  45. package/dist/core/commands/fast-mode-command.js +3 -0
  46. package/dist/core/commands/gate-result-contract.js +43 -0
  47. package/dist/core/commands/gates-command.js +6 -1
  48. package/dist/core/commands/gc-command.js +29 -2
  49. package/dist/core/commands/goal-command.js +9 -2
  50. package/dist/core/commands/gx-command.js +79 -7
  51. package/dist/core/commands/image-ux-review-command.js +188 -13
  52. package/dist/core/commands/mad-db-command.js +85 -176
  53. package/dist/core/commands/mad-sks-command.js +230 -29
  54. package/dist/core/commands/menubar-command.js +24 -2
  55. package/dist/core/commands/naruto-command.js +64 -10
  56. package/dist/core/commands/plan-command.js +76 -0
  57. package/dist/core/commands/ppt-command.js +159 -24
  58. package/dist/core/commands/qa-loop-command.js +6 -2
  59. package/dist/core/commands/release-command.js +55 -2
  60. package/dist/core/commands/research-command.js +1 -1
  61. package/dist/core/commands/review-command.js +217 -0
  62. package/dist/core/commands/route-success-helpers.js +59 -0
  63. package/dist/core/commands/run-command.js +19 -7
  64. package/dist/core/commands/seo-command.js +49 -1
  65. package/dist/core/commands/ui-command.js +161 -0
  66. package/dist/core/commands/uninstall-command.js +15 -1
  67. package/dist/core/db-safety.js +2 -2
  68. package/dist/core/doctor/doctor-dirty-planner.js +51 -7
  69. package/dist/core/doctor/doctor-transaction.js +8 -0
  70. package/dist/core/doctor/doctor-zellij-repair.js +1 -0
  71. package/dist/core/doctor/imagegen-repair.js +161 -0
  72. package/dist/core/feature-fixture-runner.js +2 -2
  73. package/dist/core/feature-fixtures.js +58 -37
  74. package/dist/core/feature-registry.js +102 -2
  75. package/dist/core/fsx.js +39 -6
  76. package/dist/core/image-ux-review/imagegen-adapter.js +48 -11
  77. package/dist/core/image-ux-review.js +16 -0
  78. package/dist/core/imagegen/imagegen-capability.js +11 -5
  79. package/dist/core/imagegen/require-imagegen.js +57 -0
  80. package/dist/core/init/skills.js +10 -5
  81. package/dist/core/mad-db/mad-db-coordinator.js +94 -18
  82. package/dist/core/mad-db/mad-db-policy.js +12 -10
  83. package/dist/core/mad-sks/executors/executor-base.js +8 -2
  84. package/dist/core/mad-sks/executors/index.js +4 -0
  85. package/dist/core/mad-sks/executors/sql-plane-executor.js +194 -0
  86. package/dist/core/naruto/naruto-active-pool.js +15 -4
  87. package/dist/core/naruto/naruto-real-worker-child.js +11 -1
  88. package/dist/core/naruto/naruto-role-policy.js +3 -0
  89. package/dist/core/naruto/naruto-task-hints.js +10 -0
  90. package/dist/core/naruto/naruto-work-graph.js +8 -2
  91. package/dist/core/naruto/naruto-work-item.js +6 -0
  92. package/dist/core/naruto/solution-tournament.js +101 -0
  93. package/dist/core/permission-gates.js +30 -0
  94. package/dist/core/pipeline-internals/runtime-core.js +11 -3
  95. package/dist/core/pipeline-internals/runtime-gates.js +27 -0
  96. package/dist/core/ppt-review/index.js +6 -1
  97. package/dist/core/ppt-review/slide-imagegen-review.js +75 -10
  98. package/dist/core/ppt.js +70 -2
  99. package/dist/core/proof/auto-finalize.js +70 -11
  100. package/dist/core/proof/proof-schema.js +2 -0
  101. package/dist/core/proof/route-adapter.js +5 -1
  102. package/dist/core/proof/route-finalizer.js +8 -5
  103. package/dist/core/proof/selftest-proof-fixtures.js +18 -5
  104. package/dist/core/proof/validation.js +2 -0
  105. package/dist/core/provider/model-router.js +53 -0
  106. package/dist/core/release/release-gate-node.js +3 -0
  107. package/dist/core/release/release-gate-resource-governor.js +1 -0
  108. package/dist/core/routes/constants.js +1 -1
  109. package/dist/core/routes.js +65 -18
  110. package/dist/core/safety/mutation-guard.js +5 -1
  111. package/dist/core/stop-gate/gate-evaluator.js +102 -0
  112. package/dist/core/stop-gate/stop-gate-check.js +16 -1
  113. package/dist/core/stop-gate/stop-gate-writer.js +4 -0
  114. package/dist/core/triwiki/agents-md-projector.js +184 -0
  115. package/dist/core/triwiki-wrongness/wrongness-schema.js +3 -1
  116. package/dist/core/trust-kernel/trust-kernel-schema.js +1 -0
  117. package/dist/core/trust-kernel/trust-report.js +1 -1
  118. package/dist/core/trust-kernel/trust-status.js +2 -0
  119. package/dist/core/ui/dashboard-html.js +111 -0
  120. package/dist/core/update/update-migration-state.js +386 -15
  121. package/dist/core/update-check.js +178 -81
  122. package/dist/core/verification/diff-quality.js +100 -0
  123. package/dist/core/verification/impact-scan.js +164 -0
  124. package/dist/core/verification/machine-feedback.js +146 -0
  125. package/dist/core/verification/mistake-rule-compiler.js +195 -0
  126. package/dist/core/version.js +1 -1
  127. package/dist/core/zellij/zellij-self-heal.js +9 -6
  128. package/dist/core/zellij/zellij-slot-telemetry.js +1 -1
  129. package/dist/scripts/check-feature-quality.js +2 -2
  130. package/dist/scripts/cli-output-consistency-check.js +57 -0
  131. package/dist/scripts/coding-bench-check.js +136 -0
  132. package/dist/scripts/doctor-imagegen-repair-check.js +62 -0
  133. package/dist/scripts/ensure-bin-executable.js +11 -3
  134. package/dist/scripts/harness-benchmark-check.js +104 -0
  135. package/dist/scripts/legacy-update-e2e-check.js +228 -0
  136. package/dist/scripts/mad-db-command-check.js +11 -8
  137. package/dist/scripts/mad-db-route-identity-check.js +8 -6
  138. package/dist/scripts/mad-db-safety-conflict-matrix-check.js +31 -8
  139. package/dist/scripts/mad-db-skill-policy-snapshot-check.js +4 -5
  140. package/dist/scripts/naruto-real-active-pool-runtime-check.js +26 -1
  141. package/dist/scripts/ppt-real-imagegen-wiring-check.js +5 -1
  142. package/dist/scripts/proof-root-cause-policy-check.js +2 -0
  143. package/dist/scripts/release-dag-full-coverage-check.js +13 -4
  144. package/dist/scripts/release-gate-dag-runner.js +14 -0
  145. package/dist/scripts/release-gate-existence-audit.js +2 -1
  146. package/dist/scripts/release-gate-planner.js +2 -1
  147. package/dist/scripts/release-gate-script-parity-check.js +45 -17
  148. package/dist/scripts/release-metadata-1-19-check.js +2 -1
  149. package/dist/scripts/seo-geo-feature-fixture-quality-check.js +1 -1
  150. package/dist/scripts/sks-1-11-gate-lib.js +8 -6
  151. package/dist/scripts/sks-menubar-install-check.js +57 -20
  152. package/dist/scripts/ux-review-real-loop-fixture-check.js +9 -4
  153. package/dist/scripts/ux-review-run-wires-imagegen-check.js +4 -0
  154. package/dist/scripts/zellij-slot-telemetry-runtime-check.js +1 -0
  155. package/docs/assets/sneakoscope-architecture-pipeline.jpg +0 -0
  156. package/docs/demo.tape +28 -0
  157. package/package.json +31 -24
  158. package/schemas/codex/completion-proof.schema.json +3 -2
  159. package/schemas/release/release-gate-node.schema.json +2 -1
  160. package/dist/scripts/agent-backfill-route-blackbox.js +0 -5
  161. package/dist/scripts/agent-dynamic-pool-route-blackbox.js +0 -5
  162. package/dist/scripts/agent-parallel-write-blackbox.js +0 -56
  163. package/dist/scripts/agent-patch-swarm-route-blackbox.js +0 -10
  164. package/dist/scripts/agent-route-blackbox-lib.js +0 -132
  165. package/dist/scripts/blackbox-command-import-smoke.js +0 -143
  166. package/dist/scripts/blackbox-global-shim.js +0 -77
  167. package/dist/scripts/blackbox-matrix.js +0 -70
  168. package/dist/scripts/blackbox-npx-one-shot.js +0 -69
  169. package/dist/scripts/blackbox-pack-install.js +0 -174
  170. package/dist/scripts/brand-neutrality-zero-leakage-blackbox.js +0 -4
  171. package/dist/scripts/build-once-runner-blackbox.js +0 -34
  172. package/dist/scripts/codex-0140-integration-blackbox.js +0 -13
  173. package/dist/scripts/codex-agent-type-blackbox.js +0 -4
  174. package/dist/scripts/codex-app-harness-blackbox.js +0 -4
  175. package/dist/scripts/codex-app-skill-agent-blackbox.js +0 -4
  176. package/dist/scripts/codex-hook-approval-blackbox.js +0 -4
  177. package/dist/scripts/codex-init-deep-directory-local-blackbox.js +0 -4
  178. package/dist/scripts/codex-native-feature-broker-blackbox.js +0 -4
  179. package/dist/scripts/codex-native-pattern-analysis-blackbox.js +0 -4
  180. package/dist/scripts/codex-native-read-repair-split-blackbox.js +0 -55
  181. package/dist/scripts/codex-native-reference-cache-blackbox.js +0 -41
  182. package/dist/scripts/core-skill-integrity-blackbox.js +0 -32
  183. package/dist/scripts/dfix-fast-blackbox-check.js +0 -37
  184. package/dist/scripts/dfix-parallel-write-blackbox.js +0 -48
  185. package/dist/scripts/dfix-patch-swarm-route-blackbox.js +0 -10
  186. package/dist/scripts/doctor-dirty-repair-blackbox.js +0 -22
  187. package/dist/scripts/doctor-dirty-semantic-blackbox.js +0 -8
  188. package/dist/scripts/doctor-native-capability-repair-blackbox.js +0 -39
  189. package/dist/scripts/doctor-transaction-engine-blackbox.js +0 -28
  190. package/dist/scripts/doctor-zellij-fix-blackbox.js +0 -4
  191. package/dist/scripts/doctor-zellij-no-homebrew-blackbox.js +0 -4
  192. package/dist/scripts/doctor-zellij-upgrade-blackbox.js +0 -4
  193. package/dist/scripts/gate-pack-runner-blackbox.js +0 -27
  194. package/dist/scripts/gate-pack-v2-blackbox.js +0 -18
  195. package/dist/scripts/geo-cli-blackbox-check.js +0 -18
  196. package/dist/scripts/loop-collision-blackbox.js +0 -3
  197. package/dist/scripts/loop-concurrency-oversubscription-blackbox.js +0 -3
  198. package/dist/scripts/loop-fixture-production-misuse-blackbox.js +0 -3
  199. package/dist/scripts/loop-kill-interrupt-real-blackbox.js +0 -3
  200. package/dist/scripts/loop-merge-strategy-blackbox.js +0 -3
  201. package/dist/scripts/loop-mesh-production-e2e-blackbox.js +0 -3
  202. package/dist/scripts/loop-side-effect-blackbox.js +0 -3
  203. package/dist/scripts/mad-db-operation-lifecycle-blackbox.js +0 -29
  204. package/dist/scripts/mad-sks-actual-executor-blackbox.js +0 -5
  205. package/dist/scripts/mad-zellij-headless-fallback-blackbox.js +0 -4
  206. package/dist/scripts/mad-zellij-self-heal-blackbox.js +0 -4
  207. package/dist/scripts/naruto-loop-mesh-blackbox.js +0 -3
  208. package/dist/scripts/naruto-real-parallelism-blackbox.js +0 -307
  209. package/dist/scripts/naruto-worktree-coding-blackbox.js +0 -29
  210. package/dist/scripts/parallel-runtime-real-blackbox.js +0 -44
  211. package/dist/scripts/pipeline-codex-native-doctor-mad-routing-real-blackbox.js +0 -77
  212. package/dist/scripts/pipeline-codex-native-e2e-blackbox.js +0 -13
  213. package/dist/scripts/pipeline-codex-native-image-routing-real-blackbox.js +0 -50
  214. package/dist/scripts/pipeline-codex-native-loop-routing-real-blackbox.js +0 -74
  215. package/dist/scripts/pipeline-codex-native-qa-routing-real-blackbox.js +0 -51
  216. package/dist/scripts/pipeline-codex-native-research-routing-real-blackbox.js +0 -45
  217. package/dist/scripts/pipeline-execution-profile-routing-blackbox.js +0 -4
  218. package/dist/scripts/postinstall-global-doctor-blackbox.js +0 -12
  219. package/dist/scripts/ppt-full-e2e-blackbox-check.js +0 -109
  220. package/dist/scripts/ppt-imagegen-blackbox-check.js +0 -46
  221. package/dist/scripts/qa-backfill-route-blackbox.js +0 -5
  222. package/dist/scripts/qa-patch-swarm-route-blackbox.js +0 -10
  223. package/dist/scripts/release-full-parallelism-blackbox.js +0 -41
  224. package/dist/scripts/release-triwiki-first-runner-blackbox.js +0 -48
  225. package/dist/scripts/release-wiring-3110-blackbox.js +0 -27
  226. package/dist/scripts/release-wiring-3112-blackbox.js +0 -17
  227. package/dist/scripts/release-wiring-3113-blackbox.js +0 -17
  228. package/dist/scripts/research-backfill-route-blackbox.js +0 -5
  229. package/dist/scripts/research-final-reviewer-blackbox.js +0 -70
  230. package/dist/scripts/research-stage-cycle-runtime-blackbox.js +0 -40
  231. package/dist/scripts/research-synthesis-writer-blackbox.js +0 -24
  232. package/dist/scripts/route-blackbox-realism-check.js +0 -21
  233. package/dist/scripts/scheduler-resource-claim-blackbox.js +0 -24
  234. package/dist/scripts/seo-cli-blackbox-check.js +0 -18
  235. package/dist/scripts/sks-3110-all-feature-regression-blackbox.js +0 -116
  236. package/dist/scripts/sks-3112-all-feature-regression-blackbox.js +0 -29
  237. package/dist/scripts/sks-3113-all-feature-regression-blackbox.js +0 -17
  238. package/dist/scripts/sks-400-all-feature-regression-blackbox.js +0 -21
  239. package/dist/scripts/sks-400-extreme-parallel-blackbox.js +0 -8
  240. package/dist/scripts/sks-400-five-minute-blackbox.js +0 -9
  241. package/dist/scripts/sks-400-legacy-purge-blackbox.js +0 -8
  242. package/dist/scripts/sks-401-all-feature-regression-blackbox.js +0 -46
  243. package/dist/scripts/sks-401-five-minute-actual-blackbox.js +0 -23
  244. package/dist/scripts/sks-402-all-feature-regression-blackbox.js +0 -9
  245. package/dist/scripts/sks-402-five-minute-real-blackbox.js +0 -22
  246. package/dist/scripts/sksd-warm-cache-blackbox.js +0 -12
  247. package/dist/scripts/team-backfill-route-blackbox.js +0 -5
  248. package/dist/scripts/team-parallel-write-blackbox.js +0 -55
  249. package/dist/scripts/team-patch-swarm-route-blackbox.js +0 -10
  250. package/dist/scripts/triwiki-affected-graph-blackbox.js +0 -28
  251. package/dist/scripts/triwiki-proof-bank-blackbox.js +0 -30
  252. package/dist/scripts/triwiki-proof-bank-lock-blackbox.js +0 -7
  253. package/dist/scripts/ux-patch-swarm-route-blackbox.js +0 -10
  254. package/dist/scripts/ux-review-imagegen-blackbox-check.js +0 -67
  255. package/dist/scripts/zellij-initial-main-only-blackbox.js +0 -28
  256. package/dist/scripts/zellij-pane-lock-concurrency-blackbox.js +0 -80
  257. package/dist/scripts/zellij-pane-lock-open-worker-integration-blackbox.js +0 -137
  258. package/dist/scripts/zellij-self-heal-typed-blackbox.js +0 -4
  259. package/dist/scripts/zellij-slot-telemetry-real-blackbox.js +0 -20
  260. package/dist/scripts/zellij-stacked-fallback-integration-blackbox.js +0 -81
  261. package/dist/scripts/zellij-worker-pane-real-ui-blackbox.js +0 -202
@@ -1,4 +1,5 @@
1
1
  import path from 'node:path';
2
+ import { ui as cliUi } from '../../cli/cli-theme.js';
2
3
  import { createMission, findLatestMission, loadMission, setCurrent } from '../mission.js';
3
4
  import { nowIso, readJson, sksRoot, writeJsonAtomic } from '../fsx.js';
4
5
  import { runNativeAgentOrchestrator } from '../agents/agent-orchestrator.js';
@@ -41,6 +42,10 @@ export async function narutoCommand(commandOrArgs = 'naruto', maybeArgs = []) {
41
42
  return glmNarutoCommand(args.filter((arg) => arg !== '--glm'));
42
43
  }
43
44
  const parsed = parseNarutoArgs(args);
45
+ if (!parsed.json)
46
+ cliUi.banner(parsed.action === 'run' ? 'swarm' : `naruto ${parsed.action}`);
47
+ if (!parsed.json && parsed.action !== 'run')
48
+ cliUi.ok('naruto command surface ready');
44
49
  if (parsed.action === 'help')
45
50
  return narutoHelp(parsed);
46
51
  if (parsed.action === 'status')
@@ -125,7 +130,8 @@ async function narutoRun(parsed) {
125
130
  writeCapable,
126
131
  leaseBasePath: patchEnvelopeBasePath,
127
132
  maxActiveWorkers: parsed.concurrency || safe.cap,
128
- worktreePolicy
133
+ worktreePolicy,
134
+ tournament: parsed.tournament
129
135
  });
130
136
  const baseRoleDistribution = buildNarutoRoleDistribution(baseWorkGraph.work_items, { readonly: parsed.readonly });
131
137
  const allocationWorkers = buildNarutoAllocationWorkers(baseWorkGraph, baseRoleDistribution, roster);
@@ -140,7 +146,8 @@ async function narutoRun(parsed) {
140
146
  leaseBasePath: patchEnvelopeBasePath,
141
147
  maxActiveWorkers: parsed.concurrency || safe.cap,
142
148
  worktreePolicy,
143
- allocationAssignments
149
+ allocationAssignments,
150
+ tournament: parsed.tournament
144
151
  });
145
152
  const roleDistribution = buildNarutoRoleDistribution(workGraph.work_items, { readonly: parsed.readonly });
146
153
  const allocationPolicy = {
@@ -407,9 +414,12 @@ async function narutoRun(parsed) {
407
414
  const finalAccepted = result.proof?.status === 'passed' || result.proof?.gpt_final_status === 'approved';
408
415
  const parallelRuntimeOk = !parsed.mock || roster.agent_count < 16 || (parallelRuntime?.passed === true
409
416
  && Number(parallelRuntime.max_observed_active_workers || 0) >= Math.min(16, activeSlots));
417
+ const regressionProof = summarizeRegressionProof(workGraph, result);
418
+ await writeJsonAtomic(path.join(mission.dir, 'regression-proof-summary.json'), regressionProof);
419
+ const tddOk = !regressionProof.required || (regressionProof.regression_test_added && regressionProof.regression_test_failed_before_fix && regressionProof.regression_test_passed_after_fix);
410
420
  await writeJsonAtomic(path.join(mission.dir, 'naruto-gate.json'), {
411
421
  schema: 'sks.naruto-gate.v1',
412
- passed: result.ok === true && nativeProofOk && finalAccepted && parallelRuntimeOk,
422
+ passed: result.ok === true && nativeProofOk && finalAccepted && parallelRuntimeOk && tddOk,
413
423
  mission_id: mission.id,
414
424
  clone_roster_built: true,
415
425
  clone_count: roster.agent_count,
@@ -424,9 +434,13 @@ async function narutoRun(parsed) {
424
434
  zellij_dashboard_ready: zellijDashboard.ok === true,
425
435
  native_agent_proof: nativeProofOk,
426
436
  parallel_runtime_proof: parallelRuntimeOk,
437
+ regression_test_added: regressionProof.regression_test_added,
438
+ regression_test_failed_before_fix: regressionProof.regression_test_failed_before_fix,
439
+ regression_test_passed_after_fix: regressionProof.regression_test_passed_after_fix,
440
+ regression_proof: 'regression-proof-summary.json',
427
441
  final_arbiter_accepted: finalAccepted,
428
442
  session_cleanup: result.proof?.all_sessions_closed === true || nativeProofOk,
429
- blockers: [...(result.proof?.blockers || []), ...(parallelRuntimeOk ? [] : ['naruto_parallel_runtime_proof_below_gate'])],
443
+ blockers: [...(result.proof?.blockers || []), ...(parallelRuntimeOk ? [] : ['naruto_parallel_runtime_proof_below_gate']), ...(tddOk ? [] : ['tdd_evidence_missing'])],
430
444
  updated_at: nowIso()
431
445
  });
432
446
  const clones = result.roster?.agent_count ?? roster.agent_count;
@@ -443,7 +457,7 @@ async function narutoRun(parsed) {
443
457
  generated_at: nowIso(),
444
458
  mission_id: mission.id
445
459
  });
446
- const summaryOk = result.ok === true && (parsed.applyPatches === true ? finalizer.ok === true : finalizer.run_ok === true);
460
+ const summaryOk = result.ok === true && tddOk && (parsed.applyPatches === true ? finalizer.ok === true : finalizer.run_ok === true);
447
461
  await setCurrent(root, {
448
462
  mission_id: mission.id,
449
463
  route: 'Naruto',
@@ -457,7 +471,7 @@ async function narutoRun(parsed) {
457
471
  prompt: parsed.prompt
458
472
  });
459
473
  // 4.0.9: Write canonical stop-gate artifacts for hook resolution.
460
- const narutoGatePassed = result.ok === true && nativeProofOk && finalAccepted && parallelRuntimeOk;
474
+ const narutoGatePassed = result.ok === true && nativeProofOk && finalAccepted && parallelRuntimeOk && tddOk;
461
475
  await writeFinalStopGate({
462
476
  root,
463
477
  missionId: mission.id,
@@ -471,8 +485,14 @@ async function narutoRun(parsed) {
471
485
  tests_passed: summaryOk,
472
486
  route_evidence_passed: nativeProofOk && finalAccepted,
473
487
  native_session_split_evidence: nativeProofOk ? 'native_agent_proof' : null,
488
+ ...(regressionProof.required ? {
489
+ regression_test_added: regressionProof.regression_test_added,
490
+ regression_test_failed_before_fix: regressionProof.regression_test_failed_before_fix,
491
+ regression_test_passed_after_fix: regressionProof.regression_test_passed_after_fix,
492
+ regression_proof_path: 'regression-proof-summary.json'
493
+ } : { regression_proof_path: null }),
474
494
  },
475
- blockers: summaryOk ? [] : [...(result.proof?.blockers || []), ...(parallelRuntimeOk ? [] : ['naruto_parallel_runtime_proof_below_gate'])],
495
+ blockers: summaryOk ? [] : [...(result.proof?.blockers || []), ...(parallelRuntimeOk ? [] : ['naruto_parallel_runtime_proof_below_gate']), ...(tddOk ? [] : ['tdd_evidence_missing'])],
476
496
  nativeGateFile: 'naruto-gate.json',
477
497
  }).catch(() => null);
478
498
  const summary = {
@@ -544,6 +564,7 @@ async function narutoRun(parsed) {
544
564
  blockers: result.fast_mode_propagation.blockers || []
545
565
  } : null,
546
566
  finalizer,
567
+ regression_proof: regressionProof,
547
568
  proof: result.proof?.status || 'missing',
548
569
  run: compactNarutoRunResult(result),
549
570
  zellij: null
@@ -687,6 +708,32 @@ function summarizeNarutoLocalWorkerResult(localWorker, result) {
687
708
  backend_counts: backendCounts
688
709
  };
689
710
  }
711
+ function summarizeRegressionProof(workGraph, result) {
712
+ const bugfixItems = (Array.isArray(workGraph?.work_items) ? workGraph.work_items : []).filter((item) => String(item?.kind || '') === 'bugfix');
713
+ const proofs = collectRegressionProofs(result).filter(validRegressionProof);
714
+ return {
715
+ schema: 'sks.regression-proof-summary.v1',
716
+ generated_at: nowIso(),
717
+ required: bugfixItems.length > 0,
718
+ bugfix_work_item_count: bugfixItems.length,
719
+ proof_count: proofs.length,
720
+ regression_test_added: bugfixItems.length === 0 ? true : proofs.some((proof) => String(proof.test_file || '').trim().length > 0),
721
+ regression_test_failed_before_fix: bugfixItems.length === 0 ? true : proofs.some((proof) => proof.failed_before === true),
722
+ regression_test_passed_after_fix: bugfixItems.length === 0 ? true : proofs.some((proof) => proof.passed_after === true),
723
+ test_files: [...new Set(proofs.map((proof) => String(proof.test_file || '')).filter(Boolean))],
724
+ blockers: bugfixItems.length === 0 || proofs.length > 0 ? [] : ['tdd_evidence_missing']
725
+ };
726
+ }
727
+ function collectRegressionProofs(result) {
728
+ const rows = Array.isArray(result?.results) ? result.results : [];
729
+ return rows.flatMap((row) => [
730
+ row?.regression_proof,
731
+ ...(Array.isArray(row?.patch_envelopes) ? row.patch_envelopes.map((envelope) => envelope?.regression_proof) : [])
732
+ ]).filter(Boolean);
733
+ }
734
+ function validRegressionProof(proof) {
735
+ return Boolean(proof && proof.failed_before === true && proof.passed_after === true && String(proof.test_file || '').trim());
736
+ }
690
737
  async function narutoStatus(parsed) {
691
738
  const root = await sksRoot();
692
739
  const id = parsed.missionId && parsed.missionId !== 'latest' ? parsed.missionId : await findLatestMission(root);
@@ -795,7 +842,7 @@ async function narutoHelp(parsed) {
795
842
  mode: 'NARUTO',
796
843
  description: 'Shadow Clone Swarm: fan out up to ' + MAX_NARUTO_AGENT_COUNT + ' parallel clone sessions.',
797
844
  usage: [
798
- 'sks naruto run "<task>" [--clones N] [--backend codex-sdk|fake|ollama|local-llm] [--local-model|--ollama|--no-ollama] [--work-items N] [--write-mode parallel|serial|off] [--apply-patches] [--dry-run-patches] [--real] [--readonly] [--json]',
845
+ 'sks naruto run "<task>" [--clones N] [--backend codex-sdk|fake|ollama|local-llm] [--local-model|--ollama|--no-ollama] [--work-items N] [--tournament 2-4] [--write-mode parallel|serial|off] [--apply-patches] [--dry-run-patches] [--real] [--readonly] [--json]',
799
846
  'sks naruto status [--mission <id>] [--json]',
800
847
  'sks naruto proof latest [--messages 20] [--json]'
801
848
  ],
@@ -866,9 +913,10 @@ function parseNarutoArgs(args = []) {
866
913
  const smoke = hasFlag(args, '--smoke');
867
914
  const parallelism = normalizeParallelism(readOption(args, '--parallelism', 'extreme'));
868
915
  const messages = normalizeMessages(readOption(args, '--messages', '8'));
869
- const valueFlags = new Set(['--clones', '--agents', '--work-items', '--concurrency', '--target-active-slots', '--backend', '--write-mode', '--max-write-agents', '--service-tier', '--mission', '--mission-id', '--ollama-model', '--local-model-model', '--ollama-base-url', '--local-model-base-url', '--parallelism', '--messages']);
916
+ const tournament = normalizeTournament(readOption(args, '--tournament', '0'));
917
+ const valueFlags = new Set(['--clones', '--agents', '--work-items', '--concurrency', '--target-active-slots', '--backend', '--write-mode', '--max-write-agents', '--service-tier', '--mission', '--mission-id', '--ollama-model', '--local-model-model', '--ollama-base-url', '--local-model-base-url', '--parallelism', '--messages', '--tournament']);
870
918
  const prompt = positionalArgs(rest, valueFlags).join(' ').trim() || 'Naruto shadow clone swarm run';
871
- return { action, prompt, clones, workItems, workItemsExplicit, concurrency, backend, backendExplicit, mock, real, readonly, ollamaEnabled: useOllama && !noOllama, noOllama, ollamaModel, ollamaBaseUrl, writeMode, applyPatches, dryRunPatches, maxWriteAgents, fastMode, serviceTier, noFast, json, missionId, noOpenZellij, attach, smoke, parallelism, messages };
919
+ return { action, prompt, clones, workItems, workItemsExplicit, concurrency, backend, backendExplicit, mock, real, readonly, ollamaEnabled: useOllama && !noOllama, noOllama, ollamaModel, ollamaBaseUrl, writeMode, applyPatches, dryRunPatches, maxWriteAgents, fastMode, serviceTier, noFast, json, missionId, noOpenZellij, attach, smoke, parallelism, messages, tournament };
872
920
  }
873
921
  function normalizeParallelism(value) {
874
922
  const text = String(value || 'extreme').toLowerCase();
@@ -880,6 +928,12 @@ function normalizeMessages(value) {
880
928
  const parsed = Number(value);
881
929
  return Math.max(0, Math.min(100, Math.floor(Number.isFinite(parsed) ? parsed : 8)));
882
930
  }
931
+ function normalizeTournament(value) {
932
+ const parsed = Number(value);
933
+ if (!Number.isFinite(parsed) || parsed < 2)
934
+ return 0;
935
+ return Math.max(2, Math.min(4, Math.floor(parsed)));
936
+ }
883
937
  async function writeNarutoArtifacts(ledgerRoot, artifacts) {
884
938
  await writeJsonAtomic(path.join(ledgerRoot, 'naruto-work-graph.json'), artifacts.workGraph);
885
939
  await writeJsonAtomic(path.join(ledgerRoot, 'naruto-role-distribution.json'), artifacts.roleDistribution);
@@ -0,0 +1,76 @@
1
+ import path from 'node:path';
2
+ import { flag } from '../../cli/args.js';
3
+ import { printJson } from '../../cli/output.js';
4
+ import { ui } from '../../cli/cli-theme.js';
5
+ import { ensureDir, nowIso, projectRoot, writeTextAtomic } from '../fsx.js';
6
+ export async function planCommand(args = []) {
7
+ const root = path.resolve(String(readOption(args, '--root', '') || await projectRoot()));
8
+ const prompt = positional(args).join(' ').trim() || String(readOption(args, '--task', '') || '').trim();
9
+ if (!prompt) {
10
+ console.error('Usage: sks plan "task" [--json]');
11
+ process.exitCode = 2;
12
+ return null;
13
+ }
14
+ const slug = slugify(prompt);
15
+ const file = path.join(root, '.sneakoscope', 'plans', `${slug}.md`);
16
+ await ensureDir(path.dirname(file));
17
+ const text = [
18
+ `# SKS Plan: ${prompt}`,
19
+ ``,
20
+ `Generated: ${nowIso()}`,
21
+ `Implementation Allowed: false`,
22
+ ``,
23
+ `## Goal`,
24
+ `- ${prompt}`,
25
+ ``,
26
+ `## Scope`,
27
+ `- Inspect the smallest relevant code and docs surface before editing.`,
28
+ `- Preserve existing SKS proof-first gates and lean-engineering policy.`,
29
+ ``,
30
+ `## Implementation Steps`,
31
+ `- Identify exact files and ownership boundaries.`,
32
+ `- Apply the smallest working change.`,
33
+ `- Update focused tests or release checks for changed behavior.`,
34
+ ``,
35
+ `## Acceptance Checks`,
36
+ `- Typecheck or targeted build passes.`,
37
+ `- Relevant SKS gate/report is written and current.`,
38
+ `- Final summary separates verified from unverified work.`,
39
+ ``,
40
+ `## Rollback Plan`,
41
+ `- Revert only files changed for this plan if verification fails.`
42
+ ].join('\n');
43
+ await writeTextAtomic(file, `${text}\n`);
44
+ const result = { schema: 'sks.plan-artifact.v1', ok: true, prompt, file: path.relative(root, file), implementation_allowed: false };
45
+ if (flag(args, '--json'))
46
+ return printJson(result);
47
+ ui.banner('plan');
48
+ ui.ok(result.file);
49
+ return result;
50
+ }
51
+ function positional(args) {
52
+ const out = [];
53
+ const optionsWithValue = new Set(['--root', '--task']);
54
+ for (let index = 0; index < args.length; index += 1) {
55
+ const arg = args[index] || '';
56
+ if (optionsWithValue.has(arg)) {
57
+ index += 1;
58
+ continue;
59
+ }
60
+ if (!arg.startsWith('--'))
61
+ out.push(arg);
62
+ }
63
+ return out;
64
+ }
65
+ function slugify(value) {
66
+ const base = value.toLowerCase().replace(/[^a-z0-9가-힣]+/gi, '-').replace(/^-+|-+$/g, '').slice(0, 60);
67
+ return base || `plan-${Date.now().toString(36)}`;
68
+ }
69
+ function readOption(args = [], name, fallback = null) {
70
+ const index = args.indexOf(name);
71
+ if (index >= 0 && args[index + 1] && !String(args[index + 1]).startsWith('--'))
72
+ return args[index + 1];
73
+ const prefixed = args.find((arg) => String(arg).startsWith(`${name}=`));
74
+ return prefixed ? prefixed.slice(name.length + 1) : fallback;
75
+ }
76
+ //# sourceMappingURL=plan-command.js.map
@@ -1,13 +1,14 @@
1
1
  import path from 'node:path';
2
- import { projectRoot, readJson, writeJsonAtomic } from '../fsx.js';
2
+ import { exists, projectRoot, readJson, writeJsonAtomic } from '../fsx.js';
3
3
  import { createMission, findLatestMission, loadMission } from '../mission.js';
4
4
  import { flag, readOption } from './command-utils.js';
5
5
  import { printJson } from '../../cli/output.js';
6
- import { writePptBuildArtifacts, writePptRouteArtifacts } from '../ppt.js';
6
+ import { PPT_AUDIENCE_STRATEGY_ARTIFACT, PPT_CLEANUP_REPORT_ARTIFACT, PPT_FACT_LEDGER_ARTIFACT, PPT_GATE_ARTIFACT, PPT_HTML_ARTIFACT, PPT_IMAGE_ASSET_LEDGER_ARTIFACT, PPT_ITERATION_REPORT_ARTIFACT, PPT_PARALLEL_REPORT_ARTIFACT, PPT_PDF_ARTIFACT, PPT_RENDER_REPORT_ARTIFACT, PPT_REVIEW_LEDGER_ARTIFACT, PPT_REVIEW_POLICY_ARTIFACT, PPT_SOURCE_LEDGER_ARTIFACT, PPT_STORYBOARD_ARTIFACT, PPT_STYLE_TOKENS_ARTIFACT, writePptBuildArtifacts, writePptRouteArtifacts } from '../ppt.js';
7
7
  import { maybeFinalizeRoute } from '../proof/auto-finalize.js';
8
8
  import { writePptImagegenReviewFixture } from '../ppt-imagegen-review.js';
9
9
  import { PPT_IMAGEGEN_REVIEW_GATE_ARTIFACT, PPT_REVIEW_ARTIFACT_PATHS, pptReviewProofEvidence, writePptImagegenReviewArtifacts } from '../ppt-review/index.js';
10
10
  import { writeRouteCollaborationArtifacts } from '../agents/route-collaboration-ledger.js';
11
+ import { requireCodexImagegen } from '../imagegen/require-imagegen.js';
11
12
  export async function pptCommand(command, args = []) {
12
13
  const root = await projectRoot();
13
14
  const action = args[0] || 'status';
@@ -25,14 +26,46 @@ export async function pptCommand(command, args = []) {
25
26
  const { dir, mission } = await loadMission(root, missionId);
26
27
  const contract = await readJson(path.join(dir, 'decision-contract.json'), { prompt: mission.prompt, answers: fixtureAnswers(), sealed_hash: 'ppt-fixture-contract' });
27
28
  if (action === 'build') {
29
+ if (!flag(args, '--mock')) {
30
+ const imagegenRequired = await requireCodexImagegen(root, { autoRepair: true, applyRepair: true });
31
+ if (!imagegenRequired.ok) {
32
+ const result = {
33
+ schema: 'sks.ppt-build.v1',
34
+ ok: false,
35
+ status: 'blocked',
36
+ mission_id: missionId,
37
+ blocker: 'codex_imagegen_unavailable',
38
+ imagegen_required: imagegenRequired,
39
+ gate: {
40
+ schema: 'sks.ppt-gate.v1',
41
+ passed: false,
42
+ status: 'blocked',
43
+ blockers: ['codex_imagegen_unavailable'],
44
+ imagegen_evidence: { passed: false, generated_image_evidence: false }
45
+ }
46
+ };
47
+ process.exitCode = 1;
48
+ if (flag(args, '--json'))
49
+ return printJson(result);
50
+ console.error('PPT build blocked: Codex App imagegen/gpt-image-2 is unavailable.');
51
+ for (const action of imagegenRequired.blocker?.next_actions || [])
52
+ console.error(`- ${action}`);
53
+ return result;
54
+ }
55
+ }
28
56
  await writePptRouteArtifacts(dir, contract);
29
57
  const build = await writePptBuildArtifacts(dir, contract);
30
- const proof = await maybeFinalizeRoute(root, { missionId, route: '$PPT', gateFile: 'ppt-gate.json', gate: build.gate, mock: flag(args, '--mock'), visual: true, artifacts: Object.keys(build.files || {}), claims: [{ id: 'ppt-build-fixture', status: 'verified_partial' }], command: { cmd: `sks ppt build ${missionId}`, status: 0 } });
31
- const result = { schema: 'sks.ppt-build.v1', ok: proof.ok, mission_id: missionId, build, proof: proof.validation };
58
+ const gate = await evaluatePptGateArtifacts(dir, build.gate);
59
+ await writeJsonAtomic(path.join(dir, 'ppt-gate.json'), gate);
60
+ const proof = await maybeFinalizeRoute(root, { missionId, route: '$PPT', gateFile: 'ppt-gate.json', gate, mock: flag(args, '--mock'), visual: true, artifacts: Object.keys(build.files || {}), claims: [{ id: 'ppt-build-fixture', status: gate.passed ? 'verified_partial' : 'blocked' }], blockers: gate.blockers || [], command: { cmd: `sks ppt build ${missionId}`, status: gate.passed ? 0 : 1 } });
61
+ const ok = proof.ok === true && gate.passed === true;
62
+ const result = { schema: 'sks.ppt-build.v1', ok, mission_id: missionId, build: { ...build, gate }, proof: proof.validation, blockers: gate.blockers || [] };
32
63
  if (flag(args, '--json'))
33
64
  return printJson(result);
34
- console.log(`PPT build: ${proof.ok ? 'ok' : 'blocked'} ${missionId}`);
35
- if (!proof.ok)
65
+ console.log(`PPT build: ${ok ? 'ok' : 'blocked'} ${missionId}`);
66
+ if (!ok && gate.blockers?.length)
67
+ console.log(`Blockers: ${gate.blockers.join(', ')}`);
68
+ if (!ok)
36
69
  process.exitCode = 1;
37
70
  return result;
38
71
  }
@@ -105,6 +138,33 @@ async function pptImagegenReview(root, command, action, args = []) {
105
138
  console.log(`PPT imagegen review: ${result.ok ? 'ok' : 'blocked'} ${id}`);
106
139
  return result;
107
140
  }
141
+ if (!mockMode) {
142
+ const imagegenRequired = await requireCodexImagegen(root, { autoRepair: true, applyRepair: true });
143
+ if (!imagegenRequired.ok) {
144
+ const result = {
145
+ schema: 'sks.ppt-imagegen-review.v1',
146
+ ok: false,
147
+ status: 'blocked',
148
+ mission_id: id,
149
+ blocker: 'codex_imagegen_unavailable',
150
+ imagegen_required: imagegenRequired,
151
+ gate: {
152
+ schema: 'sks.ppt-imagegen-review-gate.v1',
153
+ passed: false,
154
+ status: 'blocked',
155
+ blockers: ['codex_imagegen_unavailable'],
156
+ imagegen_evidence: { passed: false, generated_image_evidence: false }
157
+ }
158
+ };
159
+ process.exitCode = 1;
160
+ if (flag(args, '--json'))
161
+ return printJson(result);
162
+ console.error('PPT imagegen review blocked: Codex App imagegen/gpt-image-2 is unavailable.');
163
+ for (const action of imagegenRequired.blocker?.next_actions || [])
164
+ console.error(`- ${action}`);
165
+ return result;
166
+ }
167
+ }
108
168
  const artifacts = await writePptImagegenReviewArtifacts({
109
169
  root,
110
170
  dir,
@@ -168,12 +228,17 @@ async function pptFixture(root, command, args) {
168
228
  visualEvidence: { ppt_review: pptReviewProofEvidence(review.gate, review) },
169
229
  artifacts: [...Object.keys(build.files || {}), ...Object.values(PPT_REVIEW_ARTIFACT_PATHS), ...Object.values(native.artifacts || {})],
170
230
  claims: [{ id: 'ppt-fixture', status: 'verified_partial' }],
171
- command: { cmd: `sks ${command} fixture --mock`, status: 0 }
231
+ blockers: gate.blockers || [],
232
+ statusHint: 'blocked',
233
+ command: { cmd: `sks ${command} fixture --mock`, status: 1 }
172
234
  });
173
- const result = { schema: 'sks.ppt-fixture.v2', ok: proof.ok && native.ok, mission_id: id, build: { ...build, ok: true, gate }, imagegen_review: review, native_agent_collaboration: native, proof: proof.validation };
174
- if (flag(args, '--json'))
235
+ const result = { schema: 'sks.ppt-fixture.v2', ok: false, mission_id: id, build: { ...build, ok: false, gate }, imagegen_review: review, native_agent_collaboration: native, proof: proof.validation };
236
+ if (flag(args, '--json')) {
237
+ process.exitCode = 1;
175
238
  return printJson(result);
176
- console.log(`PPT fixture: ${proof.ok ? 'ok' : 'blocked'} ${id}`);
239
+ }
240
+ console.log(`PPT fixture: blocked ${id}`);
241
+ process.exitCode = 1;
177
242
  return result;
178
243
  }
179
244
  async function pptExplain(root, args = []) {
@@ -252,21 +317,91 @@ function missingMission(args) {
252
317
  function mockPptFixtureGate(gate = {}) {
253
318
  return {
254
319
  ...gate,
255
- passed: true,
320
+ passed: false,
321
+ ok: false,
322
+ status: 'blocked',
323
+ execution_class: 'mock_fixture',
256
324
  mock_fixture: true,
257
- unsupported_critical_claims_zero: true,
258
- image_asset_policy_satisfied: true,
259
- bounded_iteration_complete: true,
260
- critical_review_issues_zero: true,
261
- render_report_passed: true,
262
- fact_ledger_passed: true,
263
- image_asset_ledger_passed: true,
264
- review_ledger_passed: true,
265
- iteration_report_passed: true,
266
- cleanup_report_passed: true,
267
- parallel_report_passed: true,
268
- honest_mode_complete: true,
269
- blockers: []
325
+ unsupported_critical_claims_zero: false,
326
+ image_asset_policy_satisfied: false,
327
+ bounded_iteration_complete: false,
328
+ critical_review_issues_zero: false,
329
+ render_report_passed: false,
330
+ fact_ledger_passed: false,
331
+ image_asset_ledger_passed: false,
332
+ review_ledger_passed: false,
333
+ iteration_report_passed: false,
334
+ cleanup_report_passed: false,
335
+ parallel_report_passed: false,
336
+ honest_mode_complete: false,
337
+ blockers: ['ppt_fixture_mode_cannot_claim_real']
338
+ };
339
+ }
340
+ export async function evaluatePptGateArtifacts(dir, baseGate = {}) {
341
+ const factLedger = await readJson(path.join(dir, PPT_FACT_LEDGER_ARTIFACT), null);
342
+ const imageAssetLedger = await readJson(path.join(dir, PPT_IMAGE_ASSET_LEDGER_ARTIFACT), null);
343
+ const reviewLedger = await readJson(path.join(dir, PPT_REVIEW_LEDGER_ARTIFACT), null);
344
+ const iterationReport = await readJson(path.join(dir, PPT_ITERATION_REPORT_ARTIFACT), null);
345
+ const renderReport = await readJson(path.join(dir, PPT_RENDER_REPORT_ARTIFACT), null);
346
+ const cleanupReport = await readJson(path.join(dir, PPT_CLEANUP_REPORT_ARTIFACT), null);
347
+ const parallelReport = await readJson(path.join(dir, PPT_PARALLEL_REPORT_ARTIFACT), null);
348
+ const requiredArtifacts = [
349
+ 'decision-contract.json',
350
+ PPT_AUDIENCE_STRATEGY_ARTIFACT,
351
+ PPT_SOURCE_LEDGER_ARTIFACT,
352
+ PPT_FACT_LEDGER_ARTIFACT,
353
+ PPT_IMAGE_ASSET_LEDGER_ARTIFACT,
354
+ PPT_STORYBOARD_ARTIFACT,
355
+ PPT_STYLE_TOKENS_ARTIFACT,
356
+ PPT_REVIEW_POLICY_ARTIFACT,
357
+ PPT_REVIEW_LEDGER_ARTIFACT,
358
+ PPT_ITERATION_REPORT_ARTIFACT,
359
+ PPT_HTML_ARTIFACT,
360
+ PPT_PDF_ARTIFACT,
361
+ PPT_RENDER_REPORT_ARTIFACT,
362
+ PPT_CLEANUP_REPORT_ARTIFACT,
363
+ PPT_PARALLEL_REPORT_ARTIFACT
364
+ ];
365
+ const missing = [];
366
+ for (const artifact of requiredArtifacts) {
367
+ if (!(await exists(path.join(dir, artifact))))
368
+ missing.push(artifact);
369
+ }
370
+ const renderReportPassed = renderReport?.passed === true;
371
+ const factLedgerPassed = factLedger?.passed === true && Number(factLedger.unsupported_critical_claims_count || 0) === 0;
372
+ const imageAssetLedgerPassed = imageAssetLedger?.passed === true;
373
+ const imagegenEvidence = imageAssetLedger?.imagegen_evidence || { schema: 'sks.ppt-imagegen-evidence.v1', required: false, passed: true, blockers: [] };
374
+ const imagegenEvidencePassed = imagegenEvidence?.required === true ? imagegenEvidence?.passed === true : true;
375
+ const reviewLedgerPassed = reviewLedger?.passed === true;
376
+ const iterationReportPassed = iterationReport?.passed === true;
377
+ const cleanupReportPassed = cleanupReport?.source_html_preserved === true && cleanupReport?.temp_cleanup_completed === true;
378
+ const parallelReportPassed = parallelReport?.passed === true;
379
+ const blockers = [
380
+ ...missing.map((artifact) => `missing_artifact:${artifact}`),
381
+ ...(renderReportPassed ? [] : ['render_report_not_passed']),
382
+ ...(factLedgerPassed ? [] : ['fact_ledger_not_passed']),
383
+ ...(imageAssetLedgerPassed ? [] : ['image_asset_ledger_not_passed']),
384
+ ...(imagegenEvidencePassed ? [] : ['ppt_imagegen_evidence_not_passed']),
385
+ ...(reviewLedgerPassed ? [] : ['review_ledger_not_passed']),
386
+ ...(iterationReportPassed ? [] : ['iteration_report_not_passed']),
387
+ ...(cleanupReportPassed ? [] : ['cleanup_report_not_passed']),
388
+ ...(parallelReportPassed ? [] : ['parallel_report_not_passed'])
389
+ ];
390
+ const passed = blockers.length === 0;
391
+ return {
392
+ ...baseGate,
393
+ passed,
394
+ ok: passed,
395
+ status: passed ? 'pass' : 'blocked',
396
+ render_report_passed: renderReportPassed,
397
+ fact_ledger_passed: factLedgerPassed,
398
+ image_asset_ledger_passed: imageAssetLedgerPassed,
399
+ imagegen_evidence: imagegenEvidence,
400
+ review_ledger_passed: reviewLedgerPassed,
401
+ iteration_report_passed: iterationReportPassed,
402
+ cleanup_report_passed: cleanupReportPassed,
403
+ parallel_report_passed: parallelReportPassed,
404
+ blockers
270
405
  };
271
406
  }
272
407
  function fixtureAnswers() {
@@ -75,7 +75,9 @@ async function qaLoopPrepare(args) {
75
75
  const artifactResult = await writeQaLoopArtifacts(dir, { id, prompt, mode: 'qaloop' }, result.contract);
76
76
  const nativeAgentPlan = await writeQaNativeAgentLedger(dir, { id, prompt, reportFile: artifactResult.report_file });
77
77
  await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'qaloop.prepare.auto_sealed', slots: 0, hash: result.contract.sealed_hash, checklist_count: artifactResult.checklist_count });
78
- await setCurrent(root, { mission_id: id, route: 'QALoop', route_command: '$QA-LOOP', mode: 'QALOOP', phase: 'QALOOP_CLARIFICATION_CONTRACT_SEALED', questions_allowed: false, implementation_allowed: true, clarification_required: false, clarification_passed: true, ambiguity_gate_required: true, ambiguity_gate_passed: true, stop_gate: 'qa-gate.json', qa_loop_artifacts_ready: true, qa_report_file: artifactResult.report_file, qa_checklist_count: artifactResult.checklist_count, reasoning_effort: 'high', reasoning_profile: 'sks-logic-high', reasoning_temporary: true });
78
+ const clarificationPassed = true;
79
+ const ambiguityGatePassed = true;
80
+ await setCurrent(root, { mission_id: id, route: 'QALoop', route_command: '$QA-LOOP', mode: 'QALOOP', phase: 'QALOOP_CLARIFICATION_CONTRACT_SEALED', questions_allowed: false, implementation_allowed: true, clarification_required: false, clarification_passed: clarificationPassed, ambiguity_gate_required: true, ambiguity_gate_passed: ambiguityGatePassed, stop_gate: 'qa-gate.json', qa_loop_artifacts_ready: true, qa_report_file: artifactResult.report_file, qa_checklist_count: artifactResult.checklist_count, reasoning_effort: 'high', reasoning_profile: 'sks-logic-high', reasoning_temporary: true });
79
81
  if (flag(args, '--json'))
80
82
  return console.log(JSON.stringify({ schema: 'sks.qa-loop-prepare.v1', ok: true, mission_id: id, report_file: artifactResult.report_file, checklist_count: artifactResult.checklist_count, native_agent_plan: nativeAgentPlan }, null, 2));
81
83
  console.log(`QA-LOOP mission created: ${id}`);
@@ -111,7 +113,9 @@ async function qaLoopAnswer(args) {
111
113
  }
112
114
  const artifactResult = await writeQaLoopArtifacts(dir, mission, result.contract);
113
115
  await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'qaloop.contract.sealed', hash: result.contract.sealed_hash, checklist_count: artifactResult.checklist_count });
114
- await setCurrent(root, { mission_id: id, route: 'QALoop', route_command: '$QA-LOOP', mode: 'QALOOP', phase: 'QALOOP_CLARIFICATION_CONTRACT_SEALED', questions_allowed: false, implementation_allowed: true, clarification_required: false, clarification_passed: true, ambiguity_gate_passed: true, stop_gate: 'qa-gate.json', reasoning_effort: 'high', reasoning_profile: 'sks-logic-high', reasoning_temporary: true });
116
+ const clarificationPassed = true;
117
+ const ambiguityGatePassed = true;
118
+ await setCurrent(root, { mission_id: id, route: 'QALoop', route_command: '$QA-LOOP', mode: 'QALOOP', phase: 'QALOOP_CLARIFICATION_CONTRACT_SEALED', questions_allowed: false, implementation_allowed: true, clarification_required: false, clarification_passed: clarificationPassed, ambiguity_gate_passed: ambiguityGatePassed, stop_gate: 'qa-gate.json', reasoning_effort: 'high', reasoning_profile: 'sks-logic-high', reasoning_temporary: true });
115
119
  console.log(`QA-LOOP contract sealed for ${id}`);
116
120
  }
117
121
  async function qaLoopRun(args) {
@@ -1,7 +1,10 @@
1
1
  import { spawnSync } from 'node:child_process';
2
+ import path from 'node:path';
3
+ import fsp from 'node:fs/promises';
2
4
  import { flag } from '../../cli/args.js';
3
5
  import { printJson } from '../../cli/output.js';
4
- import { projectRoot } from '../fsx.js';
6
+ import { exists, projectRoot, readJson, writeJsonAtomic, writeTextAtomic } from '../fsx.js';
7
+ import { createMission } from '../mission.js';
5
8
  export async function releaseCommand(args = []) {
6
9
  const root = await projectRoot();
7
10
  const sub = args[0] && !args[0].startsWith('-') ? args[0] : 'affected';
@@ -12,18 +15,41 @@ export async function releaseCommand(args = []) {
12
15
  process.exitCode = 1;
13
16
  return null;
14
17
  }
18
+ const mission = await createMission(root, { mode: 'release-review', prompt: `Release review ${sub}` });
15
19
  const result = spawnSync(process.execPath, command.args, {
16
20
  cwd: root,
17
21
  encoding: 'utf8',
18
22
  maxBuffer: 1024 * 1024 * 20,
19
23
  env: { ...process.env, CI: process.env.CI || 'true' }
20
24
  });
25
+ const stdoutPath = path.join(mission.dir, 'release-command-stdout.log');
26
+ const stderrPath = path.join(mission.dir, 'release-command-stderr.log');
27
+ await writeTextAtomic(stdoutPath, String(result.stdout || ''));
28
+ await writeTextAtomic(stderrPath, String(result.stderr || ''));
29
+ const readiness = await findReleaseReadinessReport(root);
30
+ const requiredSections = ['five_lane_review', 'integration_evidence', 'session_cleanup'];
31
+ const missingSections = requiredSections.filter((section) => readiness.report?.[section] == null);
32
+ if (readiness.report)
33
+ await writeJsonAtomic(path.join(mission.dir, 'release-readiness-report.json'), readiness.report);
21
34
  const report = {
22
35
  schema: 'sks.release-command.v1',
23
- ok: result.status === 0,
36
+ ok: result.status === 0 && readiness.valid === true && missingSections.length === 0,
24
37
  subcommand: sub,
38
+ mission_id: mission.id,
25
39
  command: [process.execPath, ...command.args],
26
40
  status: result.status,
41
+ release_report: readiness.path,
42
+ release_report_valid: readiness.valid,
43
+ missing_sections: missingSections,
44
+ blockers: [
45
+ ...(result.status === 0 ? [] : ['release_subprocess_failed']),
46
+ ...(readiness.valid ? [] : ['release_report_missing_or_invalid']),
47
+ ...missingSections.map((section) => `release_report_missing_section:${section}`)
48
+ ],
49
+ logs: {
50
+ stdout: path.relative(root, stdoutPath),
51
+ stderr: path.relative(root, stderrPath)
52
+ },
27
53
  stdout_tail: tail(String(result.stdout || '')),
28
54
  stderr_tail: tail(String(result.stderr || ''))
29
55
  };
@@ -37,6 +63,33 @@ export async function releaseCommand(args = []) {
37
63
  process.exitCode = result.status || 1;
38
64
  return report;
39
65
  }
66
+ async function findReleaseReadinessReport(root) {
67
+ const candidates = [
68
+ path.join(root, 'release-readiness-report.json'),
69
+ path.join(root, '.sneakoscope', 'reports', 'release-readiness-report.json')
70
+ ];
71
+ const reportsDir = path.join(root, '.sneakoscope', 'reports');
72
+ const entries = await fsp.readdir(reportsDir).catch(() => []);
73
+ for (const entry of entries) {
74
+ if (/^release-readiness.*\.json$/.test(entry))
75
+ candidates.push(path.join(reportsDir, entry));
76
+ }
77
+ const existing = [];
78
+ for (const candidate of candidates) {
79
+ if (await exists(candidate)) {
80
+ const stat = await fsp.stat(candidate).catch(() => null);
81
+ existing.push({ path: candidate, mtime: stat?.mtimeMs || 0 });
82
+ }
83
+ }
84
+ existing.sort((a, b) => b.mtime - a.mtime);
85
+ const selected = existing[0]?.path || null;
86
+ const report = selected ? await readJson(selected, null) : null;
87
+ return {
88
+ path: selected ? path.relative(root, selected) : null,
89
+ report,
90
+ valid: Boolean(report && typeof report.schema === 'string' && /release-readiness/.test(report.schema))
91
+ };
92
+ }
40
93
  function commandForSubcommand(sub) {
41
94
  if (sub === 'affected')
42
95
  return { args: ['dist/scripts/release-gate-dag-runner.js', '--preset', 'affected', '--changed-since', 'auto', '--sla', '5m'] };
@@ -180,7 +180,7 @@ async function researchRun(args) {
180
180
  await runResearchCycle(dir, researchWorkGraph, { cycle: 0, status: mock ? 'mock_native_orchestrator_planned' : 'native_orchestrator_planned' });
181
181
  await setCurrent(root, { mission_id: id, mode: 'RESEARCH', phase: 'RESEARCH_RUNNING_NO_QUESTIONS', questions_allowed: false, implementation_allowed: false, research_real_run_required: !mock, research_cycle_timeout_minutes: cycleTimeoutMinutes });
182
182
  await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'research.run.started', maxCycles, mock, cycleTimeoutMinutes, real_run_required: !mock });
183
- const nativeAgentRun = await runNativeAgentOrchestrator({ root, missionId: id, route: flag(args, '--autoresearch') ? '$AutoResearch' : '$Research', prompt: mission.prompt || plan.prompt || 'Research run', backend: mock ? 'fake' : 'codex-sdk', mock, agents: requestedAgents, targetActiveSlots, desiredWorkItemCount: effectiveDesiredWorkItemCount, minimumWorkItems: effectiveMinimumWorkItems, maxQueueExpansion, concurrency: Math.min(requestedAgents, 5), readonly: true, profile, writeMode: writeMode, applyPatches: false, dryRunPatches, maxWriteAgents, roster: plan.native_agent_plan, routeCommand: 'sks research run', routeBlackboxKind: 'actual_research_command', narutoWorkGraph: researchWorkGraph });
183
+ const nativeAgentRun = await runNativeAgentOrchestrator({ root, missionId: id, route: flag(args, '--autoresearch') ? '$AutoResearch' : '$Research', prompt: mission.prompt || plan.prompt || 'Research run', backend: mock ? 'fake' : 'codex-sdk', mock, agents: requestedAgents, targetActiveSlots, desiredWorkItemCount: effectiveDesiredWorkItemCount, minimumWorkItems: effectiveMinimumWorkItems, maxQueueExpansion, concurrency: Math.min(requestedAgents, 5), readonly: !applyPatches, profile, writeMode: writeMode, applyPatches, dryRunPatches, maxWriteAgents, roster: plan.native_agent_plan, routeCommand: 'sks research run', routeBlackboxKind: 'actual_research_command', narutoWorkGraph: researchWorkGraph });
184
184
  await writeJsonAtomic(path.join(dir, 'research-native-agent-run.json'), nativeAgentRun);
185
185
  await appendJsonlBounded(path.join(dir, 'events.jsonl'), { ts: nowIso(), type: 'research.native_agents.completed', backend: nativeAgentRun.backend, ok: nativeAgentRun.ok, proof: nativeAgentRun.proof?.status });
186
186
  if (!nativeAgentRun.ok) {