codex-workflow-v2 2.0.0-beta.12.3 → 2.0.0-beta.12.5

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 (34) hide show
  1. package/README.md +18 -4
  2. package/dist/src/alpha6/downstream-proof.d.ts +29 -0
  3. package/dist/src/alpha6/downstream-proof.js +249 -0
  4. package/dist/src/alpha6/downstream-proof.js.map +1 -0
  5. package/dist/src/alpha7/corrective-recovery.js +37 -15
  6. package/dist/src/alpha7/corrective-recovery.js.map +1 -1
  7. package/dist/src/cli.js +10 -0
  8. package/dist/src/cli.js.map +1 -1
  9. package/dist/src/contracts.d.ts +19 -0
  10. package/dist/src/git.js +1 -0
  11. package/dist/src/git.js.map +1 -1
  12. package/dist/src/version.d.ts +1 -1
  13. package/dist/src/version.js +1 -1
  14. package/dist/src/workflow.d.ts +5 -0
  15. package/dist/src/workflow.js +152 -3
  16. package/dist/src/workflow.js.map +1 -1
  17. package/docs/autonomy-guardrails.md +25 -1
  18. package/docs/decisions.md +6 -0
  19. package/docs/development-flow.md +12 -0
  20. package/docs/pdf/codex-workflow-v2-architecture-ru.pdf +0 -0
  21. package/docs/pdf/codex-workflow-v2-chat-only-guide-ru.pdf +0 -0
  22. package/docs/pdf/codex-workflow-v2-technical-reference-ru.pdf +0 -0
  23. package/docs/pdf/sources/codex-workflow-v2-architecture-ru.md +26 -7
  24. package/docs/pdf/sources/codex-workflow-v2-chat-only-guide-ru.md +24 -8
  25. package/docs/pdf/sources/codex-workflow-v2-technical-reference-ru.md +35 -11
  26. package/docs/release.md +9 -1
  27. package/docs/updating-existing-project.md +6 -1
  28. package/docs/validation-report.md +84 -80
  29. package/package.json +1 -1
  30. package/plugins/codex-workflow-gateway/.codex-plugin/plugin.json +1 -1
  31. package/plugins/codex-workflow-gateway/references/protocol.md +20 -1
  32. package/plugins/codex-workflow-gateway/skills/codex-workflow-gateway/SKILL.md +30 -1
  33. package/schemas/downstream-proof-invalidation-event.schema.json +70 -0
  34. package/schemas/task.schema.json +5 -0
package/README.md CHANGED
@@ -43,7 +43,7 @@ cross-machine synchronization are not part of its contract.
43
43
  Node.js 22 or newer is required. Consumers pin the exact package version:
44
44
 
45
45
  ```bash
46
- npm install --save-dev --save-exact codex-workflow-v2@2.0.0-beta.12.3
46
+ npm install --save-dev --save-exact codex-workflow-v2@2.0.0-beta.12.5
47
47
  npx codex-workflow gateway handshake --repo .
48
48
  npx codex-workflow doctor --repo .
49
49
  ```
@@ -187,7 +187,7 @@ sidecars:
187
187
  `corrective-replan-executions.jsonl`, `step-review-events.jsonl`,
188
188
  `reviewer-attestations.jsonl`, `remediation-events.jsonl`,
189
189
  `corrective-decisions.jsonl`, `corrective-decision-recoveries.jsonl`,
190
- `remediation-mode-recoveries.jsonl`, `handoffs.jsonl`
190
+ `remediation-mode-recoveries.jsonl`, `downstream-proof-invalidations.jsonl`, `handoffs.jsonl`
191
191
  - milestone: `scope-change-events.jsonl`, `autonomy-contract-events.jsonl`
192
192
 
193
193
  Milestone scope-change recovery also uses Milestone-local `.transactions/` journal files.
@@ -214,6 +214,19 @@ definition, Git HEAD, manifest hash, and complete dirty-file set. The recovery r
214
214
  journaled corrective-replan path. It does not edit the Plan, run a second failing attempt, widen
