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,491 @@
1
+ import { stepStartPhase } from '../domain/step-start-admission.js';
2
+ import type {
3
+ AuthorizationEvent,
4
+ StepStatus,
5
+ RemediationEvent,
6
+ RootCauseReplanDirtyCarryoverCompatibility,
7
+ TaskHandoffEvent,
8
+ TaskState,
9
+ } from '../contracts.js';
10
+ import { normalizePlanKnowledgeBinding, readVerifiedPlanArtifact } from '../domain/plan-semantics.js';
11
+ import { pathAllowed } from '../domain/validation.js';
12
+ import { hashCanonical } from '../lifecycle/fingerprint.js';
13
+ import { changedFiles, currentBranch, gitIsAncestor, headCommit } from '../repository.js';
14
+ import { validateTaskHistory } from '../git.js';
15
+ import type { FileStateStore } from '../state/store.js';
16
+ import { assertMechanicalFeasibilityFresh } from './mechanical-feasibility.js';
17
+ import { hashDirtyWorktree } from './downstream-proof.js';
18
+ import { hashCompletedSteps, hashExecutionAuthorization } from './preexecution-replan.js';
19
+ import { readCurrentPlanRiskAudit, readPlanRiskAuditEvents } from './plan-risk.js';
20
+ import { deriveRollingCauseDecision, readRemediationEvents } from './remediation.js';
21
+ import { readTaskC1Posture, readTaskHandoffEvents } from './handoff.js';
22
+ import { sha256Hex } from './store-sidecars.js';
23
+
24
+ /** Phase applicability only; history, scope and writer authority still require their own proofs. */
25
+ export function isRootCauseReplacementStart(status: StepStatus | undefined, hasDirtyFiles: boolean): boolean {
26
+ return stepStartPhase(status, hasDirtyFiles) === 'planned-dirty';
27
+ }
28
+
29
+ export interface RootCauseReplanDirtyCarryoverAssessment {
30
+ applicable: boolean;
31
+ eligible: boolean;
32
+ compatibility: RootCauseReplanDirtyCarryoverCompatibility | null;
33
+ blockers: string[];
34
+ requiredActor?: string;
35
+ }
36
+
37
+ export function matchesTerminalCorrectiveYield(
38
+ event: TaskHandoffEvent,
39
+ terminal: RemediationEvent,
40
+ taskId: string,
41
+ ): boolean {
42
+ const executionTaskRevision = terminal.failureEvidence.executionTaskRevision;
43
+ if (executionTaskRevision === undefined) return false;
44
+ return event.eventType === 'yield'
45
+ && event.taskId === taskId
46
+ && event.taskRevision === executionTaskRevision + 1
47
+ && event.eventId === `HOF-${sha256Hex([
48
+ 'checks-failed-root-cause-replan-pause-v1',
49
+ taskId,
50
+ String(executionTaskRevision),
51
+ terminal.eventHash,
52
+ event.actor,
53
+ ].join('\0'))}`;
54
+ }
55
+
56
+ /**
57
+ * Proves that dirty bytes from the terminal repeated-cause failure survived one semantic
58
+ * corrective Plan replacement and are already inside the replacement Step's declared scope.
59
+ * This proof never grants or appends an allowedWrite.
60
+ */
61
+ export function assessRootCauseReplanDirtyCarryover(
62
+ store: FileStateStore,
63
+ repositoryRoot: string,
64
+ task: TaskState,
65
+ stepId: string,
66
+ options: {
67
+ historyHead?: string;
68
+ binding?: RootCauseReplanDirtyCarryoverCompatibility | null;
69
+ } = {},
70
+ ): RootCauseReplanDirtyCarryoverAssessment {
71
+ const historyHead = options.historyHead ?? options.binding?.sourceHeadCommit ?? headCommit(repositoryRoot);
72
+ const step = task.steps.find((candidate) => candidate.id === stepId) ?? null;
73
+ const allStepEvents = readRemediationEvents(store, task)
74
+ .filter((event) => event.stepId === stepId && event.failureKind === 'checks-failed');
75
+ const applicable = Boolean(step && task.planHash && allStepEvents.length >= 2);
76
+ if (!applicable || !step || !task.planHash) {
77
+ return { applicable, eligible: false, compatibility: null, blockers: [] };
78
+ }
79
+
80
+ const blockers: string[] = [];
81
+ const dirtyFiles = changedFiles(repositoryRoot).sort();
82
+ if (task.status !== 'ready') blockers.push('The replacement Task must be execution-authorized and ready.');
83
+ if (step.status !== 'planned' || step.evidence !== null) {
84
+ blockers.push(`The replacement Step ${stepId} must be planned with no execution evidence.`);
85
+ }
86
+ if (task.steps.some((candidate) => candidate.status === 'in_progress')) blockers.push('No Step may be in progress.');
87
+ if (task.workspaceOwner !== 'local') blockers.push('Corrective carryover requires the recorded local Task workspace.');
88
+ if (!task.taskBranch || currentBranch(repositoryRoot) !== task.taskBranch) {
89
+ blockers.push('Current branch must equal the recorded Task branch.');
90
+ }
91
+ if (dirtyFiles.length === 0) blockers.push('Corrective carryover requires retained dirty product bytes.');
92
+ const outsideAllowed = dirtyFiles.filter((file) => !pathAllowed(file, step.allowedWrites));
93
+ const forbidden = dirtyFiles.filter((file) => pathAllowed(file, step.forbiddenScope));
94
+ if (outsideAllowed.length > 0) blockers.push(`Dirty files fall outside ${stepId} allowedWrites: ${outsideAllowed.join(', ')}`);
95
+ if (forbidden.length > 0) blockers.push(`Dirty files intersect ${stepId} forbiddenScope: ${forbidden.join(', ')}`);
96
+ const incompleteDependencies = step.dependencies.filter((dependency) =>
97
+ !['completed', 'skipped'].includes(task.steps.find((candidate) => candidate.id === dependency)?.status ?? 'blocked'));
98
+ if (incompleteDependencies.length > 0) blockers.push(`Replacement Step dependencies are incomplete: ${incompleteDependencies.join(', ')}`);
99
+
100
+ const audits = readPlanRiskAuditEvents(store, task);
101
+ const eventPlan = (event: RemediationEvent): { planHash: string; semanticHash: string } | null => {
102
+ const revision = event.failureEvidence.executionTaskRevision;
103
+ if (revision === undefined) return null;
104
+ const planHash = [...audits].reverse().find((audit) => audit.taskRevision <= revision)?.planHash ?? null;
105
+ if (!planHash || !task.planHash) return null;
106
+ try {
107
+ return {
108
+ planHash,
109
+ semanticHash: normalizePlanKnowledgeBinding(
110
+ readVerifiedPlanArtifact(store.taskRoot(task.projectId, task.id), planHash, task.planHash),
111
+ `remediation ${event.eventId}`,
112
+ ).semanticHash,
113
+ };
114
+ } catch {
115
+ return null;
116
+ }
117
+ };
118
+ const terminal = allStepEvents.at(-1)!;
119
+ const terminalPlan = eventPlan(terminal);
120
+ const sourcePlanHash = terminalPlan?.planHash ?? null;
121
+ if (!sourcePlanHash || sourcePlanHash === task.planHash) {
122
+ blockers.push('The terminal repeated-cause event must resolve to one replaced source Plan.');
123
+ }
124
+ let cycleStart = allStepEvents.length - 1;
125
+ while (cycleStart > 0
126
+ && eventPlan(allStepEvents[cycleStart - 1]!)?.semanticHash === terminalPlan?.semanticHash) cycleStart -= 1;
127
+ const sourceCycle = allStepEvents.slice(cycleStart);
128
+ const priorEvents = allStepEvents.slice(0, cycleStart);
129
+ if (!terminalPlan || sourceCycle.some((event) => eventPlan(event)?.semanticHash !== terminalPlan.semanticHash)) {
130
+ blockers.push('The source remediation cycle has ambiguous Plan bindings.');
131
+ }
132
+ if (deriveRollingCauseDecision(sourceCycle, priorEvents) !== 'root-cause-replan') {
133
+ blockers.push('The source cycle does not end in a repeated authoritative mechanical cause.');
134
+ }
135
+ const priorFingerprints = new Set([...priorEvents, ...sourceCycle.slice(0, -1)]
136
+ .flatMap((event) => event.causeBindings?.filter((cause) => cause.authority === 'mechanical')
137
+ .map((cause) => cause.causeFingerprint) ?? []));
138
+ const repeated = [...new Set(terminal.causeBindings
139
+ ?.filter((cause) => cause.authority === 'mechanical' && !cause.planConflict && priorFingerprints.has(cause.causeFingerprint))
140
+ .map((cause) => cause.causeFingerprint) ?? [])];
141
+ if (repeated.length !== 1) blockers.push('The terminal failure must identify exactly one repeated mechanical cause.');
142
+ if (terminal.failureEvidence.executionTaskRevision === undefined) {
143
+ blockers.push('The terminal failure is missing its execution Task revision.');
144
+ }
145
+ if (!terminal.failureEvidence.dirtyWorktreeHash) blockers.push('The terminal failure is missing its dirty-worktree hash.');
146
+ if (terminal.failureEvidence.completionCommit !== historyHead) {
147
+ blockers.push('Repository HEAD changed after the terminal repeated-cause failure.');
148
+ }
149
+ try {
150
+ if ((task.invalidatedStepCommits ?? []).length > 0) {
151
+ throw new Error('Legacy carryover cannot time-classify invalidated completed-Step commits.');
152
+ }
153
+ const sourceHistoryTask: TaskState = {
154
+ ...task,
155
+ systemCommits: task.systemCommits.filter((commit) => gitIsAncestor(repositoryRoot, commit, historyHead)),
156
+ dependencyProvenanceRecoveries: (task.dependencyProvenanceRecoveries ?? []).filter((record) =>
157
+ gitIsAncestor(repositoryRoot, record.commitSha, historyHead)),
158
+ };
159
+ validateTaskHistory(repositoryRoot, sourceHistoryTask, historyHead);
160
+ } catch (error) {
161
+ blockers.push(`Completed Step authority is not exactly retained at the source boundary: ${error instanceof Error ? error.message : String(error)}`);
162
+ }
163
+ let dirtyWorktreeHash: string | null = null;
164
+ try {
165
+ dirtyWorktreeHash = hashDirtyWorktree(repositoryRoot, dirtyFiles);
166
+ if (dirtyWorktreeHash !== terminal.failureEvidence.dirtyWorktreeHash) {
167
+ blockers.push('Dirty paths or bytes changed after the terminal repeated-cause failure.');
168
+ }
169
+ } catch (error) {
170
+ blockers.push(error instanceof Error ? error.message : String(error));
171
+ }
172
+
173
+ const replacementAuthorityPlanHash = options.binding?.replacementPlanHash ?? task.planHash;
174
+ let sourcePlanSemanticHash: string | null = null;
175
+ let replacementPlanSemanticHash: string | null = null;
176
+ try {
177
+ if (!sourcePlanHash) throw new Error('Source Plan hash is unavailable.');
178
+ sourcePlanSemanticHash = normalizePlanKnowledgeBinding(
179
+ readVerifiedPlanArtifact(store.taskRoot(task.projectId, task.id), sourcePlanHash, task.planHash),
180
+ 'root-cause source',
181
+ ).semanticHash;
182
+ replacementPlanSemanticHash = normalizePlanKnowledgeBinding(
183
+ readVerifiedPlanArtifact(store.taskRoot(task.projectId, task.id), replacementAuthorityPlanHash, task.planHash),
184
+ 'root-cause replacement',
185
+ ).semanticHash;
186
+ if (sourcePlanSemanticHash === replacementPlanSemanticHash) {
187
+ blockers.push('Corrective carryover requires a semantic replacement Plan.');
188
+ }
189
+ const currentSemanticHash = normalizePlanKnowledgeBinding(
190
+ readVerifiedPlanArtifact(store.taskRoot(task.projectId, task.id), task.planHash, task.planHash),
191
+ 'current corrective Plan',
192
+ ).semanticHash;
193
+ if (currentSemanticHash !== replacementPlanSemanticHash) {
194
+ blockers.push('The current Plan is not a knowledge-only descendant of the corrective replacement Plan.');
195
+ }
196
+ } catch (error) {
197
+ blockers.push(error instanceof Error ? error.message : String(error));
198
+ }
199
+
200
+ let currentAudit: ReturnType<typeof readCurrentPlanRiskAudit> = null;
201
+ let replacementSourceAudit: ReturnType<typeof readPlanRiskAuditEvents>[number] | null = null;
202
+ try {
203
+ currentAudit = readCurrentPlanRiskAudit(store, task);
204
+ const sourceAudit = options.binding
205
+ ? audits.find((audit) => audit.eventId === options.binding!.replacementPlanRiskAuditEventId
206
+ && audit.eventHash === options.binding!.replacementPlanRiskAuditEventHash
207
+ && audit.planHash === options.binding!.replacementPlanHash) ?? null
208
+ : currentAudit?.audit ?? null;
209
+ replacementSourceAudit = sourceAudit;
210
+ if (!currentAudit || currentAudit.audit.planHash !== task.planHash
211
+ || !currentAudit.reviewRequiredStepIds.includes(stepId)
212
+ || !sourceAudit
213
+ || sourceAudit.taskRevision <= (terminal.failureEvidence.executionTaskRevision ?? Number.MAX_SAFE_INTEGER)) {
214
+ blockers.push('A fresh replacement Plan Risk Audit must guard the exact corrective Step.');
215
+ }
216
+ if (sourceAudit && currentAudit
217
+ && JSON.stringify(sourceAudit.stepClassifications) !== JSON.stringify(currentAudit.audit.stepClassifications)) {
218
+ blockers.push('Context refresh changed the replacement Plan Risk Audit classifications or proof bindings.');
219
+ }
220
+ if (sourceAudit && currentAudit) {
221
+ assertContiguousKnowledgeRebindChain(
222
+ store.taskRoot(task.projectId, task.id),
223
+ task,
224
+ replacementAuthorityPlanHash,
225
+ task.planHash,
226
+ audits,
227
+ sourceAudit,
228
+ currentAudit.audit,
229
+ );
230
+ }
231
+ } catch (error) {
232
+ blockers.push(error instanceof Error ? error.message : String(error));
233
+ }
234
+
235
+ const authorization = [...task.authorizations].reverse().find((event) =>
236
+ event.kind === 'execution' && event.decision === 'approved') ?? null;
237
+ const sourceAuthorization = options.binding
238
+ ? task.authorizations.find((event) => hashExecutionAuthorization(
239
+ event.kind === 'execution' && event.decision === 'superseded'
240
+ ? { ...event, decision: 'approved' }
241
+ : event,
242
+ ) === options.binding!.executionAuthorizationHash
243
+ && event.planHash === options.binding!.replacementPlanHash
244
+ && event.headCommit === options.binding!.executionAuthorizationHead) ?? null
245
+ : authorization;
246
+ if (!authorization) {
247
+ blockers.push('The replacement Plan requires a current execution authorization.');
248
+ } else {
249
+ validateAuthorization(
250
+ store,
251
+ repositoryRoot,
252
+ task,
253
+ authorization,
254
+ options.binding ? headCommit(repositoryRoot) : historyHead,
255
+ blockers,
256
+ );
257
+ }
258
+ if (!sourceAuthorization) blockers.push('The original corrective execution authorization is missing from immutable Task history.');
259
+ if (task.steps.some((candidate) => candidate.status === 'completed' && !candidate.evidence)) {
260
+ blockers.push('Every retained completed Step must preserve its execution evidence.');
261
+ }
262
+
263
+ const c1Posture = readTaskC1Posture(store, task);
264
+ const handoffEvents = readTaskHandoffEvents(store, task);
265
+ const sourceTaskRevision = options.binding?.sourceTaskRevision ?? task.revision;
266
+ const latestClaim = [...handoffEvents].reverse().find((event) => event.eventType === 'claim') ?? null;
267
+ const replacementAuditTime = replacementSourceAudit ? Date.parse(replacementSourceAudit.recordedAt) : null;
268
+ const claimTime = latestClaim ? Date.parse(latestClaim.recordedAt) : null;
269
+ const claimEvent = latestClaim
270
+ && latestClaim.taskRevision === sourceTaskRevision
271
+ && latestClaim.planHash === replacementAuthorityPlanHash
272
+ && latestClaim.briefHash === task.briefHash
273
+ && (!replacementSourceAudit || (claimTime !== null && Number.isFinite(claimTime)
274
+ && replacementAuditTime !== null && Number.isFinite(replacementAuditTime)
275
+ && claimTime >= replacementAuditTime
276
+ && latestClaim.taskRevision > replacementSourceAudit.taskRevision))
277
+ ? latestClaim
278
+ : null;
279
+ const matchingCorrectiveYields = handoffEvents.filter((event) =>
280
+ matchesTerminalCorrectiveYield(event, terminal, task.id));
281
+ const correctiveYield = matchingCorrectiveYields.length === 1
282
+ && matchingCorrectiveYields[0]!.planHash !== null
283
+ && eventPlan(terminal)?.semanticHash === normalizePlanKnowledgeBinding(
284
+ readVerifiedPlanArtifact(
285
+ store.taskRoot(task.projectId, task.id),
286
+ matchingCorrectiveYields[0]!.planHash!,
287
+ task.planHash!,
288
+ ),
289
+ `corrective yield ${matchingCorrectiveYields[0]!.eventId}`,
290
+ ).semanticHash
291
+ && Number.isFinite(Date.parse(matchingCorrectiveYields[0]!.recordedAt))
292
+ && Number.isFinite(Date.parse(terminal.recordedAt))
293
+ && Date.parse(matchingCorrectiveYields[0]!.recordedAt) >= Date.parse(terminal.recordedAt)
294
+ && (!replacementSourceAudit || (replacementAuditTime !== null && Number.isFinite(replacementAuditTime)
295
+ && Date.parse(matchingCorrectiveYields[0]!.recordedAt) <= replacementAuditTime
296
+ && matchingCorrectiveYields[0]!.taskRevision < replacementSourceAudit.taskRevision))
297
+ ? matchingCorrectiveYields[0]!
298
+ : null;
299
+ const unclaimed = terminal.failureEvidence.unclaimedExecutionBoundary;
300
+ const historicalPrefixIndex = unclaimed?.priorHandoffEventHash === null ? -1
301
+ : handoffEvents.findIndex(event => event.eventHash === unclaimed?.priorHandoffEventHash);
302
+ const historicalPrior = handoffEvents[historicalPrefixIndex];
303
+ const historicalNext = handoffEvents[historicalPrefixIndex + 1];
304
+ const validUnclaimed = Boolean(unclaimed
305
+ && (unclaimed.priorHandoffEventHash === null || historicalPrefixIndex >= 0)
306
+ && (!historicalPrior || (!['handoff', 'claim', 'credential-replace'].includes(historicalPrior.eventType)
307
+ && historicalPrior.taskRevision <= terminal.failureEvidence.executionTaskRevision!
308
+ && Date.parse(historicalPrior.recordedAt) <= Date.parse(terminal.recordedAt)))
309
+ && (!historicalNext || historicalNext.taskRevision > terminal.failureEvidence.executionTaskRevision!)
310
+ && matchingCorrectiveYields.length === 0);
311
+ if (unclaimed && !validUnclaimed) blockers.push('Native unclaimed execution receipt does not bind the exact historical no-C1 prefix.');
312
+ if (validUnclaimed && claimEvent && claimEvent.actor !== unclaimed!.actor) {
313
+ blockers.push('Corrective carryover must return to the same native unclaimed execution actor.');
314
+ }
315
+ if (c1Posture.state !== 'claimed' || !claimEvent || claimEvent.actor !== c1Posture.claimant) {
316
+ blockers.push('Corrective carryover requires the retained claimed C1 actor and its immutable claim event.');
317
+ }
318
+ if (!correctiveYield && !validUnclaimed) blockers.push('The terminal repeated cause is missing its completed corrective-yield boundary.');
319
+ if (correctiveYield && claimEvent
320
+ && (claimEvent.actor !== correctiveYield.actor
321
+ || c1Posture.state !== 'claimed'
322
+ || c1Posture.claimant !== correctiveYield.actor)) {
323
+ blockers.push('Corrective carryover must return to the same Worker that completed the terminal corrective yield.');
324
+ }
325
+
326
+ if (blockers.length > 0 || !sourcePlanHash || !sourcePlanSemanticHash || !replacementPlanSemanticHash
327
+ || !dirtyWorktreeHash || !currentAudit || !authorization || !sourceAuthorization
328
+ || terminal.failureEvidence.executionTaskRevision === undefined || repeated.length !== 1
329
+ || c1Posture.state !== 'claimed' || !claimEvent || (!correctiveYield && !validUnclaimed)) {
330
+ return { applicable: true, eligible: false, compatibility: null, blockers: [...new Set(blockers)],
331
+ ...(validUnclaimed ? { requiredActor: unclaimed!.actor } : {}) };
332
+ }
333
+ const base = {
334
+ kind: 'root-cause-corrective-replan-dirty-carryover' as const,
335
+ taskId: task.id,
336
+ sourceTaskRevision,
337
+ stepId,
338
+ sourcePlanHash,
339
+ sourcePlanSemanticHash,
340
+ replacementPlanHash: replacementAuthorityPlanHash,
341
+ replacementPlanSemanticHash,
342
+ sourceHeadCommit: terminal.failureEvidence.completionCommit,
343
+ remediationEvents: sourceCycle.map((event) => ({
344
+ eventId: event.eventId,
345
+ eventHash: event.eventHash,
346
+ attemptOrdinal: event.attemptOrdinal,
347
+ })),
348
+ terminalRemediationEventId: terminal.eventId,
349
+ terminalRemediationEventHash: terminal.eventHash,
350
+ terminalExecutionTaskRevision: terminal.failureEvidence.executionTaskRevision,
351
+ repeatedCauseFingerprint: repeated[0]!,
352
+ correctiveYieldEventId: correctiveYield?.eventId ?? null,
353
+ correctiveYieldEventHash: correctiveYield?.eventHash ?? null,
354
+ ...(validUnclaimed ? { unclaimedExecutionBoundary: unclaimed! } : {}),
355
+ dirtyFiles,
356
+ dirtyWorktreeHash,
357
+ replacementPlanRiskAuditEventId: options.binding?.replacementPlanRiskAuditEventId ?? currentAudit.audit.eventId,
358
+ replacementPlanRiskAuditEventHash: options.binding?.replacementPlanRiskAuditEventHash ?? currentAudit.audit.eventHash,
359
+ executionAuthorizationHash: options.binding?.executionAuthorizationHash ?? hashExecutionAuthorization(sourceAuthorization),
360
+ executionAuthorizationHead: options.binding?.executionAuthorizationHead ?? sourceAuthorization.headCommit!,
361
+ completedStepsHash: hashCompletedSteps(task),
362
+ c1ClaimEventId: claimEvent.eventId,
363
+ c1ClaimEventHash: claimEvent.eventHash,
364
+ claimant: c1Posture.claimant,
365
+ };
366
+ const compatibility = { ...base, compatibilityHash: hashCanonical({
367
+ domain: 'codex-workflow/root-cause-corrective-replan-dirty-carryover/v1',
368
+ ...base,
369
+ }) };
370
+ if (options.binding && JSON.stringify(compatibility) !== JSON.stringify(options.binding)) {
371
+ return {
372
+ applicable: true,
373
+ eligible: false,
374
+ compatibility: null,
375
+ blockers: ['Recorded corrective carryover binding no longer matches the immutable Task, Plan, audit, authorization, C1, or dirty-byte evidence.'],
376
+ };
377
+ }
378
+ return {
379
+ applicable: true,
380
+ eligible: true,
381
+ compatibility,
382
+ blockers: [],
383
+ };
384
+ }
385
+
386
+ function assertContiguousKnowledgeRebindChain(
387
+ taskRoot: string,
388
+ task: TaskState,
389
+ sourcePlanHash: string,
390
+ currentPlanHash: string,
391
+ audits: ReturnType<typeof readPlanRiskAuditEvents>,
392
+ sourceAudit: NonNullable<ReturnType<typeof readPlanRiskAuditEvents>[number]>,
393
+ currentAudit: NonNullable<ReturnType<typeof readPlanRiskAuditEvents>[number]>,
394
+ ): void {
395
+ if (sourcePlanHash === currentPlanHash) {
396
+ if (sourceAudit.eventHash !== currentAudit.eventHash) {
397
+ throw new Error('Corrective source Plan has ambiguous current Plan Risk Audit authority.');
398
+ }
399
+ return;
400
+ }
401
+ for (let index = 0; index < task.knowledgeRebinds.length; index += 1) {
402
+ const timestamp = Date.parse(task.knowledgeRebinds[index]!.recordedAt);
403
+ if (!Number.isFinite(timestamp)
404
+ || (index > 0 && timestamp <= Date.parse(task.knowledgeRebinds[index - 1]!.recordedAt))) {
405
+ throw new Error('Task knowledge-rebind timestamps are not finite and strictly increasing.');
406
+ }
407
+ }
408
+ let expected = sourcePlanHash;
409
+ const seen = new Set<string>();
410
+ const start = task.knowledgeRebinds.findIndex((record) => record.previousPlanHash === sourcePlanHash);
411
+ if (start < 0) throw new Error('Corrective Plan has no append-only Knowledge rebind chain.');
412
+ const chain = task.knowledgeRebinds.slice(start);
413
+ for (const record of chain) {
414
+ if (record.previousPlanHash !== expected) {
415
+ throw new Error('Corrective Plan knowledge-rebind provenance forks or skips a Plan hash.');
416
+ }
417
+ if (seen.has(record.reboundPlanHash) || record.reboundPlanHash === record.previousPlanHash) {
418
+ throw new Error('Corrective Plan knowledge-rebind chain is cyclic.');
419
+ }
420
+ seen.add(record.reboundPlanHash);
421
+ const before = readVerifiedPlanArtifact(taskRoot, record.previousPlanHash, currentPlanHash);
422
+ const after = readVerifiedPlanArtifact(taskRoot, record.reboundPlanHash, currentPlanHash);
423
+ if (normalizePlanKnowledgeBinding(before, 'corrective rebind source').normalized
424
+ !== normalizePlanKnowledgeBinding(after, 'corrective rebind target').normalized) {
425
+ throw new Error('Corrective Plan rebind changed semantics beyond the Knowledge Map binding.');
426
+ }
427
+ expected = record.reboundPlanHash;
428
+ }
429
+ if (expected !== currentPlanHash) {
430
+ throw new Error('Corrective Plan knowledge-rebind provenance is not contiguous to the current Plan.');
431
+ }
432
+ const planHashes = [sourcePlanHash, ...chain.map((record) => record.reboundPlanHash)];
433
+ const auditChain = planHashes.map((planHash) => audits.filter((audit) => audit.planHash === planHash));
434
+ if (auditChain.some((matches) => matches.length !== 1)
435
+ || auditChain[0]![0]!.eventHash !== sourceAudit.eventHash
436
+ || auditChain.at(-1)![0]!.eventHash !== currentAudit.eventHash) {
437
+ throw new Error('Corrective Plan Risk Audit rebound chain is missing or ambiguous.');
438
+ }
439
+ const sourceAuditIndex = audits.indexOf(auditChain[0]![0]!);
440
+ if (sourceAuditIndex < 0
441
+ || sourceAuditIndex + auditChain.length !== audits.length
442
+ || auditChain.some((matches, index) => audits[sourceAuditIndex + index] !== matches[0])) {
443
+ throw new Error('Corrective Plan Risk Audit rebound evidence is not the contiguous current audit suffix.');
444
+ }
445
+ chain.forEach((record, index) => {
446
+ const before = auditChain[index]![0]!;
447
+ const after = auditChain[index + 1]![0]!;
448
+ const expectedSummary = `${before.summary} Rebound automatically after content-only Task context refresh.`;
449
+ if (before.knowledgeMapRevision !== record.fromKnowledgeMapRevision
450
+ || before.knowledgeMapHash !== record.fromKnowledgeMapHash
451
+ || after.knowledgeMapRevision !== record.toKnowledgeMapRevision
452
+ || after.knowledgeMapHash !== record.toKnowledgeMapHash
453
+ || after.previousEventHash !== before.eventHash
454
+ || after.taskId !== before.taskId
455
+ || after.briefHash !== before.briefHash
456
+ || after.briefHash !== task.briefHash
457
+ || before.planner !== after.planner
458
+ || before.auditor !== after.auditor
459
+ || before.decision !== after.decision
460
+ || before.splitRationale !== after.splitRationale
461
+ || after.summary !== expectedSummary
462
+ || before.structuralProductionEdgeAuthority !== after.structuralProductionEdgeAuthority
463
+ || JSON.stringify(before.stepClassifications) !== JSON.stringify(after.stepClassifications)) {
464
+ throw new Error('Corrective Plan Risk Audit rebound does not match a knowledge-only Plan hop.');
465
+ }
466
+ });
467
+ }
468
+
469
+ function validateAuthorization(
470
+ store: FileStateStore,
471
+ repositoryRoot: string,
472
+ task: TaskState,
473
+ authorization: AuthorizationEvent,
474
+ expectedHead: string,
475
+ blockers: string[],
476
+ ): void {
477
+ try {
478
+ const root = store.taskRoot(task.projectId, task.id);
479
+ const briefHash = store.hashArtifact(root, 'brief.md');
480
+ const planHash = store.hashArtifact(root, 'plan.md');
481
+ if (task.briefHash !== briefHash || task.planHash !== planHash
482
+ || authorization.briefHash !== briefHash || authorization.planHash !== planHash
483
+ || !authorization.mechanicalFeasibility || authorization.headCommit !== expectedHead) {
484
+ blockers.push('Execution authorization does not bind the current Brief, Plan, and HEAD.');
485
+ return;
486
+ }
487
+ assertMechanicalFeasibilityFresh(repositoryRoot, task, authorization.mechanicalFeasibility, briefHash, planHash);
488
+ } catch (error) {
489
+ blockers.push(error instanceof Error ? error.message : String(error));
490
+ }
491
+ }