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,730 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import {
3
+ closeSync,
4
+ constants,
5
+ existsSync,
6
+ fsyncSync,
7
+ lstatSync,
8
+ mkdirSync,
9
+ openSync,
10
+ readFileSync,
11
+ readdirSync,
12
+ renameSync,
13
+ rmSync,
14
+ writeSync,
15
+ } from 'node:fs';
16
+ import path from 'node:path';
17
+ import { Ajv2020 } from 'ajv/dist/2020.js';
18
+ import type { CorruptObservedTaskTransaction, ObservedTaskTransaction } from '../contracts.js';
19
+ import { WorkflowError } from '../errors.js';
20
+ import { assertNoSymlinkPathComponents, computeSidecarEventHash } from '../alpha6/store-sidecars.js';
21
+ import { hashCanonical } from '../lifecycle/fingerprint.js';
22
+ import { deepFreeze } from '../lifecycle/immutable.js';
23
+ import {
24
+ WriterLockManager,
25
+ assertCoreTaskOperationHandle,
26
+ type CoreTaskOperationHandle,
27
+ type WriterLeaseHandle,
28
+ } from './lock.js';
29
+
30
+ const HASH = /^[a-f0-9]{64}$/;
31
+ const TASK = /^TASK-[0-9A-HJKMNP-TV-Z]{26}$/;
32
+ const PROJECT = /^[a-f0-9]{24}$/;
33
+ const TRANSITION = 'task.coordination.corrective-yield' as const;
34
+ const schema = JSON.parse(readFileSync(
35
+ new URL('../../../schemas/corrective-yield-task-transaction.private.schema.json', import.meta.url),
36
+ 'utf8',
37
+ )) as object;
38
+ const ajv = new Ajv2020({
39
+ allErrors: true,
40
+ strict: true,
41
+ formats: {
42
+ 'date-time': {
43
+ type: 'string',
44
+ validate(value: string) {
45
+ const parsed = Date.parse(value);
46
+ return Number.isFinite(parsed) && new Date(parsed).toISOString() === value;
47
+ },
48
+ },
49
+ },
50
+ });
51
+ if (!ajv.validateSchema(schema)) throw new TypeError(`Invalid corrective-yield transaction schema: ${ajv.errorsText(ajv.errors)}`);
52
+ const validateSchema = ajv.compile(schema);
53
+
54
+ export type CorrectiveYieldTransactionPhase = 'intent' | 'applying' | 'published-pending-readback' | 'completed';
55
+
56
+ interface SnapshotRecord {
57
+ exists: boolean;
58
+ contentSha256: string | null;
59
+ byteLength: number;
60
+ contentPath: 'preimages/0.bin' | null;
61
+ }
62
+
63
+ export interface CorrectiveYieldTaskTransactionJournal {
64
+ schemaVersion: 1;
65
+ kind: 'corrective-yield-task-transaction';
66
+ transactionId: string;
67
+ projectId: string;
68
+ taskId: string;
69
+ transitionId: typeof TRANSITION;
70
+ taskStateSha256: string;
71
+ actor: string;
72
+ writerLeaseTokenSha256: string;
73
+ writerLeaseContentSha256: string;
74
+ writerLeaseExpiresAt: string;
75
+ admittedAt: string;
76
+ intentSha256: string;
77
+ phase: CorrectiveYieldTransactionPhase;
78
+ phaseRevision: number;
79
+ nextTargetOrdinal: number;
80
+ handoffTarget: {
81
+ targetId: 'handoff-event';
82
+ ordinal: 0;
83
+ relativePath: 'handoffs.jsonl';
84
+ preimage: SnapshotRecord;
85
+ postimage: { contentSha256: string; byteLength: number; contentPath: 'targets/0.bin' };
86
+ previousEventHash: string | null;
87
+ resultEventHash: string;
88
+ };
89
+ phaseHistory: Array<{
90
+ sequence: number;
91
+ phase: CorrectiveYieldTransactionPhase;
92
+ nextTargetOrdinal: number;
93
+ recordedAt: string;
94
+ previousEntryHash: string | null;
95
+ entryHash: string;
96
+ }>;
97
+ completedResult: { resultSha256: string } | null;
98
+ updatedAt: string;
99
+ }
100
+
101
+ export interface CreateCorrectiveYieldIntentInput {
102
+ projectId: string;
103
+ taskId: string;
104
+ transactionId: string;
105
+ taskStateSha256: string;
106
+ actor: string;
107
+ writerLeaseTokenSha256: string;
108
+ writerLeaseContentSha256: string;
109
+ writerLeaseExpiresAt: string;
110
+ handoffPostimage: Uint8Array;
111
+ }
112
+
113
+ export class CorrectiveYieldTaskTransactionStore {
114
+ private readonly taskRoot: string;
115
+ private readonly locksRoot: string;
116
+ private readonly trustedRoot: string;
117
+ private readonly now: () => Date;
118
+ private readonly writerLocks: WriterLockManager;
119
+
120
+ constructor(options: { taskRoot: string; locksRoot: string; trustedRoot: string; now?: () => Date }) {
121
+ this.taskRoot = path.resolve(options.taskRoot);
122
+ this.locksRoot = path.resolve(options.locksRoot);
123
+ this.trustedRoot = path.resolve(options.trustedRoot);
124
+ this.now = options.now ?? (() => new Date());
125
+ this.writerLocks = new WriterLockManager(this.locksRoot, this.now, this.trustedRoot);
126
+ assertTaskRoot(this.taskRoot, this.trustedRoot);
127
+ }
128
+
129
+ createIntent(writerHandle: WriterLeaseHandle, input: CreateCorrectiveYieldIntentInput): CorrectiveYieldTaskTransactionJournal {
130
+ assertExactRecord(input, [
131
+ 'actor', 'handoffPostimage', 'projectId', 'taskId', 'taskStateSha256', 'transactionId',
132
+ 'writerLeaseContentSha256', 'writerLeaseExpiresAt', 'writerLeaseTokenSha256',
133
+ ], 'Corrective-yield intent');
134
+ assertIntent(input);
135
+ const proof = this.writerLocks.proofBinding(writerHandle);
136
+ if (proof.entityId !== input.taskId || proof.owner !== input.actor
137
+ || proof.tokenSha256 !== input.writerLeaseTokenSha256
138
+ || proof.contentSha256 !== input.writerLeaseContentSha256
139
+ || proof.expiresAt !== input.writerLeaseExpiresAt) {
140
+ throw new WorkflowError('LOCK_TOKEN_INVALID', 'Writer lease proof does not match the corrective-yield intent.');
141
+ }
142
+ const stateBytes = readRequired(this.targetPath('state.json'), this.taskRoot);
143
+ if (sha256(stateBytes) !== input.taskStateSha256) {
144
+ throw new WorkflowError('STATE_CONFLICT', 'Corrective-yield Task snapshot changed before intent.');
145
+ }
146
+ const preimage = readOptional(this.targetPath('handoffs.jsonl'), this.taskRoot);
147
+ const postimage = Buffer.from(input.handoffPostimage);
148
+ const anchors = validateAppend(preimage, postimage, input.transactionId, input.actor, input.writerLeaseTokenSha256);
149
+ const root = this.transactionRoot(input.transactionId);
150
+ if (existsSync(root)) throw new WorkflowError('STATE_CONFLICT', 'Corrective-yield transaction already exists.');
151
+ const admittedAt = this.now().toISOString();
152
+ const journal: CorrectiveYieldTaskTransactionJournal = {
153
+ schemaVersion: 1,
154
+ kind: 'corrective-yield-task-transaction',
155
+ transactionId: input.transactionId,
156
+ projectId: input.projectId,
157
+ taskId: input.taskId,
158
+ transitionId: TRANSITION,
159
+ taskStateSha256: input.taskStateSha256,
160
+ actor: input.actor,
161
+ writerLeaseTokenSha256: input.writerLeaseTokenSha256,
162
+ writerLeaseContentSha256: input.writerLeaseContentSha256,
163
+ writerLeaseExpiresAt: input.writerLeaseExpiresAt,
164
+ admittedAt,
165
+ intentSha256: '0'.repeat(64),
166
+ phase: 'intent',
167
+ phaseRevision: 1,
168
+ nextTargetOrdinal: 0,
169
+ handoffTarget: {
170
+ targetId: 'handoff-event',
171
+ ordinal: 0,
172
+ relativePath: 'handoffs.jsonl',
173
+ preimage: snapshot(preimage),
174
+ postimage: { contentSha256: sha256(postimage), byteLength: postimage.byteLength, contentPath: 'targets/0.bin' },
175
+ ...anchors,
176
+ },
177
+ phaseHistory: [],
178
+ completedResult: null,
179
+ updatedAt: admittedAt,
180
+ };
181
+ journal.intentSha256 = intentHash(journal);
182
+ journal.phaseHistory = [phaseEntry(0, 'intent', 0, admittedAt, null, journal.intentSha256)];
183
+ validateJournal(journal, this.taskRoot, false);
184
+ ensureDirectorySafe(path.dirname(root), this.trustedRoot);
185
+ let created = false;
186
+ try {
187
+ mkdirSync(root, { mode: 0o700 });
188
+ created = true;
189
+ mkdirSync(path.join(root, 'preimages'), { mode: 0o700 });
190
+ mkdirSync(path.join(root, 'targets'), { mode: 0o700 });
191
+ if (preimage) writeNewDurable(path.join(root, 'preimages', '0.bin'), preimage);
192
+ writeNewDurable(path.join(root, 'targets', '0.bin'), postimage);
193
+ this.writerLocks.proofBinding(writerHandle);
194
+ writeNewDurable(this.journalPath(input.transactionId), jsonBytes(journal));
195
+ fsyncDirectory(root);
196
+ return this.read(input.transactionId);
197
+ } catch (error) {
198
+ if (created && !existsSync(this.journalPath(input.transactionId))) rmSync(root, { recursive: true, force: true });
199
+ throw error;
200
+ }
201
+ }
202
+
203
+ read(transactionId: string): CorrectiveYieldTaskTransactionJournal {
204
+ assertHash(transactionId, 'Corrective-yield transaction id');
205
+ const file = this.journalPath(transactionId);
206
+ assertNoSymlinkPathComponents(file, { label: 'Corrective-yield journal', trustedRoot: this.trustedRoot, requireLeaf: true });
207
+ if (!existsSync(file)) throw new WorkflowError('NOT_FOUND', `Corrective-yield journal not found: ${transactionId}`);
208
+ let value: unknown;
209
+ try { value = JSON.parse(readFileSync(file, 'utf8')); } catch { throw new WorkflowError('STATE_CORRUPT', 'Corrective-yield journal is not valid JSON.'); }
210
+ validateJournal(value, this.taskRoot, true);
211
+ const journal = value as CorrectiveYieldTaskTransactionJournal;
212
+ if (journal.transactionId !== transactionId || journal.taskId !== path.basename(this.taskRoot)) {
213
+ throw new WorkflowError('STATE_CORRUPT', 'Corrective-yield journal path identity mismatch.');
214
+ }
215
+ return deepFreeze(structuredClone(journal));
216
+ }
217
+
218
+ journalPath(transactionId: string): string {
219
+ assertHash(transactionId, 'Corrective-yield transaction id');
220
+ return path.join(this.transactionRoot(transactionId), 'journal.json');
221
+ }
222
+
223
+ advanceWithWriter(
224
+ writerHandle: WriterLeaseHandle,
225
+ transactionId: string,
226
+ expectedPhase: CorrectiveYieldTransactionPhase,
227
+ expectedRevision: number,
228
+ nextPhase: CorrectiveYieldTransactionPhase,
229
+ nextTargetOrdinal: number,
230
+ ): CorrectiveYieldTaskTransactionJournal {
231
+ const journal = this.read(transactionId);
232
+ const proof = this.writerLocks.proofBinding(writerHandle);
233
+ if (proof.entityId !== journal.taskId || proof.tokenSha256 !== journal.writerLeaseTokenSha256) {
234
+ throw new WorkflowError('LOCK_TOKEN_INVALID', 'Writer proof does not own the corrective-yield transaction.');
235
+ }
236
+ return this.advance(journal, expectedPhase, expectedRevision, nextPhase, nextTargetOrdinal);
237
+ }
238
+
239
+ advanceWithCore(
240
+ handle: CoreTaskOperationHandle,
241
+ transactionId: string,
242
+ expectedPhase: CorrectiveYieldTransactionPhase,
243
+ expectedRevision: number,
244
+ nextPhase: CorrectiveYieldTransactionPhase,
245
+ nextTargetOrdinal: number,
246
+ completedResultSha256?: string,
247
+ ): CorrectiveYieldTaskTransactionJournal {
248
+ const journal = this.read(transactionId);
249
+ assertCoreTaskOperationHandle(this.locksRoot, this.trustedRoot, handle, this.now(), {
250
+ entityId: journal.taskId,
251
+ operationId: journal.transactionId,
252
+ transitionId: journal.transitionId,
253
+ });
254
+ return this.advance(journal, expectedPhase, expectedRevision, nextPhase, nextTargetOrdinal, completedResultSha256);
255
+ }
256
+
257
+ applyHandoffWithWriter(writerHandle: WriterLeaseHandle, transactionId: string): 'applied' | 'confirmed' {
258
+ const journal = this.read(transactionId);
259
+ const proof = this.writerLocks.proofBinding(writerHandle);
260
+ if (proof.entityId !== journal.taskId || proof.tokenSha256 !== journal.writerLeaseTokenSha256) {
261
+ throw new WorkflowError('LOCK_TOKEN_INVALID', 'Writer proof does not own the corrective-yield transaction.');
262
+ }
263
+ return this.applyHandoff(journal);
264
+ }
265
+
266
+ applyHandoffWithCore(handle: CoreTaskOperationHandle, transactionId: string): 'applied' | 'confirmed' {
267
+ const journal = this.read(transactionId);
268
+ assertCoreTaskOperationHandle(this.locksRoot, this.trustedRoot, handle, this.now(), {
269
+ entityId: journal.taskId,
270
+ operationId: journal.transactionId,
271
+ transitionId: journal.transitionId,
272
+ });
273
+ return this.applyHandoff(journal);
274
+ }
275
+
276
+ domainReadback(handle: CoreTaskOperationHandle, transactionId: string): string {
277
+ const journal = this.read(transactionId);
278
+ assertCoreTaskOperationHandle(this.locksRoot, this.trustedRoot, handle, this.now(), {
279
+ entityId: journal.taskId,
280
+ operationId: journal.transactionId,
281
+ transitionId: journal.transitionId,
282
+ });
283
+ if (journal.phase !== 'published-pending-readback') {
284
+ throw new WorkflowError('TASK_RECOVERY_REQUIRED', 'Corrective-yield domain readback requires its published phase.');
285
+ }
286
+ assertCanonicalHandoff(this.taskRoot, journal);
287
+ assertTaskState(this.taskRoot, journal.taskStateSha256);
288
+ return resultHash(journal);
289
+ }
290
+
291
+ terminalReadback(transactionId: string): string {
292
+ const journal = this.read(transactionId);
293
+ if (journal.phase !== 'completed' || !journal.completedResult) {
294
+ throw new WorkflowError('TASK_RECOVERY_REQUIRED', 'Corrective-yield transaction is not terminal.');
295
+ }
296
+ assertCanonicalHandoffRetained(this.taskRoot, journal);
297
+ if (existsSync(path.join(this.locksRoot, `${journal.taskId}.json`))) {
298
+ throw new WorkflowError('STATE_CONFLICT', 'Corrective-yield terminal readback requires the writer/Core lock slot to be empty.');
299
+ }
300
+ const result = resultHash(journal);
301
+ if (result !== journal.completedResult.resultSha256) {
302
+ throw new WorkflowError('STATE_CORRUPT', 'Corrective-yield completed result digest mismatch.');
303
+ }
304
+ return result;
305
+ }
306
+
307
+ handoffIsPostimage(journal: CorrectiveYieldTaskTransactionJournal): boolean {
308
+ const current = readOptional(this.targetPath('handoffs.jsonl'), this.taskRoot);
309
+ return current !== null && sha256(current) === journal.handoffTarget.postimage.contentSha256;
310
+ }
311
+
312
+ private applyHandoff(journal: CorrectiveYieldTaskTransactionJournal): 'applied' | 'confirmed' {
313
+ if (journal.phase !== 'applying' || journal.nextTargetOrdinal !== 0) {
314
+ throw new WorkflowError('TRANSITION_BLOCKED', 'Corrective-yield handoff event is not the exact next target.');
315
+ }
316
+ assertTaskState(this.taskRoot, journal.taskStateSha256);
317
+ const file = this.targetPath('handoffs.jsonl');
318
+ const current = readOptional(file, this.taskRoot);
319
+ const currentHash = current ? sha256(current) : null;
320
+ if (currentHash === journal.handoffTarget.postimage.contentSha256) return 'confirmed';
321
+ if (currentHash !== journal.handoffTarget.preimage.contentSha256) {
322
+ throw new WorkflowError('STATE_CONFLICT', 'Task handoff target found neither exact preimage nor exact yield result.');
323
+ }
324
+ const owned = readOwned(this.taskRoot, journal);
325
+ writeAtomicDurable(file, owned);
326
+ assertCanonicalHandoff(this.taskRoot, journal);
327
+ return 'applied';
328
+ }
329
+
330
+ private advance(
331
+ current: CorrectiveYieldTaskTransactionJournal,
332
+ expectedPhase: CorrectiveYieldTransactionPhase,
333
+ expectedRevision: number,
334
+ nextPhase: CorrectiveYieldTransactionPhase,
335
+ nextTargetOrdinal: number,
336
+ completedResultSha256?: string,
337
+ ): CorrectiveYieldTaskTransactionJournal {
338
+ if (current.phase !== expectedPhase || current.phaseRevision !== expectedRevision) {
339
+ throw new WorkflowError('STATE_CONFLICT', 'Corrective-yield phase compare-and-set conflict.');
340
+ }
341
+ assertPhaseTransition(current, nextPhase, nextTargetOrdinal);
342
+ if (nextTargetOrdinal >= 1) assertCanonicalHandoff(this.taskRoot, current);
343
+ if (nextPhase === 'completed') assertHash(completedResultSha256, 'Corrective-yield completed result hash');
344
+ else if (completedResultSha256 !== undefined) throw new WorkflowError('INVALID_ARGUMENT', 'Only completed yield may bind a result.');
345
+ const recordedAt = this.now().toISOString();
346
+ const previous = current.phaseHistory.at(-1)!;
347
+ const entry = phaseEntry(current.phaseRevision, nextPhase, nextTargetOrdinal, recordedAt, previous.entryHash, current.intentSha256);
348
+ const next: CorrectiveYieldTaskTransactionJournal = {
349
+ ...structuredClone(current),
350
+ phase: nextPhase,
351
+ phaseRevision: current.phaseRevision + 1,
352
+ nextTargetOrdinal,
353
+ phaseHistory: [...current.phaseHistory, entry],
354
+ completedResult: nextPhase === 'completed' ? { resultSha256: completedResultSha256! } : current.completedResult,
355
+ updatedAt: recordedAt,
356
+ };
357
+ validateJournal(next, this.taskRoot, true);
358
+ writeAtomicDurable(this.journalPath(current.transactionId), jsonBytes(next));
359
+ return this.read(current.transactionId);
360
+ }
361
+
362
+ private transactionRoot(transactionId: string): string {
363
+ assertHash(transactionId, 'Corrective-yield transaction id');
364
+ return path.join(this.taskRoot, '.yield-transactions', transactionId);
365
+ }
366
+
367
+ private targetPath(relative: string): string {
368
+ return path.join(this.taskRoot, relative);
369
+ }
370
+ }
371
+
372
+ export function inspectCorrectiveYieldTaskTransactions(
373
+ projectRoot: string,
374
+ trustedRoot: string,
375
+ ): { pending: ObservedTaskTransaction[]; completed: ObservedTaskTransaction[]; corrupt: CorruptObservedTaskTransaction[] } {
376
+ const pending: ObservedTaskTransaction[] = [];
377
+ const completed: ObservedTaskTransaction[] = [];
378
+ const corrupt: CorruptObservedTaskTransaction[] = [];
379
+ const tasksRoot = path.join(path.resolve(projectRoot), 'tasks');
380
+ if (!existsSync(tasksRoot)) return deepFreeze({ pending, completed, corrupt });
381
+ assertNoSymlinkPathComponents(tasksRoot, { label: 'Corrective-yield observation root', trustedRoot, requireLeaf: true });
382
+ for (const task of readdirSync(tasksRoot, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
383
+ if (task.isSymbolicLink()) throw new WorkflowError('STATE_CORRUPT', 'Task observation root must not be a symlink.');
384
+ if (!task.isDirectory() || !TASK.test(task.name)) continue;
385
+ const taskRoot = path.join(tasksRoot, task.name);
386
+ const root = path.join(taskRoot, '.yield-transactions');
387
+ if (!existsSync(root)) continue;
388
+ assertNoSymlinkPathComponents(root, { label: 'Corrective-yield transaction root', trustedRoot, requireLeaf: true });
389
+ for (const entry of readdirSync(root, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
390
+ const transactionId = HASH.test(entry.name) ? entry.name : 'invalid-transaction-id';
391
+ if (entry.isSymbolicLink() || !entry.isDirectory() || !HASH.test(entry.name)) {
392
+ corrupt.push({ taskId: task.name, transactionId, reason: 'journal-invalid' });
393
+ continue;
394
+ }
395
+ try {
396
+ const journal = new CorrectiveYieldTaskTransactionStore({
397
+ taskRoot,
398
+ locksRoot: path.join(projectRoot, 'locks'),
399
+ trustedRoot,
400
+ }).read(entry.name);
401
+ (journal.phase === 'completed' ? completed : pending).push({
402
+ transitionId: journal.transitionId,
403
+ taskId: journal.taskId,
404
+ transactionId: journal.transactionId,
405
+ phase: journal.phase,
406
+ phaseRevision: journal.phaseRevision,
407
+ updatedAt: journal.updatedAt,
408
+ });
409
+ } catch {
410
+ corrupt.push({ taskId: task.name, transactionId, reason: 'journal-invalid' });
411
+ }
412
+ }
413
+ }
414
+ return deepFreeze({
415
+ completed: completed.sort(orderObserved),
416
+ pending: pending.sort(orderObserved),
417
+ corrupt: corrupt.sort(orderObserved),
418
+ });
419
+ }
420
+
421
+ export function assertCorrectiveYieldTaskTransactionBoundary(
422
+ projectRoot: string,
423
+ trustedRoot: string,
424
+ taskId?: string,
425
+ ): void {
426
+ const observed = inspectCorrectiveYieldTaskTransactions(projectRoot, trustedRoot);
427
+ const corrupt = taskId ? observed.corrupt.filter((row) => row.taskId === taskId) : observed.corrupt;
428
+ if (corrupt.length > 0) throw new WorkflowError('STATE_CORRUPT', 'A corrective-yield journal failed integrity validation.', { transactions: corrupt });
429
+ const pending = taskId ? observed.pending.filter((row) => row.taskId === taskId) : observed.pending;
430
+ if (pending.length > 0) throw new WorkflowError('TASK_RECOVERY_REQUIRED', 'A corrective-yield transaction requires explicit recovery.', {
431
+ taskIds: [...new Set(pending.map((row) => row.taskId))].sort(),
432
+ transactionIds: pending.map((row) => row.transactionId).sort(),
433
+ });
434
+ }
435
+
436
+ function validateJournal(value: unknown, taskRoot: string, verifyOwned: boolean): asserts value is CorrectiveYieldTaskTransactionJournal {
437
+ if (validateSchema(value) !== true) throw new WorkflowError('STATE_CORRUPT', 'Corrective-yield transaction schema is invalid.');
438
+ const journal = value as CorrectiveYieldTaskTransactionJournal;
439
+ if (journal.intentSha256 !== intentHash(journal)) throw new WorkflowError('STATE_CORRUPT', 'Corrective-yield intent hash mismatch.');
440
+ if (journal.phaseHistory.length !== journal.phaseRevision) throw new WorkflowError('STATE_CORRUPT', 'Corrective-yield phase revision mismatch.');
441
+ journal.phaseHistory.forEach((entry, index) => {
442
+ const previous = index === 0 ? null : journal.phaseHistory[index - 1]!.entryHash;
443
+ if (entry.sequence !== index || entry.previousEntryHash !== previous
444
+ || entry.entryHash !== phaseEntryHash(index, entry.phase, entry.nextTargetOrdinal, entry.recordedAt, previous, journal.intentSha256)
445
+ || (index > 0 && entry.recordedAt < journal.phaseHistory[index - 1]!.recordedAt)) {
446
+ throw new WorkflowError('STATE_CORRUPT', 'Corrective-yield phase history is invalid.');
447
+ }
448
+ if (index === 0 && (entry.phase !== 'intent' || entry.nextTargetOrdinal !== 0)) {
449
+ throw new WorkflowError('STATE_CORRUPT', 'Corrective-yield phase history must start at intent.');
450
+ }
451
+ if (index > 0) {
452
+ const prior = journal.phaseHistory[index - 1]!;
453
+ const legal = (prior.phase === 'intent' && entry.phase === 'applying' && prior.nextTargetOrdinal === 0 && entry.nextTargetOrdinal === 0)
454
+ || (prior.phase === 'applying' && entry.phase === 'applying' && entry.nextTargetOrdinal === prior.nextTargetOrdinal + 1 && entry.nextTargetOrdinal <= 2)
455
+ || (prior.phase === 'applying' && prior.nextTargetOrdinal === 2 && entry.phase === 'published-pending-readback' && entry.nextTargetOrdinal === 2)
456
+ || (prior.phase === 'published-pending-readback' && entry.phase === 'completed' && entry.nextTargetOrdinal === 2);
457
+ if (!legal) throw new WorkflowError('STATE_CORRUPT', 'Corrective-yield phase topology is invalid.');
458
+ }
459
+ });
460
+ const last = journal.phaseHistory.at(-1)!;
461
+ if (last.phase !== journal.phase || last.nextTargetOrdinal !== journal.nextTargetOrdinal || last.recordedAt !== journal.updatedAt
462
+ || (journal.phase === 'completed') !== (journal.completedResult !== null)) {
463
+ throw new WorkflowError('STATE_CORRUPT', 'Corrective-yield current phase projection is invalid.');
464
+ }
465
+ if (journal.phaseRevision > 6 || Date.parse(journal.admittedAt) >= Date.parse(journal.writerLeaseExpiresAt)) {
466
+ throw new WorkflowError('STATE_CORRUPT', 'Corrective-yield timing or phase cardinality is invalid.');
467
+ }
468
+ const before = journal.handoffTarget.preimage;
469
+ if (before.exists !== (before.contentSha256 !== null) || before.exists !== (before.contentPath !== null)
470
+ || (before.exists ? before.byteLength < 1 : before.byteLength !== 0)) {
471
+ throw new WorkflowError('STATE_CORRUPT', 'Corrective-yield preimage ownership is invalid.');
472
+ }
473
+ if (journal.completedResult && journal.completedResult.resultSha256 !== resultHash(journal)) {
474
+ throw new WorkflowError('STATE_CORRUPT', 'Corrective-yield completed result formula mismatch.');
475
+ }
476
+ if (verifyOwned) {
477
+ const owned = readOwned(taskRoot, journal);
478
+ const preimage = readOwnedPreimage(taskRoot, journal);
479
+ const anchors = validateAppend(preimage, owned, journal.transactionId, journal.actor, journal.writerLeaseTokenSha256);
480
+ if (anchors.previousEventHash !== journal.handoffTarget.previousEventHash
481
+ || anchors.resultEventHash !== journal.handoffTarget.resultEventHash) {
482
+ throw new WorkflowError('STATE_CORRUPT', 'Corrective-yield handoff anchors mismatch.');
483
+ }
484
+ if (journal.phase === 'completed') {
485
+ assertCanonicalHandoffRetained(taskRoot, journal);
486
+ }
487
+ }
488
+ }
489
+
490
+ function assertPhaseTransition(current: CorrectiveYieldTaskTransactionJournal, phase: CorrectiveYieldTransactionPhase, ordinal: number): void {
491
+ const allowed = (current.phase === 'intent' && phase === 'applying' && ordinal === 0)
492
+ || (current.phase === 'applying' && phase === 'applying' && ordinal === current.nextTargetOrdinal + 1 && ordinal <= 2)
493
+ || (current.phase === 'applying' && current.nextTargetOrdinal === 2 && phase === 'published-pending-readback' && ordinal === 2)
494
+ || (current.phase === 'published-pending-readback' && phase === 'completed' && ordinal === 2);
495
+ if (!allowed) throw new WorkflowError('INVALID_ARGUMENT', 'Invalid corrective-yield phase transition.');
496
+ }
497
+
498
+ function validateAppend(
499
+ before: Buffer | null,
500
+ after: Buffer,
501
+ transactionId: string,
502
+ actor: string,
503
+ tokenSha256: string,
504
+ ): { previousEventHash: string | null; resultEventHash: string } {
505
+ if ((before && !after.subarray(0, before.byteLength).equals(before)) || after.byteLength <= (before?.byteLength ?? 0)) {
506
+ throw new WorkflowError('INVALID_ARGUMENT', 'Corrective-yield handoff postimage must append exact bytes.');
507
+ }
508
+ const beforeLines = jsonLines(before);
509
+ const afterLines = jsonLines(after);
510
+ if (afterLines.length !== beforeLines.length + 1) throw new WorkflowError('INVALID_ARGUMENT', 'Corrective-yield must append exactly one handoff event.');
511
+ const event = afterLines.at(-1)!;
512
+ const previousEventHash = beforeLines.length === 0 ? null : eventHash(beforeLines.at(-1)!);
513
+ if (event.eventId !== `HOF-${transactionId}` || event.eventType !== 'yield' || event.actor !== actor
514
+ || event.writerLeaseTokenHash !== tokenSha256 || event.targetActor !== null || event.handback !== null
515
+ || event.previousEventHash !== previousEventHash) {
516
+ throw new WorkflowError('INVALID_ARGUMENT', 'Corrective-yield event does not bind the exact claimant transaction.');
517
+ }
518
+ return { previousEventHash, resultEventHash: eventHash(event) };
519
+ }
520
+
521
+ function intentHash(journal: CorrectiveYieldTaskTransactionJournal): string {
522
+ const { intentSha256: _intent, phase: _phase, phaseRevision: _revision, nextTargetOrdinal: _ordinal,
523
+ phaseHistory: _history, completedResult: _completed, updatedAt: _updated, ...intent } = journal;
524
+ return hashCanonical({ domain: 'codex-workflow/corrective-yield-intent/v1', intent });
525
+ }
526
+
527
+ function resultHash(journal: CorrectiveYieldTaskTransactionJournal): string {
528
+ return hashCanonical({
529
+ domain: 'codex-workflow/corrective-yield-result/v1',
530
+ transactionId: journal.transactionId,
531
+ intentSha256: journal.intentSha256,
532
+ taskStateSha256: journal.taskStateSha256,
533
+ handoffSha256: journal.handoffTarget.postimage.contentSha256,
534
+ yieldEventHash: journal.handoffTarget.resultEventHash,
535
+ writerLeaseReleased: true,
536
+ });
537
+ }
538
+
539
+ function phaseEntry(
540
+ sequence: number,
541
+ phase: CorrectiveYieldTransactionPhase,
542
+ nextTargetOrdinal: number,
543
+ recordedAt: string,
544
+ previousEntryHash: string | null,
545
+ intentSha256: string,
546
+ ) {
547
+ return { sequence, phase, nextTargetOrdinal, recordedAt, previousEntryHash,
548
+ entryHash: phaseEntryHash(sequence, phase, nextTargetOrdinal, recordedAt, previousEntryHash, intentSha256) };
549
+ }
550
+
551
+ function phaseEntryHash(
552
+ sequence: number,
553
+ phase: CorrectiveYieldTransactionPhase,
554
+ nextTargetOrdinal: number,
555
+ recordedAt: string,
556
+ previousEntryHash: string | null,
557
+ intentSha256: string,
558
+ ): string {
559
+ return hashCanonical({ domain: 'codex-workflow/corrective-yield-phase/v1', sequence, phase, nextTargetOrdinal, recordedAt, previousEntryHash, intentSha256 });
560
+ }
561
+
562
+ function snapshot(content: Buffer | null): SnapshotRecord {
563
+ return content === null
564
+ ? { exists: false, contentSha256: null, byteLength: 0, contentPath: null }
565
+ : { exists: true, contentSha256: sha256(content), byteLength: content.byteLength, contentPath: 'preimages/0.bin' };
566
+ }
567
+
568
+ function assertIntent(input: CreateCorrectiveYieldIntentInput): void {
569
+ if (!PROJECT.test(input.projectId) || !TASK.test(input.taskId) || !HASH.test(input.transactionId)
570
+ || !HASH.test(input.taskStateSha256) || !HASH.test(input.writerLeaseTokenSha256)
571
+ || !HASH.test(input.writerLeaseContentSha256) || !input.actor.trim() || input.actor.length > 256
572
+ || !(input.handoffPostimage instanceof Uint8Array) || input.handoffPostimage.byteLength === 0
573
+ || input.handoffPostimage.byteLength > 1_048_576) {
574
+ throw new WorkflowError('INVALID_ARGUMENT', 'Corrective-yield intent is invalid.');
575
+ }
576
+ assertIso(input.writerLeaseExpiresAt, 'Corrective-yield writer lease expiry');
577
+ }
578
+
579
+ function assertExactRecord(value: unknown, keys: string[], label: string): asserts value is Record<string, unknown> {
580
+ if (!value || typeof value !== 'object' || Array.isArray(value) || Object.getPrototypeOf(value) !== Object.prototype) {
581
+ throw new WorkflowError('INVALID_ARGUMENT', `${label} must be a plain object.`);
582
+ }
583
+ const actual = Object.keys(value).sort();
584
+ const expected = [...keys].sort();
585
+ if (actual.length !== expected.length || actual.some((key, index) => key !== expected[index])) {
586
+ throw new WorkflowError('INVALID_ARGUMENT', `${label} has unknown or missing fields.`);
587
+ }
588
+ }
589
+
590
+ function assertCanonicalHandoff(taskRoot: string, journal: CorrectiveYieldTaskTransactionJournal): void {
591
+ const current = readOptional(path.join(taskRoot, journal.handoffTarget.relativePath), taskRoot);
592
+ if (!current || sha256(current) !== journal.handoffTarget.postimage.contentSha256) {
593
+ throw new WorkflowError('STATE_CONFLICT', 'Corrective-yield handoff target does not match its exact result.');
594
+ }
595
+ }
596
+
597
+ function assertCanonicalHandoffRetained(taskRoot: string, journal: CorrectiveYieldTaskTransactionJournal): void {
598
+ const current = readOptional(path.join(taskRoot, journal.handoffTarget.relativePath), taskRoot);
599
+ const owned = readOwned(taskRoot, journal);
600
+ if (!current || current.byteLength < owned.byteLength
601
+ || !current.subarray(0, owned.byteLength).equals(owned)) {
602
+ throw new WorkflowError('STATE_CONFLICT', 'Corrective-yield handoff result is not retained in canonical history.');
603
+ }
604
+ jsonLines(current);
605
+ }
606
+
607
+ function assertTaskState(taskRoot: string, expectedHash: string): void {
608
+ if (sha256(readRequired(path.join(taskRoot, 'state.json'), taskRoot)) !== expectedHash) {
609
+ throw new WorkflowError('STATE_CONFLICT', 'Task state changed across corrective-yield.');
610
+ }
611
+ }
612
+
613
+ function readOwned(taskRoot: string, journal: CorrectiveYieldTaskTransactionJournal): Buffer {
614
+ const file = path.join(taskRoot, '.yield-transactions', journal.transactionId, journal.handoffTarget.postimage.contentPath);
615
+ const content = readRequired(file, taskRoot);
616
+ if (content.byteLength !== journal.handoffTarget.postimage.byteLength || sha256(content) !== journal.handoffTarget.postimage.contentSha256) {
617
+ throw new WorkflowError('STATE_CORRUPT', 'Corrective-yield owned postimage is corrupt.');
618
+ }
619
+ return content;
620
+ }
621
+
622
+ function readOwnedPreimage(taskRoot: string, journal: CorrectiveYieldTaskTransactionJournal): Buffer | null {
623
+ if (!journal.handoffTarget.preimage.exists) return null;
624
+ const file = path.join(taskRoot, '.yield-transactions', journal.transactionId, journal.handoffTarget.preimage.contentPath!);
625
+ const content = readRequired(file, taskRoot);
626
+ if (content.byteLength !== journal.handoffTarget.preimage.byteLength || sha256(content) !== journal.handoffTarget.preimage.contentSha256) {
627
+ throw new WorkflowError('STATE_CORRUPT', 'Corrective-yield owned preimage is corrupt.');
628
+ }
629
+ return content;
630
+ }
631
+
632
+ function jsonLines(content: Buffer | null): Array<Record<string, unknown>> {
633
+ if (!content) return [];
634
+ const text = content.toString('utf8');
635
+ if (!text.endsWith('\n')) throw new WorkflowError('INVALID_ARGUMENT', 'Corrective-yield handoff bytes must end with newline.');
636
+ const events = text.slice(0, -1).split('\n').map((line) => {
637
+ const value = JSON.parse(line) as unknown;
638
+ if (!value || typeof value !== 'object' || Array.isArray(value)) throw new WorkflowError('INVALID_ARGUMENT', 'Corrective-yield handoff line is invalid.');
639
+ return value as Record<string, unknown>;
640
+ });
641
+ let previousEventHash: string | null = null;
642
+ for (const event of events) {
643
+ if (event.previousEventHash !== previousEventHash
644
+ || event.eventHash !== computeSidecarEventHash(event as never)) {
645
+ throw new WorkflowError('INVALID_ARGUMENT', 'Corrective-yield handoff event chain is invalid.');
646
+ }
647
+ previousEventHash = eventHash(event);
648
+ }
649
+ return events;
650
+ }
651
+
652
+ function eventHash(event: Record<string, unknown>): string {
653
+ if (typeof event.eventHash !== 'string' || !HASH.test(event.eventHash)) throw new WorkflowError('INVALID_ARGUMENT', 'Handoff event hash is invalid.');
654
+ return event.eventHash;
655
+ }
656
+
657
+ function readOptional(file: string, trustedRoot: string): Buffer | null {
658
+ assertNoSymlinkPathComponents(file, { label: 'Corrective-yield target', trustedRoot, requireLeaf: true });
659
+ return existsSync(file) ? readFileSync(file) : null;
660
+ }
661
+
662
+ function readRequired(file: string, trustedRoot: string): Buffer {
663
+ const value = readOptional(file, trustedRoot);
664
+ if (!value) throw new WorkflowError('STATE_CORRUPT', `Corrective-yield required file is missing: ${file}`);
665
+ return value;
666
+ }
667
+
668
+ function writeNewDurable(file: string, content: Buffer): void {
669
+ let descriptor: number | null = null;
670
+ try {
671
+ descriptor = openSync(file, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW, 0o600);
672
+ writeSync(descriptor, content);
673
+ fsyncSync(descriptor);
674
+ } finally { if (descriptor !== null) closeSync(descriptor); }
675
+ }
676
+
677
+ function writeAtomicDurable(file: string, content: Buffer): void {
678
+ const temporary = path.join(path.dirname(file), `.${path.basename(file)}.${randomUUID()}.tmp`);
679
+ let descriptor: number | null = null;
680
+ try {
681
+ descriptor = openSync(temporary, constants.O_CREAT | constants.O_EXCL | constants.O_WRONLY | constants.O_NOFOLLOW, 0o600);
682
+ writeSync(descriptor, content);
683
+ fsyncSync(descriptor);
684
+ closeSync(descriptor);
685
+ descriptor = null;
686
+ renameSync(temporary, file);
687
+ fsyncDirectory(path.dirname(file));
688
+ } finally {
689
+ if (descriptor !== null) closeSync(descriptor);
690
+ rmSync(temporary, { force: true });
691
+ }
692
+ }
693
+
694
+ function ensureDirectorySafe(directory: string, trustedRoot: string): void {
695
+ if (!existsSync(directory)) mkdirSync(directory, { recursive: true, mode: 0o700 });
696
+ assertNoSymlinkPathComponents(directory, { label: 'Corrective-yield transaction directory', trustedRoot, requireLeaf: true });
697
+ }
698
+
699
+ function fsyncDirectory(directory: string): void {
700
+ const descriptor = openSync(directory, 'r');
701
+ try { fsyncSync(descriptor); } finally { closeSync(descriptor); }
702
+ }
703
+
704
+ function assertTaskRoot(taskRoot: string, trustedRoot: string): void {
705
+ const resolvedTask = path.resolve(taskRoot);
706
+ const resolvedTrusted = path.resolve(trustedRoot);
707
+ if (!resolvedTask.startsWith(`${resolvedTrusted}${path.sep}`)) throw new WorkflowError('INVALID_ARGUMENT', 'Corrective-yield Task root is outside trusted state.');
708
+ assertNoSymlinkPathComponents(resolvedTask, { label: 'Corrective-yield Task root', trustedRoot: resolvedTrusted, requireLeaf: true });
709
+ }
710
+
711
+ function assertHash(value: unknown, label: string): asserts value is string {
712
+ if (typeof value !== 'string' || !HASH.test(value)) throw new WorkflowError('INVALID_ARGUMENT', `${label} must be lowercase SHA-256.`);
713
+ }
714
+
715
+ function assertIso(value: string, label: string): void {
716
+ const parsed = Date.parse(value);
717
+ if (!Number.isFinite(parsed) || new Date(parsed).toISOString() !== value) throw new WorkflowError('INVALID_ARGUMENT', `${label} must be canonical UTC.`);
718
+ }
719
+
720
+ function jsonBytes(value: unknown): Buffer {
721
+ return Buffer.from(`${JSON.stringify(value, null, 2)}\n`, 'utf8');
722
+ }
723
+
724
+ function sha256(value: Buffer | string): string {
725
+ return createHash('sha256').update(value).digest('hex');
726
+ }
727
+
728
+ function orderObserved(left: { taskId: string; transactionId: string }, right: { taskId: string; transactionId: string }): number {
729
+ return left.taskId.localeCompare(right.taskId) || left.transactionId.localeCompare(right.transactionId);
730
+ }