215
215
  `allowedWrites`, or modify the worktree.
216
216
 
217
+ When an active downstream proof exposes a required change in files owned by a completed transitive
218
+ predecessor Step, fresh `next` advertises `task downstream-proof-recover` instead of the impossible
219
+ `task step-complete`. The lease-bound transaction preserves worktree and HEAD, invalidates only the
220
+ affected predecessor completion evidence, retains its commits as historical `invalidatedStepCommits`, yields
221
+ C1, and returns the same Task to ordinary planning. A fresh replacement Plan, Plan Risk Audit, and
222
+ execution authorization are required. Unrelated dirty files, non-predecessor ownership, unregistered
223
+ history, or damaged invalidation evidence fail closed.
224
+
225
+ Docker socket `EPERM`/permission errors observed inside a sandbox are access-boundary evidence, not
226
+ daemon-failure evidence. The gateway repeats the exact read-only probe or Plan check once with
227
+ sandbox escalation without consuming a remediation/infra retry. Docker Desktop restart or image
228
+ mutation requires separate authority and an escalated probe that actually proves daemon failure.
229
+
217
230
  For a terminal split, follow the [canonical replacement stop procedure](docs/split-required-recovery.md).
218
231
  P04-A intentionally disables structural replacement; P04-B/P05 must define the topology-preserving
219
232
  transaction before the route can be used.
@@ -302,10 +315,11 @@ alpha regressions that must not recur are tracked in the
302
315
  - [Chat-only development guide](docs/pdf/codex-workflow-v2-chat-only-guide-ru.pdf)
303
316
  - [Deep technical reference](docs/pdf/codex-workflow-v2-technical-reference-ru.pdf)
304
317
 
305
- These beta.12.3 documents cover delegated Discovery, Milestone Autonomy Contract issuance,
318
+ These beta.12.5 documents cover delegated Discovery, Milestone Autonomy Contract issuance,
306
319
  dependency-aware Task routing, C1 credential handling, external-sealed reviews, bounded
307
320
  Plan-integrity recovery, Task-local dependency provenance recovery, the human-confirmed legacy
308
- attempt-four stop override (including append-only multi-rebind and dependency-only HEAD continuation), and the P04-A
321
+ attempt-four stop override (including append-only multi-rebind, dependency-only HEAD continuation,
322
+ and evidence-accounted post-audit recovery revisions), and the P04-A
309
323
  structural-replacement stop boundary. Their reviewable
310
324
  sources live in [`docs/pdf/sources`](docs/pdf/sources), and `npm run docs:pdf:check` proves the
311
325
  tracked binaries were generated from the current source and package version. The PDFs remain
