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,908 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, lstatSync, readFileSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { types as utilTypes } from 'node:util';
5
+ import type { SidecarEventRecord, TaskPlanInput, TaskState } from '../contracts.js';
6
+ import { readTaskC1Posture } from '../alpha6/handoff.js';
7
+ import { readCorrectivePlanningAudit, readFailedStepPlanningRecovery } from '../alpha6/failed-step-planning-recovery.js';
8
+ import {
9
+ findCurrentGuardedRemediationPosture,
10
+ effectiveStepAllowedWrites,
11
+ readCorrectiveDecisionEvents,
12
+ readRemediationEvents,
13
+ } from '../alpha6/remediation.js';
14
+ import { pathAllowed, validatePlan } from '../domain/validation.js';
15
+ import { WorkflowError } from '../errors.js';
16
+ import { hashKnowledgeMap } from '../memory.js';
17
+ import { changedFiles, headCommit } from '../repository.js';
18
+ import { hashDirtyWorktree } from '../alpha6/downstream-proof.js';
19
+ import { PACKAGE_VERSION } from '../version.js';
20
+ import { GuardedTransitionCatalog } from '../lifecycle/catalog.js';
21
+ import { correctiveReplanBindingManifest } from '../lifecycle/corrective-replan-binding-manifest.js';
22
+ import {
23
+ createCorrectiveReplanCredentialKernel,
24
+ type CorrectiveReplanCredentialKernel,
25
+ type HumanChallengeHandle,
26
+ type PlanValidationHandle,
27
+ type ReceiptHandle,
28
+ type StoredPlanValidationEventHandle,
29
+ type VerifiedConfirmationHandle,
30
+ } from '../lifecycle/corrective-replan-credential-core.js';
31
+ import { correctiveReplanCredentialSchemaValidators } from '../lifecycle/corrective-replan-credential-schema.js';
32
+ import {
33
+ evaluateTaskCorrectiveReplanAvailability,
34
+ evaluateTaskCorrectiveReplanAdmission,
35
+ evaluateTaskCorrectiveReplanCandidate,
36
+ identifyCorrectiveReplanPosture,
37
+ prepareCorrectiveReplanCandidate,
38
+ taskCorrectiveReplanDefinition,
39
+ } from '../lifecycle/corrective-replan.js';
40
+ import { hashCanonical } from '../lifecycle/fingerprint.js';
41
+ import { deepFreeze } from '../lifecycle/immutable.js';
42
+ import { LIFECYCLE_EPOCH } from '../lifecycle/types.js';
43
+ import {
44
+ correctiveReplanExecutionKeyHash,
45
+ correctiveReplanRiskAuditHash,
46
+ correctiveReplanStepSetHash,
47
+ createCorrectiveReplanExecutor,
48
+ type CorrectiveReplanExecutionResult,
49
+ type CorrectiveReplanRiskAuditInput,
50
+ } from './corrective-replan-executor.js';
51
+ import {
52
+ assertTaskTransactionBoundary,
53
+ CorrectiveReplanTaskTransactionStore,
54
+ } from './corrective-replan-transaction.js';
55
+ import { assertCorrectiveYieldTaskTransactionBoundary } from './corrective-yield-transaction.js';
56
+ import { inspectWriterLease, listCoreTaskOperationMutexes } from './lock.js';
57
+ import {
58
+ assertCorrectiveReplanPublicOutput,
59
+ validateCorrectiveReplanPublicValue,
60
+ } from './corrective-replan-public-schema.js';
61
+ import type { FileStateStore } from './store.js';
62
+
63
+ type JsonRecord = Record<string, unknown>;
64
+ const HASH = /^[a-f0-9]{64}$/;
65
+ const TTL_MS = 900_000;
66
+ const OWNED_JSON_MAX_NODES = 4096;
67
+ const OWNED_JSON_MAX_BYTES = 262_144;
68
+ const correctiveCatalogHash = new GuardedTransitionCatalog([taskCorrectiveReplanDefinition]).catalogHash;
69
+
70
+ export interface CorrectiveReplanAuthorityPresentation {
71
+ readonly planAuthorIdentityHash: string;
72
+ readonly planValidatorIdentityHash: string;
73
+ readonly riskAuditorIdentityHash: string;
74
+ readonly actorIdentityHash: string;
75
+ readonly actorRole: 'delivery-coordinator' | 'technical-planner';
76
+ readonly humanPrincipalIdentityHash: string;
77
+ }
78
+
79
+ export interface CorrectiveReplanValidationEvidence {
80
+ readonly validatorContextHash: string;
81
+ readonly findings: readonly Readonly<{
82
+ findingId: string;
83
+ severity: 'low' | 'medium' | 'high';
84
+ requirementRef: string;
85
+ evidenceHash: string;
86
+ }>[];
87
+ readonly dispositions: readonly Readonly<{
88
+ findingId: string;
89
+ decision: 'resolved' | 'accepted-documented-limitation';
90
+ evidenceHash: string;
91
+ }>[];
92
+ }
93
+
94
+ export interface CorrectiveReplanPublicCandidate {
95
+ readonly plan: TaskPlanInput;
96
+ readonly validation: CorrectiveReplanValidationEvidence;
97
+ readonly riskAudit: CorrectiveReplanRiskAuditInput;
98
+ readonly authority: CorrectiveReplanAuthorityPresentation;
99
+ }
100
+
101
+ export interface CorrectiveReplanHumanGate {
102
+ readonly kind: 'corrective-replan-human-gate';
103
+ readonly version: 1;
104
+ readonly taskId: string;
105
+ readonly preparedAtMs: number;
106
+ readonly expiresAtMs: number;
107
+ readonly candidatePlanHash: string;
108
+ readonly bindingSha256: string;
109
+ readonly attestationId: string;
110
+ readonly attestationHash: string;
111
+ readonly challengeId: string;
112
+ readonly runtimeFingerprint: string;
113
+ readonly catalogHash: string;
114
+ readonly lifecycleEpoch: number;
115
+ readonly preparationSha256: string;
116
+ }
117
+
118
+ export interface CorrectiveReplanHumanGateResult {
119
+ readonly kind: 'corrective-replan-human-gate-result';
120
+ readonly gate: CorrectiveReplanHumanGate;
121
+ readonly delivery: Readonly<{
122
+ kind: 'corrective-replan-human-challenge-delivery';
123
+ version: 1;
124
+ challengeId: string;
125
+ confirmationCode: string;
126
+ expiresAtMs: number;
127
+ }>;
128
+ }
129
+
130
+ export interface CorrectiveReplanPublicCredential {
131
+ readonly kind: 'corrective-replan-public-credential';
132
+ readonly version: 1;
133
+ readonly gate: CorrectiveReplanHumanGate;
134
+ readonly receipt: Readonly<JsonRecord>;
135
+ readonly planValidationEvent: Readonly<JsonRecord>;
136
+ }
137
+
138
+ export interface CorrectiveReplanPublicController {
139
+ prepare(input: { taskId: string; candidate: CorrectiveReplanPublicCandidate }): CorrectiveReplanHumanGateResult;
140
+ confirm(input: {
141
+ taskId: string;
142
+ candidate: CorrectiveReplanPublicCandidate;
143
+ gate: CorrectiveReplanHumanGate;
144
+ confirmationCode: string;
145
+ }): CorrectiveReplanPublicCredential;
146
+ execute(input: {
147
+ taskId: string;
148
+ candidate: CorrectiveReplanPublicCandidate;
149
+ credential: CorrectiveReplanPublicCredential;
150
+ }): CorrectiveReplanExecutionResult;
151
+ recover(input: { taskId: string; transactionId: string }): CorrectiveReplanExecutionResult;
152
+ }
153
+
154
+ interface ControllerOptions {
155
+ readonly store: FileStateStore;
156
+ readonly projectId: string;
157
+ readonly repositoryRoot: string;
158
+ readonly now?: () => Date;
159
+ }
160
+
161
+ interface Chain {
162
+ readonly kernel: CorrectiveReplanCredentialKernel;
163
+ readonly clock: { set(value: number): void };
164
+ readonly gate: CorrectiveReplanHumanGate;
165
+ readonly delivery: JsonRecord;
166
+ readonly planValidation: PlanValidationHandle;
167
+ readonly challenge: HumanChallengeHandle;
168
+ readonly enableTaskOperationRevalidation: () => void;
169
+ }
170
+
171
+ interface ConfirmedChain extends Chain {
172
+ readonly confirmation: VerifiedConfirmationHandle;
173
+ readonly receipt: ReceiptHandle;
174
+ readonly receiptValue: JsonRecord;
175
+ readonly event: StoredPlanValidationEventHandle;
176
+ readonly eventValue: JsonRecord;
177
+ }
178
+
179
+ export function createCorrectiveReplanPublicController(options: ControllerOptions): CorrectiveReplanPublicController {
180
+ const now = options.now ?? (() => new Date());
181
+ const taskRoot = (taskId: string) => options.store.taskRoot(options.projectId, taskId);
182
+ const executor = (taskId: string, kernel: CorrectiveReplanCredentialKernel) => createCorrectiveReplanExecutor({
183
+ taskRoot: taskRoot(taskId),
184
+ locksRoot: options.store.lockRoot(options.projectId),
185
+ trustedRoot: options.store.root,
186
+ credentialKernel: kernel,
187
+ now,
188
+ admissionCheck: (input) => assertCanonicalAdmission(options, taskId, input, candidateForAdmission.get(kernel)),
189
+ });
190
+ const candidateForAdmission = new WeakMap<object, CorrectiveReplanPublicCandidate>();
191
+
192
+ const prepare = (input: { taskId: string; candidate: CorrectiveReplanPublicCandidate }): CorrectiveReplanHumanGateResult => {
193
+ const owned = ownRequest(input);
194
+ assertPublicCandidate(owned.candidate);
195
+ const preparedAtMs = now().getTime();
196
+ const chain = buildChain(options, owned.taskId, owned.candidate, preparedAtMs);
197
+ return assertCorrectiveReplanPublicOutput('humanGateResult', deepFreeze({
198
+ kind: 'corrective-replan-human-gate-result',
199
+ gate: chain.gate,
200
+ delivery: chain.delivery as CorrectiveReplanHumanGateResult['delivery'],
201
+ }));
202
+ };
203
+
204
+ const confirm = (input: {
205
+ taskId: string;
206
+ candidate: CorrectiveReplanPublicCandidate;
207
+ gate: CorrectiveReplanHumanGate;
208
+ confirmationCode: string;
209
+ }): CorrectiveReplanPublicCredential => {
210
+ const owned = ownRequest(input);
211
+ assertPublicCandidate(owned.candidate);
212
+ assertGateTime(owned.gate, now().getTime());
213
+ const chain = buildChain(options, owned.taskId, owned.candidate, owned.gate.preparedAtMs);
214
+ if (!same(chain.gate, owned.gate)) throw new WorkflowError('STATE_CONFLICT', 'Corrective-replan Human gate is stale or malformed.');
215
+ const confirmedAtMs = now().getTime();
216
+ chain.clock.set(confirmedAtMs);
217
+ const confirmation = accepted(chain.kernel.confirmHuman({
218
+ challenge: chain.challenge,
219
+ confirmation: {
220
+ kind: 'corrective-replan-human-confirmation',
221
+ version: 1,
222
+ challengeId: chain.delivery.challengeId,
223
+ confirmationCode: owned.confirmationCode,
224
+ },
225
+ }), 'Human confirmation was rejected.');
226
+ const receipt = accepted(chain.kernel.issueReceipt({
227
+ planValidation: chain.planValidation,
228
+ confirmation: confirmation.handle,
229
+ }), 'Corrective-replan receipt issuance was rejected.');
230
+ const event = accepted(chain.kernel.materializeStoredEvent({ receipt: receipt.handle }), 'Plan Validation event materialization was rejected.');
231
+ return assertCorrectiveReplanPublicOutput('credential', deepFreeze({
232
+ kind: 'corrective-replan-public-credential',
233
+ version: 1,
234
+ gate: chain.gate,
235
+ receipt: receipt.receipt as JsonRecord,
236
+ planValidationEvent: event.event as JsonRecord,
237
+ }));
238
+ };
239
+
240
+ const execute = (input: {
241
+ taskId: string;
242
+ candidate: CorrectiveReplanPublicCandidate;
243
+ credential: CorrectiveReplanPublicCredential;
244
+ }): CorrectiveReplanExecutionResult => {
245
+ const owned = ownRequest(input);
246
+ assertPublicCandidate(owned.candidate);
247
+ assertPublicCredential(owned.taskId, owned.credential);
248
+ const receipt = record(owned.credential.receipt);
249
+ const binding = record(receipt.binding);
250
+ const transactionId = correctiveReplanExecutionKeyHash(binding);
251
+ const transactions = new CorrectiveReplanTaskTransactionStore({
252
+ taskRoot: taskRoot(owned.taskId),
253
+ locksRoot: options.store.lockRoot(options.projectId),
254
+ trustedRoot: options.store.root,
255
+ now,
256
+ });
257
+ if (existsSync(transactions.journalPath(transactionId))) {
258
+ const journal = transactions.read(transactionId);
259
+ if (journal.receiptHash !== receipt.receiptHash) throw new WorkflowError('STATE_CONFLICT', 'Corrective-replan replay receipt mismatch.');
260
+ if (journal.phase !== 'completed') {
261
+ throw new WorkflowError('TASK_RECOVERY_REQUIRED', 'Corrective-replan transaction requires explicit recovery.', {
262
+ taskId: owned.taskId,
263
+ transactionId,
264
+ });
265
+ }
266
+ return assertCorrectiveReplanPublicOutput('executionResult', executor(owned.taskId, inertKernel()).replay({
267
+ taskId: owned.taskId,
268
+ transactionId,
269
+ receiptHash: string(receipt.receiptHash),
270
+ }));
271
+ }
272
+ assertGateTime(owned.credential.gate, now().getTime());
273
+ const predecessor = validatedEventPredecessor(options.store, taskRoot(owned.taskId), owned.credential.planValidationEvent);
274
+ const chain = confirmChain(options, owned.taskId, owned.candidate, owned.credential, predecessor);
275
+ candidateForAdmission.set(chain.kernel as object, owned.candidate);
276
+ chain.enableTaskOperationRevalidation();
277
+ chain.clock.set(now().getTime());
278
+ return assertCorrectiveReplanPublicOutput('executionResult', executor(owned.taskId, chain.kernel).execute({
279
+ projectId: options.projectId,
280
+ taskId: owned.taskId,
281
+ receipt: chain.receipt,
282
+ candidateReceipt: owned.credential.receipt,
283
+ planValidationEvent: chain.event,
284
+ candidatePlanValidationEvent: owned.credential.planValidationEvent,
285
+ plan: owned.candidate.plan,
286
+ riskAudit: owned.candidate.riskAudit,
287
+ }));
288
+ };
289
+
290
+ const recover = (input: { taskId: string; transactionId: string }): CorrectiveReplanExecutionResult => {
291
+ const owned = ownRequest(input);
292
+ return assertCorrectiveReplanPublicOutput('executionResult', executor(owned.taskId, inertKernel()).recover({ taskId: owned.taskId, transactionId: owned.transactionId }));
293
+ };
294
+
295
+ return deepFreeze({ prepare, confirm, execute, recover });
296
+ }
297
+
298
+ function assertCanonicalAdmission(
299
+ options: ControllerOptions,
300
+ taskId: string,
301
+ input: Readonly<{
302
+ task: TaskState;
303
+ plan: TaskPlanInput;
304
+ riskAudit: CorrectiveReplanRiskAuditInput;
305
+ receiptVerified: true;
306
+ validationEventVerified: true;
307
+ taskOperationLockHeld: true;
308
+ }>,
309
+ candidate: CorrectiveReplanPublicCandidate | undefined,
310
+ ): void {
311
+ if (!candidate || candidate.plan !== input.plan && !same(candidate.plan, input.plan)) {
312
+ throw new WorkflowError('STATE_CONFLICT', 'Corrective-replan admission candidate identity is missing.');
313
+ }
314
+ const audit = readCorrectivePlanningAudit(options.store, input.task);
315
+ const decisions = readCorrectiveDecisionEvents(options.store, input.task);
316
+ const remediations = readRemediationEvents(options.store, input.task);
317
+ const classified = audit ? findCurrentGuardedRemediationPosture(options.store, input.task, audit) : null;
318
+ const posture = classified?.posture === 'replan-required'
319
+ ? classified
320
+ : audit ? identifyCorrectiveReplanPosture(input.task, audit.reviewRequiredStepIds, decisions, remediations) : null;
321
+ if (!posture || posture.posture !== 'replan-required' || !posture.correctiveDecision) {
322
+ throw new WorkflowError('TRANSITION_BLOCKED', 'Corrective-replan posture changed before execution.');
323
+ }
324
+ const evaluation = evaluateTaskCorrectiveReplanAdmission(
325
+ input.task,
326
+ posture,
327
+ prepareCorrectiveReplanCandidate(input.task, input.plan),
328
+ {
329
+ actorRoleEligible: ['delivery-coordinator', 'technical-planner'].includes(candidate.authority.actorRole),
330
+ c1ClaimAbsent: readTaskC1Posture(options.store, input.task).state === 'none',
331
+ writerLeaseAbsent: input.taskOperationLockHeld,
332
+ planValidationPassed: input.validationEventVerified,
333
+ receiptValid: input.receiptVerified,
334
+ riskAuditPassed: input.riskAudit.decision === 'approved' || input.riskAudit.decision === 'approved-with-rationale',
335
+ },
336
+ );
337
+ if (!evaluation.allowed || evaluation.pendingGuardIds.length > 0) {
338
+ throw new WorkflowError('TRANSITION_BLOCKED', 'Canonical corrective-replan admission rejected execution.', {
339
+ taskId,
340
+ guardResults: evaluation.guardResults,
341
+ });
342
+ }
343
+ }
344
+
345
+ function buildChain(
346
+ options: ControllerOptions,
347
+ taskId: string,
348
+ candidate: CorrectiveReplanPublicCandidate,
349
+ preparedAtMs: number,
350
+ previousEventHashOverride?: string | null,
351
+ ): Chain {
352
+ if (!Number.isSafeInteger(preparedAtMs) || preparedAtMs < 0) throw new WorkflowError('INVALID_ARGUMENT', 'Corrective-replan preparation time is invalid.');
353
+ const initialSnapshot = trustedSnapshot(options, taskId, candidate, preparedAtMs);
354
+ const preparationSeed = hashCanonical({
355
+ domain: 'codex-workflow/corrective-replan-public-preparation/v1',
356
+ projectId: options.projectId,
357
+ taskId,
358
+ preparedAtMs,
359
+ snapshot: { ...initialSnapshot, bindingManifest: correctiveReplanBindingManifest.manifestSha256 },
360
+ });
361
+ let clockMs = preparedAtMs;
362
+ const clock = { set(value: number) { clockMs = value; } };
363
+ const ids = (kind: string): string => {
364
+ const prefix: Record<string, string> = {
365
+ 'plan-validation-attestation': 'PVA',
366
+ 'human-challenge': 'HCH',
367
+ receipt: 'RCP',
368
+ 'plan-validation-event': 'PVE',
369
+ };
370
+ return `${prefix[kind] ?? 'ID'}-${hashCanonical({ preparationSeed, kind, clockMs }).slice(0, 32)}`;
371
+ };
372
+ const baseValues = record(initialSnapshot.baseValues);
373
+ const code = publicConfirmationCode({
374
+ taskId,
375
+ preparedAtMs,
376
+ candidatePlanHash: string(baseValues.candidatePlanHash),
377
+ runtimeFingerprint: string(baseValues.runtimeFingerprint),
378
+ catalogHash: string(baseValues.catalogHash),
379
+ lifecycleEpoch: number(baseValues.lifecycleEpoch),
380
+ });
381
+ let allowTaskOperationRevalidation = false;
382
+ const kernel = createCorrectiveReplanCredentialKernel({
383
+ snapshotSource: { readCorrectiveReplanSnapshot(requestedTaskId: string) {
384
+ return requestedTaskId === taskId
385
+ ? trustedSnapshot(options, taskId, candidate, preparedAtMs, allowTaskOperationRevalidation)
386
+ : null;
387
+ } },
388
+ coreClock: { nowMs: () => clockMs },
389
+ idSource: { nextId: ids },
390
+ challengeCodeSource: { issueConfirmationCode: () => code },
391
+ validationEventTailSource: { readPlanValidationTail(requestedTaskId: string) {
392
+ if (requestedTaskId !== taskId) return null;
393
+ const tail = previousEventHashOverride === undefined
394
+ ? currentValidationTail(options.store, options.store.taskRoot(options.projectId, taskId))
395
+ : previousEventHashOverride;
396
+ return tail === null ? null : { eventHash: tail };
397
+ } },
398
+ });
399
+ const validation = accepted(kernel.issuePlanValidation({ taskId, draft: validationDraft(initialSnapshot, candidate.validation) }), 'Plan Validation issuance was rejected.');
400
+ const challenge = accepted(kernel.issueHumanChallenge({ planValidation: validation.handle }), 'Human challenge issuance was rejected.');
401
+ const delivery = record(challenge.delivery);
402
+ const binding = record(validation.binding);
403
+ const attestation = record(validation.attestation);
404
+ const values = record(binding.values);
405
+ const gateBase = {
406
+ kind: 'corrective-replan-human-gate' as const,
407
+ version: 1 as const,
408
+ taskId,
409
+ preparedAtMs,
410
+ expiresAtMs: number(delivery.expiresAtMs),
411
+ candidatePlanHash: string(values.candidatePlanHash),
412
+ bindingSha256: string(binding.bindingSha256),
413
+ attestationId: string(attestation.attestationId),
414
+ attestationHash: string(attestation.attestationHash),
415
+ challengeId: string(delivery.challengeId),
416
+ runtimeFingerprint: string(values.runtimeFingerprint),
417
+ catalogHash: string(values.catalogHash),
418
+ lifecycleEpoch: number(values.lifecycleEpoch),
419
+ };
420
+ const gate = deepFreeze({ ...gateBase, preparationSha256: hashCanonical(gateBase) });
421
+ return {
422
+ kernel,
423
+ clock,
424
+ gate,
425
+ delivery,
426
+ planValidation: validation.handle as PlanValidationHandle,
427
+ challenge: challenge.handle as HumanChallengeHandle,
428
+ enableTaskOperationRevalidation() { allowTaskOperationRevalidation = true; },
429
+ };
430
+ }
431
+
432
+ function confirmChain(
433
+ options: ControllerOptions,
434
+ taskId: string,
435
+ candidate: CorrectiveReplanPublicCandidate,
436
+ credential: CorrectiveReplanPublicCredential,
437
+ previousEventHash: string | null,
438
+ ): ConfirmedChain {
439
+ const chain = buildChain(options, taskId, candidate, credential.gate.preparedAtMs, previousEventHash);
440
+ if (!same(chain.gate, credential.gate)) throw new WorkflowError('STATE_CONFLICT', 'Corrective-replan Human gate no longer matches current state.');
441
+ const receiptCandidate = record(credential.receipt);
442
+ const confirmedAtMs = number(get(receiptCandidate, '/human/confirmedAtMs'));
443
+ const issuedAtMs = number(receiptCandidate.issuedAtMs);
444
+ if (!Number.isSafeInteger(confirmedAtMs) || confirmedAtMs !== issuedAtMs) throw new WorkflowError('INVALID_ARGUMENT', 'Corrective-replan receipt time is invalid.');
445
+ chain.clock.set(confirmedAtMs);
446
+ const confirmation = accepted(chain.kernel.confirmHuman({
447
+ challenge: chain.challenge,
448
+ confirmation: {
449
+ kind: 'corrective-replan-human-confirmation', version: 1,
450
+ challengeId: chain.delivery.challengeId,
451
+ confirmationCode: deterministicCode(chain.gate),
452
+ },
453
+ }), 'Human confirmation reconstruction was rejected.');
454
+ const receipt = accepted(chain.kernel.issueReceipt({
455
+ planValidation: chain.planValidation,
456
+ confirmation: confirmation.handle,
457
+ }), 'Receipt reconstruction was rejected.');
458
+ if (!same(receipt.receipt, credential.receipt)) throw new WorkflowError('STATE_CONFLICT', 'Corrective-replan receipt is not the exact Core materialization.');
459
+ const eventCandidate = record(credential.planValidationEvent);
460
+ const recordedAtMs = number(eventCandidate.recordedAtMs);
461
+ if (!Number.isSafeInteger(recordedAtMs) || recordedAtMs !== issuedAtMs) throw new WorkflowError('INVALID_ARGUMENT', 'Plan Validation event time is invalid.');
462
+ chain.clock.set(recordedAtMs);
463
+ const event = accepted(chain.kernel.materializeStoredEvent({ receipt: receipt.handle }), 'Plan Validation event reconstruction was rejected.');
464
+ if (!same(event.event, credential.planValidationEvent)) throw new WorkflowError('STATE_CONFLICT', 'Plan Validation event is not the exact Core materialization.');
465
+ return {
466
+ ...chain,
467
+ confirmation: confirmation.handle as VerifiedConfirmationHandle,
468
+ receipt: receipt.handle as ReceiptHandle,
469
+ receiptValue: receipt.receipt as JsonRecord,
470
+ event: event.handle as StoredPlanValidationEventHandle,
471
+ eventValue: event.event as JsonRecord,
472
+ };
473
+ }
474
+
475
+ function trustedSnapshot(
476
+ options: ControllerOptions,
477
+ taskId: string,
478
+ candidate: CorrectiveReplanPublicCandidate,
479
+ preparedAtMs: number,
480
+ allowTaskOperationRevalidation = false,
481
+ ): JsonRecord {
482
+ const projectRoot = options.store.projectRoot(options.projectId);
483
+ assertTaskTransactionBoundary(projectRoot, options.store.root, taskId);
484
+ assertCorrectiveYieldTaskTransactionBoundary(projectRoot, options.store.root, taskId);
485
+ const taskOperation = listCoreTaskOperationMutexes(options.store.lockRoot(options.projectId), options.store.root)
486
+ .find((operation) => operation.entityId === taskId);
487
+ if (taskOperation && !allowTaskOperationRevalidation) {
488
+ throw new WorkflowError('LOCKED', 'A Core Task operation already owns corrective-replan preparation.', {
489
+ taskId,
490
+ operationId: taskOperation.operationId,
491
+ transitionId: taskOperation.transitionId,
492
+ expiresAt: taskOperation.expiresAt,
493
+ });
494
+ }
495
+ const task = options.store.readTask(options.projectId, taskId);
496
+ validateCandidate(task, candidate);
497
+ const taskRoot = options.store.taskRoot(options.projectId, taskId);
498
+ if (options.store.hashArtifact(taskRoot, 'brief.md') !== task.briefHash
499
+ || !task.planHash || options.store.hashArtifact(taskRoot, 'plan.md') !== task.planHash) {
500
+ throw new WorkflowError('STATE_CORRUPT', 'Corrective-replan Task artifact binding is stale.');
501
+ }
502
+ const knowledge = options.store.readKnowledgeMap(options.projectId);
503
+ const knowledgeHash = hashKnowledgeMap(knowledge);
504
+ if (candidate.plan.knowledgeMapRevision !== knowledge.revision || candidate.plan.knowledgeMapHash !== knowledgeHash) {
505
+ throw new WorkflowError('TRANSITION_BLOCKED', 'Corrective-replan candidate does not bind the active Project Knowledge Map.');
506
+ }
507
+ const gitHead = headCommit(options.repositoryRoot);
508
+ const audit = readCorrectivePlanningAudit(options.store, task);
509
+ if (!audit) throw new WorkflowError('TRANSITION_BLOCKED', 'Corrective replan requires a current Plan Risk Audit.');
510
+ const classified = findCurrentGuardedRemediationPosture(options.store, task, audit);
511
+ const decisions = readCorrectiveDecisionEvents(options.store, task);
512
+ const remediations = readRemediationEvents(options.store, task);
513
+ const posture = classified?.posture === 'replan-required'
514
+ ? classified
515
+ : identifyCorrectiveReplanPosture(task, audit.reviewRequiredStepIds, decisions, remediations);
516
+ if (!posture || posture.posture !== 'replan-required' || !posture.correctiveDecision) {
517
+ throw new WorkflowError('TRANSITION_BLOCKED', 'Task is not in the exact corrective-replan posture.');
518
+ }
519
+ if (readTaskC1Posture(options.store, task).state !== 'none') {
520
+ throw new WorkflowError('TRANSITION_BLOCKED', 'Corrective replan requires corrective-yield before preparation.');
521
+ }
522
+ const writerLease = inspectWriterLease(options.store.lockRoot(options.projectId), task.id, options.store.root);
523
+ if (writerLease) {
524
+ throw new WorkflowError('TRANSITION_BLOCKED', 'Corrective replan requires the active writer lease to be released before preparation.', {
525
+ taskId: task.id,
526
+ leaseOwner: writerLease.owner,
527
+ expiresAt: writerLease.expiresAt,
528
+ });
529
+ }
530
+ const availability = evaluateTaskCorrectiveReplanAvailability(task, posture);
531
+ if (!availability.allowed) throw new WorkflowError('TRANSITION_BLOCKED', 'Corrective-replan availability guards failed.', { guardResults: availability.guardResults });
532
+ const prepared = prepareCorrectiveReplanCandidate(task, candidate.plan);
533
+ const candidateEvaluation = evaluateTaskCorrectiveReplanCandidate(task, posture, prepared);
534
+ if (!candidateEvaluation.allowed) throw new WorkflowError('TRANSITION_BLOCKED', 'Corrective-replan candidate guards failed.', { guardResults: candidateEvaluation.guardResults });
535
+ const worktree = guardedStepWorktreeSnapshot(options.store, options.repositoryRoot, task, candidate.plan, posture.stepId, gitHead);
536
+ const matchingRemediations = remediations.filter((event) => event.stepId === posture.stepId);
537
+ const remediationTail = matchingRemediations.at(-1);
538
+ if (!remediationTail || matchingRemediations.length !== posture.attemptCount) throw new WorkflowError('STATE_CORRUPT', 'Corrective-replan remediation tail is ambiguous.');
539
+ assertCorrectiveReplanKnowledgeTransition(
540
+ options.repositoryRoot,
541
+ task,
542
+ posture.stepId,
543
+ gitHead,
544
+ matchingRemediations.map((event) => event.eventId),
545
+ knowledge.revision,
546
+ knowledgeHash,
547
+ );
548
+ const planningRecovery = readFailedStepPlanningRecovery(options.store, task);
549
+ const activeGrant = planningRecovery ? [planningRecovery.priorAuthorization] : task.authorizations.filter((authorization) => authorization.kind === 'execution'
550
+ && authorization.decision === 'approved' && authorization.planHash === task.planHash && authorization.briefHash === task.briefHash);
551
+ if (activeGrant.length !== 1) throw new WorkflowError('TRANSITION_BLOCKED', 'Corrective replan requires one exact prior Plan authorization.');
552
+ const authority = candidate.authority;
553
+ const baseValues: JsonRecord = {
554
+ transitionId: 'task.plan.corrective-replan',
555
+ runtimeFingerprint: taskCorrectiveReplanDefinition.runtimeSemanticFingerprint,
556
+ catalogHash: correctiveCatalogHash,
557
+ lifecycleEpoch: LIFECYCLE_EPOCH,
558
+ taskId: task.id,
559
+ revision: task.revision,
560
+ currentPlanHash: task.planHash,
561
+ briefHash: task.briefHash,
562
+ knowledgeRevision: task.knowledgeMapRevision,
563
+ knowledgeHash: task.knowledgeMapHash,
564
+ targetKnowledgeRevision: knowledge.revision,
565
+ targetKnowledgeHash: knowledgeHash,
566
+ gitHead,
567
+ guardedStepId: posture.stepId,
568
+ correctiveDecisionEventId: posture.correctiveDecision.eventId,
569
+ correctiveDecisionHash: posture.correctiveDecision.eventHash,
570
+ attemptCount: posture.attemptCount,
571
+ remediationTailHash: remediationTail.eventHash,
572
+ candidatePlanHash: prepared.planHash,
573
+ candidateStepSetHash: correctiveReplanStepSetHash(prepared.stepIds),
574
+ planAuthorIdentityHash: authority.planAuthorIdentityHash,
575
+ planValidatorIdentityHash: authority.planValidatorIdentityHash,
576
+ riskAuditHash: '',
577
+ riskAuditorIdentityHash: authority.riskAuditorIdentityHash,
578
+ actorIdentityHash: authority.actorIdentityHash,
579
+ actorRole: authority.actorRole,
580
+ humanPrincipalIdentityHash: authority.humanPrincipalIdentityHash,
581
+ capabilityId: 'capability.task.corrective-replan',
582
+ capabilityVersion: 1,
583
+ };
584
+ baseValues.riskAuditHash = correctiveReplanRiskAuditHash(baseValues, candidate.riskAudit);
585
+ const requirements = coverage(task.requirements, candidate.plan.requirements, 'requirements');
586
+ const acceptance = coverage(task.acceptance, candidate.plan.acceptance, 'acceptance');
587
+ const architecture = {
588
+ sourceHash: hashCanonical({ domain: 'codex-workflow/corrective-replan-architecture-source/v1',
589
+ baseBranch: task.baseBranch, baseCommit: task.baseCommit, briefHash: task.briefHash, currentPlanHash: task.planHash }),
590
+ compatibilityHash: hashCanonical({ domain: 'codex-workflow/corrective-replan-architecture-compatibility/v1',
591
+ graphUse: candidate.plan.graphUse, knowledgeTargets: candidate.plan.knowledgeTargets,
592
+ steps: candidate.plan.steps.map(({ id, dependencies, allowedWrites, forbiddenScope }) => ({ id, dependencies, allowedWrites, forbiddenScope })) }),
593
+ passed: true,
594
+ };
595
+ const plannerContextHash = hashCanonical({
596
+ domain: 'codex-workflow/corrective-replan-planner-context/v1',
597
+ taskId: task.id,
598
+ revision: task.revision,
599
+ candidatePlanHash: prepared.planHash,
600
+ planAuthorIdentityHash: authority.planAuthorIdentityHash,
601
+ });
602
+ return {
603
+ snapshotVersion: 2,
604
+ bindingManifest: correctiveReplanBindingManifest,
605
+ worktree,
606
+ baseValues,
607
+ validationExpectation: {
608
+ source: {
609
+ taskId: task.id, revision: task.revision, currentPlanHash: task.planHash,
610
+ briefHash: task.briefHash, knowledgeRevision: task.knowledgeMapRevision,
611
+ knowledgeHash: task.knowledgeMapHash, gitHead,
612
+ guardedStepId: posture.stepId, correctiveDecisionHash: posture.correctiveDecision.eventHash,
613
+ remediationTailHash: remediationTail.eventHash,
614
+ },
615
+ candidate: { planHash: prepared.planHash, stepSetHash: correctiveReplanStepSetHash(prepared.stepIds) },
616
+ requirements,
617
+ acceptance,
618
+ architecture,
619
+ planAuthor: { identityHash: authority.planAuthorIdentityHash },
620
+ plannerContext: { contextHash: plannerContextHash, capturedAtMs: preparedAtMs },
621
+ validator: { identityHash: authority.planValidatorIdentityHash },
622
+ },
623
+ priorPlanAuthorization: { grantHash: hashCanonical(activeGrant[0]), requiredDisposition: 'superseded' },
624
+ };
625
+ }
626
+
627
+ function assertCorrectiveReplanKnowledgeTransition(
628
+ repositoryRoot: string,
629
+ task: TaskState,
630
+ stepId: string,
631
+ gitHead: string,
632
+ remediationEventIds: readonly string[],
633
+ targetKnowledgeRevision: number,
634
+ targetKnowledgeHash: string,
635
+ ): void {
636
+ if (task.knowledgeMapRevision === targetKnowledgeRevision && task.knowledgeMapHash === targetKnowledgeHash) return;
637
+ const compatibility = [...(task.dependencyProvenanceRecoveries ?? [])].reverse().find((record) =>
638
+ record.commitSha === gitHead
639
+ && record.packageVersion === PACKAGE_VERSION
640
+ && task.systemCommits.includes(record.commitSha)
641
+ && record.planIntegrity?.stepId === stepId
642
+ && record.planIntegrity.planHash === task.planHash
643
+ && same(record.planIntegrity.remediationEventIds, remediationEventIds)
644
+ && same(record.planIntegrity.dirtyFiles, changedFiles(repositoryRoot).sort())
645
+ && record.planIntegrity.dirtyWorktreeHash === hashDirtyWorktree(
646
+ repositoryRoot,
647
+ record.planIntegrity.dirtyFiles,
648
+ ));
649
+ if (!compatibility) {
650
+ throw new WorkflowError(
651
+ 'TRANSITION_BLOCKED',
652
+ 'Corrective-replan source and target Project Knowledge bindings differ without exact Plan-integrity update compatibility.',
653
+ {
654
+ taskId: task.id,
655
+ sourceKnowledgeRevision: task.knowledgeMapRevision,
656
+ targetKnowledgeRevision,
657
+ },
658
+ );
659
+ }
660
+ }
661
+
662
+ function guardedStepWorktreeSnapshot(
663
+ store: FileStateStore,
664
+ repositoryRoot: string,
665
+ task: TaskState,
666
+ candidatePlan: TaskPlanInput,
667
+ guardedStepId: string,
668
+ gitHead: string,
669
+ ): JsonRecord {
670
+ const currentStep = task.steps.find((step) => step.id === guardedStepId);
671
+ const candidateStep = candidatePlan.steps.find((step) => step.id === guardedStepId);
672
+ if (!currentStep || !candidateStep) {
673
+ throw new WorkflowError('TRANSITION_BLOCKED', `Corrective replan must retain guarded Step ${guardedStepId}.`);
674
+ }
675
+ const files = changedFiles(repositoryRoot).sort();
676
+ const currentAllowedWrites = effectiveStepAllowedWrites(store, task, guardedStepId);
677
+ const unexpected = files.filter((file) => !pathAllowed(file, currentAllowedWrites)
678
+ || !pathAllowed(file, candidateStep.allowedWrites)
679
+ || pathAllowed(file, currentStep.forbiddenScope)
680
+ || pathAllowed(file, candidateStep.forbiddenScope));
681
+ if (unexpected.length > 0) {
682
+ throw new WorkflowError('GIT_PRECONDITION_FAILED', 'Corrective replan found changes outside the guarded Step boundary.', {
683
+ taskId: task.id,
684
+ stepId: guardedStepId,
685
+ changedFiles: unexpected,
686
+ });
687
+ }
688
+ const entries = files.map((file) => {
689
+ const absolute = path.resolve(repositoryRoot, file);
690
+ const relative = path.relative(repositoryRoot, absolute).replaceAll('\\', '/');
691
+ if (!relative || relative.startsWith('../') || path.isAbsolute(relative)) {
692
+ throw new WorkflowError('GIT_PRECONDITION_FAILED', 'Corrective replan found an invalid changed path.', { file });
693
+ }
694
+ if (!existsSync(absolute)) return { path: relative, disposition: 'deleted' };
695
+ const stat = lstatSync(absolute);
696
+ if (!stat.isFile()) {
697
+ throw new WorkflowError('GIT_PRECONDITION_FAILED', 'Corrective replan accepts only regular-file Step changes.', { file: relative });
698
+ }
699
+ return {
700
+ path: relative,
701
+ disposition: 'present',
702
+ executable: (stat.mode & 0o111) !== 0,
703
+ contentSha256: createHash('sha256').update(readFileSync(absolute)).digest('hex'),
704
+ };
705
+ });
706
+ return {
707
+ kind: 'guarded-step-worktree',
708
+ version: 1,
709
+ gitHead,
710
+ changeSha256: hashCanonical({ domain: 'codex-workflow/corrective-replan-worktree/v1', entries }),
711
+ entries,
712
+ };
713
+ }
714
+
715
+ function validateCandidate(task: TaskState, candidate: CorrectiveReplanPublicCandidate): void {
716
+ validatePlan(candidate.plan);
717
+ for (const requirement of task.requirements) if (!candidate.plan.requirements.includes(requirement)) {
718
+ throw new WorkflowError('TRANSITION_BLOCKED', `Replacement Plan omits Task requirement ${requirement}.`);
719
+ }
720
+ for (const acceptance of task.acceptance) if (!candidate.plan.acceptance.includes(acceptance)) {
721
+ throw new WorkflowError('TRANSITION_BLOCKED', `Replacement Plan omits Task acceptance ${acceptance}.`);
722
+ }
723
+ const authority = candidate.authority;
724
+ const identities = [authority.planAuthorIdentityHash, authority.planValidatorIdentityHash, authority.riskAuditorIdentityHash,
725
+ authority.actorIdentityHash, authority.humanPrincipalIdentityHash];
726
+ if (!identities.every((value) => HASH.test(value))
727
+ || !['delivery-coordinator', 'technical-planner'].includes(authority.actorRole)
728
+ || authority.planAuthorIdentityHash === authority.planValidatorIdentityHash
729
+ || authority.planAuthorIdentityHash === authority.riskAuditorIdentityHash
730
+ || authority.planAuthorIdentityHash === authority.humanPrincipalIdentityHash) {
731
+ throw new WorkflowError('TRANSITION_BLOCKED', 'Corrective-replan authority tuple is invalid.');
732
+ }
733
+ const dispositions = new Map(candidate.validation.dispositions.map((entry) => [entry.findingId, entry.decision]));
734
+ if (candidate.validation.findings.some((finding) => finding.severity === 'high'
735
+ && dispositions.get(finding.findingId) !== 'resolved')) {
736
+ throw new WorkflowError('TRANSITION_BLOCKED', 'High-severity Plan Validation findings must be resolved.');
737
+ }
738
+ }
739
+
740
+ function assertPublicCandidate(candidate: unknown): asserts candidate is CorrectiveReplanPublicCandidate {
741
+ if (!validateCorrectiveReplanPublicValue('candidate', candidate)) {
742
+ throw new WorkflowError('INVALID_ARGUMENT', 'Corrective-replan candidate is invalid.');
743
+ }
744
+ }
745
+
746
+ function validationDraft(snapshot: JsonRecord, evidence: CorrectiveReplanValidationEvidence): JsonRecord {
747
+ const expected = record(snapshot.validationExpectation);
748
+ return {
749
+ schemaVersion: 1,
750
+ requirements: structuredClone(expected.requirements),
751
+ acceptance: structuredClone(expected.acceptance),
752
+ architecture: structuredClone(expected.architecture),
753
+ validatorContext: { contextHash: evidence.validatorContextHash },
754
+ passed: true,
755
+ findings: structuredClone(evidence.findings),
756
+ dispositions: structuredClone(evidence.dispositions),
757
+ };
758
+ }
759
+
760
+ function coverage(required: readonly string[], candidate: readonly string[], kind: string): JsonRecord {
761
+ return {
762
+ sourceHash: hashCanonical({ domain: `codex-workflow/corrective-replan-${kind}-source/v1`, values: [...required] }),
763
+ coverageHash: hashCanonical({ domain: `codex-workflow/corrective-replan-${kind}-coverage/v1`, required: [...required], candidate: [...candidate] }),
764
+ passed: true,
765
+ };
766
+ }
767
+
768
+ function assertPublicCredential(taskId: string, credential: CorrectiveReplanPublicCredential): void {
769
+ if (!validateCorrectiveReplanPublicValue('credential', credential)
770
+ || !exactKeys(credential, ['kind', 'version', 'gate', 'receipt', 'planValidationEvent'])
771
+ || credential.kind !== 'corrective-replan-public-credential' || credential.version !== 1
772
+ || credential.gate.taskId !== taskId
773
+ || validateSchema('schema.corrective-replan-receipt', credential.receipt) !== true
774
+ || validateSchema('schema.corrective-replan-stored-plan-validation-event', credential.planValidationEvent) !== true) {
775
+ throw new WorkflowError('INVALID_ARGUMENT', 'Corrective-replan public credential is invalid.');
776
+ }
777
+ }
778
+
779
+ function assertGateTime(gate: CorrectiveReplanHumanGate, nowMs: number): void {
780
+ if (!validateCorrectiveReplanPublicValue('humanGate', gate)
781
+ || !exactKeys(gate, ['kind', 'version', 'taskId', 'preparedAtMs', 'expiresAtMs', 'candidatePlanHash', 'bindingSha256',
782
+ 'attestationId', 'attestationHash', 'challengeId', 'runtimeFingerprint', 'catalogHash', 'lifecycleEpoch', 'preparationSha256'])) {
783
+ throw new WorkflowError('INVALID_ARGUMENT', 'Corrective-replan Human gate is malformed.');
784
+ }
785
+ const { preparationSha256, ...base } = gate;
786
+ if (preparationSha256 !== hashCanonical(base) || !Number.isSafeInteger(nowMs)
787
+ || nowMs < gate.preparedAtMs || nowMs >= gate.expiresAtMs || gate.expiresAtMs - gate.preparedAtMs > TTL_MS) {
788
+ throw new WorkflowError('TRANSITION_BLOCKED', 'Corrective-replan Human gate is expired or invalid.');
789
+ }
790
+ }
791
+
792
+ function validatedEventPredecessor(store: FileStateStore, taskRoot: string, candidate: unknown): string | null {
793
+ const event = record(candidate);
794
+ const previous = event.previousEventHash;
795
+ if (!(previous === null || HASH.test(string(previous)))) throw new WorkflowError('INVALID_ARGUMENT', 'Plan Validation event predecessor is invalid.');
796
+ const events = validationEvents(store, taskRoot);
797
+ const tail = events.at(-1) as unknown as JsonRecord | undefined;
798
+ if ((tail?.eventHash ?? null) === previous) return previous as string | null;
799
+ if (tail?.eventHash === event.eventHash && same(tail, event)) return previous as string | null;
800
+ throw new WorkflowError('STATE_CONFLICT', 'Plan Validation event predecessor no longer matches current evidence.');
801
+ }
802
+
803
+ function currentValidationTail(store: FileStateStore, taskRoot: string): string | null {
804
+ return stringOrNull((validationEvents(store, taskRoot).at(-1) as unknown as JsonRecord | undefined)?.eventHash ?? null);
805
+ }
806
+
807
+ function validationEvents(store: FileStateStore, taskRoot: string): SidecarEventRecord[] {
808
+ return store.readSidecarEvents(taskRoot, 'plan-validations.jsonl', {
809
+ validateEvent(event) {
810
+ if (validateSchema('schema.corrective-replan-stored-plan-validation-event', event) !== true) {
811
+ throw new WorkflowError('STATE_CORRUPT', 'Stored Plan Validation event is invalid.');
812
+ }
813
+ },
814
+ });
815
+ }
816
+
817
+ function validateSchema(id: string, value: unknown): boolean {
818
+ return correctiveReplanCredentialSchemaValidators.find((entry) => entry.id === id)?.callable(value) === true;
819
+ }
820
+
821
+ function deterministicCode(gate: CorrectiveReplanHumanGate): string {
822
+ return publicConfirmationCode(gate);
823
+ }
824
+
825
+ function publicConfirmationCode(gate: Pick<CorrectiveReplanHumanGate,
826
+ 'taskId' | 'preparedAtMs' | 'candidatePlanHash' | 'runtimeFingerprint' | 'catalogHash' | 'lifecycleEpoch'>): string {
827
+ // The public code is an audited same-principal acknowledgement, not an authentication secret.
828
+ // Reconstructing it is deliberately confined to the Core controller and never emitted after prepare.
829
+ const seed = hashCanonical({
830
+ domain: 'codex-workflow/corrective-replan-public-preparation/v1',
831
+ taskId: gate.taskId,
832
+ preparedAtMs: gate.preparedAtMs,
833
+ candidatePlanHash: gate.candidatePlanHash,
834
+ runtimeFingerprint: gate.runtimeFingerprint,
835
+ catalogHash: gate.catalogHash,
836
+ lifecycleEpoch: gate.lifecycleEpoch,
837
+ });
838
+ return String(Number.parseInt(seed.slice(0, 12), 16) % 1_000_000).padStart(6, '0');
839
+ }
840
+
841
+ function inertKernel(): CorrectiveReplanCredentialKernel {
842
+ return createCorrectiveReplanCredentialKernel({
843
+ snapshotSource: { readCorrectiveReplanSnapshot: () => null },
844
+ coreClock: { nowMs: () => 0 },
845
+ idSource: { nextId: () => 'unreachable' },
846
+ challengeCodeSource: { issueConfirmationCode: () => '000000' },
847
+ validationEventTailSource: { readPlanValidationTail: () => null },
848
+ });
849
+ }
850
+
851
+ function accepted(value: unknown, message: string): JsonRecord {
852
+ const result = value as { ok?: boolean; value?: JsonRecord | null; issues?: unknown };
853
+ if (result.ok !== true || !result.value) throw new WorkflowError('TRANSITION_BLOCKED', message, { issues: result.issues ?? [] });
854
+ return result.value;
855
+ }
856
+
857
+ function ownRequest<T>(input: T): T {
858
+ const copied = copyOwned(input, new Set<object>(), 0, { nodes: 0, bytes: 0 });
859
+ if (copied === null) throw new WorkflowError('INVALID_ARGUMENT', 'Corrective-replan request must be a bounded plain JSON value.');
860
+ return copied as T;
861
+ }
862
+
863
+ function copyOwned(
864
+ value: unknown,
865
+ active: Set<object>,
866
+ depth: number,
867
+ budget: { nodes: number; bytes: number },
868
+ ): unknown | null {
869
+ if (++budget.nodes > OWNED_JSON_MAX_NODES || (budget.bytes += 8) > OWNED_JSON_MAX_BYTES || depth > 32) return null;
870
+ if (typeof value === 'string') {
871
+ budget.bytes += Buffer.byteLength(value, 'utf8');
872
+ return budget.bytes > OWNED_JSON_MAX_BYTES ? null : value;
873
+ }
874
+ if (value === null || typeof value === 'boolean') return value;
875
+ if (typeof value === 'number') return Number.isSafeInteger(value) ? value : null;
876
+ if (typeof value !== 'object' || utilTypes.isProxy(value) || active.has(value)) return null;
877
+ const isArray = Array.isArray(value);
878
+ if (isArray && value.length > OWNED_JSON_MAX_NODES) return null;
879
+ const prototype = Object.getPrototypeOf(value);
880
+ if (prototype !== (isArray ? Array.prototype : Object.prototype) && prototype !== null) return null;
881
+ const descriptors = Object.getOwnPropertyDescriptors(value);
882
+ const keys = Reflect.ownKeys(value).filter((key) => !isArray || key !== 'length');
883
+ if (keys.some((key) => typeof key !== 'string') || (isArray && keys.length !== value.length)) return null;
884
+ const output: unknown[] | JsonRecord = isArray ? [] : Object.create(null);
885
+ active.add(value);
886
+ for (const key of keys as string[]) {
887
+ const descriptor = descriptors[key];
888
+ budget.bytes += Buffer.byteLength(key, 'utf8');
889
+ if (budget.bytes > OWNED_JSON_MAX_BYTES) return null;
890
+ if (!descriptor?.enumerable || !('value' in descriptor)) return null;
891
+ const copied = copyOwned(descriptor.value, active, depth + 1, budget);
892
+ if (copied === null && descriptor.value !== null) return null;
893
+ (output as JsonRecord)[key] = copied;
894
+ }
895
+ active.delete(value);
896
+ return output;
897
+ }
898
+
899
+ function exactKeys(value: unknown, keys: readonly string[]): value is JsonRecord {
900
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
901
+ && Object.keys(value).sort().join('\0') === [...keys].sort().join('\0');
902
+ }
903
+ function record(value: unknown): JsonRecord { return value !== null && typeof value === 'object' && !Array.isArray(value) ? value as JsonRecord : {}; }
904
+ function get(value: unknown, pointer: string): unknown { return pointer.split('/').filter(Boolean).reduce((current: unknown, key) => record(current)[key], value); }
905
+ function same(left: unknown, right: unknown): boolean { try { return hashCanonical(left) === hashCanonical(right); } catch { return false; } }
906
+ function string(value: unknown): string { return typeof value === 'string' ? value : ''; }
907
+ function number(value: unknown): number { return typeof value === 'number' ? value : Number.NaN; }
908
+ function stringOrNull(value: unknown): string | null { return value === null ? null : string(value); }