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,125 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { changedFiles, runGit } from './repository.js';
3
+
4
+ const HEX_64 = /^[a-f0-9]{64}$/;
5
+
6
+ export interface AppOwnedPreflightBase {
7
+ contract: 'codex-workflow-app-owned-preflight-v1';
8
+ authority: 'diagnostic-only';
9
+ owner: 'codex-app';
10
+ packageMutatesAppState: false;
11
+ atomicAppEffectClaimed: false;
12
+ }
13
+
14
+ export function watchdogPreflight(input: {
15
+ previousRouteFingerprint: string | null;
16
+ currentRouteFingerprint: string;
17
+ lastSuccessfulPollAt: string | null;
18
+ lastSignificantObservationAt: string | null;
19
+ }): AppOwnedPreflightBase & Record<string, unknown> {
20
+ const valid = HEX_64.test(input.currentRouteFingerprint)
21
+ && (input.previousRouteFingerprint === null || HEX_64.test(input.previousRouteFingerprint));
22
+ return {
23
+ ...appOwnedBase(),
24
+ eligible: valid,
25
+ routeChanged: input.previousRouteFingerprint !== input.currentRouteFingerprint,
26
+ schedulingPerformed: false,
27
+ checkpoint: {
28
+ lastSuccessfulPollAt: input.lastSuccessfulPollAt,
29
+ lastSignificantObservationAt: input.lastSignificantObservationAt,
30
+ },
31
+ blockers: valid ? [] : ['Route fingerprints must be exact SHA-256 values.'],
32
+ };
33
+ }
34
+
35
+ export function coordinatorReplacementPreflight(input: {
36
+ previousCoordinatorTerminal: boolean;
37
+ replacementCoordinatorId: string;
38
+ activeSupervisorIds: string[];
39
+ coreReadbackFingerprint: string;
40
+ }): AppOwnedPreflightBase & Record<string, unknown> {
41
+ const supervisors = [...new Set(input.activeSupervisorIds)];
42
+ const eligible = input.previousCoordinatorTerminal
43
+ && input.replacementCoordinatorId.trim().length > 0
44
+ && supervisors.length === 1
45
+ && supervisors[0] === input.replacementCoordinatorId
46
+ && HEX_64.test(input.coreReadbackFingerprint);
47
+ return {
48
+ ...appOwnedBase(),
49
+ eligibleToRequestReplacement: eligible,
50
+ replacementPerformed: false,
51
+ activeSupervisorIds: supervisors,
52
+ blockers: eligible
53
+ ? []
54
+ : ['Terminal predecessor, one replacement supervisor, and exact Core readback are required.'],
55
+ };
56
+ }
57
+
58
+ export function appConsentPreflight(input: {
59
+ requestedTaskId: string;
60
+ presentedTaskId: string | null;
61
+ presentedTaskRevision: number | null;
62
+ consentReceiptHash: string | null;
63
+ }): AppOwnedPreflightBase & Record<string, unknown> {
64
+ const receiptStructurallyValid = input.presentedTaskId === input.requestedTaskId
65
+ && Number.isInteger(input.presentedTaskRevision)
66
+ && (input.presentedTaskRevision ?? -1) >= 0
67
+ && typeof input.consentReceiptHash === 'string'
68
+ && HEX_64.test(input.consentReceiptHash);
69
+ return {
70
+ ...appOwnedBase(),
71
+ receiptStructurallyValid,
72
+ consentGrantedByPackage: false,
73
+ semanticAuthorityEstablished: false,
74
+ requiredAppReadback: receiptStructurallyValid ? 'verify-consent-with-host' : 'obtain-current-consent-receipt',
75
+ };
76
+ }
77
+
78
+ export function worktreeObservationPreflight(
79
+ repositoryRoot: string,
80
+ limit = 32,
81
+ ): AppOwnedPreflightBase & Record<string, unknown> {
82
+ const boundedLimit = Number.isInteger(limit) && limit > 0 ? Math.min(limit, 32) : 32;
83
+ const blocks = runGit(repositoryRoot, ['worktree', 'list', '--porcelain'])
84
+ .split(/\n\n+/)
85
+ .filter(Boolean);
86
+ const records = blocks.slice(0, boundedLimit).map((block) => {
87
+ const fields = new Map<string, string>();
88
+ for (const line of block.split('\n')) {
89
+ const separator = line.indexOf(' ');
90
+ if (separator > 0) fields.set(line.slice(0, separator), line.slice(separator + 1));
91
+ }
92
+ const worktree = fields.get('worktree') ?? '';
93
+ const dirty = changedFiles(worktree).sort();
94
+ return {
95
+ worktree,
96
+ head: fields.get('HEAD') ?? null,
97
+ branch: fields.get('branch')?.replace(/^refs\/heads\//, '') ?? null,
98
+ dirtyCount: dirty.length,
99
+ dirtyFingerprint: sha256(dirty.join('\n')),
100
+ };
101
+ });
102
+ return {
103
+ ...appOwnedBase(),
104
+ bounded: true,
105
+ limit: boundedLimit,
106
+ truncated: blocks.length > boundedLimit,
107
+ records,
108
+ observationFingerprint: sha256(JSON.stringify(records)),
109
+ worktreeSelectionPerformed: false,
110
+ };
111
+ }
112
+
113
+ function appOwnedBase(): AppOwnedPreflightBase {
114
+ return {
115
+ contract: 'codex-workflow-app-owned-preflight-v1',
116
+ authority: 'diagnostic-only',
117
+ owner: 'codex-app',
118
+ packageMutatesAppState: false,
119
+ atomicAppEffectClaimed: false,
120
+ };
121
+ }
122
+
123
+ function sha256(value: string): string {
124
+ return createHash('sha256').update(value).digest('hex');
125
+ }
@@ -0,0 +1,175 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import type { TaskState, SidecarEventRecord } from './contracts.js';
5
+ import { requireCurrentStrictStepReview } from './alpha6/review.js';
6
+ import { readCurrentPlanRiskAudit } from './alpha6/plan-risk.js';
7
+ import { canonicalJsonStringify, sha256Hex, readSidecarEvents } from './alpha6/store-sidecars.js';
8
+ import { inspectBoundedWorkflowDependencyCommit } from './dependency-provenance.js';
9
+ import { validateTaskHistory } from './git.js';
10
+ import { currentBranch, headCommit, isClean, runGit } from './repository.js';
11
+ import { FileStateStore } from './state/store.js';
12
+ import { PACKAGE_VERSION } from './version.js';
13
+
14
+ export const PENDING_REVIEW_SOURCE_VERSION: string = '2.0.0-beta.13.1';
15
+ export interface PendingReviewUpdateBinding {
16
+ protocol: 'pending-review-update-v1';
17
+ projectId: string;
18
+ taskId: string;
19
+ taskRevision: number;
20
+ taskBranch: string;
21
+ baseBranch: string;
22
+ baseHead: string;
23
+ planHash: string;
24
+ briefHash: string;
25
+ stepId: string;
26
+ completionCommit: string;
27
+ pendingEventHash: string;
28
+ evidenceHash: string;
29
+ sourceVersion: string;
30
+ targetVersion: string;
31
+ targetRuntimeHash: string;
32
+ }
33
+ export interface PendingReviewUpdateTransport {
34
+ source: PendingReviewUpdateBinding;
35
+ baseTransportCommit: string;
36
+ }
37
+ export const PENDING_REVIEW_UPDATE_SIDECAR = 'pending-review-updates.jsonl';
38
+ export interface PendingReviewUpdateEvent extends SidecarEventRecord, PendingReviewUpdateTransport {
39
+ taskTransportCommit: string;
40
+ }
41
+ function requireCondition(condition: unknown, message: string): asserts condition {
42
+ if (!condition) throw new Error(message);
43
+ }
44
+
45
+ /** Source inspection never writes state, refreshes Knowledge, or manufactures pending evidence. */
46
+ export function pendingReviewSourceBinding(
47
+ repository: string, store: FileStateStore, task: TaskState,
48
+ completionCommit = headCommit(repository), baseHead = runGit(repository, ['rev-parse', task.baseBranch]),
49
+ ): PendingReviewUpdateBinding {
50
+ requireCondition(task.status === 'in_progress' && task.workspaceOwner === 'local', 'Expected a local in-progress Task.');
51
+ requireCondition(task.taskBranch && currentBranch(repository) === task.taskBranch, 'Expected the exact Task branch.');
52
+ requireCondition(isClean(repository), 'Expected a clean checkout.');
53
+ const active = task.steps.filter((step) => step.status === 'in_progress');
54
+ requireCondition(active.length === 1 && active[0]?.evidence?.commitSha === completionCommit,
55
+ 'Exactly one in-progress Step must have completion evidence at the bound product HEAD.');
56
+ const step = active[0]!;
57
+ requireCondition(task.planHash && task.briefHash, 'Missing Plan or Brief binding.');
58
+ const taskRoot = store.taskRoot(task.projectId, task.id);
59
+ requireCondition(sha256Hex(readFileSync(path.join(taskRoot, 'plan.md'), 'utf8')) === task.planHash
60
+ && sha256Hex(readFileSync(path.join(taskRoot, 'brief.md'), 'utf8')) === task.briefHash,
61
+ 'Plan or Brief artifact hash mismatch.');
62
+ const audit = readCurrentPlanRiskAudit(store, task);
63
+ requireCondition(audit?.reviewRequiredStepIds.includes(step.id), 'The current Plan audit must require strict review.');
64
+ const cycle = requireCurrentStrictStepReview(store, task, step.id);
65
+ requireCondition(cycle.resolution === 'pending' && cycle.pendingEvent, 'Expected a hash-valid pending review chain.');
66
+ validateTaskHistory(repository, task, completionCommit);
67
+ for (const revision of [completionCommit, baseHead]) {
68
+ const manifest = JSON.parse(runGit(repository, ['show', `${revision}:package.json`])) as {
69
+ devDependencies?: Record<string, string>; dependencies?: Record<string, string> };
70
+ const lock = JSON.parse(runGit(repository, ['show', `${revision}:package-lock.json`])) as {
71
+ packages?: Record<string, { version?: string; devDependencies?: Record<string, string>; dependencies?: Record<string, string> }> };
72
+ requireCondition(manifest.devDependencies?.['codex-workflow-v2'] === PENDING_REVIEW_SOURCE_VERSION
73
+ && manifest.dependencies?.['codex-workflow-v2'] === undefined
74
+ && lock.packages?.['']?.devDependencies?.['codex-workflow-v2'] === PENDING_REVIEW_SOURCE_VERSION
75
+ && lock.packages?.['']?.dependencies?.['codex-workflow-v2'] === undefined
76
+ && lock.packages?.['node_modules/codex-workflow-v2']?.version === PENDING_REVIEW_SOURCE_VERSION,
77
+ 'Every source manifest and lock root/package entry must bind the exact source devDependency.');
78
+ }
79
+ return {
80
+ protocol: 'pending-review-update-v1', projectId: task.projectId, taskId: task.id,
81
+ taskRevision: task.revision, taskBranch: task.taskBranch, baseBranch: task.baseBranch, baseHead,
82
+ planHash: task.planHash, briefHash: task.briefHash, stepId: step.id, completionCommit,
83
+ pendingEventHash: cycle.pendingEvent.eventHash, evidenceHash: cycle.binding.evidenceHash,
84
+ sourceVersion: PENDING_REVIEW_SOURCE_VERSION, targetVersion: PACKAGE_VERSION,
85
+ targetRuntimeHash: pendingReviewRunnerFingerprint(),
86
+ };
87
+ }
88
+
89
+ export function assertPendingReviewTransport(
90
+ repository: string, source: PendingReviewUpdateBinding, taskHead: string, baseHead: string,
91
+ ): void {
92
+ requireCondition(source.protocol === 'pending-review-update-v1'
93
+ && source.sourceVersion === PENDING_REVIEW_SOURCE_VERSION
94
+ && source.targetVersion === PACKAGE_VERSION && source.targetVersion !== source.sourceVersion
95
+ && source.targetRuntimeHash === pendingReviewRunnerFingerprint(),
96
+ 'Unsupported pending review source/target compatibility.');
97
+ for (const [commit, parent] of [[taskHead, source.completionCommit], [baseHead, source.baseHead]]) {
98
+ const inspected = inspectBoundedWorkflowDependencyCommit(repository, commit!);
99
+ requireCondition(inspected !== null && inspected.parentCommitSha === parent && inspected.packageVersion === source.targetVersion,
100
+ 'Expected an exact dependency-only transport on the bound parent.');
101
+ requireCondition(runGit(repository, ['rev-list', '--parents', '-n', '1', commit!]).split(' ').length === 2,
102
+ 'Dependency transport must have exactly one parent.');
103
+ const manifest = JSON.parse(runGit(repository, ['show', `${parent}:package.json`])) as { devDependencies?: Record<string, string> };
104
+ requireCondition(manifest.devDependencies?.['codex-workflow-v2'] === source.sourceVersion,
105
+ 'Dependency transport parent must retain the exact source pin.');
106
+ }
107
+ requireCondition(runGit(repository, ['rev-parse', source.baseBranch]) === baseHead,
108
+ 'The bound base branch moved after transport.');
109
+ }
110
+
111
+ /** This is deliberately only used by external sealed Step review, never generic review/seal logic. */
112
+ export function verifiedPendingReviewTransport(repository: string, task: TaskState, stepId: string, taskRoot: string): PendingReviewUpdateTransport | null {
113
+ if (!existsSync(path.join(taskRoot, PENDING_REVIEW_UPDATE_SIDECAR))) return null;
114
+ const head = headCommit(repository);
115
+ const events = readSidecarEvents<PendingReviewUpdateEvent>(path.join(taskRoot, PENDING_REVIEW_UPDATE_SIDECAR), { trustedRoot: path.resolve(taskRoot, '../../../..') });
116
+ const matching = events.filter((event) => event.taskTransportCommit === head && event.source?.stepId === stepId);
117
+ if (matching.length === 0) return null;
118
+ const store = new FileStateStore({ root: path.resolve(taskRoot, '../../../..') });
119
+ requireCondition(store.taskRoot(task.projectId, task.id) === path.resolve(taskRoot), 'Unexpected Task sidecar root.');
120
+ // Review payloads can carry authorized check-support allowedWrites augmentations.
121
+ // Only canonical stored Step definitions establish the pending evidence hash.
122
+ const canonical = store.readTask(task.projectId, task.id);
123
+ requireCondition(canonical.revision === task.revision && canonical.planHash === task.planHash
124
+ && canonical.briefHash === task.briefHash
125
+ && canonicalJsonStringify(canonical.steps.find((step) => step.id === stepId)?.evidence ?? null)
126
+ === canonicalJsonStringify(task.steps.find((step) => step.id === stepId)?.evidence ?? null),
127
+ 'Review payload no longer matches the canonical Task.');
128
+ task = canonical;
129
+ requireCondition(events.length === 1 && matching.length === 1, 'Expected one exact pending review update sidecar.');
130
+ const transport = matching[0]!;
131
+ const records = (task.dependencyProvenanceRecoveries ?? []).filter((record) => record.commitSha === head);
132
+ requireCondition(records.length === 1, 'Missing unique dependency registration for the review bridge.');
133
+ const record = records[0]!;
134
+ const source = transport.source;
135
+ const cycle = requireCurrentStrictStepReview(store, task, stepId);
136
+ requireCondition(cycle.pendingEvent?.eventHash === source.pendingEventHash
137
+ && cycle.binding.evidenceHash === source.evidenceHash, 'Transported pending review binding changed.');
138
+ const step = task.steps.find((candidate) => candidate.id === stepId);
139
+ requireCondition(source.projectId === task.projectId && source.taskId === task.id
140
+ && source.taskBranch === task.taskBranch && source.baseBranch === task.baseBranch
141
+ && currentBranch(repository) === source.taskBranch && isClean(repository)
142
+ && source.planHash === task.planHash && source.briefHash === task.briefHash
143
+ && source.completionCommit === step?.evidence?.commitSha
144
+ && source.taskRevision < task.revision && task.systemCommits.includes(head)
145
+ && record.parentCommitSha === source.completionCommit && record.packageVersion === source.targetVersion,
146
+ 'Pending review transport no longer matches the current Task.');
147
+ assertPendingReviewTransport(repository, source, head, transport.baseTransportCommit);
148
+ validateTaskHistory(repository, task);
149
+ return transport;
150
+ }
151
+
152
+ /** Verify the exact external runner's published build bytes without installing or writing. */
153
+ export function pendingReviewRunnerFingerprint(): string {
154
+ const moduleDirectory = path.dirname(fileURLToPath(import.meta.url));
155
+ const packageRoot = path.resolve(moduleDirectory, '../..');
156
+ const packageContent = readFileSync(path.join(packageRoot, 'package.json'), 'utf8');
157
+ const pkg = JSON.parse(packageContent) as { name: string; version: string };
158
+ const manifest = JSON.parse(readFileSync(path.join(packageRoot, 'dist/reviewer-runtime-build.json'), 'utf8')) as {
159
+ protocol: string; packageName: string; packageVersion: string; packageManifestSha256: string;
160
+ files: Array<{ path: string; sha256: string }>;
161
+ };
162
+ requireCondition(pkg.name === 'codex-workflow-v2' && pkg.version === PACKAGE_VERSION
163
+ && manifest.protocol === 'codex-workflow-runtime-build-manifest-v1'
164
+ && manifest.packageName === pkg.name && manifest.packageVersion === pkg.version
165
+ && manifest.packageManifestSha256 === sha256Hex(packageContent), 'Exact target runner package/build manifest mismatch.');
166
+ requireCondition(['cli.js', 'workflow.js', 'reviewer.js', 'gateway-handshake.js', 'pending-review-update.js']
167
+ .every((name) => manifest.files.some((entry) => entry.path === `dist/src/${name}`)), 'Target runner build entrypoints are incomplete.');
168
+ for (const entry of manifest.files) {
169
+ requireCondition(/^dist\/src\/(?:[a-zA-Z0-9_-]+\/)*[a-zA-Z0-9_-]+\.js$/.test(entry.path)
170
+ && /^[a-f0-9]{64}$/.test(entry.sha256), 'Invalid target build entry.');
171
+ requireCondition(sha256Hex(readFileSync(path.join(moduleDirectory, entry.path.slice('dist/src/'.length)))) === entry.sha256,
172
+ 'Exact target runner runtime bytes differ from build evidence.');
173
+ }
174
+ return sha256Hex(canonicalJsonStringify(manifest));
175
+ }
@@ -0,0 +1,99 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { realpathSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import { spawnSync } from 'node:child_process';
5
+ import { WorkflowError } from './errors.js';
6
+
7
+ export interface RepositoryIdentity {
8
+ projectId: string;
9
+ repositoryRoot: string;
10
+ gitCommonDir: string;
11
+ }
12
+
13
+ // Bound memory use while allowing complete multi-megabyte review diffs. Overflow is
14
+ // an explicit failure, including for probes that allow ordinary Git exit failures.
15
+ const GIT_MAX_BUFFER = 64 * 1024 * 1024;
16
+ const GIT_DIAGNOSTIC_LIMIT = 4096;
17
+
18
+ export function runGit(repositoryRoot: string, args: string[], allowFailure = false): string {
19
+ const result = spawnSync('git', ['-C', repositoryRoot, ...args], {
20
+ encoding: 'utf8',
21
+ maxBuffer: GIT_MAX_BUFFER,
22
+ });
23
+ if (result.error || result.signal || (result.status !== 0 && !allowFailure)) {
24
+ throw new WorkflowError('COMMAND_FAILED', `git ${args.join(' ')} failed`, {
25
+ stdout: (result.stdout ?? '').slice(0, GIT_DIAGNOSTIC_LIMIT).trim(),
26
+ stderr: (result.stderr ?? '').slice(0, GIT_DIAGNOSTIC_LIMIT).trim(),
27
+ stdoutTruncated: (result.stdout?.length ?? 0) > GIT_DIAGNOSTIC_LIMIT,
28
+ stderrTruncated: (result.stderr?.length ?? 0) > GIT_DIAGNOSTIC_LIMIT,
29
+ exitCode: result.status,
30
+ signal: result.signal,
31
+ errorCode: (result.error as NodeJS.ErrnoException | undefined)?.code ?? null,
32
+ maxBufferBytes: GIT_MAX_BUFFER,
33
+ });
34
+ }
35
+ return result.stdout.trim();
36
+ }
37
+
38
+ export function resolveRepositoryIdentity(repository: string): RepositoryIdentity {
39
+ const repositoryRoot = realpathSync(runGit(repository, ['rev-parse', '--show-toplevel']));
40
+ const rawCommonDir = runGit(repositoryRoot, ['rev-parse', '--git-common-dir']);
41
+ const commonCandidate = path.isAbsolute(rawCommonDir)
42
+ ? rawCommonDir
43
+ : path.resolve(repositoryRoot, rawCommonDir);
44
+ const gitCommonDir = realpathSync(commonCandidate);
45
+ const projectId = createHash('sha256').update(gitCommonDir).digest('hex').slice(0, 24);
46
+ return { projectId, repositoryRoot, gitCommonDir };
47
+ }
48
+
49
+ export function currentBranch(repositoryRoot: string): string | null {
50
+ const branch = runGit(repositoryRoot, ['branch', '--show-current'], true);
51
+ return branch || null;
52
+ }
53
+
54
+ export function headCommit(repositoryRoot: string): string {
55
+ return runGit(repositoryRoot, ['rev-parse', 'HEAD']);
56
+ }
57
+
58
+ export function changedFiles(repositoryRoot: string): string[] {
59
+ const result = spawnSync(
60
+ 'git',
61
+ ['-C', repositoryRoot, 'status', '--porcelain=v1', '--untracked-files=all'],
62
+ { encoding: 'utf8' },
63
+ );
64
+ if (result.status !== 0) {
65
+ throw new WorkflowError('COMMAND_FAILED', 'Unable to inspect Git working tree.', {
66
+ stdout: result.stdout.trim(),
67
+ stderr: result.stderr.trim(),
68
+ exitCode: result.status,
69
+ });
70
+ }
71
+ const output = result.stdout.trimEnd();
72
+ if (!output) return [];
73
+ return output.split('\n').map((line) => {
74
+ const entry = line.slice(3).trim();
75
+ const renameTarget = entry.includes(' -> ') ? entry.split(' -> ').at(-1) : entry;
76
+ return renameTarget?.replaceAll('\\', '/') ?? entry;
77
+ });
78
+ }
79
+
80
+ export function isClean(repositoryRoot: string): boolean {
81
+ return changedFiles(repositoryRoot).length === 0;
82
+ }
83
+
84
+ export function gitIsAncestor(repositoryRoot: string, ancestor: string, descendant: string): boolean {
85
+ const result = spawnSync(
86
+ 'git',
87
+ ['-C', repositoryRoot, 'merge-base', '--is-ancestor', ancestor, descendant],
88
+ { encoding: 'utf8' },
89
+ );
90
+ if (result.status === 0) return true;
91
+ if (result.status === 1) return false;
92
+ throw new WorkflowError('COMMAND_FAILED', 'Unable to verify Git ancestry.', {
93
+ ancestor,
94
+ descendant,
95
+ stdout: result.stdout.trim(),
96
+ stderr: result.stderr.trim(),
97
+ exitCode: result.status,
98
+ });
99
+ }