@@ -0,0 +1,29 @@
1
+ import type { DownstreamProofInvalidationEvent, TaskState } from '../contracts.js';
2
+ import type { FileStateStore } from '../state/store.js';
3
+ export interface DownstreamProofInvalidatedStep {
4
+ stepId: string;
5
+ evidenceCommit: string;
6
+ triggerFiles: string[];
7
+ }
8
+ export interface DownstreamProofRecoveryAssessment {
9
+ applicable: boolean;
10
+ eligible: boolean;
11
+ activeStepId: string | null;
12
+ dirtyFiles: string[];
13
+ outsideActiveStepFiles: string[];
14
+ invalidatedSteps: DownstreamProofInvalidatedStep[];
15
+ preservedHeadCommit: string | null;
16
+ blockers: string[];
17
+ }
18
+ export interface PreparedDownstreamProofInvalidation {
19
+ event: DownstreamProofInvalidationEvent;
20
+ postimage: string;
21
+ }
22
+ export declare function assessDownstreamProofRecovery(store: FileStateStore, repositoryRoot: string, task: TaskState): DownstreamProofRecoveryAssessment;
23
+ export declare function prepareDownstreamProofInvalidation(store: FileStateStore, resultTask: TaskState, assessment: DownstreamProofRecoveryAssessment, input: {
24
+ previousTaskRevision: number;
25
+ actor: string;
26
+ reason: string;
27
+ }, now: Date): PreparedDownstreamProofInvalidation;
28
+ export declare function readDownstreamProofInvalidations(store: FileStateStore, task: TaskState): DownstreamProofInvalidationEvent[];
29
+ export declare function downstreamProofInvalidationSidecar(): string;
@@ -0,0 +1,249 @@
1
+ import { WorkflowError } from '../errors.js';
2
+ import { pathAllowed } from '../domain/validation.js';
3
+ import { changedFiles, currentBranch, headCommit, } from '../repository.js';
4
+ import { validateTaskHistory } from '../git.js';
5
+ import { createUlid } from '../ulid.js';
6
+ import { PACKAGE_VERSION } from '../version.js';
7
+ import { prepareSidecarAppend } from './store-sidecars.js';
8
+ const SIDECAR = 'downstream-proof-invalidations.jsonl';
9
+ const HASH_64 = /^[a-f0-9]{64}$/;
10
+ const GIT_HASH = /^[a-f0-9]{40}$/;
11
+ const EVENT_ID = /^DPI-[0-9A-HJKMNP-TV-Z]{26}$/;
12
+ export function assessDownstreamProofRecovery(store, repositoryRoot, task) {
13
+ readDownstreamProofInvalidations(store, task);
14
+ const activeStep = task.steps.find((step) => step.status === 'in_progress') ?? null;
15
+ if (task.status !== 'in_progress' || !activeStep)
16
+ return emptyAssessment();
17
+ const dirtyFiles = changedFiles(repositoryRoot).sort();
18
+ const outsideActiveStepFiles = dirtyFiles.filter((file) => !pathAllowed(file, activeStep.allowedWrites));
19
+ if (outsideActiveStepFiles.length === 0) {
20
+ return {
21
+ ...emptyAssessment(),
22
+ activeStepId: activeStep.id,
23
+ dirtyFiles,
24
+ };
25
+ }
26
+ const blockers = [];
27
+ if (task.workspaceOwner !== 'local') {
28
+ blockers.push('Recovery requires a local Task workspace because the dirty file set is repository-local evidence.');
29
+ }
30
+ if (!task.taskBranch || currentBranch(repositoryRoot) !== task.taskBranch) {
31
+ blockers.push(`Current branch must equal the recorded Task branch ${task.taskBranch ?? '<missing>'}.`);
32
+ }
33
+ if (!task.planHash)
34
+ blockers.push('Task has no current Plan hash.');
35
+ if (activeStep.evidence !== null) {
36
+ blockers.push(`Active Step ${activeStep.id} already has completion evidence and must use strict-review recovery.`);
37
+ }
38
+ const predecessorIds = transitivePredecessors(task, activeStep.id, blockers);
39
+ const seedIds = new Set();
40
+ for (const file of outsideActiveStepFiles) {
41
+ const owners = task.steps.filter((step) => step.status === 'completed'
42
+ && predecessorIds.has(step.id)
43
+ && pathAllowed(file, step.allowedWrites));
44
+ if (owners.length === 0) {
45
+ blockers.push(`${file} is outside ${activeStep.id} and is not owned by a completed transitive predecessor.`);
46
+ continue;
47
+ }
48
+ for (const owner of owners)
49
+ seedIds.add(owner.id);
50
+ }
51
+ const invalidatedIds = completedDependentClosure(task, seedIds, predecessorIds);
52
+ const invalidatedSteps = task.steps
53
+ .filter((step) => invalidatedIds.has(step.id))
54
+ .map((step) => {
55
+ if (!step.evidence) {
56
+ blockers.push(`Completed predecessor ${step.id} has no completion evidence.`);
57
+ }
58
+ return {
59
+ stepId: step.id,
60
+ evidenceCommit: step.evidence?.commitSha ?? '',
61
+ triggerFiles: outsideActiveStepFiles.filter((file) => pathAllowed(file, step.allowedWrites)),
62
+ };
63
+ });
64
+ if (invalidatedSteps.length === 0)
65
+ blockers.push('No completed predecessor completion authority can be invalidated.');
66
+ try {
67
+ validateTaskHistory(repositoryRoot, task);
68
+ }
69
+ catch (error) {
70
+ blockers.push(error instanceof Error ? error.message : String(error));
71
+ }
72
+ return {
73
+ applicable: true,
74
+ eligible: blockers.length === 0,
75
+ activeStepId: activeStep.id,
76
+ dirtyFiles,
77
+ outsideActiveStepFiles,
78
+ invalidatedSteps,
79
+ preservedHeadCommit: headCommit(repositoryRoot),
80
+ blockers,
81
+ };
82
+ }
83
+ export function prepareDownstreamProofInvalidation(store, resultTask, assessment, input, now) {
84
+ if (!assessment.applicable || !assessment.eligible || !assessment.activeStepId || !assessment.preservedHeadCommit) {
85
+ throw new WorkflowError('TRANSITION_BLOCKED', 'Downstream proof recovery assessment is not eligible.', {
86
+ blockers: assessment.blockers,
87
+ });
88
+ }
89
+ const actor = normalizeRequired(input.actor, 'Recovery actor');
90
+ const reason = normalizeRequired(input.reason, 'Recovery reason');
91
+ if (!resultTask.planHash)
92
+ throw new WorkflowError('STATE_CORRUPT', 'Recovered Task has no Plan hash.');
93
+ if (resultTask.revision !== input.previousTaskRevision + 1) {
94
+ throw new WorkflowError('STATE_CONFLICT', 'Recovered Task revision does not advance exactly once.');
95
+ }
96
+ const events = readDownstreamProofInvalidations(store, resultTask);
97
+ const prepared = prepareSidecarAppend(events, {
98
+ eventId: `DPI-${createUlid(now.getTime())}`,
99
+ recordedAt: now.toISOString(),
100
+ taskId: resultTask.id,
101
+ previousTaskRevision: input.previousTaskRevision,
102
+ resultTaskRevision: resultTask.revision,
103
+ planHash: resultTask.planHash,
104
+ activeStepId: assessment.activeStepId,
105
+ invalidatedSteps: assessment.invalidatedSteps.map((step) => ({
106
+ stepId: step.stepId,
107
+ evidenceCommit: step.evidenceCommit,
108
+ triggerFiles: [...step.triggerFiles],
109
+ })),
110
+ dirtyFiles: [...assessment.dirtyFiles],
111
+ preservedHeadCommit: assessment.preservedHeadCommit,
112
+ actor,
113
+ reason,
114
+ packageVersion: PACKAGE_VERSION,
115
+ }, {
116
+ validateEvent: (event) => validateInvalidationEvent(event, resultTask),
117
+ });
118
+ return { event: prepared.event, postimage: prepared.content };
119
+ }
120
+ export function readDownstreamProofInvalidations(store, task) {
121
+ return store.readSidecarEvents(store.taskRoot(task.projectId, task.id), SIDECAR, { validateEvent: (event) => validateInvalidationEvent(event, task) });
122
+ }
123
+ export function downstreamProofInvalidationSidecar() {
124
+ return SIDECAR;
125
+ }
126
+ function emptyAssessment() {
127
+ return {
128
+ applicable: false,
129
+ eligible: false,
130
+ activeStepId: null,
131
+ dirtyFiles: [],
132
+ outsideActiveStepFiles: [],
133
+ invalidatedSteps: [],
134
+ preservedHeadCommit: null,
135
+ blockers: [],
136
+ };
137
+ }
138
+ function transitivePredecessors(task, stepId, blockers) {
139
+ const byId = new Map(task.steps.map((step) => [step.id, step]));
140
+ const result = new Set();
141
+ const visiting = new Set();
142
+ const visit = (candidateId) => {
143
+ if (result.has(candidateId))
144
+ return;
145
+ if (visiting.has(candidateId)) {
146
+ blockers.push(`Step dependency cycle reaches ${candidateId}.`);
147
+ return;
148
+ }
149
+ const candidate = byId.get(candidateId);
150
+ if (!candidate) {
151
+ blockers.push(`Step dependency references missing Step ${candidateId}.`);
152
+ return;
153
+ }
154
+ visiting.add(candidateId);
155
+ for (const dependency of candidate.dependencies) {
156
+ visit(dependency);
157
+ result.add(dependency);
158
+ }
159
+ visiting.delete(candidateId);
160
+ };
161
+ visit(stepId);
162
+ result.delete(stepId);
163
+ return result;
164
+ }
165
+ function completedDependentClosure(task, seedIds, predecessorIds) {
166
+ const result = new Set(seedIds);
167
+ let changed = true;
168
+ while (changed) {
169
+ changed = false;
170
+ for (const step of task.steps) {
171
+ if (step.status !== 'completed' || !predecessorIds.has(step.id) || result.has(step.id))
172
+ continue;
173
+ if (step.dependencies.some((dependency) => result.has(dependency))) {
174
+ result.add(step.id);
175
+ changed = true;
176
+ }
177
+ }
178
+ }
179
+ return result;
180
+ }
181
+ function validateInvalidationEvent(event, task) {
182
+ const expectedKeys = [
183
+ 'activeStepId', 'actor', 'dirtyFiles', 'eventHash', 'eventId', 'invalidatedSteps', 'packageVersion',
184
+ 'planHash', 'preservedHeadCommit', 'previousEventHash', 'previousTaskRevision', 'reason', 'recordedAt',
185
+ 'resultTaskRevision', 'taskId',
186
+ ];
187
+ if (!event || typeof event !== 'object' || Array.isArray(event)
188
+ || JSON.stringify(Object.keys(event).sort()) !== JSON.stringify(expectedKeys.sort())) {
189
+ throw new WorkflowError('STATE_CORRUPT', 'Downstream proof invalidation event contains unexpected fields.');
190
+ }
191
+ if (typeof event.eventId !== 'string' || !EVENT_ID.test(event.eventId)) {
192
+ throw new WorkflowError('STATE_CORRUPT', 'Downstream proof invalidation eventId is invalid.');
193
+ }
194
+ if (event.taskId !== task.id)
195
+ throw new WorkflowError('STATE_CORRUPT', 'Downstream proof invalidation Task identity mismatch.');
196
+ if (!Number.isInteger(event.previousTaskRevision) || event.previousTaskRevision < 1
197
+ || event.resultTaskRevision !== event.previousTaskRevision + 1
198
+ || event.resultTaskRevision > task.revision) {
199
+ throw new WorkflowError('STATE_CORRUPT', 'Downstream proof invalidation revision binding is invalid.');
200
+ }
201
+ if (typeof event.planHash !== 'string' || !HASH_64.test(event.planHash)) {
202
+ throw new WorkflowError('STATE_CORRUPT', 'Downstream proof invalidation planHash is invalid.');
203
+ }
204
+ if (typeof event.preservedHeadCommit !== 'string' || !GIT_HASH.test(event.preservedHeadCommit)) {
205
+ throw new WorkflowError('STATE_CORRUPT', 'Downstream proof invalidation preservedHeadCommit is invalid.');
206
+ }
207
+ for (const [value, label] of [
208
+ [event.activeStepId, 'activeStepId'],
209
+ [event.actor, 'actor'],
210
+ [event.reason, 'reason'],
211
+ [event.packageVersion, 'packageVersion'],
212
+ ]) {
213
+ if (typeof value !== 'string' || !value.trim()) {
214
+ throw new WorkflowError('STATE_CORRUPT', `Downstream proof invalidation ${label} is required.`);
215
+ }
216
+ }
217
+ if (!Array.isArray(event.dirtyFiles) || event.dirtyFiles.length === 0
218
+ || event.dirtyFiles.some((file) => typeof file !== 'string' || !file.trim())
219
+ || new Set(event.dirtyFiles).size !== event.dirtyFiles.length
220
+ || JSON.stringify(event.dirtyFiles) !== JSON.stringify([...event.dirtyFiles].sort())) {
221
+ throw new WorkflowError('STATE_CORRUPT', 'Downstream proof invalidation dirtyFiles are invalid.');
222
+ }
223
+ if (!Array.isArray(event.invalidatedSteps) || event.invalidatedSteps.length === 0) {
224
+ throw new WorkflowError('STATE_CORRUPT', 'Downstream proof invalidation must identify invalidated Steps.');
225
+ }
226
+ const stepIds = new Set();
227
+ for (const step of event.invalidatedSteps) {
228
+ if (!step || typeof step !== 'object' || Array.isArray(step)
229
+ || JSON.stringify(Object.keys(step).sort()) !== JSON.stringify(['evidenceCommit', 'stepId', 'triggerFiles'])) {
230
+ throw new WorkflowError('STATE_CORRUPT', 'Downstream proof invalidated Step contains unexpected fields.');
231
+ }
232
+ if (typeof step.stepId !== 'string' || !step.stepId.trim() || stepIds.has(step.stepId)
233
+ || typeof step.evidenceCommit !== 'string' || !GIT_HASH.test(step.evidenceCommit)
234
+ || !(task.invalidatedStepCommits ?? []).includes(step.evidenceCommit)
235
+ || !Array.isArray(step.triggerFiles)
236
+ || new Set(step.triggerFiles).size !== step.triggerFiles.length
237
+ || step.triggerFiles.some((file) => typeof file !== 'string' || !event.dirtyFiles.includes(file))) {
238
+ throw new WorkflowError('STATE_CORRUPT', 'Downstream proof invalidated Step evidence is invalid.');
239
+ }
240
+ stepIds.add(step.stepId);
241
+ }
242
+ }
243
+ function normalizeRequired(value, label) {
244
+ if (typeof value !== 'string' || !value.trim()) {
245
+ throw new WorkflowError('INVALID_ARGUMENT', `${label} is required.`);
246
+ }
247
+ return value.trim();
248
+ }
249
+ //# sourceMappingURL=downstream-proof.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"downstream-proof.js","sourceRoot":"","sources":["../../../src/alpha6/downstream-proof.ts"],"names":[],"mappings":"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"}
@@ -275,8 +275,9 @@ function assessStopOverrideContextRebind(store, task, stepId, stopDecision, reme
275
275
  });
