codex-workflow-v2 2.0.0-beta.13.8 → 2.0.0-beta.14

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 (281) hide show
  1. package/README.md +27 -388
  2. package/dist/reviewer-runtime-build.json +96 -32
  3. package/dist/src/alpha6/adoption.d.ts +2 -0
  4. package/dist/src/alpha6/adoption.js +22 -0
  5. package/dist/src/alpha6/adoption.js.map +1 -1
  6. package/dist/src/alpha6/captured-check-evidence.d.ts +51 -0
  7. package/dist/src/alpha6/captured-check-evidence.js +152 -0
  8. package/dist/src/alpha6/captured-check-evidence.js.map +1 -0
  9. package/dist/src/alpha6/component-owner.d.ts +15 -1
  10. package/dist/src/alpha6/component-owner.js +44 -2
  11. package/dist/src/alpha6/component-owner.js.map +1 -1
  12. package/dist/src/alpha6/corrective-decision-boundary.d.ts +4 -0
  13. package/dist/src/alpha6/corrective-decision-boundary.js +47 -0
  14. package/dist/src/alpha6/corrective-decision-boundary.js.map +1 -0
  15. package/dist/src/alpha6/downstream-proof.d.ts +1 -25
  16. package/dist/src/alpha6/downstream-proof.js +0 -171
  17. package/dist/src/alpha6/downstream-proof.js.map +1 -1
  18. package/dist/src/alpha6/failed-step-planning-recovery.d.ts +10 -0
  19. package/dist/src/alpha6/failed-step-planning-recovery.js +93 -0
  20. package/dist/src/alpha6/failed-step-planning-recovery.js.map +1 -0
  21. package/dist/src/alpha6/literal-test-invocation.d.ts +2 -0
  22. package/dist/src/alpha6/literal-test-invocation.js +106 -0
  23. package/dist/src/alpha6/literal-test-invocation.js.map +1 -0
  24. package/dist/src/alpha6/milestone.d.ts +2 -0
  25. package/dist/src/alpha6/milestone.js +10 -1
  26. package/dist/src/alpha6/milestone.js.map +1 -1
  27. package/dist/src/alpha6/navigation-dirty-carryover.d.ts +11 -0
  28. package/dist/src/alpha6/navigation-dirty-carryover.js +130 -0
  29. package/dist/src/alpha6/navigation-dirty-carryover.js.map +1 -0
  30. package/dist/src/alpha6/plan-integrity.js +7 -5
  31. package/dist/src/alpha6/plan-integrity.js.map +1 -1
  32. package/dist/src/alpha6/plan-risk.d.ts +2 -0
  33. package/dist/src/alpha6/plan-risk.js +10 -3
  34. package/dist/src/alpha6/plan-risk.js.map +1 -1
  35. package/dist/src/alpha6/remediation.d.ts +6 -21
  36. package/dist/src/alpha6/remediation.js +161 -476
  37. package/dist/src/alpha6/remediation.js.map +1 -1
  38. package/dist/src/alpha6/root-cause-replan-carryover.d.ts +4 -1
  39. package/dist/src/alpha6/root-cause-replan-carryover.js +29 -5
  40. package/dist/src/alpha6/root-cause-replan-carryover.js.map +1 -1
  41. package/dist/src/beta1/project-transaction.d.ts +3 -1
  42. package/dist/src/beta1/project-transaction.js +6 -2
  43. package/dist/src/beta1/project-transaction.js.map +1 -1
  44. package/dist/src/checks/runner.d.ts +26 -0
  45. package/dist/src/checks/runner.js +223 -0
  46. package/dist/src/checks/runner.js.map +1 -0
  47. package/dist/src/checks/task-sync.d.ts +10 -0
  48. package/dist/src/checks/task-sync.js +41 -0
  49. package/dist/src/checks/task-sync.js.map +1 -0
  50. package/dist/src/checks/worker.d.ts +1 -0
  51. package/dist/src/checks/worker.js +154 -0
  52. package/dist/src/checks/worker.js.map +1 -0
  53. package/dist/src/cli-actions.d.ts +2 -2
  54. package/dist/src/cli-actions.js +3 -15
  55. package/dist/src/cli-actions.js.map +1 -1
  56. package/dist/src/cli.js +14 -98
  57. package/dist/src/cli.js.map +1 -1
  58. package/dist/src/contracts.d.ts +35 -32
  59. package/dist/src/dependency-provenance.d.ts +2 -2
  60. package/dist/src/dependency-provenance.js +12 -69
  61. package/dist/src/dependency-provenance.js.map +1 -1
  62. package/dist/src/domain/base-sync-conflict.d.ts +13 -0
  63. package/dist/src/domain/base-sync-conflict.js +32 -0
  64. package/dist/src/domain/base-sync-conflict.js.map +1 -0
  65. package/dist/src/domain/step-start-admission.d.ts +4 -0
  66. package/dist/src/domain/step-start-admission.js +9 -0
  67. package/dist/src/domain/step-start-admission.js.map +1 -0
  68. package/dist/src/domain/virgin-registration.d.ts +3 -0
  69. package/dist/src/domain/virgin-registration.js +65 -0
  70. package/dist/src/domain/virgin-registration.js.map +1 -0
  71. package/dist/src/errors.d.ts +1 -1
  72. package/dist/src/errors.js.map +1 -1
  73. package/dist/src/gateway-handshake.js +0 -8
  74. package/dist/src/gateway-handshake.js.map +1 -1
  75. package/dist/src/git.d.ts +3 -1
  76. package/dist/src/git.js +41 -14
  77. package/dist/src/git.js.map +1 -1
  78. package/dist/src/graph.js +25 -3
  79. package/dist/src/graph.js.map +1 -1
  80. package/dist/src/index.d.ts +1 -0
  81. package/dist/src/lifecycle/corrective-replan.js +3 -3
  82. package/dist/src/lifecycle/corrective-replan.js.map +1 -1
  83. package/dist/src/navigation-actions.d.ts +9 -0
  84. package/dist/src/navigation-actions.js +72 -0
  85. package/dist/src/navigation-actions.js.map +1 -0
  86. package/dist/src/navigation-update-artifact.d.ts +5 -0
  87. package/dist/src/navigation-update-artifact.js +227 -0
  88. package/dist/src/navigation-update-artifact.js.map +1 -0
  89. package/dist/src/navigation-update.d.ts +39 -0
  90. package/dist/src/navigation-update.js +82 -0
  91. package/dist/src/navigation-update.js.map +1 -0
  92. package/dist/src/observation.js +52 -29
  93. package/dist/src/observation.js.map +1 -1
  94. package/dist/src/observed-routes.js +5 -6
  95. package/dist/src/observed-routes.js.map +1 -1
  96. package/dist/src/pending-review-update.d.ts +0 -13
  97. package/dist/src/pending-review-update.js +1 -6
  98. package/dist/src/pending-review-update.js.map +1 -1
  99. package/dist/src/reviewer.d.ts +1 -1
  100. package/dist/src/reviewer.js +20 -17
  101. package/dist/src/reviewer.js.map +1 -1
  102. package/dist/src/runtime.d.ts +3 -0
  103. package/dist/src/runtime.js +6 -0
  104. package/dist/src/runtime.js.map +1 -0
  105. package/dist/src/state/corrective-replan-executor.d.ts +10 -0
  106. package/dist/src/state/corrective-replan-executor.js +37 -1
  107. package/dist/src/state/corrective-replan-executor.js.map +1 -1
  108. package/dist/src/state/corrective-replan-public-schema.js +15 -2
  109. package/dist/src/state/corrective-replan-public-schema.js.map +1 -1
  110. package/dist/src/state/corrective-replan-public.js +6 -5
  111. package/dist/src/state/corrective-replan-public.js.map +1 -1
  112. package/dist/src/state/corrective-replan-transaction.d.ts +1 -0
  113. package/dist/src/state/corrective-replan-transaction.js +12 -11
  114. package/dist/src/state/corrective-replan-transaction.js.map +1 -1
  115. package/dist/src/state/corrective-yield-executor.js +2 -2
  116. package/dist/src/state/corrective-yield-executor.js.map +1 -1
  117. package/dist/src/state/corrective-yield-transaction.d.ts +1 -0
  118. package/dist/src/state/corrective-yield-transaction.js +11 -10
  119. package/dist/src/state/corrective-yield-transaction.js.map +1 -1
  120. package/dist/src/state/lock.d.ts +4 -0
  121. package/dist/src/state/lock.js +21 -0
  122. package/dist/src/state/lock.js.map +1 -1
  123. package/dist/src/version.d.ts +1 -1
  124. package/dist/src/version.js +1 -1
  125. package/dist/src/version.js.map +1 -1
  126. package/dist/src/workflow-blocker-route.d.ts +8 -0
  127. package/dist/src/workflow-blocker-route.js +106 -0
  128. package/dist/src/workflow-blocker-route.js.map +1 -0
  129. package/dist/src/workflow.d.ts +48 -63
  130. package/dist/src/workflow.js +698 -1470
  131. package/dist/src/workflow.js.map +1 -1
  132. package/docs/autonomy-guardrails.md +26 -304
  133. package/docs/decisions.md +14 -112
  134. package/docs/development-flow.md +35 -238
  135. package/docs/project-memory.md +31 -50
  136. package/docs/release-app-evidence.md +190 -0
  137. package/docs/release.md +95 -363
  138. package/docs/updating-existing-project.md +19 -717
  139. package/package.json +11 -13
  140. package/plugins/codex-workflow-gateway/.codex-plugin/plugin.json +2 -2
  141. package/plugins/codex-workflow-gateway/references/chat-dispatch.md +73 -197
  142. package/plugins/codex-workflow-gateway/references/codebase-memory-routing.md +57 -0
  143. package/plugins/codex-workflow-gateway/references/protocol.md +42 -445
  144. package/plugins/codex-workflow-gateway/scripts/chat-dispatch.mjs +7 -1
  145. package/plugins/codex-workflow-gateway/scripts/chat-model-policy.mjs +10 -8
  146. package/plugins/codex-workflow-gateway/scripts/chat-registry.mjs +23 -5
  147. package/plugins/codex-workflow-gateway/skills/codex-workflow-gateway/SKILL.md +66 -740
  148. package/references/state-machine.md +4 -4
  149. package/roles/technical-planner.md +1 -1
  150. package/schemas/corrective-decision-event.schema.json +3 -1
  151. package/schemas/project-knowledge-map.schema.json +3 -1
  152. package/schemas/remediation-event.schema.json +10 -1
  153. package/schemas/task.schema.json +46 -2
  154. package/schemas/transition-payloads.schema.json +16 -1
  155. package/src/alpha6/adoption.ts +1123 -0
  156. package/src/alpha6/captured-check-evidence.ts +132 -0
  157. package/src/alpha6/check-support-anchor.ts +128 -0
  158. package/src/alpha6/component-owner.ts +168 -0
  159. package/src/alpha6/corrective-decision-boundary.ts +39 -0
  160. package/src/alpha6/downstream-proof.ts +520 -0
  161. package/src/alpha6/failed-step-planning-recovery.ts +88 -0
  162. package/src/alpha6/handoff.ts +1443 -0
  163. package/src/alpha6/journal.ts +473 -0
  164. package/src/alpha6/literal-test-invocation.ts +84 -0
  165. package/src/alpha6/mechanical-feasibility.ts +488 -0
  166. package/src/alpha6/milestone.ts +2192 -0
  167. package/src/alpha6/navigation-dirty-carryover.ts +154 -0
  168. package/src/alpha6/npm-check-contract.ts +47 -0
  169. package/src/alpha6/plan-integrity.ts +298 -0
  170. package/src/alpha6/plan-risk.ts +1480 -0
  171. package/src/alpha6/preexecution-replan.ts +187 -0
  172. package/src/alpha6/remediation-cause.ts +98 -0
  173. package/src/alpha6/remediation.ts +2438 -0
  174. package/src/alpha6/review.ts +1198 -0
  175. package/src/alpha6/root-cause-replan-carryover.ts +491 -0
  176. package/src/alpha6/store-sidecars.ts +335 -0
  177. package/src/alpha7/autonomy.ts +411 -0
  178. package/src/alpha7/corrective-recovery.ts +1332 -0
  179. package/src/artifacts.ts +130 -0
  180. package/src/beta1/project-transaction.ts +355 -0
  181. package/src/change-explanation.ts +153 -0
  182. package/src/checks/runner.ts +245 -0
  183. package/src/checks/task-sync.ts +41 -0
  184. package/src/checks/worker.ts +149 -0
  185. package/src/cli-actions.ts +107 -0
  186. package/src/cli.ts +1457 -0
  187. package/src/contracts.ts +1494 -0
  188. package/src/credential-output.ts +89 -0
  189. package/src/credential-transport.ts +215 -0
  190. package/src/delegation.ts +190 -0
  191. package/src/dependency-provenance.ts +472 -0
  192. package/src/diagnostics.ts +93 -0
  193. package/src/domain/base-sync-conflict.ts +35 -0
  194. package/src/domain/completed-step-carryover.ts +76 -0
  195. package/src/domain/discovery.ts +27 -0
  196. package/src/domain/plan-semantics.ts +58 -0
  197. package/src/domain/step-start-admission.ts +10 -0
  198. package/src/domain/validation.ts +177 -0
  199. package/src/domain/virgin-registration.ts +46 -0
  200. package/src/errors.ts +24 -0
  201. package/src/fs-utils.ts +61 -0
  202. package/src/gateway-handshake.ts +95 -0
  203. package/src/git.ts +183 -0
  204. package/src/graph.ts +342 -0
  205. package/src/historical-step-provenance.ts +136 -0
  206. package/src/index.ts +23 -0
  207. package/src/lifecycle/canonical-hash.ts +28 -0
  208. package/src/lifecycle/catalog.ts +202 -0
  209. package/src/lifecycle/compiler-inspection.ts +29 -0
  210. package/src/lifecycle/core-static-readiness.ts +132 -0
  211. package/src/lifecycle/corrective-replan-authority.ts +136 -0
  212. package/src/lifecycle/corrective-replan-binding-manifest.ts +51 -0
  213. package/src/lifecycle/corrective-replan-credential-core.ts +408 -0
  214. package/src/lifecycle/corrective-replan-credential-schema.ts +54 -0
  215. package/src/lifecycle/corrective-replan-credentials.ts +48 -0
  216. package/src/lifecycle/corrective-replan.ts +843 -0
  217. package/src/lifecycle/evaluator.ts +48 -0
  218. package/src/lifecycle/fingerprint.ts +488 -0
  219. package/src/lifecycle/immutable.ts +8 -0
  220. package/src/lifecycle/implementation-table.ts +118 -0
  221. package/src/lifecycle/index.ts +8 -0
  222. package/src/lifecycle/schema-artifact.ts +263 -0
  223. package/src/lifecycle/semantic-registry.ts +572 -0
  224. package/src/lifecycle/types.ts +838 -0
  225. package/src/memory.ts +273 -0
  226. package/src/migration.ts +161 -0
  227. package/src/navigation-actions.ts +70 -0
  228. package/src/navigation-update-artifact.ts +198 -0
  229. package/src/navigation-update.ts +121 -0
  230. package/src/observation.ts +225 -0
  231. package/src/observed-routes.ts +660 -0
  232. package/src/operational-contract.ts +125 -0
  233. package/src/pending-review-update.ts +175 -0
  234. package/src/repository.ts +99 -0
  235. package/src/reviewer.ts +1879 -0
  236. package/src/runtime.ts +6 -0
  237. package/src/state/corrective-replan-executor.ts +818 -0
  238. package/src/state/corrective-replan-public-schema.ts +83 -0
  239. package/src/state/corrective-replan-public.ts +908 -0
  240. package/src/state/corrective-replan-transaction.ts +949 -0
  241. package/src/state/corrective-yield-executor.ts +327 -0
  242. package/src/state/corrective-yield-transaction.ts +730 -0
  243. package/src/state/lock.ts +902 -0
  244. package/src/state/store.ts +567 -0
  245. package/src/transition-core.ts +330 -0
  246. package/src/ulid.ts +24 -0
  247. package/src/version.ts +2 -0
  248. package/src/workflow-blocker-route.ts +109 -0
  249. package/src/workflow.ts +10172 -0
  250. package/docs/alpha7.1-implementation-brief.md +0 -268
  251. package/docs/alpha7.2-corrective-context-refresh-brief.md +0 -484
  252. package/docs/alpha7.2.1-remediation-recovery-brief.md +0 -86
  253. package/docs/beta1-stabilization-brief.md +0 -165
  254. package/docs/beta11-plan-integrity-recovery-brief.md +0 -38
  255. package/docs/beta13.2-signal-review-recovery.md +0 -38
  256. package/docs/beta2-initial-assembly-navigation-brief.md +0 -616
  257. package/docs/change-model.md +0 -118
  258. package/docs/delegated-approval.md +0 -254
  259. package/docs/lifecycle/state-machine-stabilization.md +0 -641
  260. package/docs/pdf/README.md +0 -24
  261. package/docs/pdf/codex-workflow-v2-architecture-ru.pdf +0 -0
  262. package/docs/pdf/codex-workflow-v2-chat-only-guide-ru.pdf +0 -0
  263. package/docs/pdf/codex-workflow-v2-technical-reference-ru.pdf +0 -0
  264. package/docs/pdf/requirements.txt +0 -1
  265. package/docs/pdf/sources/codex-workflow-v2-architecture-ru.md +0 -478
  266. package/docs/pdf/sources/codex-workflow-v2-chat-only-guide-ru.md +0 -508
  267. package/docs/pdf/sources/codex-workflow-v2-technical-reference-ru.md +0 -778
  268. package/docs/pending-review-update.md +0 -15
  269. package/docs/problem-briefs/01-pre-implementation-integrity.md +0 -482
  270. package/docs/problem-briefs/02-minimal-step-integrity.md +0 -411
  271. package/docs/problem-briefs/03-minimal-agent-context-integrity.md +0 -358
  272. package/docs/problem-briefs/04-task-dependency-and-structural-replacement-integrity.md +0 -573
  273. package/docs/problem-briefs/BRIEF-TEMPLATE.md +0 -56
  274. package/docs/problem-briefs/README.md +0 -120
  275. package/docs/problem-briefs/evidence/p01-mechanical-feasibility-corpus.md +0 -90
  276. package/docs/problem-briefs/evidence/signal-v4-pre-m3-replay.md +0 -246
  277. package/docs/split-required-recovery.md +0 -47
  278. package/docs/stable-release-defect-register.md +0 -730
  279. package/docs/validation-report.md +0 -182
  280. package/scripts/generate-pdf-docs.py +0 -524
  281. package/scripts/run-pdf-docs.mjs +0 -62
