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,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
+ }