276
276
  }
277
277
  assertCorrectiveAuditorIndependence(store, task, stepId, stopDecision.auditor);
278
- const rebindEvidence = assessAppendOnlyContextPlanRebindEvidence(store, task, stepId, stopDecision, { allowTaskRevisionAdvance: step.status === 'in_progress' });
279
- requireStopOverrideHeadContinuity(repositoryRoot, task, override, rebindEvidence.authorization);
278
+ const rebindEvidence = assessAppendOnlyContextPlanRebindEvidence(store, task, stepId, stopDecision);
279
+ const verifiedHeadRecoveries = requireStopOverrideHeadContinuity(repositoryRoot, task, override, rebindEvidence.authorization);
280
+ requireEvidenceBoundAuditTaskRevision(task, rebindEvidence.reboundAudit, verifiedHeadRecoveries, step.status === 'in_progress');
280
281
  const stopAt = Date.parse(stopDecision.recordedAt);
281
282
  const overrideAt = Date.parse(override.recordedAt);
282
283
  const firstRebindAt = Date.parse(rebindEvidence.rebindChain[0].recordedAt);
@@ -373,7 +374,7 @@ function assessContextOnlyPlanRebindEvidence(store, task, stepId, sourceDecision
373
374
  autonomyContract,
374
375
  };
