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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/README.md +27 -388
  2. package/dist/reviewer-runtime-build.json +89 -29
  3. package/dist/src/alpha6/adoption.d.ts +2 -0
  4. package/dist/src/alpha6/adoption.js +22 -0
  5. package/dist/src/alpha6/adoption.js.map +1 -1
  6. package/dist/src/alpha6/captured-check-evidence.d.ts +51 -0
  7. package/dist/src/alpha6/captured-check-evidence.js +152 -0
  8. package/dist/src/alpha6/captured-check-evidence.js.map +1 -0
  9. package/dist/src/alpha6/component-owner.d.ts +15 -1
  10. package/dist/src/alpha6/component-owner.js +44 -2
  11. package/dist/src/alpha6/component-owner.js.map +1 -1
  12. package/dist/src/alpha6/corrective-decision-boundary.d.ts +4 -0
  13. package/dist/src/alpha6/corrective-decision-boundary.js +47 -0
  14. package/dist/src/alpha6/corrective-decision-boundary.js.map +1 -0
  15. package/dist/src/alpha6/downstream-proof.d.ts +1 -25
  16. package/dist/src/alpha6/downstream-proof.js +0 -171
  17. package/dist/src/alpha6/downstream-proof.js.map +1 -1
  18. package/dist/src/alpha6/literal-test-invocation.d.ts +2 -0
  19. package/dist/src/alpha6/literal-test-invocation.js +106 -0
  20. package/dist/src/alpha6/literal-test-invocation.js.map +1 -0
  21. package/dist/src/alpha6/milestone.d.ts +2 -0
  22. package/dist/src/alpha6/milestone.js +10 -1
  23. package/dist/src/alpha6/milestone.js.map +1 -1
  24. package/dist/src/alpha6/navigation-dirty-carryover.d.ts +11 -0
  25. package/dist/src/alpha6/navigation-dirty-carryover.js +130 -0
  26. package/dist/src/alpha6/navigation-dirty-carryover.js.map +1 -0
  27. package/dist/src/alpha6/plan-integrity.js +7 -5
  28. package/dist/src/alpha6/plan-integrity.js.map +1 -1
  29. package/dist/src/alpha6/remediation.d.ts +6 -21
  30. package/dist/src/alpha6/remediation.js +161 -476
  31. package/dist/src/alpha6/remediation.js.map +1 -1
  32. package/dist/src/alpha6/root-cause-replan-carryover.d.ts +4 -1
  33. package/dist/src/alpha6/root-cause-replan-carryover.js +29 -5
  34. package/dist/src/alpha6/root-cause-replan-carryover.js.map +1 -1
  35. package/dist/src/beta1/project-transaction.d.ts +3 -1
  36. package/dist/src/beta1/project-transaction.js +6 -2
  37. package/dist/src/beta1/project-transaction.js.map +1 -1
  38. package/dist/src/checks/runner.d.ts +26 -0
  39. package/dist/src/checks/runner.js +223 -0
  40. package/dist/src/checks/runner.js.map +1 -0
  41. package/dist/src/checks/task-sync.d.ts +10 -0
  42. package/dist/src/checks/task-sync.js +41 -0
  43. package/dist/src/checks/task-sync.js.map +1 -0
  44. package/dist/src/checks/worker.d.ts +1 -0
  45. package/dist/src/checks/worker.js +154 -0
  46. package/dist/src/checks/worker.js.map +1 -0
  47. package/dist/src/cli-actions.d.ts +2 -2
  48. package/dist/src/cli-actions.js +3 -15
  49. package/dist/src/cli-actions.js.map +1 -1
  50. package/dist/src/cli.js +14 -98
  51. package/dist/src/cli.js.map +1 -1
  52. package/dist/src/contracts.d.ts +35 -32
  53. package/dist/src/dependency-provenance.d.ts +2 -2
  54. package/dist/src/dependency-provenance.js +12 -69
  55. package/dist/src/dependency-provenance.js.map +1 -1
  56. package/dist/src/domain/base-sync-conflict.d.ts +13 -0
  57. package/dist/src/domain/base-sync-conflict.js +32 -0
  58. package/dist/src/domain/base-sync-conflict.js.map +1 -0
  59. package/dist/src/domain/step-start-admission.d.ts +4 -0
  60. package/dist/src/domain/step-start-admission.js +9 -0
  61. package/dist/src/domain/step-start-admission.js.map +1 -0
  62. package/dist/src/domain/virgin-registration.d.ts +3 -0
  63. package/dist/src/domain/virgin-registration.js +65 -0
  64. package/dist/src/domain/virgin-registration.js.map +1 -0
  65. package/dist/src/errors.d.ts +1 -1
  66. package/dist/src/errors.js.map +1 -1
  67. package/dist/src/gateway-handshake.js +0 -8
  68. package/dist/src/gateway-handshake.js.map +1 -1
  69. package/dist/src/git.d.ts +3 -1
  70. package/dist/src/git.js +41 -14
  71. package/dist/src/git.js.map +1 -1
  72. package/dist/src/graph.js +25 -3
  73. package/dist/src/graph.js.map +1 -1
  74. package/dist/src/index.d.ts +1 -0
  75. package/dist/src/navigation-actions.d.ts +9 -0
  76. package/dist/src/navigation-actions.js +72 -0
  77. package/dist/src/navigation-actions.js.map +1 -0
  78. package/dist/src/navigation-update-artifact.d.ts +5 -0
  79. package/dist/src/navigation-update-artifact.js +227 -0
  80. package/dist/src/navigation-update-artifact.js.map +1 -0
  81. package/dist/src/navigation-update.d.ts +39 -0
  82. package/dist/src/navigation-update.js +82 -0
  83. package/dist/src/navigation-update.js.map +1 -0
  84. package/dist/src/observation.js +52 -29
  85. package/dist/src/observation.js.map +1 -1
  86. package/dist/src/observed-routes.js +5 -6
  87. package/dist/src/observed-routes.js.map +1 -1
  88. package/dist/src/pending-review-update.d.ts +0 -13
  89. package/dist/src/pending-review-update.js +1 -6
  90. package/dist/src/pending-review-update.js.map +1 -1
  91. package/dist/src/reviewer.d.ts +1 -1
  92. package/dist/src/reviewer.js +20 -17
  93. package/dist/src/reviewer.js.map +1 -1
  94. package/dist/src/runtime.d.ts +3 -0
  95. package/dist/src/runtime.js +6 -0
  96. package/dist/src/runtime.js.map +1 -0
  97. package/dist/src/state/corrective-replan-executor.d.ts +10 -0
  98. package/dist/src/state/corrective-replan-executor.js +37 -1
  99. package/dist/src/state/corrective-replan-executor.js.map +1 -1
  100. package/dist/src/state/corrective-replan-public-schema.js +15 -2
  101. package/dist/src/state/corrective-replan-public-schema.js.map +1 -1
  102. package/dist/src/state/corrective-replan-public.js +1 -1
  103. package/dist/src/state/corrective-replan-public.js.map +1 -1
  104. package/dist/src/state/corrective-replan-transaction.d.ts +1 -0
  105. package/dist/src/state/corrective-replan-transaction.js +12 -11
  106. package/dist/src/state/corrective-replan-transaction.js.map +1 -1
  107. package/dist/src/state/corrective-yield-transaction.d.ts +1 -0
  108. package/dist/src/state/corrective-yield-transaction.js +11 -10
  109. package/dist/src/state/corrective-yield-transaction.js.map +1 -1
  110. package/dist/src/state/lock.d.ts +4 -0
  111. package/dist/src/state/lock.js +21 -0
  112. package/dist/src/state/lock.js.map +1 -1
  113. package/dist/src/version.d.ts +1 -1
  114. package/dist/src/version.js +1 -1
  115. package/dist/src/version.js.map +1 -1
  116. package/dist/src/workflow-blocker-route.d.ts +8 -0
  117. package/dist/src/workflow-blocker-route.js +106 -0
  118. package/dist/src/workflow-blocker-route.js.map +1 -0
  119. package/dist/src/workflow.d.ts +48 -63
  120. package/dist/src/workflow.js +666 -1460
  121. package/dist/src/workflow.js.map +1 -1
  122. package/docs/autonomy-guardrails.md +26 -304
  123. package/docs/decisions.md +14 -112
  124. package/docs/development-flow.md +35 -238
  125. package/docs/project-memory.md +31 -50
  126. package/docs/release-app-evidence.md +190 -0
  127. package/docs/release.md +95 -388
  128. package/docs/updating-existing-project.md +19 -717
  129. package/package.json +11 -13
  130. package/plugins/codex-workflow-gateway/.codex-plugin/plugin.json +2 -2
  131. package/plugins/codex-workflow-gateway/references/chat-dispatch.md +73 -197
  132. package/plugins/codex-workflow-gateway/references/codebase-memory-routing.md +57 -0
  133. package/plugins/codex-workflow-gateway/references/protocol.md +42 -445
  134. package/plugins/codex-workflow-gateway/scripts/chat-dispatch.mjs +7 -1
  135. package/plugins/codex-workflow-gateway/scripts/chat-model-policy.mjs +10 -8
  136. package/plugins/codex-workflow-gateway/scripts/chat-registry.mjs +23 -5
  137. package/plugins/codex-workflow-gateway/skills/codex-workflow-gateway/SKILL.md +66 -757
  138. package/references/state-machine.md +4 -4
  139. package/roles/technical-planner.md +1 -1
  140. package/schemas/corrective-decision-event.schema.json +3 -1
  141. package/schemas/project-knowledge-map.schema.json +3 -1
  142. package/schemas/remediation-event.schema.json +10 -1
  143. package/schemas/task.schema.json +46 -2
  144. package/schemas/transition-payloads.schema.json +16 -1
  145. package/src/alpha6/adoption.ts +1123 -0
  146. package/src/alpha6/captured-check-evidence.ts +132 -0
  147. package/src/alpha6/check-support-anchor.ts +128 -0
  148. package/src/alpha6/component-owner.ts +168 -0
  149. package/src/alpha6/corrective-decision-boundary.ts +39 -0
  150. package/src/alpha6/downstream-proof.ts +520 -0
  151. package/src/alpha6/failed-step-planning-recovery.ts +88 -0
  152. package/src/alpha6/handoff.ts +1443 -0
  153. package/src/alpha6/journal.ts +473 -0
  154. package/src/alpha6/literal-test-invocation.ts +84 -0
  155. package/src/alpha6/mechanical-feasibility.ts +488 -0
  156. package/src/alpha6/milestone.ts +2192 -0
  157. package/src/alpha6/navigation-dirty-carryover.ts +154 -0
  158. package/src/alpha6/npm-check-contract.ts +47 -0
  159. package/src/alpha6/plan-integrity.ts +298 -0
  160. package/src/alpha6/plan-risk.ts +1480 -0
  161. package/src/alpha6/preexecution-replan.ts +187 -0
  162. package/src/alpha6/remediation-cause.ts +98 -0
  163. package/src/alpha6/remediation.ts +2438 -0
  164. package/src/alpha6/review.ts +1198 -0
  165. package/src/alpha6/root-cause-replan-carryover.ts +491 -0
  166. package/src/alpha6/store-sidecars.ts +335 -0
  167. package/src/alpha7/autonomy.ts +411 -0
  168. package/src/alpha7/corrective-recovery.ts +1332 -0
  169. package/src/artifacts.ts +130 -0
  170. package/src/beta1/project-transaction.ts +355 -0
  171. package/src/change-explanation.ts +153 -0
  172. package/src/checks/runner.ts +245 -0
  173. package/src/checks/task-sync.ts +41 -0
  174. package/src/checks/worker.ts +149 -0
  175. package/src/cli-actions.ts +107 -0
  176. package/src/cli.ts +1457 -0
  177. package/src/contracts.ts +1494 -0
  178. package/src/credential-output.ts +89 -0
  179. package/src/credential-transport.ts +215 -0
  180. package/src/delegation.ts +190 -0
  181. package/src/dependency-provenance.ts +472 -0
  182. package/src/diagnostics.ts +93 -0
  183. package/src/domain/base-sync-conflict.ts +35 -0
  184. package/src/domain/completed-step-carryover.ts +76 -0
  185. package/src/domain/discovery.ts +27 -0
  186. package/src/domain/plan-semantics.ts +58 -0
  187. package/src/domain/step-start-admission.ts +10 -0
  188. package/src/domain/validation.ts +177 -0
  189. package/src/domain/virgin-registration.ts +46 -0
  190. package/src/errors.ts +24 -0
  191. package/src/fs-utils.ts +61 -0
  192. package/src/gateway-handshake.ts +95 -0
  193. package/src/git.ts +183 -0
  194. package/src/graph.ts +342 -0
  195. package/src/historical-step-provenance.ts +136 -0
  196. package/src/index.ts +23 -0
  197. package/src/lifecycle/canonical-hash.ts +28 -0
  198. package/src/lifecycle/catalog.ts +202 -0
  199. package/src/lifecycle/compiler-inspection.ts +29 -0
  200. package/src/lifecycle/core-static-readiness.ts +132 -0
  201. package/src/lifecycle/corrective-replan-authority.ts +136 -0
  202. package/src/lifecycle/corrective-replan-binding-manifest.ts +51 -0
  203. package/src/lifecycle/corrective-replan-credential-core.ts +408 -0
  204. package/src/lifecycle/corrective-replan-credential-schema.ts +54 -0
  205. package/src/lifecycle/corrective-replan-credentials.ts +48 -0
  206. package/src/lifecycle/corrective-replan.ts +843 -0
  207. package/src/lifecycle/evaluator.ts +48 -0
  208. package/src/lifecycle/fingerprint.ts +488 -0
  209. package/src/lifecycle/immutable.ts +8 -0
  210. package/src/lifecycle/implementation-table.ts +118 -0
  211. package/src/lifecycle/index.ts +8 -0
  212. package/src/lifecycle/schema-artifact.ts +263 -0
  213. package/src/lifecycle/semantic-registry.ts +572 -0
  214. package/src/lifecycle/types.ts +838 -0
  215. package/src/memory.ts +273 -0
  216. package/src/migration.ts +161 -0
  217. package/src/navigation-actions.ts +70 -0
  218. package/src/navigation-update-artifact.ts +198 -0
  219. package/src/navigation-update.ts +121 -0
  220. package/src/observation.ts +225 -0
  221. package/src/observed-routes.ts +660 -0
  222. package/src/operational-contract.ts +125 -0
  223. package/src/pending-review-update.ts +175 -0
  224. package/src/repository.ts +99 -0
  225. package/src/reviewer.ts +1879 -0
  226. package/src/runtime.ts +6 -0
  227. package/src/state/corrective-replan-executor.ts +818 -0
  228. package/src/state/corrective-replan-public-schema.ts +83 -0
  229. package/src/state/corrective-replan-public.ts +908 -0
  230. package/src/state/corrective-replan-transaction.ts +949 -0
  231. package/src/state/corrective-yield-executor.ts +327 -0
  232. package/src/state/corrective-yield-transaction.ts +730 -0
  233. package/src/state/lock.ts +902 -0
  234. package/src/state/store.ts +567 -0
  235. package/src/transition-core.ts +330 -0
  236. package/src/ulid.ts +24 -0
  237. package/src/version.ts +2 -0
  238. package/src/workflow-blocker-route.ts +109 -0
  239. package/src/workflow.ts +10172 -0
  240. package/docs/alpha7.1-implementation-brief.md +0 -268
  241. package/docs/alpha7.2-corrective-context-refresh-brief.md +0 -484
  242. package/docs/alpha7.2.1-remediation-recovery-brief.md +0 -86
  243. package/docs/beta1-stabilization-brief.md +0 -165
  244. package/docs/beta11-plan-integrity-recovery-brief.md +0 -38
  245. package/docs/beta13.2-signal-review-recovery.md +0 -38
  246. package/docs/beta2-initial-assembly-navigation-brief.md +0 -616
  247. package/docs/change-model.md +0 -118
  248. package/docs/delegated-approval.md +0 -254
  249. package/docs/lifecycle/state-machine-stabilization.md +0 -641
  250. package/docs/pdf/README.md +0 -24
  251. package/docs/pdf/codex-workflow-v2-architecture-ru.pdf +0 -0
  252. package/docs/pdf/codex-workflow-v2-chat-only-guide-ru.pdf +0 -0
  253. package/docs/pdf/codex-workflow-v2-technical-reference-ru.pdf +0 -0
  254. package/docs/pdf/requirements.txt +0 -1
  255. package/docs/pdf/sources/codex-workflow-v2-architecture-ru.md +0 -478
  256. package/docs/pdf/sources/codex-workflow-v2-chat-only-guide-ru.md +0 -506
  257. package/docs/pdf/sources/codex-workflow-v2-technical-reference-ru.md +0 -778
  258. package/docs/pending-review-update.md +0 -15
  259. package/docs/problem-briefs/01-pre-implementation-integrity.md +0 -482
  260. package/docs/problem-briefs/02-minimal-step-integrity.md +0 -411
  261. package/docs/problem-briefs/03-minimal-agent-context-integrity.md +0 -358
  262. package/docs/problem-briefs/04-task-dependency-and-structural-replacement-integrity.md +0 -573
  263. package/docs/problem-briefs/BRIEF-TEMPLATE.md +0 -56
  264. package/docs/problem-briefs/README.md +0 -120
  265. package/docs/problem-briefs/evidence/p01-mechanical-feasibility-corpus.md +0 -90
  266. package/docs/problem-briefs/evidence/signal-v4-pre-m3-replay.md +0 -246
  267. package/docs/split-required-recovery.md +0 -47
  268. package/docs/stable-release-defect-register.md +0 -730
  269. package/docs/validation-report.md +0 -182
  270. package/scripts/generate-pdf-docs.py +0 -524
  271. package/scripts/run-pdf-docs.mjs +0 -62
