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

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 (297) hide show
  1. package/README.md +27 -388
  2. package/dist/reviewer-runtime-build.json +98 -34
  3. package/dist/src/alpha6/adoption.d.ts +3 -1
  4. package/dist/src/alpha6/adoption.js +48 -8
  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 +53 -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 +8 -21
  30. package/dist/src/alpha6/remediation.js +225 -474
  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/alpha7/autonomy.d.ts +9 -0
  36. package/dist/src/alpha7/autonomy.js +40 -1
  37. package/dist/src/alpha7/autonomy.js.map +1 -1
  38. package/dist/src/beta1/project-transaction.d.ts +3 -1
  39. package/dist/src/beta1/project-transaction.js +20 -6
  40. package/dist/src/beta1/project-transaction.js.map +1 -1
  41. package/dist/src/checks/runner.d.ts +26 -0
  42. package/dist/src/checks/runner.js +223 -0
  43. package/dist/src/checks/runner.js.map +1 -0
  44. package/dist/src/checks/task-sync.d.ts +10 -0
  45. package/dist/src/checks/task-sync.js +41 -0
  46. package/dist/src/checks/task-sync.js.map +1 -0
  47. package/dist/src/checks/worker.d.ts +1 -0
  48. package/dist/src/checks/worker.js +154 -0
  49. package/dist/src/checks/worker.js.map +1 -0
  50. package/dist/src/cli-actions.d.ts +3 -3
  51. package/dist/src/cli-actions.js +8 -15
  52. package/dist/src/cli-actions.js.map +1 -1
  53. package/dist/src/cli.js +43 -97
  54. package/dist/src/cli.js.map +1 -1
  55. package/dist/src/contracts.d.ts +54 -34
  56. package/dist/src/delegation.d.ts +5 -1
  57. package/dist/src/delegation.js +4 -0
  58. package/dist/src/delegation.js.map +1 -1
  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/failed-checkpoint-update.d.ts +69 -0
  74. package/dist/src/failed-checkpoint-update.js +171 -0
  75. package/dist/src/failed-checkpoint-update.js.map +1 -0
  76. package/dist/src/gateway-handshake.js +0 -8
  77. package/dist/src/gateway-handshake.js.map +1 -1
  78. package/dist/src/git.d.ts +3 -1
  79. package/dist/src/git.js +41 -14
  80. package/dist/src/git.js.map +1 -1
  81. package/dist/src/graph.js +25 -3
  82. package/dist/src/graph.js.map +1 -1
  83. package/dist/src/index.d.ts +1 -0
  84. package/dist/src/lifecycle/corrective-replan-credential-core.d.ts +2 -0
  85. package/dist/src/lifecycle/corrective-replan-credential-core.js +45 -9
  86. package/dist/src/lifecycle/corrective-replan-credential-core.js.map +1 -1
  87. package/dist/src/lifecycle/corrective-replan.js +12 -8
  88. package/dist/src/lifecycle/corrective-replan.js.map +1 -1
  89. package/dist/src/lifecycle/semantic-registry.js +4 -2
  90. package/dist/src/lifecycle/semantic-registry.js.map +1 -1
  91. package/dist/src/navigation-actions.d.ts +9 -0
  92. package/dist/src/navigation-actions.js +72 -0
  93. package/dist/src/navigation-actions.js.map +1 -0
  94. package/dist/src/navigation-update-artifact.d.ts +5 -0
  95. package/dist/src/navigation-update-artifact.js +227 -0
  96. package/dist/src/navigation-update-artifact.js.map +1 -0
  97. package/dist/src/navigation-update.d.ts +39 -0
  98. package/dist/src/navigation-update.js +82 -0
  99. package/dist/src/navigation-update.js.map +1 -0
  100. package/dist/src/observation.js +52 -29
  101. package/dist/src/observation.js.map +1 -1
  102. package/dist/src/observed-routes.js +10 -6
  103. package/dist/src/observed-routes.js.map +1 -1
  104. package/dist/src/pending-review-update.d.ts +0 -13
  105. package/dist/src/pending-review-update.js +1 -6
  106. package/dist/src/pending-review-update.js.map +1 -1
  107. package/dist/src/reviewer.d.ts +1 -1
  108. package/dist/src/reviewer.js +20 -17
  109. package/dist/src/reviewer.js.map +1 -1
  110. package/dist/src/runtime.d.ts +3 -0
  111. package/dist/src/runtime.js +6 -0
  112. package/dist/src/runtime.js.map +1 -0
  113. package/dist/src/state/corrective-replan-executor.d.ts +10 -0
  114. package/dist/src/state/corrective-replan-executor.js +38 -1
  115. package/dist/src/state/corrective-replan-executor.js.map +1 -1
  116. package/dist/src/state/corrective-replan-public-schema.js +15 -2
  117. package/dist/src/state/corrective-replan-public-schema.js.map +1 -1
  118. package/dist/src/state/corrective-replan-public.d.ts +9 -1
  119. package/dist/src/state/corrective-replan-public.js +50 -8
  120. package/dist/src/state/corrective-replan-public.js.map +1 -1
  121. package/dist/src/state/corrective-replan-transaction.d.ts +1 -0
  122. package/dist/src/state/corrective-replan-transaction.js +12 -11
  123. package/dist/src/state/corrective-replan-transaction.js.map +1 -1
  124. package/dist/src/state/corrective-yield-transaction.d.ts +1 -0
  125. package/dist/src/state/corrective-yield-transaction.js +11 -10
  126. package/dist/src/state/corrective-yield-transaction.js.map +1 -1
  127. package/dist/src/state/lock.d.ts +4 -0
  128. package/dist/src/state/lock.js +21 -0
  129. package/dist/src/state/lock.js.map +1 -1
  130. package/dist/src/version.d.ts +1 -1
  131. package/dist/src/version.js +1 -1
  132. package/dist/src/workflow-blocker-route.d.ts +8 -0
  133. package/dist/src/workflow-blocker-route.js +106 -0
  134. package/dist/src/workflow-blocker-route.js.map +1 -0
  135. package/dist/src/workflow.d.ts +88 -64
  136. package/dist/src/workflow.js +832 -1456
  137. package/dist/src/workflow.js.map +1 -1
  138. package/docs/autonomy-guardrails.md +26 -304
  139. package/docs/decisions.md +14 -112
  140. package/docs/development-flow.md +36 -237
  141. package/docs/project-memory.md +31 -50
  142. package/docs/release-app-evidence.md +190 -0
  143. package/docs/release.md +101 -388
  144. package/docs/updating-existing-project.md +65 -726
  145. package/package.json +11 -13
  146. package/plugins/codex-workflow-gateway/.codex-plugin/plugin.json +2 -2
  147. package/plugins/codex-workflow-gateway/references/chat-dispatch.md +88 -200
  148. package/plugins/codex-workflow-gateway/references/codebase-memory-routing.md +57 -0
  149. package/plugins/codex-workflow-gateway/references/protocol.md +51 -458
  150. package/plugins/codex-workflow-gateway/scripts/chat-dispatch.mjs +20 -4
  151. package/plugins/codex-workflow-gateway/scripts/chat-model-policy.mjs +21 -34
  152. package/plugins/codex-workflow-gateway/scripts/chat-registry.mjs +23 -5
  153. package/plugins/codex-workflow-gateway/skills/codex-workflow-gateway/SKILL.md +92 -778
  154. package/references/state-machine.md +7 -5
  155. package/roles/delivery-coordinator.md +31 -11
  156. package/roles/scope-lead.md +1 -1
  157. package/roles/technical-planner.md +5 -2
  158. package/schemas/adoption-posture-event.schema.json +11 -0
  159. package/schemas/authorization-event.schema.json +1 -1
  160. package/schemas/corrective-decision-event.schema.json +26 -4
  161. package/schemas/corrective-replan-credentials.private.schema.json +8 -5
  162. package/schemas/corrective-replan-execution-event.private.schema.json +9 -0
  163. package/schemas/corrective-replan-public.schema.json +2 -1
  164. package/schemas/delegation-grant.schema.json +1 -1
  165. package/schemas/project-knowledge-map.schema.json +3 -1
  166. package/schemas/remediation-event.schema.json +10 -1
  167. package/schemas/task.schema.json +64 -2
  168. package/schemas/transition-definition.schema.json +4 -0
  169. package/schemas/transition-payloads.schema.json +16 -1
  170. package/src/alpha6/adoption.ts +1146 -0
  171. package/src/alpha6/captured-check-evidence.ts +132 -0
  172. package/src/alpha6/check-support-anchor.ts +128 -0
  173. package/src/alpha6/component-owner.ts +168 -0
  174. package/src/alpha6/corrective-decision-boundary.ts +45 -0
  175. package/src/alpha6/downstream-proof.ts +520 -0
  176. package/src/alpha6/failed-step-planning-recovery.ts +88 -0
  177. package/src/alpha6/handoff.ts +1443 -0
  178. package/src/alpha6/journal.ts +473 -0
  179. package/src/alpha6/literal-test-invocation.ts +84 -0
  180. package/src/alpha6/mechanical-feasibility.ts +488 -0
  181. package/src/alpha6/milestone.ts +2192 -0
  182. package/src/alpha6/navigation-dirty-carryover.ts +154 -0
  183. package/src/alpha6/npm-check-contract.ts +47 -0
  184. package/src/alpha6/plan-integrity.ts +298 -0
  185. package/src/alpha6/plan-risk.ts +1480 -0
  186. package/src/alpha6/preexecution-replan.ts +187 -0
  187. package/src/alpha6/remediation-cause.ts +98 -0
  188. package/src/alpha6/remediation.ts +2505 -0
  189. package/src/alpha6/review.ts +1198 -0
  190. package/src/alpha6/root-cause-replan-carryover.ts +491 -0
  191. package/src/alpha6/store-sidecars.ts +335 -0
  192. package/src/alpha7/autonomy.ts +449 -0
  193. package/src/alpha7/corrective-recovery.ts +1332 -0
  194. package/src/artifacts.ts +130 -0
  195. package/src/beta1/project-transaction.ts +364 -0
  196. package/src/change-explanation.ts +153 -0
  197. package/src/checks/runner.ts +245 -0
  198. package/src/checks/task-sync.ts +41 -0
  199. package/src/checks/worker.ts +149 -0
  200. package/src/cli-actions.ts +112 -0
  201. package/src/cli.ts +1490 -0
  202. package/src/contracts.ts +1512 -0
  203. package/src/credential-output.ts +89 -0
  204. package/src/credential-transport.ts +215 -0
  205. package/src/delegation.ts +194 -0
  206. package/src/dependency-provenance.ts +472 -0
  207. package/src/diagnostics.ts +93 -0
  208. package/src/domain/base-sync-conflict.ts +35 -0
  209. package/src/domain/completed-step-carryover.ts +76 -0
  210. package/src/domain/discovery.ts +27 -0
  211. package/src/domain/plan-semantics.ts +58 -0
  212. package/src/domain/step-start-admission.ts +10 -0
  213. package/src/domain/validation.ts +177 -0
  214. package/src/domain/virgin-registration.ts +46 -0
  215. package/src/errors.ts +24 -0
  216. package/src/failed-checkpoint-update.ts +203 -0
  217. package/src/fs-utils.ts +61 -0
  218. package/src/gateway-handshake.ts +95 -0
  219. package/src/git.ts +183 -0
  220. package/src/graph.ts +342 -0
  221. package/src/historical-step-provenance.ts +136 -0
  222. package/src/index.ts +23 -0
  223. package/src/lifecycle/canonical-hash.ts +28 -0
  224. package/src/lifecycle/catalog.ts +202 -0
  225. package/src/lifecycle/compiler-inspection.ts +29 -0
  226. package/src/lifecycle/core-static-readiness.ts +132 -0
  227. package/src/lifecycle/corrective-replan-authority.ts +136 -0
  228. package/src/lifecycle/corrective-replan-binding-manifest.ts +51 -0
  229. package/src/lifecycle/corrective-replan-credential-core.ts +437 -0
  230. package/src/lifecycle/corrective-replan-credential-schema.ts +54 -0
  231. package/src/lifecycle/corrective-replan-credentials.ts +48 -0
  232. package/src/lifecycle/corrective-replan.ts +847 -0
  233. package/src/lifecycle/evaluator.ts +48 -0
  234. package/src/lifecycle/fingerprint.ts +488 -0
  235. package/src/lifecycle/immutable.ts +8 -0
  236. package/src/lifecycle/implementation-table.ts +118 -0
  237. package/src/lifecycle/index.ts +8 -0
  238. package/src/lifecycle/schema-artifact.ts +263 -0
  239. package/src/lifecycle/semantic-registry.ts +574 -0
  240. package/src/lifecycle/types.ts +838 -0
  241. package/src/memory.ts +273 -0
  242. package/src/migration.ts +161 -0
  243. package/src/navigation-actions.ts +70 -0
  244. package/src/navigation-update-artifact.ts +198 -0
  245. package/src/navigation-update.ts +121 -0
  246. package/src/observation.ts +225 -0
  247. package/src/observed-routes.ts +665 -0
  248. package/src/operational-contract.ts +125 -0
  249. package/src/pending-review-update.ts +175 -0
  250. package/src/repository.ts +99 -0
  251. package/src/reviewer.ts +1879 -0
  252. package/src/runtime.ts +6 -0
  253. package/src/state/corrective-replan-executor.ts +819 -0
  254. package/src/state/corrective-replan-public-schema.ts +83 -0
  255. package/src/state/corrective-replan-public.ts +952 -0
  256. package/src/state/corrective-replan-transaction.ts +949 -0
  257. package/src/state/corrective-yield-executor.ts +327 -0
  258. package/src/state/corrective-yield-transaction.ts +730 -0
  259. package/src/state/lock.ts +902 -0
  260. package/src/state/store.ts +567 -0
  261. package/src/transition-core.ts +330 -0
  262. package/src/ulid.ts +24 -0
  263. package/src/version.ts +2 -0
  264. package/src/workflow-blocker-route.ts +109 -0
  265. package/src/workflow.ts +10342 -0
  266. package/docs/alpha7.1-implementation-brief.md +0 -268
  267. package/docs/alpha7.2-corrective-context-refresh-brief.md +0 -484
  268. package/docs/alpha7.2.1-remediation-recovery-brief.md +0 -86
  269. package/docs/beta1-stabilization-brief.md +0 -165
  270. package/docs/beta11-plan-integrity-recovery-brief.md +0 -38
  271. package/docs/beta13.2-signal-review-recovery.md +0 -38
  272. package/docs/beta2-initial-assembly-navigation-brief.md +0 -616
  273. package/docs/change-model.md +0 -118
  274. package/docs/delegated-approval.md +0 -254
  275. package/docs/lifecycle/state-machine-stabilization.md +0 -641
  276. package/docs/pdf/README.md +0 -24
  277. package/docs/pdf/codex-workflow-v2-architecture-ru.pdf +0 -0
  278. package/docs/pdf/codex-workflow-v2-chat-only-guide-ru.pdf +0 -0
  279. package/docs/pdf/codex-workflow-v2-technical-reference-ru.pdf +0 -0
  280. package/docs/pdf/requirements.txt +0 -1
  281. package/docs/pdf/sources/codex-workflow-v2-architecture-ru.md +0 -478
  282. package/docs/pdf/sources/codex-workflow-v2-chat-only-guide-ru.md +0 -506
  283. package/docs/pdf/sources/codex-workflow-v2-technical-reference-ru.md +0 -778
  284. package/docs/pending-review-update.md +0 -15
  285. package/docs/problem-briefs/01-pre-implementation-integrity.md +0 -482
  286. package/docs/problem-briefs/02-minimal-step-integrity.md +0 -411
  287. package/docs/problem-briefs/03-minimal-agent-context-integrity.md +0 -358
  288. package/docs/problem-briefs/04-task-dependency-and-structural-replacement-integrity.md +0 -573
  289. package/docs/problem-briefs/BRIEF-TEMPLATE.md +0 -56
  290. package/docs/problem-briefs/README.md +0 -120
  291. package/docs/problem-briefs/evidence/p01-mechanical-feasibility-corpus.md +0 -90
  292. package/docs/problem-briefs/evidence/signal-v4-pre-m3-replay.md +0 -246
  293. package/docs/split-required-recovery.md +0 -47
  294. package/docs/stable-release-defect-register.md +0 -730
  295. package/docs/validation-report.md +0 -182
  296. package/scripts/generate-pdf-docs.py +0 -524
  297. 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
+ }