375
376
  }
376
- function assessAppendOnlyContextPlanRebindEvidence(store, task, stepId, sourceDecision, options = {}) {
377
+ function assessAppendOnlyContextPlanRebindEvidence(store, task, stepId, sourceDecision) {
377
378
  if (!task.planHash || task.knowledgeMapRevision === null || !task.knowledgeMapHash) {
378
379
  throw new WorkflowError('STATE_CORRUPT', `Task ${task.id} is missing current Plan or Knowledge bindings.`);
379
380
  }
@@ -383,7 +384,7 @@ function assessAppendOnlyContextPlanRebindEvidence(store, task, stepId, sourceDe
383
384
  const planRiskAudits = readPlanRiskAuditEvents(store, task);
384
385
  const rebindChain = requireAppendOnlyRebindChain(task, sourceDecision, planRiskAudits);
385
386
  const planEvidence = comparePlanRebindChainArtifacts(store.taskRoot(task.projectId, task.id), sourceDecision.planHash, task.planHash, rebindChain);
386
- const { sourceAudit, reboundAudit, auditChain } = requireMechanicalAuditReboundChain(task, sourceDecision, planRiskAudits, rebindChain, options.allowTaskRevisionAdvance ?? false);
387
+ const { sourceAudit, reboundAudit, auditChain } = requireMechanicalAuditReboundChain(task, sourceDecision, planRiskAudits, rebindChain);
387
388
  if (!reboundAudit.stepClassifications.some((classification) => classification.stepId === stepId && classification.reviewRequired)) {
388
389
  throw new WorkflowError('TRANSITION_BLOCKED', `Step ${stepId} is not guarded by the current Plan Risk Audit.`);
389
390
  }
@@ -611,7 +612,7 @@ function requireAppendOnlyRebindChain(task, decision, planRiskAudits) {
611
612
  }
612
613
  return chain;
613
614
  }
614
- function requireMechanicalAuditReboundChain(task, decision, events, rebindChain, allowTaskRevisionAdvance) {
615
+ function requireMechanicalAuditReboundChain(task, decision, events, rebindChain) {
615
616
  const planHashes = [decision.planHash, ...rebindChain.map((record) => record.reboundPlanHash)];
616
617
  const auditChain = planHashes.map((planHash) => {
617
618
  const matches = events.filter((event) => event.planHash === planHash);
@@ -635,15 +636,6 @@ function requireMechanicalAuditReboundChain(task, decision, events, rebindChain,
635
636
  throw new WorkflowError('STATE_CORRUPT', 'Plan Risk Audit rebind chain does not end at the current append-only boundary.');
636
637
  }
637
638
  const reboundAudit = auditChain.at(-1);
638
- const authorizedTaskRevision = reboundAudit.taskRevision + 1;
639
- if ((!allowTaskRevisionAdvance && authorizedTaskRevision !== task.revision)
640
- || (allowTaskRevisionAdvance && authorizedTaskRevision > task.revision)) {
641
- throw new WorkflowError('STATE_CORRUPT', 'Mechanical rebound audit chain does not bind the required Task authorization boundary.', {
642
- reboundAuditTaskRevision: reboundAudit.taskRevision,
643
- currentTaskRevision: task.revision,
644
- allowTaskRevisionAdvance,
645
- });
646
- }
647
639
  return { sourceAudit: auditChain[0], reboundAudit, auditChain };
648
640
  }
649
641
  function assertMechanicalAuditSuccessor(task, sourceAudit, reboundAudit) {
@@ -667,7 +659,7 @@ function requireStopOverrideHeadContinuity(repositoryRoot, task, override, autho
667
659
  throw new WorkflowError('TRANSITION_BLOCKED', 'Current execution authorization does not bind a Git HEAD.');
668
660
  }
669
661
  if (authorizedHead === override.headCommit)
670
- return;
662
+ return [];
671
663
  if (!gitIsAncestor(repositoryRoot, override.headCommit, authorizedHead)) {
672
664
  throw new WorkflowError('TRANSITION_BLOCKED', 'Current execution HEAD is not an append-only descendant of the stop-override HEAD.', {
673
665
  overrideHeadCommit: override.headCommit,
@@ -680,6 +672,9 @@ function requireStopOverrideHeadContinuity(repositoryRoot, task, override, autho
680
672
  throw new WorkflowError('STATE_CORRUPT', 'Git reports distinct stop-override and authorization HEADs without an intervening commit.');
681
673
  }
682
674
  let expectedParent = override.headCommit;
675
+ let previousRecoveryIndex = -1;
676
+ let previousRecoveryAt = Number.NEGATIVE_INFINITY;
677
+ const verifiedRecoveries = [];
683
678
  for (const commit of commits) {
684
679
  const matches = (task.dependencyProvenanceRecoveries ?? []).filter((record) => record.commitSha === commit);
685
680
  if (matches.length !== 1) {
@@ -689,12 +684,17 @@ function requireStopOverrideHeadContinuity(repositoryRoot, task, override, autho
689
684
  });
690
685
  }
691
686
  const recovery = matches[0];
687
+ const recoveryIndex = (task.dependencyProvenanceRecoveries ?? []).indexOf(recovery);
688
+ const recoveryAt = Date.parse(recovery.recordedAt);
692
689
  const parents = runGit(repositoryRoot, ['show', '-s', '--format=%P', commit]).split(/\s+/).filter(Boolean);
693
690
  const filesOutput = runGit(repositoryRoot, ['diff-tree', '--no-commit-id', '--name-only', '-r', commit]);
694
691
  const files = filesOutput ? filesOutput.split('\n').sort() : [];
695
692
  if (parents.length !== 1
696
693
  || parents[0] !== expectedParent
697
694
  || recovery.parentCommitSha !== expectedParent
695
+ || recoveryIndex <= previousRecoveryIndex
696
+ || !Number.isFinite(recoveryAt)
697
+ || recoveryAt <= previousRecoveryAt
698
698
  || recovery.packageName !== PACKAGE_NAME
699
699
  || canonicalJsonStringify(recovery.files) !== canonicalJsonStringify(['package-lock.json', 'package.json'])
700
700
  || canonicalJsonStringify(files) !== canonicalJsonStringify(['package-lock.json', 'package.json'])
@@ -707,11 +707,33 @@ function requireStopOverrideHeadContinuity(repositoryRoot, task, override, autho
707
707
  files,
708
708
  });
709
709
  }
710
+ verifiedRecoveries.push(recovery);
711
+ previousRecoveryIndex = recoveryIndex;
712
+ previousRecoveryAt = recoveryAt;
710
713
  expectedParent = commit;
711
714
  }
712
715
  if (expectedParent !== authorizedHead) {
713
716
  throw new WorkflowError('STATE_CORRUPT', 'Dependency-provenance Git chain does not terminate at the authorized HEAD.');
714
717
  }
718
+ return verifiedRecoveries;
719
+ }
720
+ function requireEvidenceBoundAuditTaskRevision(task, reboundAudit, verifiedHeadRecoveries, allowTaskRevisionAdvance) {
721
+ const auditAt = Date.parse(reboundAudit.recordedAt);
722
+ if (!Number.isFinite(auditAt)) {
723
+ throw new WorkflowError('STATE_CORRUPT', 'Current rebound Plan Risk Audit timestamp is invalid.');
724
+ }
725
+ const postAuditRecoveries = verifiedHeadRecoveries.filter((recovery) => Date.parse(recovery.recordedAt) > auditAt);
726
+ const evidenceBoundTaskRevision = reboundAudit.taskRevision + 1 + postAuditRecoveries.length;
727
+ if ((!allowTaskRevisionAdvance && evidenceBoundTaskRevision !== task.revision)
728
+ || (allowTaskRevisionAdvance && evidenceBoundTaskRevision > task.revision)) {
729
+ throw new WorkflowError('STATE_CORRUPT', 'Mechanical rebound audit chain does not bind the evidence-accounted Task authorization boundary.', {
730
+ reboundAuditTaskRevision: reboundAudit.taskRevision,
731
+ currentTaskRevision: task.revision,
732
+ postAuditDependencyRecoveryCommits: postAuditRecoveries.map((recovery) => recovery.commitSha),
733
+ evidenceBoundTaskRevision,
734
+ allowTaskRevisionAdvance,
735
+ });
736
+ }
715
737
  }
716
738
  function assertRebindMatchesPlans(rebind, plans) {
717
739
  if (rebind.fromKnowledgeMapRevision !== plans.decisionBinding.revision