codex-workflow-v2 2.0.0-beta.13.9 → 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 (271) hide show
  1. package/README.md +27 -388
  2. package/dist/reviewer-runtime-build.json +89 -29
  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/literal-test-invocation.d.ts +2 -0
  19. package/dist/src/alpha6/literal-test-invocation.js +106 -0
  20. package/dist/src/alpha6/literal-test-invocation.js.map +1 -0
  21. package/dist/src/alpha6/milestone.d.ts +2 -0
  22. package/dist/src/alpha6/milestone.js +10 -1
  23. package/dist/src/alpha6/milestone.js.map +1 -1
  24. package/dist/src/alpha6/navigation-dirty-carryover.d.ts +11 -0
  25. package/dist/src/alpha6/navigation-dirty-carryover.js +130 -0
  26. package/dist/src/alpha6/navigation-dirty-carryover.js.map +1 -0
  27. package/dist/src/alpha6/plan-integrity.js +7 -5
  28. package/dist/src/alpha6/plan-integrity.js.map +1 -1
  29. package/dist/src/alpha6/remediation.d.ts +6 -21
  30. package/dist/src/alpha6/remediation.js +161 -476
  31. package/dist/src/alpha6/remediation.js.map +1 -1
  32. package/dist/src/alpha6/root-cause-replan-carryover.d.ts +4 -1
  33. package/dist/src/alpha6/root-cause-replan-carryover.js +29 -5
  34. package/dist/src/alpha6/root-cause-replan-carryover.js.map +1 -1
  35. package/dist/src/beta1/project-transaction.d.ts +3 -1
  36. package/dist/src/beta1/project-transaction.js +6 -2
  37. package/dist/src/beta1/project-transaction.js.map +1 -1
  38. package/dist/src/checks/runner.d.ts +26 -0
  39. package/dist/src/checks/runner.js +223 -0
  40. package/dist/src/checks/runner.js.map +1 -0
  41. package/dist/src/checks/task-sync.d.ts +10 -0
  42. package/dist/src/checks/task-sync.js +41 -0
  43. package/dist/src/checks/task-sync.js.map +1 -0
  44. package/dist/src/checks/worker.d.ts +1 -0
  45. package/dist/src/checks/worker.js +154 -0
  46. package/dist/src/checks/worker.js.map +1 -0
  47. package/dist/src/cli-actions.d.ts +2 -2
  48. package/dist/src/cli-actions.js +3 -15
  49. package/dist/src/cli-actions.js.map +1 -1
  50. package/dist/src/cli.js +14 -98
  51. package/dist/src/cli.js.map +1 -1
  52. package/dist/src/contracts.d.ts +35 -32
  53. package/dist/src/dependency-provenance.d.ts +2 -2
  54. package/dist/src/dependency-provenance.js +12 -69
  55. package/dist/src/dependency-provenance.js.map +1 -1
  56. package/dist/src/domain/base-sync-conflict.d.ts +13 -0
  57. package/dist/src/domain/base-sync-conflict.js +32 -0
  58. package/dist/src/domain/base-sync-conflict.js.map +1 -0
  59. package/dist/src/domain/step-start-admission.d.ts +4 -0
  60. package/dist/src/domain/step-start-admission.js +9 -0
  61. package/dist/src/domain/step-start-admission.js.map +1 -0
  62. package/dist/src/domain/virgin-registration.d.ts +3 -0
  63. package/dist/src/domain/virgin-registration.js +65 -0
  64. package/dist/src/domain/virgin-registration.js.map +1 -0
  65. package/dist/src/errors.d.ts +1 -1
  66. package/dist/src/errors.js.map +1 -1
  67. package/dist/src/gateway-handshake.js +0 -8
  68. package/dist/src/gateway-handshake.js.map +1 -1
  69. package/dist/src/git.d.ts +3 -1
  70. package/dist/src/git.js +41 -14
  71. package/dist/src/git.js.map +1 -1
  72. package/dist/src/graph.js +25 -3
  73. package/dist/src/graph.js.map +1 -1
  74. package/dist/src/index.d.ts +1 -0
  75. package/dist/src/navigation-actions.d.ts +9 -0
  76. package/dist/src/navigation-actions.js +72 -0
  77. package/dist/src/navigation-actions.js.map +1 -0
  78. package/dist/src/navigation-update-artifact.d.ts +5 -0
  79. package/dist/src/navigation-update-artifact.js +227 -0
  80. package/dist/src/navigation-update-artifact.js.map +1 -0
  81. package/dist/src/navigation-update.d.ts +39 -0
  82. package/dist/src/navigation-update.js +82 -0
  83. package/dist/src/navigation-update.js.map +1 -0
  84. package/dist/src/observation.js +52 -29
  85. package/dist/src/observation.js.map +1 -1
  86. package/dist/src/observed-routes.js +5 -6
  87. package/dist/src/observed-routes.js.map +1 -1
  88. package/dist/src/pending-review-update.d.ts +0 -13
  89. package/dist/src/pending-review-update.js +1 -6
  90. package/dist/src/pending-review-update.js.map +1 -1
  91. package/dist/src/reviewer.d.ts +1 -1
  92. package/dist/src/reviewer.js +20 -17
  93. package/dist/src/reviewer.js.map +1 -1
  94. package/dist/src/runtime.d.ts +3 -0
  95. package/dist/src/runtime.js +6 -0
  96. package/dist/src/runtime.js.map +1 -0
  97. package/dist/src/state/corrective-replan-executor.d.ts +10 -0
  98. package/dist/src/state/corrective-replan-executor.js +37 -1
  99. package/dist/src/state/corrective-replan-executor.js.map +1 -1
  100. package/dist/src/state/corrective-replan-public-schema.js +15 -2
  101. package/dist/src/state/corrective-replan-public-schema.js.map +1 -1
  102. package/dist/src/state/corrective-replan-public.js +1 -1
  103. package/dist/src/state/corrective-replan-public.js.map +1 -1
  104. package/dist/src/state/corrective-replan-transaction.d.ts +1 -0
  105. package/dist/src/state/corrective-replan-transaction.js +12 -11
  106. package/dist/src/state/corrective-replan-transaction.js.map +1 -1
  107. package/dist/src/state/corrective-yield-transaction.d.ts +1 -0
  108. package/dist/src/state/corrective-yield-transaction.js +11 -10
  109. package/dist/src/state/corrective-yield-transaction.js.map +1 -1
  110. package/dist/src/state/lock.d.ts +4 -0
  111. package/dist/src/state/lock.js +21 -0
  112. package/dist/src/state/lock.js.map +1 -1
  113. package/dist/src/version.d.ts +1 -1
  114. package/dist/src/version.js +1 -1
  115. package/dist/src/version.js.map +1 -1
  116. package/dist/src/workflow-blocker-route.d.ts +8 -0
  117. package/dist/src/workflow-blocker-route.js +106 -0
  118. package/dist/src/workflow-blocker-route.js.map +1 -0
  119. package/dist/src/workflow.d.ts +48 -63
  120. package/dist/src/workflow.js +666 -1460
  121. package/dist/src/workflow.js.map +1 -1
  122. package/docs/autonomy-guardrails.md +26 -304
  123. package/docs/decisions.md +14 -112
  124. package/docs/development-flow.md +35 -238
  125. package/docs/project-memory.md +31 -50
  126. package/docs/release-app-evidence.md +190 -0
  127. package/docs/release.md +95 -388
  128. package/docs/updating-existing-project.md +19 -717
  129. package/package.json +11 -13
  130. package/plugins/codex-workflow-gateway/.codex-plugin/plugin.json +2 -2
  131. package/plugins/codex-workflow-gateway/references/chat-dispatch.md +73 -197
  132. package/plugins/codex-workflow-gateway/references/codebase-memory-routing.md +57 -0
  133. package/plugins/codex-workflow-gateway/references/protocol.md +42 -445
  134. package/plugins/codex-workflow-gateway/scripts/chat-dispatch.mjs +7 -1
  135. package/plugins/codex-workflow-gateway/scripts/chat-model-policy.mjs +10 -8
  136. package/plugins/codex-workflow-gateway/scripts/chat-registry.mjs +23 -5
  137. package/plugins/codex-workflow-gateway/skills/codex-workflow-gateway/SKILL.md +66 -757
  138. package/references/state-machine.md +4 -4
  139. package/roles/technical-planner.md +1 -1
  140. package/schemas/corrective-decision-event.schema.json +3 -1
  141. package/schemas/project-knowledge-map.schema.json +3 -1
  142. package/schemas/remediation-event.schema.json +10 -1
  143. package/schemas/task.schema.json +46 -2
  144. package/schemas/transition-payloads.schema.json +16 -1
  145. package/src/alpha6/adoption.ts +1123 -0
  146. package/src/alpha6/captured-check-evidence.ts +132 -0
  147. package/src/alpha6/check-support-anchor.ts +128 -0
  148. package/src/alpha6/component-owner.ts +168 -0
  149. package/src/alpha6/corrective-decision-boundary.ts +39 -0
  150. package/src/alpha6/downstream-proof.ts +520 -0
  151. package/src/alpha6/failed-step-planning-recovery.ts +88 -0
  152. package/src/alpha6/handoff.ts +1443 -0
  153. package/src/alpha6/journal.ts +473 -0
  154. package/src/alpha6/literal-test-invocation.ts +84 -0
  155. package/src/alpha6/mechanical-feasibility.ts +488 -0
  156. package/src/alpha6/milestone.ts +2192 -0
  157. package/src/alpha6/navigation-dirty-carryover.ts +154 -0
  158. package/src/alpha6/npm-check-contract.ts +47 -0
  159. package/src/alpha6/plan-integrity.ts +298 -0
  160. package/src/alpha6/plan-risk.ts +1480 -0
  161. package/src/alpha6/preexecution-replan.ts +187 -0
  162. package/src/alpha6/remediation-cause.ts +98 -0
  163. package/src/alpha6/remediation.ts +2438 -0
  164. package/src/alpha6/review.ts +1198 -0
  165. package/src/alpha6/root-cause-replan-carryover.ts +491 -0
  166. package/src/alpha6/store-sidecars.ts +335 -0
  167. package/src/alpha7/autonomy.ts +411 -0
  168. package/src/alpha7/corrective-recovery.ts +1332 -0
  169. package/src/artifacts.ts +130 -0
  170. package/src/beta1/project-transaction.ts +355 -0
  171. package/src/change-explanation.ts +153 -0
  172. package/src/checks/runner.ts +245 -0
  173. package/src/checks/task-sync.ts +41 -0
  174. package/src/checks/worker.ts +149 -0
  175. package/src/cli-actions.ts +107 -0
  176. package/src/cli.ts +1457 -0
  177. package/src/contracts.ts +1494 -0
  178. package/src/credential-output.ts +89 -0
  179. package/src/credential-transport.ts +215 -0
  180. package/src/delegation.ts +190 -0
  181. package/src/dependency-provenance.ts +472 -0
  182. package/src/diagnostics.ts +93 -0
  183. package/src/domain/base-sync-conflict.ts +35 -0
  184. package/src/domain/completed-step-carryover.ts +76 -0
  185. package/src/domain/discovery.ts +27 -0
  186. package/src/domain/plan-semantics.ts +58 -0
  187. package/src/domain/step-start-admission.ts +10 -0
  188. package/src/domain/validation.ts +177 -0
  189. package/src/domain/virgin-registration.ts +46 -0
  190. package/src/errors.ts +24 -0
  191. package/src/fs-utils.ts +61 -0
  192. package/src/gateway-handshake.ts +95 -0
  193. package/src/git.ts +183 -0
  194. package/src/graph.ts +342 -0
  195. package/src/historical-step-provenance.ts +136 -0
  196. package/src/index.ts +23 -0
  197. package/src/lifecycle/canonical-hash.ts +28 -0
  198. package/src/lifecycle/catalog.ts +202 -0
  199. package/src/lifecycle/compiler-inspection.ts +29 -0
  200. package/src/lifecycle/core-static-readiness.ts +132 -0
  201. package/src/lifecycle/corrective-replan-authority.ts +136 -0
  202. package/src/lifecycle/corrective-replan-binding-manifest.ts +51 -0
  203. package/src/lifecycle/corrective-replan-credential-core.ts +408 -0
  204. package/src/lifecycle/corrective-replan-credential-schema.ts +54 -0
  205. package/src/lifecycle/corrective-replan-credentials.ts +48 -0
  206. package/src/lifecycle/corrective-replan.ts +843 -0
  207. package/src/lifecycle/evaluator.ts +48 -0
  208. package/src/lifecycle/fingerprint.ts +488 -0
  209. package/src/lifecycle/immutable.ts +8 -0
  210. package/src/lifecycle/implementation-table.ts +118 -0
  211. package/src/lifecycle/index.ts +8 -0
  212. package/src/lifecycle/schema-artifact.ts +263 -0
  213. package/src/lifecycle/semantic-registry.ts +572 -0
  214. package/src/lifecycle/types.ts +838 -0
  215. package/src/memory.ts +273 -0
  216. package/src/migration.ts +161 -0
  217. package/src/navigation-actions.ts +70 -0
  218. package/src/navigation-update-artifact.ts +198 -0
  219. package/src/navigation-update.ts +121 -0
  220. package/src/observation.ts +225 -0
  221. package/src/observed-routes.ts +660 -0
  222. package/src/operational-contract.ts +125 -0
  223. package/src/pending-review-update.ts +175 -0
  224. package/src/repository.ts +99 -0
  225. package/src/reviewer.ts +1879 -0
  226. package/src/runtime.ts +6 -0
  227. package/src/state/corrective-replan-executor.ts +818 -0
  228. package/src/state/corrective-replan-public-schema.ts +83 -0
  229. package/src/state/corrective-replan-public.ts +908 -0
  230. package/src/state/corrective-replan-transaction.ts +949 -0
  231. package/src/state/corrective-yield-executor.ts +327 -0
  232. package/src/state/corrective-yield-transaction.ts +730 -0
  233. package/src/state/lock.ts +902 -0
  234. package/src/state/store.ts +567 -0
  235. package/src/transition-core.ts +330 -0
  236. package/src/ulid.ts +24 -0
  237. package/src/version.ts +2 -0
  238. package/src/workflow-blocker-route.ts +109 -0
  239. package/src/workflow.ts +10172 -0
  240. package/docs/alpha7.1-implementation-brief.md +0 -268
  241. package/docs/alpha7.2-corrective-context-refresh-brief.md +0 -484
  242. package/docs/alpha7.2.1-remediation-recovery-brief.md +0 -86
  243. package/docs/beta1-stabilization-brief.md +0 -165
  244. package/docs/beta11-plan-integrity-recovery-brief.md +0 -38
  245. package/docs/beta13.2-signal-review-recovery.md +0 -38
  246. package/docs/beta2-initial-assembly-navigation-brief.md +0 -616
  247. package/docs/change-model.md +0 -118
  248. package/docs/delegated-approval.md +0 -254
  249. package/docs/lifecycle/state-machine-stabilization.md +0 -641
  250. package/docs/pdf/README.md +0 -24
  251. package/docs/pdf/codex-workflow-v2-architecture-ru.pdf +0 -0
  252. package/docs/pdf/codex-workflow-v2-chat-only-guide-ru.pdf +0 -0
  253. package/docs/pdf/codex-workflow-v2-technical-reference-ru.pdf +0 -0
  254. package/docs/pdf/requirements.txt +0 -1
  255. package/docs/pdf/sources/codex-workflow-v2-architecture-ru.md +0 -478
  256. package/docs/pdf/sources/codex-workflow-v2-chat-only-guide-ru.md +0 -506
  257. package/docs/pdf/sources/codex-workflow-v2-technical-reference-ru.md +0 -778
  258. package/docs/pending-review-update.md +0 -15
  259. package/docs/problem-briefs/01-pre-implementation-integrity.md +0 -482
  260. package/docs/problem-briefs/02-minimal-step-integrity.md +0 -411
  261. package/docs/problem-briefs/03-minimal-agent-context-integrity.md +0 -358
  262. package/docs/problem-briefs/04-task-dependency-and-structural-replacement-integrity.md +0 -573
  263. package/docs/problem-briefs/BRIEF-TEMPLATE.md +0 -56
  264. package/docs/problem-briefs/README.md +0 -120
  265. package/docs/problem-briefs/evidence/p01-mechanical-feasibility-corpus.md +0 -90
  266. package/docs/problem-briefs/evidence/signal-v4-pre-m3-replay.md +0 -246
  267. package/docs/split-required-recovery.md +0 -47
  268. package/docs/stable-release-defect-register.md +0 -730
  269. package/docs/validation-report.md +0 -182
  270. package/scripts/generate-pdf-docs.py +0 -524
  271. 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
+ }