@@ -0,0 +1,567 @@
1
+ import { existsSync, lstatSync, readFileSync, readdirSync } from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+ import { createHash } from 'node:crypto';
5
+ import type {
6
+ DiscoveryState,
7
+ DelegationGrant,
8
+ EntityState,
9
+ GraphBinding,
10
+ GraphKind,
11
+ MilestoneTransactionJournal,
12
+ MilestoneTransactionJournalFileSnapshot,
13
+ MilestoneTransactionPhase,
14
+ MilestoneState,
15
+ ProjectRecord,
16
+ ProjectKnowledgeMap,
17
+ RevisionedRecord,
18
+ SidecarEventRecord,
19
+ TaskState,
20
+ } from '../contracts.js';
21
+ import { STATE_SCHEMA_VERSION } from '../contracts.js';
22
+ import { WorkflowError } from '../errors.js';
23
+ import { ensureDirectory, readJson, writeJsonAtomic, writeTextAtomic } from '../fs-utils.js';
24
+ import type { RepositoryIdentity } from '../repository.js';
25
+ import {
26
+ captureJournalTargetSnapshot,
27
+ createJournalSnapshotFromContent,
28
+ listMilestoneTransactionJournals,
29
+ readJournalSnapshotContent,
30
+ readMilestoneTransactionJournal,
31
+ removeMilestoneTransactionJournal,
32
+ resolveMilestoneTransactionDirectory,
33
+ resolveMilestoneTransactionJournalPath,
34
+ resolveMilestoneTransactionTargetPath,
35
+ updateMilestoneTransactionJournalPhase,
36
+ writeMilestoneTransactionJournal,
37
+ type JournalSnapshotMode,
38
+ } from '../alpha6/journal.js';
39
+ import {
40
+ assertNoSymlinkPathComponents,
41
+ prepareSidecarAppend,
42
+ readSidecarEvents as readValidatedSidecarEvents,
43
+ resolveSidecarPath,
44
+ type SidecarEventValidator,
45
+ type SidecarAppendInput,
46
+ } from '../alpha6/store-sidecars.js';
47
+ import { assertTaskTransactionBoundary as assertCorrectiveReplanTaskTransactionBoundary } from './corrective-replan-transaction.js';
48
+ import { assertCorrectiveYieldTaskTransactionBoundary } from './corrective-yield-transaction.js';
49
+ import { inspectCoreTaskOperationMutex } from './lock.js';
50
+
51
+ export interface StateStoreOptions {
52
+ root?: string;
53
+ now?: () => Date;
54
+ }
55
+
56
+ function assertTaskTransactionBoundary(projectRoot: string, trustedRoot: string, taskId?: string): void {
57
+ assertCorrectiveReplanTaskTransactionBoundary(projectRoot, trustedRoot, taskId);
58
+ assertCorrectiveYieldTaskTransactionBoundary(projectRoot, trustedRoot, taskId);
59
+ }
60
+
61
+ export interface StagedArtifact {
62
+ entityRoot: string;
63
+ name: string;
64
+ hash: string;
65
+ stagedFile: string;
66
+ }
67
+
68
+ export interface StagedSidecarAppend<T extends SidecarEventRecord> extends StagedArtifact {
69
+ file: string;
70
+ event: T;
71
+ }
72
+
73
+ export class FileStateStore {
74
+ readonly root: string;
75
+ private readonly now: () => Date;
76
+
77
+ constructor(options: StateStoreOptions = {}) {
78
+ const codexHome = process.env.CODEX_HOME || path.join(os.homedir(), '.codex');
79
+ this.root = path.resolve(
80
+ options.root || process.env.CODEX_WORKFLOW_STATE_HOME || path.join(codexHome, 'workflow-state', 'v2'),
81
+ );
82
+ this.now = options.now ?? (() => new Date());
83
+ }
84
+
85
+ readProject(projectId: string): ProjectRecord {
86
+ const file = path.join(this.projectRoot(projectId), 'project.json');
87
+ if (!existsSync(file)) throw new WorkflowError('NOT_FOUND', `Project state not found: ${file}`);
88
+ const project = readJson<ProjectRecord>(file);
89
+ if (project.schemaVersion !== STATE_SCHEMA_VERSION) {
90
+ throw new WorkflowError('STATE_CORRUPT', `Unsupported project state schema: ${file}`, {
91
+ expectedSchemaVersion: STATE_SCHEMA_VERSION,
92
+ actualSchemaVersion: project.schemaVersion,
93
+ });
94
+ }
95
+ if (project.projectId !== projectId) {
96
+ throw new WorkflowError('STATE_CORRUPT', `Unexpected project identity: ${file}`, {
97
+ expectedProjectId: projectId,
98
+ actualProjectId: project.projectId,
99
+ });
100
+ }
101
+ return project;
102
+ }
103
+
104
+ registerProject(identity: RepositoryIdentity): ProjectRecord {
105
+ const projectRoot = this.projectRoot(identity.projectId);
106
+ ensureDirectory(projectRoot);
107
+ const file = path.join(projectRoot, 'project.json');
108
+ const timestamp = this.now().toISOString();
109
+ if (existsSync(file)) {
110
+ const existing = this.readProject(identity.projectId);
111
+ const repositoryRoots = [...new Set([...existing.repositoryRoots, identity.repositoryRoot])];
112
+ if (repositoryRoots.length === existing.repositoryRoots.length) return existing;
113
+ const updated: ProjectRecord = { ...existing, repositoryRoots, updatedAt: timestamp };
114
+ writeJsonAtomic(file, updated);
115
+ return updated;
116
+ }
117
+ const created: ProjectRecord = {
118
+ schemaVersion: STATE_SCHEMA_VERSION,
119
+ projectId: identity.projectId,
120
+ gitCommonDir: identity.gitCommonDir,
121
+ repositoryRoots: [identity.repositoryRoot],
122
+ createdAt: timestamp,
123
+ updatedAt: timestamp,
124
+ };
125
+ writeJsonAtomic(file, created);
126
+ return created;
127
+ }
128
+
129
+ projectRoot(projectId: string): string {
130
+ this.assertId(projectId, /^[a-f0-9]{24}$/);
131
+ return path.join(this.root, 'projects', projectId);
132
+ }
133
+
134
+ discoveryRoot(projectId: string, discoveryId: string): string {
135
+ this.assertId(discoveryId, /^DISC-[0-9A-HJKMNP-TV-Z]{26}$/);
136
+ return path.join(this.projectRoot(projectId), 'discoveries', discoveryId);
137
+ }
138
+
139
+ taskRoot(projectId: string, taskId: string): string {
140
+ this.assertId(taskId, /^TASK-[0-9A-HJKMNP-TV-Z]{26}$/);
141
+ return path.join(this.projectRoot(projectId), 'tasks', taskId);
142
+ }
143
+
144
+ milestoneRoot(projectId: string, milestoneId: string): string {
145
+ this.assertId(milestoneId, /^MS-[0-9A-HJKMNP-TV-Z]{26}$/);
146
+ return path.join(this.projectRoot(projectId), 'milestones', milestoneId);
147
+ }
148
+
149
+ delegationRoot(projectId: string, grantId: string): string {
150
+ this.assertId(grantId, /^DGR-[0-9A-HJKMNP-TV-Z]{26}$/);
151
+ return path.join(this.projectRoot(projectId), 'delegations', grantId);
152
+ }
153
+
154
+ snapshotRoot(projectId: string): string {
155
+ return path.join(this.projectRoot(projectId), 'snapshots');
156
+ }
157
+
158
+ lockRoot(projectId: string): string {
159
+ return path.join(this.projectRoot(projectId), 'locks');
160
+ }
161
+
162
+ projectSidecarPath(projectId: string, sidecarName: string): string {
163
+ return resolveSidecarPath(this.projectRoot(projectId), sidecarName, this.root);
164
+ }
165
+
166
+ taskSidecarPath(projectId: string, taskId: string, sidecarName: string): string {
167
+ return resolveSidecarPath(this.taskRoot(projectId, taskId), sidecarName, this.root);
168
+ }
169
+
170
+ milestoneSidecarPath(projectId: string, milestoneId: string, sidecarName: string): string {
171
+ return resolveSidecarPath(this.milestoneRoot(projectId, milestoneId), sidecarName, this.root);
172
+ }
173
+
174
+ writeKnowledgeMap(map: ProjectKnowledgeMap, expectedRevision: number | null): ProjectKnowledgeMap {
175
+ return this.writeRevisioned(path.join(this.projectRoot(map.projectId), 'knowledge-map.json'), map, expectedRevision);
176
+ }
177
+
178
+ readKnowledgeMap(projectId: string): ProjectKnowledgeMap {
179
+ return this.readEntity(path.join(this.projectRoot(projectId), 'knowledge-map.json'), 'knowledge-map');
180
+ }
181
+
182
+ writeGraphBinding(binding: GraphBinding, expectedRevision: number | null): GraphBinding {
183
+ return this.writeRevisioned(
184
+ path.join(this.projectRoot(binding.projectId), 'graph-bindings', `${binding.graphKind}.json`),
185
+ binding,
186
+ expectedRevision,
187
+ );
188
+ }
189
+
190
+ readGraphBinding(projectId: string, graphKind: GraphKind): GraphBinding {
191
+ return this.readEntity(
192
+ path.join(this.projectRoot(projectId), 'graph-bindings', `${graphKind}.json`),
193
+ 'graph-binding',
194
+ );
195
+ }
196
+
197
+ writeDiscovery(discovery: DiscoveryState, expectedRevision: number | null): DiscoveryState {
198
+ return this.writeRevisioned(
199
+ path.join(this.discoveryRoot(discovery.projectId, discovery.id), 'state.json'),
200
+ discovery,
201
+ expectedRevision,
202
+ );
203
+ }
204
+
205
+ readDiscovery(projectId: string, discoveryId: string): DiscoveryState {
206
+ return this.readEntity(path.join(this.discoveryRoot(projectId, discoveryId), 'state.json'), 'discovery');
207
+ }
208
+
209
+ writeTask(task: TaskState, expectedRevision: number | null): TaskState {
210
+ assertTaskTransactionBoundary(this.projectRoot(task.projectId), this.root, task.id);
211
+ this.assertNoCoreTaskOperation(task.projectId, task.id);
212
+ return this.writeRevisioned(path.join(this.taskRoot(task.projectId, task.id), 'state.json'), task, expectedRevision);
213
+ }
214
+
215
+ readTask(projectId: string, taskId: string): TaskState {
216
+ assertTaskTransactionBoundary(this.projectRoot(projectId), this.root, taskId);
217
+ const task = this.readEntity<TaskState>(path.join(this.taskRoot(projectId, taskId), 'state.json'), 'task');
218
+ assertTaskTransactionBoundary(this.projectRoot(projectId), this.root, taskId);
219
+ return task;
220
+ }
221
+
222
+ writeMilestone(milestone: MilestoneState, expectedRevision: number | null): MilestoneState {
223
+ return this.writeRevisioned(
224
+ path.join(this.milestoneRoot(milestone.projectId, milestone.id), 'state.json'),
225
+ milestone,
226
+ expectedRevision,
227
+ );
228
+ }
229
+
230
+ readMilestone(projectId: string, milestoneId: string): MilestoneState {
231
+ return this.readEntity(path.join(this.milestoneRoot(projectId, milestoneId), 'state.json'), 'milestone');
232
+ }
233
+
234
+ writeDelegation(grant: DelegationGrant, expectedRevision: number | null): DelegationGrant {
235
+ return this.writeRevisioned(
236
+ path.join(this.delegationRoot(grant.projectId, grant.id), 'state.json'),
237
+ grant,
238
+ expectedRevision,
239
+ );
240
+ }
241
+
242
+ readDelegation(projectId: string, grantId: string): DelegationGrant {
243
+ return this.readEntity(path.join(this.delegationRoot(projectId, grantId), 'state.json'), 'delegation-grant');
244
+ }
245
+
246
+ listTasks(projectId: string): TaskState[] {
247
+ assertTaskTransactionBoundary(this.projectRoot(projectId), this.root);
248
+ const tasks = this.listEntities<TaskState>(path.join(this.projectRoot(projectId), 'tasks'), 'task');
249
+ assertTaskTransactionBoundary(this.projectRoot(projectId), this.root);
250
+ return tasks;
251
+ }
252
+
253
+ listDiscoveries(projectId: string): DiscoveryState[] {
254
+ return this.listEntities<DiscoveryState>(path.join(this.projectRoot(projectId), 'discoveries'), 'discovery');
255
+ }
256
+
257
+ listMilestones(projectId: string): MilestoneState[] {
258
+ return this.listEntities<MilestoneState>(path.join(this.projectRoot(projectId), 'milestones'), 'milestone');
259
+ }
260
+
261
+ listDelegations(projectId: string, inertAutonomyGrantIds: ReadonlySet<string> = new Set()): DelegationGrant[] {
262
+ return this.listEntities<DelegationGrant>(
263
+ path.join(this.projectRoot(projectId), 'delegations'),
264
+ 'delegation-grant',
265
+ inertAutonomyGrantIds,
266
+ );
267
+ }
268
+
269
+ writeArtifact(entityRoot: string, name: string, content: string): string {
270
+ this.assertTaskTransactionBoundaryForEntityRoot(entityRoot, true);
271
+ if (!/^[a-z][a-z0-9-]*\.md$/.test(name)) {
272
+ throw new WorkflowError('INVALID_ARGUMENT', `Invalid artifact name: ${name}`);
273
+ }
274
+ const file = path.join(entityRoot, name);
275
+ writeTextAtomic(file, content);
276
+ return createHash('sha256').update(readFileSync(file)).digest('hex');
277
+ }
278
+
279
+ stageArtifact(entityRoot: string, name: string, content: string): StagedArtifact {
280
+ this.assertTaskTransactionBoundaryForEntityRoot(entityRoot, true);
281
+ if (!/^[a-z][a-z0-9-]*\.(?:md|json|jsonl)$/.test(name)) {
282
+ throw new WorkflowError('INVALID_ARGUMENT', `Invalid artifact name: ${name}`);
283
+ }
284
+ const normalized = content.endsWith('\n') ? content : `${content}\n`;
285
+ const hash = createHash('sha256').update(normalized).digest('hex');
286
+ const stagedFile = path.join(entityRoot, '.versions', `${name}.${hash}`);
287
+ if (!existsSync(stagedFile)) writeTextAtomic(stagedFile, normalized);
288
+ return { entityRoot, name, hash, stagedFile };
289
+ }
290
+
291
+ publishArtifact(staged: StagedArtifact): string {
292
+ this.assertTaskTransactionBoundaryForEntityRoot(staged.entityRoot, true);
293
+ writeTextAtomic(path.join(staged.entityRoot, staged.name), readFileSync(staged.stagedFile, 'utf8'));
294
+ const actual = this.hashArtifact(staged.entityRoot, staged.name);
295
+ if (actual !== staged.hash) {
296
+ throw new WorkflowError('STATE_CORRUPT', `Published artifact hash mismatch: ${staged.name}`, {
297
+ expected: staged.hash,
298
+ actual,
299
+ });
300
+ }
301
+ return actual;
302
+ }
303
+
304
+ hashArtifact(entityRoot: string, name: string): string {
305
+ this.assertTaskTransactionBoundaryForEntityRoot(entityRoot);
306
+ const file = path.join(entityRoot, name);
307
+ if (!existsSync(file)) throw new WorkflowError('NOT_FOUND', `Artifact not found: ${name}`);
308
+ const hash = createHash('sha256').update(readFileSync(file)).digest('hex');
309
+ this.assertTaskTransactionBoundaryForEntityRoot(entityRoot);
310
+ return hash;
311
+ }
312
+
313
+ readSidecarEvents<T extends SidecarEventRecord>(
314
+ entityRoot: string,
315
+ sidecarName: string,
316
+ options: { validateEvent?: SidecarEventValidator<T> } = {},
317
+ ): T[] {
318
+ this.assertTaskTransactionBoundaryForEntityRoot(entityRoot);
319
+ this.assertAlpha6StateRoot(entityRoot);
320
+ const events = readValidatedSidecarEvents<T>(resolveSidecarPath(entityRoot, sidecarName, this.root), {
321
+ ...options,
322
+ trustedRoot: this.root,
323
+ });
324
+ this.assertTaskTransactionBoundaryForEntityRoot(entityRoot);
325
+ return events;
326
+ }
327
+
328
+ stageSidecarAppend<T extends SidecarEventRecord>(
329
+ entityRoot: string,
330
+ sidecarName: string,
331
+ event: SidecarAppendInput<T>,
332
+ options: { validateEvent?: SidecarEventValidator<T> } = {},
333
+ ): StagedSidecarAppend<T> {
334
+ this.assertAlpha6StateRoot(entityRoot);
335
+ const current = this.readSidecarEvents<T>(entityRoot, sidecarName, options);
336
+ const prepared = prepareSidecarAppend(current, event, options);
337
+ const sidecarFile = resolveSidecarPath(entityRoot, sidecarName, this.root);
338
+ assertNoSymlinkPathComponents(sidecarFile, { label: 'Sidecar file', trustedRoot: this.root });
339
+ const staged = this.stageArtifact(entityRoot, sidecarName, prepared.content);
340
+ if (staged.hash !== prepared.contentHash) {
341
+ throw new WorkflowError('STATE_CORRUPT', `Staged sidecar hash mismatch: ${sidecarName}`, {
342
+ expectedHash: prepared.contentHash,
343
+ actualHash: staged.hash,
344
+ });
345
+ }
346
+ const stagedEvents = readValidatedSidecarEvents<T>(staged.stagedFile, {
347
+ ...options,
348
+ trustedRoot: this.root,
349
+ });
350
+ if (stagedEvents.length !== current.length + 1) {
351
+ throw new WorkflowError('STATE_CORRUPT', `Staged sidecar append length mismatch: ${sidecarName}`, {
352
+ expectedLength: current.length + 1,
353
+ actualLength: stagedEvents.length,
354
+ });
355
+ }
356
+ const appended = stagedEvents[stagedEvents.length - 1];
357
+ if (!appended || appended.eventHash !== prepared.event.eventHash) {
358
+ throw new WorkflowError('STATE_CORRUPT', `Staged sidecar append validation failed: ${sidecarName}`, {
359
+ expectedEventHash: prepared.event.eventHash,
360
+ actualEventHash: appended?.eventHash ?? null,
361
+ });
362
+ }
363
+ return {
364
+ ...staged,
365
+ file: sidecarFile,
366
+ event: prepared.event,
367
+ };
368
+ }
369
+
370
+ appendSidecarEvent<T extends SidecarEventRecord>(
371
+ entityRoot: string,
372
+ sidecarName: string,
373
+ event: SidecarAppendInput<T>,
374
+ options: { validateEvent?: SidecarEventValidator<T> } = {},
375
+ ): T {
376
+ const staged = this.stageSidecarAppend(entityRoot, sidecarName, event, options);
377
+ this.publishArtifact(staged);
378
+ return staged.event;
379
+ }
380
+
381
+ milestoneTransactionDirectory(projectId: string, milestoneId: string): string {
382
+ return resolveMilestoneTransactionDirectory(this.milestoneRoot(projectId, milestoneId));
383
+ }
384
+
385
+ milestoneTransactionJournalPath(projectId: string, milestoneId: string, transactionId: string): string {
386
+ return resolveMilestoneTransactionJournalPath(this.milestoneRoot(projectId, milestoneId), transactionId);
387
+ }
388
+
389
+ milestoneTransactionTargetPath(projectId: string, milestoneId: string, relativePath: string): string {
390
+ return resolveMilestoneTransactionTargetPath(this.milestoneRoot(projectId, milestoneId), relativePath);
391
+ }
392
+
393
+ captureMilestoneTransactionSnapshot(
394
+ projectId: string,
395
+ milestoneId: string,
396
+ relativePath: string,
397
+ options: { mode?: JournalSnapshotMode; transactionId?: string; snapshotPath?: string } = {},
398
+ ): MilestoneTransactionJournalFileSnapshot {
399
+ this.assertAlpha6StateRoot(this.milestoneRoot(projectId, milestoneId));
400
+ return captureJournalTargetSnapshot(this.milestoneRoot(projectId, milestoneId), relativePath, options);
401
+ }
402
+
403
+ stageMilestoneTransactionSnapshot(
404
+ projectId: string,
405
+ milestoneId: string,
406
+ content: string | Buffer,
407
+ options: { mode?: JournalSnapshotMode; transactionId?: string; snapshotPath?: string } = {},
408
+ ): MilestoneTransactionJournalFileSnapshot {
409
+ this.assertAlpha6StateRoot(this.milestoneRoot(projectId, milestoneId));
410
+ return createJournalSnapshotFromContent(this.milestoneRoot(projectId, milestoneId), content, options);
411
+ }
412
+
413
+ readMilestoneTransactionSnapshot(
414
+ projectId: string,
415
+ milestoneId: string,
416
+ snapshot: MilestoneTransactionJournalFileSnapshot,
417
+ ): Buffer | null {
418
+ this.assertAlpha6StateRoot(this.milestoneRoot(projectId, milestoneId));
419
+ return readJournalSnapshotContent(this.milestoneRoot(projectId, milestoneId), snapshot);
420
+ }
421
+
422
+ writeMilestoneTransactionJournal(
423
+ projectId: string,
424
+ milestoneId: string,
425
+ journal: MilestoneTransactionJournal,
426
+ ): MilestoneTransactionJournal {
427
+ this.assertAlpha6StateRoot(this.milestoneRoot(projectId, milestoneId));
428
+ return writeMilestoneTransactionJournal(this.milestoneRoot(projectId, milestoneId), journal);
429
+ }
430
+
431
+ readMilestoneTransactionJournal(
432
+ projectId: string,
433
+ milestoneId: string,
434
+ transactionId: string,
435
+ ): MilestoneTransactionJournal {
436
+ this.assertAlpha6StateRoot(this.milestoneRoot(projectId, milestoneId));
437
+ return readMilestoneTransactionJournal(this.milestoneRoot(projectId, milestoneId), transactionId, {
438
+ expectedMilestoneId: milestoneId,
439
+ });
440
+ }
441
+
442
+ listMilestoneTransactionJournals(projectId: string, milestoneId: string): MilestoneTransactionJournal[] {
443
+ this.assertAlpha6StateRoot(this.milestoneRoot(projectId, milestoneId));
444
+ return listMilestoneTransactionJournals(this.milestoneRoot(projectId, milestoneId), {
445
+ expectedMilestoneId: milestoneId,
446
+ });
447
+ }
448
+
449
+ updateMilestoneTransactionJournalPhase(
450
+ projectId: string,
451
+ milestoneId: string,
452
+ transactionId: string,
453
+ phase: MilestoneTransactionPhase,
454
+ ): MilestoneTransactionJournal {
455
+ this.assertAlpha6StateRoot(this.milestoneRoot(projectId, milestoneId));
456
+ return updateMilestoneTransactionJournalPhase(
457
+ this.milestoneRoot(projectId, milestoneId),
458
+ transactionId,
459
+ phase,
460
+ this.now().toISOString(),
461
+ { expectedMilestoneId: milestoneId },
462
+ );
463
+ }
464
+
465
+ removeMilestoneTransactionJournal(projectId: string, milestoneId: string, transactionId: string): void {
466
+ this.assertAlpha6StateRoot(this.milestoneRoot(projectId, milestoneId));
467
+ removeMilestoneTransactionJournal(this.milestoneRoot(projectId, milestoneId), transactionId);
468
+ }
469
+
470
+ private writeRevisioned<T extends RevisionedRecord>(file: string, record: T, expectedRevision: number | null): T {
471
+ const timestamp = this.now().toISOString();
472
+ if (!existsSync(file)) {
473
+ if (expectedRevision !== null) {
474
+ throw new WorkflowError('STATE_CONFLICT', `Cannot update missing state ${record.id}`, {
475
+ expectedRevision,
476
+ actualRevision: null,
477
+ });
478
+ }
479
+ const created = { ...record, revision: 1, updatedAt: timestamp };
480
+ writeJsonAtomic(file, created);
481
+ return created;
482
+ }
483
+
484
+ const existing = readJson<T>(file);
485
+ if (expectedRevision === null || existing.revision !== expectedRevision) {
486
+ throw new WorkflowError('STATE_CONFLICT', `State revision conflict for ${record.id}`, {
487
+ expectedRevision,
488
+ actualRevision: existing.revision,
489
+ });
490
+ }
491
+ const updated = { ...record, revision: existing.revision + 1, updatedAt: timestamp };
492
+ writeJsonAtomic(file, updated);
493
+ return updated;
494
+ }
495
+
496
+ private readEntity<T extends EntityState>(file: string, kind: T['kind']): T {
497
+ if (!existsSync(file)) throw new WorkflowError('NOT_FOUND', `${kind} state not found: ${file}`);
498
+ const entity = readJson<EntityState>(file);
499
+ if (entity.kind !== kind || entity.schemaVersion !== STATE_SCHEMA_VERSION) {
500
+ throw new WorkflowError('STATE_CORRUPT', `Unexpected ${kind} state schema: ${file}`);
501
+ }
502
+ return entity as T;
503
+ }
504
+
505
+ private listEntities<T extends EntityState>(directory: string, kind: T['kind'], knownUnpublishedIds: ReadonlySet<string> = new Set()): T[] {
506
+ if (!existsSync(directory)) return [];
507
+ return readdirSync(directory, { withFileTypes: true })
508
+ .filter((entry) => entry.isDirectory())
509
+ .filter((entry) => {
510
+ if (!knownUnpublishedIds.has(entry.name)) return true;
511
+ try {
512
+ // A dangling symlink or a malformed published state is not an absent write.
513
+ lstatSync(path.join(directory, entry.name, 'state.json'));
514
+ return true;
515
+ } catch (error) {
516
+ if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
517
+ return false;
518
+ }
519
+ })
520
+ .map((entry) => this.readEntity<T>(path.join(directory, entry.name, 'state.json'), kind))
521
+ .sort((left, right) => left.createdAt.localeCompare(right.createdAt));
522
+ }
523
+
524
+ private assertId(value: string, pattern: RegExp): void {
525
+ if (!pattern.test(value)) throw new WorkflowError('INVALID_ARGUMENT', `Invalid state identifier: ${value}`);
526
+ }
527
+
528
+ private assertAlpha6StateRoot(entityRoot: string): void {
529
+ const resolvedRoot = path.resolve(this.root);
530
+ const resolvedEntityRoot = path.resolve(entityRoot);
531
+ if (resolvedEntityRoot !== resolvedRoot && !resolvedEntityRoot.startsWith(`${resolvedRoot}${path.sep}`)) {
532
+ throw new WorkflowError('INVALID_ARGUMENT', `State path is outside of the configured store root: ${entityRoot}`);
533
+ }
534
+ const relativePath = path.relative(resolvedRoot, resolvedEntityRoot);
535
+ if (relativePath.startsWith('..') || path.isAbsolute(relativePath)) {
536
+ throw new WorkflowError('INVALID_ARGUMENT', `State path is outside of the configured store root: ${entityRoot}`);
537
+ }
538
+ let current = resolvedRoot;
539
+ for (const segment of relativePath.split(path.sep).filter(Boolean)) {
540
+ current = path.join(current, segment);
541
+ if (!existsSync(current)) continue;
542
+ if (lstatSync(current).isSymbolicLink()) {
543
+ throw new WorkflowError('STATE_CORRUPT', `Alpha6 state root must not traverse a symlink: ${entityRoot}`, {
544
+ symlinkPath: current,
545
+ });
546
+ }
547
+ }
548
+ }
549
+
550
+ private assertTaskTransactionBoundaryForEntityRoot(entityRoot: string, mutation = false): void {
551
+ const relative = path.relative(this.root, path.resolve(entityRoot)).split(path.sep);
552
+ if (relative.length === 4 && relative[0] === 'projects' && relative[2] === 'tasks') {
553
+ assertTaskTransactionBoundary(this.projectRoot(relative[1]!), this.root, relative[3]!);
554
+ if (mutation) this.assertNoCoreTaskOperation(relative[1]!, relative[3]!);
555
+ }
556
+ }
557
+
558
+ private assertNoCoreTaskOperation(projectId: string, taskId: string): void {
559
+ const operation = inspectCoreTaskOperationMutex(this.lockRoot(projectId), taskId, this.root);
560
+ if (operation) {
561
+ throw new WorkflowError('LOCKED', `A Core Task operation owns ${taskId}.`, {
562
+ operationId: operation.operationId,
563
+ transitionId: operation.transitionId,
564
+ });
565
+ }
566
+ }
567
+ }