@@ -0,0 +1,488 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import type {
3
+ MechanicalFeasibilityAnalyzer,
4
+ MechanicalFeasibilityEvidence,
5
+ MechanicalFeasibilityFinding,
6
+ TaskState,
7
+ } from '../contracts.js';
8
+ import { pathAllowed } from '../domain/validation.js';
9
+ import { WorkflowError } from '../errors.js';
10
+ import { currentBranch, gitIsAncestor, headCommit, runGit } from '../repository.js';
11
+ import {
12
+ hasRequiredSelector,
13
+ inspectRequiredSelectorContract,
14
+ parseNpmRunCheck,
15
+ } from './npm-check-contract.js';
16
+ import { inspectCheckSupportAnchors } from './check-support-anchor.js';
17
+ import { canonicalJsonStringify, sha256Hex } from './store-sidecars.js';
18
+
19
+ export const MECHANICAL_FEASIBILITY_ANALYZERS: readonly MechanicalFeasibilityAnalyzer[] = [
20
+ { analyzerId: 'wf2.root-npm-script', analyzerVersion: 1 },
21
+ { analyzerId: 'wf2.root-npm-script-arguments', analyzerVersion: 1 },
22
+ { analyzerId: 'wf2.check-support-anchor', analyzerVersion: 1 },
23
+ { analyzerId: 'wf2.exact-output-path', analyzerVersion: 1 },
24
+ { analyzerId: 'wf2.exact-predecessor-path', analyzerVersion: 1 },
25
+ ] as const;
26
+
27
+ const EXACT_PATH = /^path:([A-Za-z0-9._-]+(?:\/[A-Za-z0-9._-]+)*)$/;
28
+
29
+ interface EvaluationSource {
30
+ sourceKind: MechanicalFeasibilityEvidence['sourceKind'];
31
+ sourceBranch: string;
32
+ evaluatedHead: string;
33
+ baseCommit: string | null;
34
+ }
35
+
36
+ export function mechanicalFeasibilityAnalyzerSetHash(): string {
37
+ return sha256Hex(canonicalJsonStringify({ analyzers: MECHANICAL_FEASIBILITY_ANALYZERS }));
38
+ }
39
+
40
+ export function mechanicalFeasibilityAnalyzerInputHash(task: TaskState): string {
41
+ return sha256Hex(canonicalJsonStringify({
42
+ steps: task.steps.map((step) => ({
43
+ id: step.id,
44
+ dependencies: step.dependencies,
45
+ expectedOutputs: step.expectedOutputs,
46
+ allowedWrites: step.allowedWrites,
47
+ checks: step.checks,
48
+ })),
49
+ }));
50
+ }
51
+
52
+ export function hashMechanicalFeasibilityEvidence(evidence: MechanicalFeasibilityEvidence): string {
53
+ return sha256Hex(canonicalJsonStringify(evidence));
54
+ }
55
+
56
+ export function evaluateMechanicalFeasibility(
57
+ repositoryRoot: string,
58
+ task: TaskState,
59
+ briefHash: string,
60
+ planHash: string,
61
+ ): MechanicalFeasibilityEvidence {
62
+ const source = selectEvaluationSource(repositoryRoot, task);
63
+ const packageJson = readGitPath(repositoryRoot, source.evaluatedHead, 'package.json');
64
+ const manifest = parseRootManifest(packageJson);
65
+ const byId = new Map(task.steps.map((step) => [step.id, step]));
66
+ const findings: MechanicalFeasibilityFinding[] = [];
67
+
68
+ for (const step of task.steps) {
69
+ const predecessorIds = transitivePredecessorIds(step.id, byId);
70
+ const manifestWriters = [step.id, ...predecessorIds]
71
+ .filter((stepId) => pathAllowed('package.json', requireStep(byId, stepId).allowedWrites))
72
+ .sort();
73
+
74
+ for (const check of step.checks) {
75
+ const invocation = parseNpmRunCheck(check);
76
+ if (!invocation) {
77
+ findings.push(finding(
78
+ 'wf2.root-npm-script',
79
+ 'unverified',
80
+ step.id,
81
+ check,
82
+ 'Check is outside the exact root npm grammar.',
83
+ 'UNSUPPORTED_CHECK_SYNTAX',
84
+ ));
85
+ continue;
86
+ }
87
+ const script = invocation.script;
88
+ const scriptCommand = manifest.scripts.get(script) ?? null;
89
+ if (scriptCommand !== null) {
90
+ findings.push(finding(
91
+ 'wf2.root-npm-script',
92
+ 'pass',
93
+ step.id,
94
+ check,
95
+ `Root package.json at ${source.evaluatedHead} declares script ${script}.`,
96
+ 'ROOT_NPM_SCRIPT_PRESENT',
97
+ ));
98
+ const selectorContract = inspectRequiredSelectorContract(
99
+ scriptCommand,
100
+ (runnerPath) => readGitPath(repositoryRoot, source.evaluatedHead, runnerPath),
101
+ );
102
+ if (selectorContract) {
103
+ const runnerWriters = [step.id, ...predecessorIds]
104
+ .filter((stepId) => pathAllowed(selectorContract.runnerPath, requireStep(byId, stepId).allowedWrites))
105
+ .sort();
106
+ if (hasRequiredSelector(invocation.forwardedArguments)) {
107
+ findings.push(finding(
108
+ 'wf2.root-npm-script-arguments',
109
+ 'pass',
110
+ step.id,
111
+ check,
112
+ `Repository-local affected runner ${selectorContract.runnerPath} receives an explicit base, head, or files selector.`,
113
+ 'ROOT_NPM_SCRIPT_REQUIRED_SELECTOR_PRESENT',
114
+ ));
115
+ } else if (runnerWriters.length > 0) {
116
+ findings.push(finding(
117
+ 'wf2.root-npm-script-arguments',
118
+ 'unverified',
119
+ step.id,
120
+ check,
121
+ `Repository-local affected runner ${selectorContract.runnerPath} currently requires a selector but is writable by ${runnerWriters.join(', ')}.`,
122
+ 'ROOT_NPM_SCRIPT_REQUIRED_SELECTOR_MAY_CHANGE',
123
+ ));
124
+ } else {
125
+ findings.push(finding(
126
+ 'wf2.root-npm-script-arguments',
127
+ 'blocked',
128
+ step.id,
129
+ check,
130
+ `Repository-local affected runner ${selectorContract.runnerPath} requires --base, --head, or --files, but the check forwards no selector and no responsible Step may change the runner.`,
131
+ 'ROOT_NPM_SCRIPT_REQUIRED_SELECTOR_MISSING',
132
+ ));
133
+ }
134
+ }
135
+ const supportConflicts = inspectCheckSupportAnchors({
136
+ step,
137
+ scripts: manifest.scripts,
138
+ readFile: (relativePath) => {
139
+ const content = readGitPath(repositoryRoot, source.evaluatedHead, relativePath);
140
+ return { content, hash: content === null ? null : sha256Hex(content) };
141
+ },
142
+ pathExists: (relativePath) => gitPathExists(repositoryRoot, source.evaluatedHead, relativePath),
143
+ });
144
+ for (const conflict of supportConflicts) {
145
+ findings.push(finding(
146
+ 'wf2.check-support-anchor',
147
+ 'blocked',
148
+ step.id,
149
+ check,
150
+ `${conflict.runnerPath} directly imports checksum/catalog authority ${conflict.supportPath}; absent output ${conflict.expectedOutputPaths.join(', ')} requires that support authority to change, but ${step.id} cannot write it.`,
151
+ conflict.reasonCode,
152
+ ));
153
+ }
154
+ continue;
155
+ }
156
+ if (manifestWriters.length > 0) {
157
+ findings.push(finding(
158
+ 'wf2.root-npm-script',
159
+ 'unverified',
160
+ step.id,
161
+ check,
162
+ `Script ${script} is absent at ${source.evaluatedHead}; package.json is writable by ${manifestWriters.join(', ')}.`,
163
+ 'ROOT_NPM_SCRIPT_MAY_BE_CREATED',
164
+ ));
165
+ continue;
166
+ }
167
+ findings.push(finding(
168
+ 'wf2.root-npm-script',
169
+ 'blocked',
170
+ step.id,
171
+ check,
172
+ manifest.state === 'valid'
173
+ ? `Script ${script} is absent from root package.json at ${source.evaluatedHead}, and no responsible Step may write package.json.`
174
+ : `Root package.json is ${manifest.state} at ${source.evaluatedHead}, and no responsible Step may write package.json.`,
175
+ manifest.state === 'valid' ? 'ROOT_NPM_SCRIPT_MISSING' : `ROOT_PACKAGE_JSON_${manifest.state.toUpperCase()}`,
176
+ ));
177
+ }
178
+
179
+ for (const output of step.expectedOutputs) {
180
+ const relativePath = decodeExactPath(output);
181
+ if (relativePath === null) {
182
+ findings.push(finding(
183
+ 'wf2.exact-output-path',
184
+ 'unverified',
185
+ step.id,
186
+ output,
187
+ 'Expected output is outside the exact path:<repository-relative POSIX path> grammar.',
188
+ 'UNSUPPORTED_OUTPUT_SYNTAX',
189
+ ));
190
+ continue;
191
+ }
192
+ if (gitPathExists(repositoryRoot, source.evaluatedHead, relativePath)) {
193
+ findings.push(finding(
194
+ 'wf2.exact-output-path',
195
+ 'pass',
196
+ step.id,
197
+ output,
198
+ `${relativePath} exists at ${source.evaluatedHead}.`,
199
+ 'EXACT_OUTPUT_PRESENT',
200
+ ));
201
+ continue;
202
+ }
203
+ if (pathAllowed(relativePath, step.allowedWrites)) {
204
+ findings.push(finding(
205
+ 'wf2.exact-output-path',
206
+ 'pass',
207
+ step.id,
208
+ output,
209
+ `${step.id} may write absent exact output ${relativePath}.`,
210
+ 'EXACT_OUTPUT_WRITABLE',
211
+ ));
212
+ continue;
213
+ }
214
+ const providers = predecessorIds
215
+ .filter((predecessorId) => {
216
+ const predecessor = requireStep(byId, predecessorId);
217
+ return predecessor.expectedOutputs.some((candidate) => decodeExactPath(candidate) === relativePath)
218
+ && pathAllowed(relativePath, predecessor.allowedWrites);
219
+ })
220
+ .sort();
221
+ if (providers.length > 0) {
222
+ findings.push(finding(
223
+ 'wf2.exact-predecessor-path',
224
+ 'pass',
225
+ step.id,
226
+ output,
227
+ `Transitive declared predecessor ${providers.join(', ')} owns and may write ${relativePath}.`,
228
+ 'EXACT_PREDECESSOR_OUTPUT_AVAILABLE',
229
+ ));
230
+ continue;
231
+ }
232
+ findings.push(finding(
233
+ 'wf2.exact-output-path',
234
+ 'blocked',
235
+ step.id,
236
+ output,
237
+ `${relativePath} is absent at ${source.evaluatedHead}, outside ${step.id} write bounds, and has no exact writable declared predecessor.`,
238
+ 'EXACT_OUTPUT_UNSATISFIABLE',
239
+ ));
240
+ }
241
+ }
242
+
243
+ findings.sort(compareFindings);
244
+ return {
245
+ schemaVersion: 1,
246
+ taskId: task.id,
247
+ evaluatedTaskRevision: task.revision,
248
+ briefHash,
249
+ planHash,
250
+ ...source,
251
+ analyzerSetHash: mechanicalFeasibilityAnalyzerSetHash(),
252
+ analyzerInputHash: mechanicalFeasibilityAnalyzerInputHash(task),
253
+ analyzers: MECHANICAL_FEASIBILITY_ANALYZERS.map((analyzer) => ({ ...analyzer })),
254
+ findings,
255
+ decision: findings.some((candidate) => candidate.result === 'blocked') ? 'blocked' : 'clear',
256
+ };
257
+ }
258
+
259
+ export function assertMechanicalFeasibilityFresh(
260
+ repositoryRoot: string,
261
+ task: TaskState,
262
+ evidence: MechanicalFeasibilityEvidence,
263
+ briefHash: string,
264
+ planHash: string,
265
+ ): void {
266
+ assertEvidenceShape(evidence, task.id);
267
+ const currentAnalyzerSetHash = mechanicalFeasibilityAnalyzerSetHash();
268
+ const currentAnalyzerInputHash = mechanicalFeasibilityAnalyzerInputHash(task);
269
+ if (
270
+ evidence.taskId !== task.id
271
+ || evidence.briefHash !== briefHash
272
+ || evidence.planHash !== planHash
273
+ || evidence.analyzerSetHash !== currentAnalyzerSetHash
274
+ || evidence.analyzerInputHash !== currentAnalyzerInputHash
275
+ ) {
276
+ throw new WorkflowError('TRANSITION_BLOCKED', 'Mechanical-feasibility authorization binding is stale.', {
277
+ taskId: task.id,
278
+ authorizedBriefHash: evidence.briefHash,
279
+ currentBriefHash: briefHash,
280
+ authorizedPlanHash: evidence.planHash,
281
+ currentPlanHash: planHash,
282
+ authorizedAnalyzerSetHash: evidence.analyzerSetHash,
283
+ currentAnalyzerSetHash,
284
+ authorizedAnalyzerInputHash: evidence.analyzerInputHash,
285
+ currentAnalyzerInputHash,
286
+ requiredAction: 'Record a fresh execution authorization.',
287
+ });
288
+ }
289
+ if (evidence.decision !== 'clear' || evidence.findings.some((findingValue) => findingValue.result === 'blocked')) {
290
+ throw new WorkflowError('STATE_CORRUPT', 'Approved mechanical-feasibility evidence is not clear.', {
291
+ taskId: task.id,
292
+ decision: evidence.decision,
293
+ });
294
+ }
295
+
296
+ if (task.baseCommit === null) {
297
+ const currentBaseHead = runGit(repositoryRoot, ['rev-parse', task.baseBranch]);
298
+ if (
299
+ evidence.sourceKind !== 'pre-start-base'
300
+ || evidence.sourceBranch !== task.baseBranch
301
+ || evidence.baseCommit !== null
302
+ || evidence.evaluatedHead !== currentBaseHead
303
+ ) {
304
+ throw staleSourceError(task, evidence, currentBaseHead);
305
+ }
306
+ return;
307
+ }
308
+
309
+ if (!task.taskBranch) {
310
+ throw new WorkflowError('STATE_CORRUPT', `Started Task ${task.id} has no Task branch.`);
311
+ }
312
+ const branch = currentBranch(repositoryRoot);
313
+ if (branch !== task.taskBranch) {
314
+ throw new WorkflowError('GIT_PRECONDITION_FAILED', 'Mechanical-feasibility freshness requires the owned Task branch.', {
315
+ taskId: task.id,
316
+ expectedBranch: task.taskBranch,
317
+ currentBranch: branch,
318
+ });
319
+ }
320
+ const currentTaskHead = headCommit(repositoryRoot);
321
+ let sourceMatches = false;
322
+ if (evidence.sourceKind === 'pre-start-base') {
323
+ sourceMatches = evidence.sourceBranch === task.baseBranch
324
+ && evidence.baseCommit === null
325
+ && task.baseCommit === evidence.evaluatedHead;
326
+ } else if (evidence.sourceKind === 'corrective-task-workspace') {
327
+ sourceMatches = evidence.sourceBranch === task.taskBranch
328
+ && evidence.baseCommit === task.baseCommit;
329
+ }
330
+ if (!sourceMatches || !gitIsAncestor(repositoryRoot, evidence.evaluatedHead, currentTaskHead)) {
331
+ throw staleSourceError(task, evidence, currentTaskHead);
332
+ }
333
+ }
334
+
335
+ function selectEvaluationSource(repositoryRoot: string, task: TaskState): EvaluationSource {
336
+ if (task.baseCommit === null) {
337
+ return {
338
+ sourceKind: 'pre-start-base',
339
+ sourceBranch: task.baseBranch,
340
+ evaluatedHead: runGit(repositoryRoot, ['rev-parse', task.baseBranch]),
341
+ baseCommit: null,
342
+ };
343
+ }
344
+ if (!task.taskBranch) {
345
+ throw new WorkflowError('STATE_CORRUPT', `Started Task ${task.id} has no Task branch.`);
346
+ }
347
+ const branch = currentBranch(repositoryRoot);
348
+ if (branch !== task.taskBranch) {
349
+ throw new WorkflowError('GIT_PRECONDITION_FAILED', 'Mechanical feasibility must be evaluated in the owned Task workspace.', {
350
+ taskId: task.id,
351
+ expectedBranch: task.taskBranch,
352
+ currentBranch: branch,
353
+ });
354
+ }
355
+ return {
356
+ sourceKind: 'corrective-task-workspace',
357
+ sourceBranch: task.taskBranch,
358
+ evaluatedHead: headCommit(repositoryRoot),
359
+ baseCommit: task.baseCommit,
360
+ };
361
+ }
362
+
363
+ function parseRootManifest(content: string | null): { state: 'valid' | 'missing' | 'invalid'; scripts: Map<string, string> } {
364
+ if (content === null) return { state: 'missing', scripts: new Map() };
365
+ try {
366
+ const parsed = JSON.parse(content) as unknown;
367
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
368
+ return { state: 'invalid', scripts: new Map() };
369
+ }
370
+ const scriptsValue = (parsed as Record<string, unknown>).scripts;
371
+ if (!scriptsValue || typeof scriptsValue !== 'object' || Array.isArray(scriptsValue)) {
372
+ return { state: 'valid', scripts: new Map() };
373
+ }
374
+ return {
375
+ state: 'valid',
376
+ scripts: new Map(Object.entries(scriptsValue)
377
+ .filter((entry): entry is [string, string] => typeof entry[1] === 'string')),
378
+ };
379
+ } catch {
380
+ return { state: 'invalid', scripts: new Map() };
381
+ }
382
+ }
383
+
384
+ function decodeExactPath(subject: string): string | null {
385
+ const match = EXACT_PATH.exec(subject);
386
+ if (!match) return null;
387
+ const relativePath = match[1]!;
388
+ const segments = relativePath.split('/');
389
+ if (segments.some((segment) => segment === '.' || segment === '..')) return null;
390
+ return relativePath;
391
+ }
392
+
393
+ function transitivePredecessorIds(
394
+ stepId: string,
395
+ byId: ReadonlyMap<string, TaskState['steps'][number]>,
396
+ ): string[] {
397
+ const found = new Set<string>();
398
+ const visit = (candidateId: string): void => {
399
+ const candidate = requireStep(byId, candidateId);
400
+ for (const dependencyId of candidate.dependencies) {
401
+ if (found.has(dependencyId)) continue;
402
+ requireStep(byId, dependencyId);
403
+ found.add(dependencyId);
404
+ visit(dependencyId);
405
+ }
406
+ };
407
+ visit(stepId);
408
+ return [...found].sort();
409
+ }
410
+
411
+ function requireStep(
412
+ byId: ReadonlyMap<string, TaskState['steps'][number]>,
413
+ stepId: string,
414
+ ): TaskState['steps'][number] {
415
+ const step = byId.get(stepId);
416
+ if (!step) throw new WorkflowError('STATE_CORRUPT', `Mechanical-feasibility input references missing Step ${stepId}.`);
417
+ return step;
418
+ }
419
+
420
+ function finding(
421
+ analyzerId: MechanicalFeasibilityAnalyzer['analyzerId'],
422
+ result: MechanicalFeasibilityFinding['result'],
423
+ stepId: string,
424
+ subject: string,
425
+ evidence: string,
426
+ reasonCode: string,
427
+ ): MechanicalFeasibilityFinding {
428
+ return { analyzerId, analyzerVersion: 1, result, stepId, subject, evidence, reasonCode };
429
+ }
430
+
431
+ function compareFindings(left: MechanicalFeasibilityFinding, right: MechanicalFeasibilityFinding): number {
432
+ return left.stepId.localeCompare(right.stepId)
433
+ || left.analyzerId.localeCompare(right.analyzerId)
434
+ || left.subject.localeCompare(right.subject)
435
+ || left.result.localeCompare(right.result);
436
+ }
437
+
438
+ function readGitPath(repositoryRoot: string, commit: string, relativePath: string): string | null {
439
+ const result = spawnSync('git', ['-C', repositoryRoot, 'show', `${commit}:${relativePath}`], { encoding: 'utf8' });
440
+ if (result.status === 0) return result.stdout;
441
+ if (result.status === 128) return null;
442
+ throw new WorkflowError('COMMAND_FAILED', `Unable to read ${relativePath} at ${commit}.`, {
443
+ stderr: result.stderr.trim(),
444
+ exitCode: result.status,
445
+ });
446
+ }
447
+
448
+ function gitPathExists(repositoryRoot: string, commit: string, relativePath: string): boolean {
449
+ const result = spawnSync('git', ['-C', repositoryRoot, 'cat-file', '-e', `${commit}:${relativePath}`], { encoding: 'utf8' });
450
+ if (result.status === 0) return true;
451
+ if (result.status === 128) return false;
452
+ throw new WorkflowError('COMMAND_FAILED', `Unable to inspect ${relativePath} at ${commit}.`, {
453
+ stderr: result.stderr.trim(),
454
+ exitCode: result.status,
455
+ });
456
+ }
457
+
458
+ function assertEvidenceShape(evidence: MechanicalFeasibilityEvidence, taskId: string): void {
459
+ const analyzersCanonical = canonicalJsonStringify(evidence.analyzers);
460
+ const currentAnalyzersCanonical = canonicalJsonStringify(MECHANICAL_FEASIBILITY_ANALYZERS);
461
+ const sortedFindings = [...evidence.findings].sort(compareFindings);
462
+ if (
463
+ evidence.schemaVersion !== 1
464
+ || analyzersCanonical !== currentAnalyzersCanonical
465
+ || canonicalJsonStringify(evidence.findings) !== canonicalJsonStringify(sortedFindings)
466
+ ) {
467
+ throw new WorkflowError('STATE_CORRUPT', 'Mechanical-feasibility evidence shape or canonical order is invalid.', {
468
+ taskId,
469
+ });
470
+ }
471
+ }
472
+
473
+ function staleSourceError(
474
+ task: TaskState,
475
+ evidence: MechanicalFeasibilityEvidence,
476
+ currentHead: string,
477
+ ): WorkflowError {
478
+ return new WorkflowError('TRANSITION_BLOCKED', 'Mechanical-feasibility evaluation source is stale.', {
479
+ taskId: task.id,
480
+ sourceKind: evidence.sourceKind,
481
+ sourceBranch: evidence.sourceBranch,
482
+ evaluatedHead: evidence.evaluatedHead,
483
+ currentHead,
484
+ authorizedBaseCommit: evidence.baseCommit,
485
+ currentBaseCommit: task.baseCommit,
486
+ requiredAction: 'Record a fresh execution authorization.',
487
+ });
488
+ }