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,27 @@
1
+ import type { DiscoveryState } from '../contracts.js';
2
+
3
+ export interface DiscoveryReadiness {
4
+ ready: boolean;
5
+ status: 'needs_discovery' | 'needs_clarification' | 'ready_to_materialize';
6
+ missing: string[];
7
+ }
8
+
9
+ export function assessDiscovery(discovery: DiscoveryState): DiscoveryReadiness {
10
+ const missing: string[] = [];
11
+ if (!discovery.goal.trim()) missing.push('goal');
12
+ if (!discovery.outcome?.trim()) missing.push('observable outcome');
13
+ if (discovery.scope.length === 0) missing.push('scope');
14
+ if (discovery.acceptance.length === 0) missing.push('acceptance scenario');
15
+ if (discovery.blockingUnknowns.length > 0) missing.push('resolved blocking unknowns');
16
+ if (missing.length === 0) return { ready: true, status: 'ready_to_materialize', missing };
17
+ const hasInitialUnderstanding = Boolean(discovery.goal.trim() && (discovery.outcome || discovery.scope.length > 0));
18
+ return {
19
+ ready: false,
20
+ status: hasInitialUnderstanding ? 'needs_clarification' : 'needs_discovery',
21
+ missing,
22
+ };
23
+ }
24
+
25
+ export function mergeUnique(current: string[], additions: string[]): string[] {
26
+ return [...new Set([...current, ...additions].map((value) => value.trim()).filter(Boolean))];
27
+ }
@@ -0,0 +1,58 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { WorkflowError } from '../errors.js';
5
+
6
+ const HASH64 = /^[a-f0-9]{64}$/;
7
+ const KNOWLEDGE_LINE = /^- Knowledge map: revision ([1-9]\d*), hash ([a-f0-9]{64})$/gm;
8
+ const KNOWLEDGE_SENTINEL = '- Knowledge map: revision <context>, hash <context>';
9
+
10
+ export interface NormalizedPlanSemantics {
11
+ normalized: string;
12
+ semanticHash: string;
13
+ binding: { revision: number; hash: string };
14
+ }
15
+
16
+ export function normalizePlanKnowledgeBinding(content: string, label: string): NormalizedPlanSemantics {
17
+ const matches = [...content.matchAll(KNOWLEDGE_LINE)];
18
+ if (matches.length !== 1) {
19
+ throw new WorkflowError('STATE_CORRUPT', `${label} Plan must contain exactly one canonical Knowledge Map binding line.`, {
20
+ matchCount: matches.length,
21
+ });
22
+ }
23
+ const match = matches[0]!;
24
+ const normalized = content.slice(0, match.index) + KNOWLEDGE_SENTINEL + content.slice(match.index! + match[0].length);
25
+ return {
26
+ normalized,
27
+ semanticHash: sha256Hex(normalized),
28
+ binding: { revision: Number(match[1]), hash: match[2]! },
29
+ };
30
+ }
31
+
32
+ export function assertKnowledgeBindingOnlyPlanChange(historical: string, current: string): string {
33
+ const source = normalizePlanKnowledgeBinding(historical, 'historical');
34
+ const target = normalizePlanKnowledgeBinding(current, 'current');
35
+ if (source.normalized !== target.normalized) {
36
+ throw new WorkflowError('TRANSITION_BLOCKED', 'Corrective recovery permits only the Knowledge Map binding line to change.');
37
+ }
38
+ return source.semanticHash;
39
+ }
40
+
41
+ export function readVerifiedPlanArtifact(taskRoot: string, planHash: string, currentPlanHash: string): string {
42
+ if (!HASH64.test(planHash)) throw new WorkflowError('STATE_CORRUPT', 'Plan hash is malformed.', { planHash });
43
+ const file = planHash === currentPlanHash
44
+ ? path.join(taskRoot, 'plan.md')
45
+ : path.join(taskRoot, '.versions', `plan.md.${planHash}`);
46
+ if (!existsSync(file)) {
47
+ throw new WorkflowError('STATE_CORRUPT', 'Plan artifact required by lifecycle evidence is missing.', { planHash, file });
48
+ }
49
+ const content = readFileSync(file, 'utf8');
50
+ if (sha256Hex(content) !== planHash) {
51
+ throw new WorkflowError('STATE_CORRUPT', 'Plan artifact content does not match its recorded hash.', { planHash, file });
52
+ }
53
+ return content;
54
+ }
55
+
56
+ function sha256Hex(value: string): string {
57
+ return createHash('sha256').update(value).digest('hex');
58
+ }
@@ -0,0 +1,10 @@
1
+ import type { StepStatus } from '../contracts.js';
2
+
3
+ export type StepStartPhase = 'planned-clean' | 'planned-dirty' | 'failed-retry' | 'not-startable';
4
+
5
+ /** Replacement carryover admits a first start; a retry uses its current write scope. */
6
+ export function stepStartPhase(status: StepStatus | undefined, hasDirtyFiles: boolean): StepStartPhase {
7
+ if (status === 'planned') return hasDirtyFiles ? 'planned-dirty' : 'planned-clean';
8
+ if (status === 'failed') return 'failed-retry';
9
+ return 'not-startable';
10
+ }
@@ -0,0 +1,177 @@
1
+ import type {
2
+ PlanRiskAuditStepClassification,
3
+ TaskPlanInput,
4
+ TaskState,
5
+ } from '../contracts.js';
6
+ import { WorkflowError } from '../errors.js';
7
+
8
+ export function validatePlan(plan: TaskPlanInput): void {
9
+ const issues: string[] = [];
10
+ if (!plan.objective.trim()) issues.push('Plan objective is required.');
11
+ if (plan.requirements.length === 0) issues.push('At least one requirement is required.');
12
+ if (plan.acceptance.length === 0) issues.push('At least one acceptance scenario is required.');
13
+ if (plan.steps.length === 0) issues.push('At least one STEP-* is required.');
14
+ if (!plan.knowledgeImpactReason.trim()) issues.push('knowledgeImpactReason is required.');
15
+ if (!Number.isInteger(plan.knowledgeMapRevision) || plan.knowledgeMapRevision < 1) {
16
+ issues.push('knowledgeMapRevision must be a positive integer.');
17
+ }
18
+ if (!/^[a-f0-9]{64}$/.test(plan.knowledgeMapHash)) issues.push('knowledgeMapHash must be a SHA-256 hash.');
19
+ if (plan.knowledgeImpact === 'none' && plan.knowledgeTargets.length > 0) {
20
+ issues.push('knowledgeImpact none cannot declare knowledgeTargets.');
21
+ }
22
+ if (plan.knowledgeImpact !== 'none' && plan.knowledgeTargets.length === 0) {
23
+ issues.push(`knowledgeImpact ${plan.knowledgeImpact} requires at least one knowledgeTarget.`);
24
+ }
25
+ for (const target of plan.knowledgeTargets) {
26
+ if (!target.reason.trim()) issues.push(`Knowledge target ${target.path} requires a reason.`);
27
+ if (target.requirementIds.length === 0) issues.push(`Knowledge target ${target.path} requires requirementIds.`);
28
+ }
29
+
30
+ const ids = new Set<string>();
31
+ for (const step of plan.steps) {
32
+ if (!/^STEP-[0-9]{3}(?:-[A-Z0-9-]+)?$/.test(step.id)) issues.push(`Invalid Step id: ${step.id}`);
33
+ if (ids.has(step.id)) issues.push(`Duplicate Step id: ${step.id}`);
34
+ ids.add(step.id);
35
+ if (!step.title.trim() || !step.objective.trim()) issues.push(`${step.id} requires title and objective.`);
36
+ if (step.requirements.length === 0) issues.push(`${step.id} must cover at least one requirement.`);
37
+ if (step.allowedWrites.length === 0) issues.push(`${step.id} must declare allowedWrites.`);
38
+ if (step.expectedOutputs.length === 0) issues.push(`${step.id} must declare expectedOutputs.`);
39
+ if (step.checks.length === 0) issues.push(`${step.id} must declare at least one executable check.`);
40
+ }
41
+
42
+ for (const step of plan.steps) {
43
+ for (const dependency of step.dependencies) {
44
+ if (!ids.has(dependency)) issues.push(`${step.id} depends on missing ${dependency}.`);
45
+ if (dependency === step.id) issues.push(`${step.id} cannot depend on itself.`);
46
+ }
47
+ }
48
+
49
+ const covered = new Set(plan.steps.flatMap((step) => step.requirements));
50
+ for (const requirement of plan.requirements) {
51
+ if (!covered.has(requirement)) issues.push(`Requirement is not covered by any Step: ${requirement}`);
52
+ }
53
+
54
+ const visiting = new Set<string>();
55
+ const visited = new Set<string>();
56
+ const byId = new Map(plan.steps.map((step) => [step.id, step]));
57
+ const visit = (stepId: string): void => {
58
+ if (visited.has(stepId)) return;
59
+ if (visiting.has(stepId)) {
60
+ issues.push(`Step dependency cycle includes ${stepId}.`);
61
+ return;
62
+ }
63
+ visiting.add(stepId);
64
+ for (const dependency of byId.get(stepId)?.dependencies ?? []) visit(dependency);
65
+ visiting.delete(stepId);
66
+ visited.add(stepId);
67
+ };
68
+ for (const step of plan.steps) visit(step.id);
69
+
70
+ const productionEdges = plan.productionEdges ?? [];
71
+ if (plan.requiresProductionEdges && productionEdges.length === 0) {
72
+ issues.push('Plan requiresProductionEdges but declares no explicit production edge.');
73
+ }
74
+ if (!plan.requiresProductionEdges && productionEdges.length > 0) {
75
+ issues.push('Plan declares productionEdges without the explicit requiresProductionEdges boundary.');
76
+ }
77
+ const edgeKeys = new Set<string>();
78
+ for (const edge of productionEdges) {
79
+ const key = `${edge.ownerStepId}\0${edge.consumerStepId}\0${edge.checkRef}`;
80
+ if (edgeKeys.has(key)) issues.push(`Duplicate production edge: ${edge.ownerStepId} -> ${edge.consumerStepId}.`);
81
+ edgeKeys.add(key);
82
+ const owner = byId.get(edge.ownerStepId);
83
+ const consumer = byId.get(edge.consumerStepId);
84
+ if (!owner) issues.push(`Production edge owner is missing: ${edge.ownerStepId}.`);
85
+ if (!consumer) issues.push(`Production edge consumer is missing: ${edge.consumerStepId}.`);
86
+ if (owner && consumer && owner.id === consumer.id) issues.push(`Production edge cannot self-reference ${owner.id}.`);
87
+ if (owner && consumer && !transitiveDependencies(consumer.id, byId).has(owner.id)) {
88
+ issues.push(`Production edge owner ${owner.id} is not a transitive dependency of consumer ${consumer.id}.`);
89
+ }
90
+ if (consumer && !consumer.checks.includes(edge.checkRef)) {
91
+ issues.push(`Production edge ${edge.ownerStepId} -> ${edge.consumerStepId} names a check not executed by its consumer.`);
92
+ }
93
+ }
94
+
95
+ if (issues.length > 0) throw new WorkflowError('TRANSITION_BLOCKED', 'Plan is not execution-ready.', { issues });
96
+ }
97
+
98
+ /**
99
+ * Derive the narrow beta.13 structural integration obligation from the independent
100
+ * Plan Risk Audit rather than trusting the planner-owned opt-in flag. This is not
101
+ * semantic output-to-evidence authority (P02): it only proves that a multi-Step
102
+ * runtime-composition Plan names an inbound production hand-off and executes its
103
+ * exact check in the consumer Step.
104
+ */
105
+ export function validateStructuralProductionEdgeAuthority(
106
+ task: Pick<TaskState, 'id' | 'steps' | 'productionEdges' | 'requiresProductionEdges'>,
107
+ classifications: readonly PlanRiskAuditStepClassification[],
108
+ ): void {
109
+ if (task.steps.length < 2) return;
110
+ const integrationConsumerIds = classifications
111
+ .filter((classification) => classification.categories.includes('runtime-composition'))
112
+ .map((classification) => classification.stepId)
113
+ .sort();
114
+ if (integrationConsumerIds.length === 0) return;
115
+
116
+ const edges = task.productionEdges ?? [];
117
+ const byId = new Map(task.steps.map((step) => [step.id, step]));
118
+ const missingConsumerEdgeIds = integrationConsumerIds.filter((consumerStepId) =>
119
+ !edges.some((edge) => edge.consumerStepId === consumerStepId));
120
+ const blockingGuardIds = [
121
+ ...(task.requiresProductionEdges === true ? [] : ['task.production-edge.explicit-boundary']),
122
+ ...(edges.length > 0 ? [] : ['task.production-edge.non-empty']),
123
+ ...(missingConsumerEdgeIds.length > 0 ? ['task.production-edge.runtime-consumer-covered'] : []),
124
+ ];
125
+ for (const edge of edges) {
126
+ const consumer = byId.get(edge.consumerStepId);
127
+ const owner = byId.get(edge.ownerStepId);
128
+ if (!consumer || !owner || !transitiveDependencies(consumer.id, byId).has(owner.id)
129
+ || !consumer.checks.includes(edge.checkRef)) {
130
+ blockingGuardIds.push('task.production-edge.executable-predecessor-binding');
131
+ break;
132
+ }
133
+ }
134
+ if (blockingGuardIds.length === 0) return;
135
+ throw new WorkflowError(
136
+ 'TRANSITION_BLOCKED',
137
+ `Runtime-composition authority for Task ${task.id} requires explicit production-edge coverage.`,
138
+ {
139
+ diagnosticCode: 'STRUCTURAL_PRODUCTION_EDGE_REQUIRED',
140
+ triggerAuthority: 'plan-risk-audit.runtime-composition',
141
+ integrationConsumerStepIds: integrationConsumerIds,
142
+ missingConsumerEdgeIds,
143
+ blockingGuardIds: [...new Set(blockingGuardIds)],
144
+ semanticAuthorityClaimed: false,
145
+ requiredAction: 'Declare requiresProductionEdges=true and bind each runtime-composition consumer to a transitive owner and one exact consumer check.',
146
+ },
147
+ );
148
+ }
149
+
150
+ function transitiveDependencies(
151
+ stepId: string,
152
+ byId: ReadonlyMap<string, TaskPlanInput['steps'][number]>,
153
+ ): Set<string> {
154
+ const result = new Set<string>();
155
+ const visit = (candidateId: string): void => {
156
+ for (const dependency of byId.get(candidateId)?.dependencies ?? []) {
157
+ if (result.has(dependency)) continue;
158
+ result.add(dependency);
159
+ visit(dependency);
160
+ }
161
+ };
162
+ visit(stepId);
163
+ return result;
164
+ }
165
+
166
+ export function pathAllowed(file: string, patterns: string[]): boolean {
167
+ const normalized = file.replaceAll('\\', '/').replace(/^\.\//, '');
168
+ return patterns.some((pattern) => {
169
+ const candidate = pattern.replaceAll('\\', '/').replace(/^\.\//, '');
170
+ if (candidate.endsWith('/**')) return normalized === candidate.slice(0, -3) || normalized.startsWith(candidate.slice(0, -2));
171
+ if (candidate.endsWith('/*')) {
172
+ const prefix = candidate.slice(0, -1);
173
+ return normalized.startsWith(prefix) && !normalized.slice(prefix.length).includes('/');
174
+ }
175
+ return normalized === candidate;
176
+ });
177
+ }
@@ -0,0 +1,46 @@
1
+ import { lstatSync, readdirSync, readFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { createHash } from 'node:crypto';
4
+ import type { RepositoryIdentity } from '../repository.js';
5
+ import { isVirginProjectRegistrationContent } from '../alpha6/adoption.js';
6
+ import { assertNoSymlinkPathComponents } from '../alpha6/store-sidecars.js';
7
+
8
+ /** Read-only bootstrap admission. Retained or unrecognized state is never bootstrap. */
9
+ export function isVirginRegistrationRoot(projectRoot: string, trustedRoot: string, identity: RepositoryIdentity, packageVersion: string): boolean {
10
+ try {
11
+ const relative = path.relative(path.resolve(trustedRoot), path.resolve(projectRoot));
12
+ if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) return false;
13
+ // The trusted state home itself can be absent on first use. Do not create it
14
+ // during observation, and do not confuse that case with invalid retained data.
15
+ try { lstatSync(trustedRoot); } catch (error) {
16
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') return true;
17
+ throw error;
18
+ }
19
+ assertNoSymlinkPathComponents(projectRoot, { label: 'Bootstrap project root', trustedRoot });
20
+ let root;
21
+ try { root = lstatSync(projectRoot); } catch (error) {
22
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') return true;
23
+ throw error;
24
+ }
25
+ if (!root.isDirectory() || root.isSymbolicLink()) return false;
26
+ const validFile = (file: string, hash?: string) => {
27
+ const stat = lstatSync(file);
28
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 64 * 1024) return false;
29
+ const bytes = readFileSync(file);
30
+ return (!hash || createHash('sha256').update(bytes).digest('hex') === hash)
31
+ && isVirginProjectRegistrationContent(bytes.toString('utf8'), identity, packageVersion);
32
+ };
33
+ for (const entry of readdirSync(projectRoot, { withFileTypes: true })) {
34
+ if (entry.isSymbolicLink()) return false;
35
+ if (entry.name === 'adoption-posture.jsonl') {
36
+ if (!validFile(path.join(projectRoot, entry.name))) return false;
37
+ } else if (entry.name === '.versions' && entry.isDirectory()) {
38
+ for (const snapshot of readdirSync(path.join(projectRoot, '.versions'))) {
39
+ const match = /^adoption-posture\.jsonl\.([a-f0-9]{64})$/.exec(snapshot);
40
+ if (!match || !validFile(path.join(projectRoot, '.versions', snapshot), match[1])) return false;
41
+ }
42
+ } else return false;
43
+ }
44
+ return true;
45
+ } catch { return false; }
46
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,24 @@
1
+ export type WorkflowErrorCode =
2
+ | 'INVALID_ARGUMENT'
3
+ | 'NOT_FOUND'
4
+ | 'STATE_CONFLICT'
5
+ | 'STATE_CORRUPT'
6
+ | 'LOCKED'
7
+ | 'LOCK_TOKEN_INVALID'
8
+ | 'TASK_RECOVERY_REQUIRED'
9
+ | 'TRANSITION_BLOCKED'
10
+ | 'GIT_PRECONDITION_FAILED'
11
+ | 'COMMAND_FAILED'
12
+ | 'CHECK_INFRASTRUCTURE_FAILED';
13
+
14
+ export class WorkflowError extends Error {
15
+ readonly code: WorkflowErrorCode;
16
+ readonly details: Record<string, unknown>;
17
+
18
+ constructor(code: WorkflowErrorCode, message: string, details: Record<string, unknown> = {}) {
19
+ super(message);
20
+ this.name = 'WorkflowError';
21
+ this.code = code;
22
+ this.details = details;
23
+ }
24
+ }
@@ -0,0 +1,61 @@
1
+ import {
2
+ chmodSync,
3
+ existsSync,
4
+ lstatSync,
5
+ mkdirSync,
6
+ readFileSync,
7
+ renameSync,
8
+ rmSync,
9
+ writeFileSync,
10
+ } from 'node:fs';
11
+ import path from 'node:path';
12
+ import { randomUUID } from 'node:crypto';
13
+ import { WorkflowError } from './errors.js';
14
+
15
+ export function ensureDirectory(directory: string): void {
16
+ mkdirSync(directory, { recursive: true, mode: 0o700 });
17
+ if (lstatSync(directory).isSymbolicLink()) {
18
+ throw new WorkflowError('STATE_CORRUPT', `State directory must not be a symlink: ${directory}`);
19
+ }
20
+ }
21
+
22
+ export function readJson<T>(file: string): T {
23
+ try {
24
+ return JSON.parse(readFileSync(file, 'utf8')) as T;
25
+ } catch (error) {
26
+ throw new WorkflowError('STATE_CORRUPT', `Cannot read JSON state: ${file}`, {
27
+ cause: error instanceof Error ? error.message : String(error),
28
+ });
29
+ }
30
+ }
31
+
32
+ export function writeJsonAtomic(file: string, value: unknown): void {
33
+ const directory = path.dirname(file);
34
+ ensureDirectory(directory);
35
+ if (existsSync(file) && lstatSync(file).isSymbolicLink()) {
36
+ throw new WorkflowError('STATE_CORRUPT', `Refusing to replace state symlink: ${file}`);
37
+ }
38
+ const temporary = path.join(directory, `.${path.basename(file)}.${randomUUID()}.tmp`);
39
+ try {
40
+ writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\n`, { encoding: 'utf8', mode: 0o600 });
41
+ chmodSync(temporary, 0o600);
42
+ renameSync(temporary, file);
43
+ } finally {
44
+ rmSync(temporary, { force: true });
45
+ }
46
+ }
47
+
48
+ export function writeTextAtomic(file: string, content: string): void {
49
+ const directory = path.dirname(file);
50
+ ensureDirectory(directory);
51
+ const temporary = path.join(directory, `.${path.basename(file)}.${randomUUID()}.tmp`);
52
+ try {
53
+ writeFileSync(temporary, content.endsWith('\n') ? content : `${content}\n`, {
54
+ encoding: 'utf8',
55
+ mode: 0o600,
56
+ });
57
+ renameSync(temporary, file);
58
+ } finally {
59
+ rmSync(temporary, { force: true });
60
+ }
61
+ }
@@ -0,0 +1,95 @@
1
+ import { PROTOCOL_VERSION, STATE_SCHEMA_VERSION } from './contracts.js';
2
+ import { PACKAGE_NAME, PACKAGE_VERSION } from './version.js';
3
+
4
+ export interface GatewayHandshake {
5
+ packageName: typeof PACKAGE_NAME;
6
+ packageVersion: typeof PACKAGE_VERSION;
7
+ protocolVersion: typeof PROTOCOL_VERSION;
8
+ stateSchemaVersion: typeof STATE_SCHEMA_VERSION;
9
+ capabilities: string[];
10
+ }
11
+
12
+ export function gatewayHandshake(): GatewayHandshake {
13
+ return {
14
+ packageName: PACKAGE_NAME,
15
+ packageVersion: PACKAGE_VERSION,
16
+ protocolVersion: PROTOCOL_VERSION,
17
+ stateSchemaVersion: STATE_SCHEMA_VERSION,
18
+ capabilities: workflowCapabilities(),
19
+ };
20
+ }
21
+
22
+ export function workflowCapabilities(): string[] {
23
+ return [
24
+ 'project-memory-v1',
25
+ 'graph-binding-v1',
26
+ 'local-state-v1',
27
+ 'milestone-lifecycle-v1',
28
+ 'milestone-human-final-gate-v1',
29
+ 'delegated-approval-v1',
30
+ 'task-knowledge-rebind-v1',
31
+ 'task-context-refresh-v1',
32
+ 'corrective-plan-audit-v1',
33
+ 'safe-update-preflight-v1',
34
+ 'bounded-dependency-provenance-recovery-v1',
35
+ 'task-local-dependency-provenance-recovery-v1',
36
+ 'root-cause-corrective-replan-dirty-carryover-v1',
37
+ 'stop-override-context-rebind-v1',
38
+ 'stop-override-context-rebind-chain-v1',
39
+ 'stop-override-post-audit-dependency-revision-v1',
40
+ 'retained-reviewed-history-recovery-v1',
41
+ 'historical-step-provenance-recovery-v1',
42
+ 'milestone-progress-projection-v1',
43
+ 'bounded-plan-integrity-recovery-v1',
44
+ 'check-support-scope-augmentation-v1',
45
+ 'historical-check-support-scope-adoption-v1',
46
+ 'historical-check-support-dirty-carryover-v1',
47
+ 'downstream-proof-predecessor-recovery-v1',
48
+ 'active-downstream-proof-dependency-recovery-v1',
49
+ 'task-mechanical-feasibility-v1',
50
+ 'task-human-gate-lease-release-v1',
51
+ 'strict-reviewer-v1',
52
+ 'legacy-snapshot-v1',
53
+ 'plan-risk-audit-sidecar-v1',
54
+ 'step-strict-review-sidecar-v1',
55
+ 'step-remediation-circuit-breaker-sidecar-v1',
56
+ 'milestone-scope-change-sidecar-v1',
57
+ 'milestone-membership-integrity-v1',
58
+ 'milestone-task-dependency-dag-v1',
59
+ 'milestone-initial-plan-transaction-v1',
60
+ 'task-c1-handoff-sidecar-v1',
61
+ 'semantic-plan-risk-audit-v2',
62
+ 'guarded-remediation-continuation-v1',
63
+ 'cause-bound-rolling-refinement-v1',
64
+ 'failed-step-continuation-transaction-v1',
65
+ 'host-safe-credential-reference-v1',
66
+ 'structural-production-edge-contract-v1',
67
+ 'structural-production-edge-authority-v1',
68
+ 'observed-route-matrix-v2',
69
+ 'compact-status-v1',
70
+ 'reviewer-runtime-preflight-v1',
71
+ 'explicit-review-mode-v1',
72
+ 'app-owned-operational-preflight-v1',
73
+ 'milestone-reverse-membership-integrity-v1',
74
+ 'task-c1-handoff-bundle-v2',
75
+ 'alpha6-adoption-posture-v1',
76
+ 'milestone-initial-assembly-v1',
77
+ 'milestone-initial-assembly-navigation-v2',
78
+ 'milestone-autonomy-contract-v1',
79
+ 'milestone-autonomy-content-refresh-v1',
80
+ 'milestone-autonomous-membership-evolution-v1',
81
+ 'audited-remediation-topology-v1',
82
+ 'audited-remediation-dependency-knowledge-refresh-v1',
83
+ 'corrective-derived-task-authority-v1',
84
+ 'task-c1-derived-worker-actor-v1',
85
+ 'guarded-corrective-auditor-routing-v1',
86
+ 'corrective-decision-context-recovery-v1',
87
+ 'plan-proof-obligations-v1',
88
+ 'structural-task-replacement-disabled-v1',
89
+ 'project-composite-transaction-journal-v1',
90
+ 'corrective-yield-journal-v1',
91
+ 'corrective-replan-journal-v1',
92
+ 'corrective-replan-human-gate-v1',
93
+ 'corrective-replan-protocol-v2',
94
+ ];
95
+ }