codex-workflow-v2 2.0.0-beta.13.15 → 2.0.0-beta.13.16
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.
- package/dist/reviewer-runtime-build.json +6 -6
- package/dist/src/graph.js +25 -3
- package/dist/src/graph.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/workflow.js +1 -1
- package/docs/pdf/codex-workflow-v2-architecture-ru.pdf +0 -0
- package/docs/pdf/codex-workflow-v2-chat-only-guide-ru.pdf +0 -0
- package/docs/pdf/codex-workflow-v2-technical-reference-ru.pdf +0 -0
- package/docs/pdf/sources/codex-workflow-v2-architecture-ru.md +1 -1
- package/docs/pdf/sources/codex-workflow-v2-chat-only-guide-ru.md +2 -2
- package/docs/pdf/sources/codex-workflow-v2-technical-reference-ru.md +2 -2
- package/package.json +2 -1
- package/plugins/codex-workflow-gateway/.codex-plugin/plugin.json +1 -1
- package/plugins/codex-workflow-gateway/references/codebase-memory-routing.md +16 -13
- package/src/alpha6/adoption.ts +1105 -0
- package/src/alpha6/captured-check-evidence.ts +126 -0
- package/src/alpha6/check-support-anchor.ts +128 -0
- package/src/alpha6/component-owner.ts +168 -0
- package/src/alpha6/corrective-decision-boundary.ts +39 -0
- package/src/alpha6/downstream-proof.ts +732 -0
- package/src/alpha6/failed-step-planning-recovery.ts +88 -0
- package/src/alpha6/handoff.ts +1443 -0
- package/src/alpha6/journal.ts +473 -0
- package/src/alpha6/literal-test-invocation.ts +84 -0
- package/src/alpha6/mechanical-feasibility.ts +488 -0
- package/src/alpha6/milestone.ts +2182 -0
- package/src/alpha6/npm-check-contract.ts +47 -0
- package/src/alpha6/plan-integrity-update.ts +20 -0
- package/src/alpha6/plan-integrity.ts +298 -0
- package/src/alpha6/plan-risk.ts +1480 -0
- package/src/alpha6/preexecution-replan.ts +187 -0
- package/src/alpha6/remediation-cause.ts +98 -0
- package/src/alpha6/remediation.ts +2965 -0
- package/src/alpha6/review.ts +1198 -0
- package/src/alpha6/root-cause-replan-carryover.ts +471 -0
- package/src/alpha6/store-sidecars.ts +335 -0
- package/src/alpha7/autonomy.ts +411 -0
- package/src/alpha7/corrective-recovery.ts +1332 -0
- package/src/artifacts.ts +130 -0
- package/src/beta1/project-transaction.ts +351 -0
- package/src/change-explanation.ts +153 -0
- package/src/cli-actions.ts +119 -0
- package/src/cli.ts +1583 -0
- package/src/contracts.ts +1510 -0
- package/src/credential-output.ts +89 -0
- package/src/credential-transport.ts +215 -0
- package/src/delegation.ts +190 -0
- package/src/dependency-provenance.ts +538 -0
- package/src/diagnostics.ts +93 -0
- package/src/domain/completed-step-carryover.ts +76 -0
- package/src/domain/discovery.ts +27 -0
- package/src/domain/plan-semantics.ts +58 -0
- package/src/domain/validation.ts +177 -0
- package/src/errors.ts +23 -0
- package/src/fs-utils.ts +61 -0
- package/src/gateway-handshake.ts +103 -0
- package/src/git.ts +158 -0
- package/src/graph.ts +342 -0
- package/src/historical-step-provenance.ts +136 -0
- package/src/index.ts +21 -0
- package/src/lifecycle/canonical-hash.ts +28 -0
- package/src/lifecycle/catalog.ts +202 -0
- package/src/lifecycle/compiler-inspection.ts +29 -0
- package/src/lifecycle/core-static-readiness.ts +132 -0
- package/src/lifecycle/corrective-replan-authority.ts +136 -0
- package/src/lifecycle/corrective-replan-binding-manifest.ts +51 -0
- package/src/lifecycle/corrective-replan-credential-core.ts +408 -0
- package/src/lifecycle/corrective-replan-credential-schema.ts +54 -0
- package/src/lifecycle/corrective-replan-credentials.ts +48 -0
- package/src/lifecycle/corrective-replan.ts +843 -0
- package/src/lifecycle/evaluator.ts +48 -0
- package/src/lifecycle/fingerprint.ts +488 -0
- package/src/lifecycle/immutable.ts +8 -0
- package/src/lifecycle/implementation-table.ts +118 -0
- package/src/lifecycle/index.ts +8 -0
- package/src/lifecycle/schema-artifact.ts +263 -0
- package/src/lifecycle/semantic-registry.ts +572 -0
- package/src/lifecycle/types.ts +838 -0
- package/src/memory.ts +273 -0
- package/src/migration.ts +161 -0
- package/src/navigation-actions.ts +70 -0
- package/src/observation.ts +198 -0
- package/src/observed-routes.ts +661 -0
- package/src/operational-contract.ts +125 -0
- package/src/pending-review-update.ts +191 -0
- package/src/repository.ts +99 -0
- package/src/reviewer.ts +1876 -0
- package/src/state/corrective-replan-executor.ts +818 -0
- package/src/state/corrective-replan-public-schema.ts +83 -0
- package/src/state/corrective-replan-public.ts +908 -0
- package/src/state/corrective-replan-transaction.ts +947 -0
- package/src/state/corrective-yield-executor.ts +327 -0
- package/src/state/corrective-yield-transaction.ts +728 -0
- package/src/state/lock.ts +880 -0
- package/src/state/store.ts +567 -0
- package/src/transition-core.ts +330 -0
- package/src/ulid.ts +24 -0
- package/src/version.ts +2 -0
- package/src/workflow-blocker-route.ts +109 -0
- package/src/workflow.ts +11436 -0
|
@@ -0,0 +1,843 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { renderPlan } from '../artifacts.js';
|
|
3
|
+
import type {
|
|
4
|
+
CorrectiveDecisionEvent,
|
|
5
|
+
RemediationEvent,
|
|
6
|
+
StepStatus,
|
|
7
|
+
TaskPlanInput,
|
|
8
|
+
TaskState,
|
|
9
|
+
TaskStatus,
|
|
10
|
+
} from '../contracts.js';
|
|
11
|
+
import { definitionManifestHash, runtimeSemanticFingerprint } from './fingerprint.js';
|
|
12
|
+
import { deepFreeze } from './immutable.js';
|
|
13
|
+
import { evaluateTransition } from './evaluator.js';
|
|
14
|
+
import type {
|
|
15
|
+
BlockedTransitionNavigation,
|
|
16
|
+
ExecutableTransitionDefinition,
|
|
17
|
+
HumanGateTransitionNavigation,
|
|
18
|
+
TransitionDefinitionMetadata,
|
|
19
|
+
TransitionEvaluation,
|
|
20
|
+
TransitionGuard,
|
|
21
|
+
TransitionGuardMetadata,
|
|
22
|
+
TransitionGuardPhase,
|
|
23
|
+
TransitionGuardReasonClass,
|
|
24
|
+
TransitionGuardResult,
|
|
25
|
+
TransitionGuardStage,
|
|
26
|
+
} from './types.js';
|
|
27
|
+
import { correctiveReplanBindingManifest } from './corrective-replan-binding-manifest.js';
|
|
28
|
+
import {
|
|
29
|
+
LIFECYCLE_EPOCH,
|
|
30
|
+
TRANSITION_DEFINITION_FORMAT_VERSION,
|
|
31
|
+
} from './types.js';
|
|
32
|
+
|
|
33
|
+
export const TASK_CORRECTIVE_REPLAN_TRANSITION_ID = 'task.plan.corrective-replan' as const;
|
|
34
|
+
|
|
35
|
+
export interface CorrectiveReplanPosture {
|
|
36
|
+
readonly posture: string;
|
|
37
|
+
readonly stepId: string;
|
|
38
|
+
readonly attemptCount: number;
|
|
39
|
+
readonly correctiveDecision: CorrectiveDecisionEvent | null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function identifyCorrectiveReplanPosture(
|
|
43
|
+
task: Readonly<TaskState>,
|
|
44
|
+
guardedStepIds: readonly string[],
|
|
45
|
+
decisions: readonly CorrectiveDecisionEvent[],
|
|
46
|
+
remediations: readonly RemediationEvent[],
|
|
47
|
+
): CorrectiveReplanPosture | null {
|
|
48
|
+
const matches = decisions.filter((decision) =>
|
|
49
|
+
decision.taskId === task.id
|
|
50
|
+
&& decision.decision === 'replan-required'
|
|
51
|
+
&& decision.planHash === task.planHash
|
|
52
|
+
&& guardedStepIds.includes(decision.stepId));
|
|
53
|
+
if (matches.length !== 1) return null;
|
|
54
|
+
const decision = matches[0]!;
|
|
55
|
+
return {
|
|
56
|
+
posture: 'replan-required',
|
|
57
|
+
stepId: decision.stepId,
|
|
58
|
+
attemptCount: remediations.filter((event) => event.stepId === decision.stepId).length,
|
|
59
|
+
correctiveDecision: decision,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface PreparedCorrectiveReplanCandidate {
|
|
64
|
+
readonly planBytes: string;
|
|
65
|
+
readonly planHash: string;
|
|
66
|
+
readonly stepIds: readonly string[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface CorrectiveReplanSnapshot {
|
|
70
|
+
readonly taskId: string;
|
|
71
|
+
readonly taskRevision: number;
|
|
72
|
+
readonly taskStatus: TaskStatus;
|
|
73
|
+
readonly currentPlanHash: string | null;
|
|
74
|
+
readonly stepId: string;
|
|
75
|
+
readonly stepStatus: StepStatus | null;
|
|
76
|
+
readonly inProgressStepIds: readonly string[];
|
|
77
|
+
readonly remediationAttemptCount: number;
|
|
78
|
+
readonly correctivePosture: string;
|
|
79
|
+
readonly correctiveDecision: CorrectiveDecisionEvent | null;
|
|
80
|
+
readonly candidatePlanHash: string | null;
|
|
81
|
+
readonly candidateStepIds: readonly string[] | null;
|
|
82
|
+
readonly actorRoleEligible: boolean;
|
|
83
|
+
readonly c1ClaimAbsent: boolean;
|
|
84
|
+
readonly writerLeaseAbsent: boolean;
|
|
85
|
+
readonly planValidationPassed: boolean;
|
|
86
|
+
readonly receiptValid: boolean;
|
|
87
|
+
readonly riskAuditPassed: boolean;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface CorrectiveReplanAdmissionFacts {
|
|
91
|
+
readonly actorRoleEligible: boolean;
|
|
92
|
+
readonly c1ClaimAbsent: boolean;
|
|
93
|
+
readonly writerLeaseAbsent: boolean;
|
|
94
|
+
readonly planValidationPassed: boolean;
|
|
95
|
+
readonly receiptValid: boolean;
|
|
96
|
+
readonly riskAuditPassed: boolean;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface CorrectiveReplanAvailabilityBindings {
|
|
100
|
+
readonly taskId: string;
|
|
101
|
+
readonly expectedRevision: number;
|
|
102
|
+
readonly currentPlanHash: string;
|
|
103
|
+
readonly stepId: string;
|
|
104
|
+
readonly correctiveDecisionEventId: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export interface CorrectiveReplanExecutionBindings extends CorrectiveReplanAvailabilityBindings {
|
|
108
|
+
readonly candidatePlanHash: string;
|
|
109
|
+
readonly candidateStepIds: readonly string[];
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export type CorrectiveReplanBindings =
|
|
113
|
+
| CorrectiveReplanAvailabilityBindings
|
|
114
|
+
| CorrectiveReplanExecutionBindings;
|
|
115
|
+
|
|
116
|
+
const bindingGuardMetadata = guardMetadata(
|
|
117
|
+
'task.corrective-replan.bindings-current',
|
|
118
|
+
'Task, revision, Plan, Step, and corrective-decision bindings match the evaluated snapshot.',
|
|
119
|
+
'availability',
|
|
120
|
+
'observation',
|
|
121
|
+
['field.corrective-decision-event-id', 'field.current-plan-hash', 'field.step-id', 'field.task-id', 'field.task-revision'],
|
|
122
|
+
['input.expected-revision', 'input.task-id'],
|
|
123
|
+
'external-race',
|
|
124
|
+
);
|
|
125
|
+
const sourceGuardMetadata = guardMetadata(
|
|
126
|
+
'task.corrective-replan.source-posture',
|
|
127
|
+
'The exact guarded Step is failed and its Task is in a corrective replan source posture.',
|
|
128
|
+
'availability',
|
|
129
|
+
'observation',
|
|
130
|
+
['field.step-status', 'field.task-status'],
|
|
131
|
+
[],
|
|
132
|
+
'lifecycle-blocker',
|
|
133
|
+
);
|
|
134
|
+
const noStepInProgressGuardMetadata = guardMetadata(
|
|
135
|
+
'task.corrective-replan.no-step-in-progress',
|
|
136
|
+
'No Step in the Task is in progress while its Plan semantics are being replaced.',
|
|
137
|
+
'availability',
|
|
138
|
+
'observation',
|
|
139
|
+
['field.in-progress-step-ids'],
|
|
140
|
+
[],
|
|
141
|
+
'lifecycle-blocker',
|
|
142
|
+
);
|
|
143
|
+
const decisionGuardMetadata = guardMetadata(
|
|
144
|
+
'task.corrective-replan.active-decision',
|
|
145
|
+
'One active replan-required decision is bound to the current Plan and guarded Step.',
|
|
146
|
+
'availability',
|
|
147
|
+
'observation',
|
|
148
|
+
['field.corrective-decision-decision', 'field.corrective-decision-plan-hash', 'field.corrective-decision-step-id', 'field.corrective-posture'],
|
|
149
|
+
[],
|
|
150
|
+
'lifecycle-blocker',
|
|
151
|
+
);
|
|
152
|
+
const attemptGuardMetadata = guardMetadata(
|
|
153
|
+
'task.corrective-replan.attempt-threshold',
|
|
154
|
+
'The decision is bound either to the bounded first-failure Plan-integrity route or to the ordinary two-failure threshold.',
|
|
155
|
+
'availability',
|
|
156
|
+
'observation',
|
|
157
|
+
['field.corrective-decision-triggering-attempt-count', 'field.remediation-attempt-count'],
|
|
158
|
+
[],
|
|
159
|
+
'lifecycle-blocker',
|
|
160
|
+
);
|
|
161
|
+
const semanticPlanChangeGuardMetadata = guardMetadata(
|
|
162
|
+
'task.corrective-replan.semantic-plan-change',
|
|
163
|
+
'The rendered candidate Plan has a new content-addressed semantic binding.',
|
|
164
|
+
'execution',
|
|
165
|
+
'candidate',
|
|
166
|
+
['field.candidate-plan-hash', 'field.current-plan-hash'],
|
|
167
|
+
['input.corrective-replacement-plan'],
|
|
168
|
+
'candidate-blocker',
|
|
169
|
+
);
|
|
170
|
+
const guardedStepRetainedGuardMetadata = guardMetadata(
|
|
171
|
+
'task.corrective-replan.guarded-step-retained',
|
|
172
|
+
'The candidate Plan retains the exact guarded Step identity.',
|
|
173
|
+
'execution',
|
|
174
|
+
'candidate',
|
|
175
|
+
['field.candidate-step-ids', 'field.step-id'],
|
|
176
|
+
['input.corrective-replacement-plan'],
|
|
177
|
+
'candidate-blocker',
|
|
178
|
+
);
|
|
179
|
+
const actorRoleEligibleGuardMetadata = guardMetadata(
|
|
180
|
+
'task.corrective-replan.actor-role-eligible', 'The receipt-bound actor has an eligible planning/coordinator role.',
|
|
181
|
+
'execution', 'authority', ['field.actor-role-eligible'], ['input.corrective-execution-receipt'], 'authority-blocker',
|
|
182
|
+
);
|
|
183
|
+
const noC1ClaimGuardMetadata = guardMetadata(
|
|
184
|
+
'task.corrective-replan.no-c1-claim', 'No C1 claim remains when the exact Task operation lock is acquired.',
|
|
185
|
+
'execution', 'authority', ['field.c1-claim-absent'], ['input.corrective-execution-receipt'], 'external-race',
|
|
186
|
+
);
|
|
187
|
+
const noWriterLeaseGuardMetadata = guardMetadata(
|
|
188
|
+
'task.corrective-replan.no-writer-lease', 'No Writer lease remains when the exact Task operation lock is acquired.',
|
|
189
|
+
'execution', 'authority', ['field.writer-lease-absent'], ['input.corrective-execution-receipt'], 'external-race',
|
|
190
|
+
);
|
|
191
|
+
const planValidationPassedGuardMetadata = guardMetadata(
|
|
192
|
+
'task.corrective-replan.plan-validation-passed', 'A fresh independent Plan Validation is bound to the replacement candidate.',
|
|
193
|
+
'execution', 'candidate', ['field.plan-validation-passed'], ['input.corrective-plan-validation'], 'candidate-blocker',
|
|
194
|
+
);
|
|
195
|
+
const receiptValidGuardMetadata = guardMetadata(
|
|
196
|
+
'task.corrective-replan.receipt-valid', 'The Human-confirmed receipt matches every current execution binding.',
|
|
197
|
+
'execution', 'authority', ['field.receipt-valid'], ['input.corrective-execution-receipt'], 'authority-blocker',
|
|
198
|
+
);
|
|
199
|
+
const riskAuditPassedGuardMetadata = guardMetadata(
|
|
200
|
+
'task.corrective-replan.risk-audit-passed', 'The replacement Plan Risk Audit is approved and candidate-bound.',
|
|
201
|
+
'execution', 'candidate', ['field.risk-audit-passed'], ['input.corrective-plan-risk-audit'], 'candidate-blocker',
|
|
202
|
+
);
|
|
203
|
+
|
|
204
|
+
const guards: readonly TransitionGuard<CorrectiveReplanSnapshot, CorrectiveReplanBindings>[] = [
|
|
205
|
+
{
|
|
206
|
+
metadata: bindingGuardMetadata,
|
|
207
|
+
evaluate(snapshot, bindings) {
|
|
208
|
+
const decision = snapshot.correctiveDecision;
|
|
209
|
+
const passed = snapshot.taskId === bindings.taskId
|
|
210
|
+
&& snapshot.taskRevision === bindings.expectedRevision
|
|
211
|
+
&& snapshot.currentPlanHash === bindings.currentPlanHash
|
|
212
|
+
&& snapshot.stepId === bindings.stepId
|
|
213
|
+
&& decision?.eventId === bindings.correctiveDecisionEventId;
|
|
214
|
+
return verdict(bindingGuardMetadata, passed, 'BINDINGS_STALE', 'Corrective replan bindings no longer match current state.');
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
metadata: sourceGuardMetadata,
|
|
219
|
+
evaluate(snapshot) {
|
|
220
|
+
const taskAllowed = ['ready', 'needs_fix', 'blocked', 'awaiting_execution_authorization'].includes(snapshot.taskStatus);
|
|
221
|
+
const passed = taskAllowed && snapshot.stepStatus === 'failed';
|
|
222
|
+
return verdict(
|
|
223
|
+
sourceGuardMetadata,
|
|
224
|
+
passed,
|
|
225
|
+
'SOURCE_POSTURE_BLOCKED',
|
|
226
|
+
`Corrective replan requires the exact guarded Step to be failed; Task is ${snapshot.taskStatus} and Step is ${snapshot.stepStatus ?? 'missing'}.`,
|
|
227
|
+
);
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
metadata: noStepInProgressGuardMetadata,
|
|
232
|
+
evaluate(snapshot) {
|
|
233
|
+
return verdict(
|
|
234
|
+
noStepInProgressGuardMetadata,
|
|
235
|
+
snapshot.inProgressStepIds.length === 0,
|
|
236
|
+
'STEP_IN_PROGRESS',
|
|
237
|
+
`Corrective replan is blocked while Steps are in progress: ${snapshot.inProgressStepIds.join(', ')}.`,
|
|
238
|
+
);
|
|
239
|
+
},
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
metadata: decisionGuardMetadata,
|
|
243
|
+
evaluate(snapshot) {
|
|
244
|
+
const decision = snapshot.correctiveDecision;
|
|
245
|
+
const passed = snapshot.correctivePosture === 'replan-required'
|
|
246
|
+
&& decision?.decision === 'replan-required'
|
|
247
|
+
&& decision.stepId === snapshot.stepId
|
|
248
|
+
&& decision.planHash === snapshot.currentPlanHash;
|
|
249
|
+
return verdict(
|
|
250
|
+
decisionGuardMetadata,
|
|
251
|
+
passed,
|
|
252
|
+
'ACTIVE_REPLAN_DECISION_MISSING',
|
|
253
|
+
'The exact guarded Step does not have a replan-required decision for the current Plan.',
|
|
254
|
+
);
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
metadata: attemptGuardMetadata,
|
|
259
|
+
evaluate(snapshot) {
|
|
260
|
+
const decision = snapshot.correctiveDecision;
|
|
261
|
+
const ordinaryThreshold = snapshot.remediationAttemptCount >= 2
|
|
262
|
+
&& decision?.triggeringAttemptCount === snapshot.remediationAttemptCount + 1;
|
|
263
|
+
const boundedPlanIntegrity = snapshot.remediationAttemptCount === 1
|
|
264
|
+
&& decision?.triggeringAttemptCount === 2
|
|
265
|
+
&& decision.recoveryBasis === 'plan-integrity'
|
|
266
|
+
&& decision.planIntegrityEvidence !== undefined;
|
|
267
|
+
const passed = ordinaryThreshold || boundedPlanIntegrity;
|
|
268
|
+
return verdict(
|
|
269
|
+
attemptGuardMetadata,
|
|
270
|
+
passed,
|
|
271
|
+
'ATTEMPT_BINDING_INVALID',
|
|
272
|
+
'Corrective replan requires either one bounded Plan-integrity failure or two ordinary failed attempts, with an exact next-ordinal decision.',
|
|
273
|
+
);
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
metadata: semanticPlanChangeGuardMetadata,
|
|
278
|
+
evaluate(snapshot, bindings) {
|
|
279
|
+
const candidateBindings = executionBindings(bindings);
|
|
280
|
+
const passed = candidateBindings !== null
|
|
281
|
+
&& snapshot.candidatePlanHash !== null
|
|
282
|
+
&& candidateBindings.candidatePlanHash === snapshot.candidatePlanHash
|
|
283
|
+
&& snapshot.currentPlanHash !== snapshot.candidatePlanHash;
|
|
284
|
+
return verdict(
|
|
285
|
+
semanticPlanChangeGuardMetadata,
|
|
286
|
+
passed,
|
|
287
|
+
'SEMANTIC_PLAN_UNCHANGED',
|
|
288
|
+
'Corrective replan requires candidate Plan bytes whose semantic hash differs from the current Plan.',
|
|
289
|
+
);
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
{
|
|
293
|
+
metadata: guardedStepRetainedGuardMetadata,
|
|
294
|
+
evaluate(snapshot, bindings) {
|
|
295
|
+
const candidateBindings = executionBindings(bindings);
|
|
296
|
+
const passed = candidateBindings !== null
|
|
297
|
+
&& snapshot.candidateStepIds !== null
|
|
298
|
+
&& sameStrings(candidateBindings.candidateStepIds, snapshot.candidateStepIds)
|
|
299
|
+
&& snapshot.candidateStepIds.includes(snapshot.stepId);
|
|
300
|
+
return verdict(
|
|
301
|
+
guardedStepRetainedGuardMetadata,
|
|
302
|
+
passed,
|
|
303
|
+
'GUARDED_STEP_REMOVED',
|
|
304
|
+
`Candidate Plan must retain guarded Step ${snapshot.stepId}.`,
|
|
305
|
+
);
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
booleanGuard(actorRoleEligibleGuardMetadata, 'actorRoleEligible', 'ROLE_NOT_ELIGIBLE'),
|
|
309
|
+
booleanGuard(noC1ClaimGuardMetadata, 'c1ClaimAbsent', 'C1_CLAIM_ACTIVE'),
|
|
310
|
+
booleanGuard(noWriterLeaseGuardMetadata, 'writerLeaseAbsent', 'WRITER_LEASE_ACTIVE'),
|
|
311
|
+
booleanGuard(planValidationPassedGuardMetadata, 'planValidationPassed', 'PLAN_VALIDATION_REQUIRED'),
|
|
312
|
+
booleanGuard(receiptValidGuardMetadata, 'receiptValid', 'RECEIPT_INVALID'),
|
|
313
|
+
booleanGuard(riskAuditPassedGuardMetadata, 'riskAuditPassed', 'RISK_AUDIT_REQUIRED'),
|
|
314
|
+
];
|
|
315
|
+
|
|
316
|
+
const metadata: TransitionDefinitionMetadata = {
|
|
317
|
+
definitionFormatVersion: TRANSITION_DEFINITION_FORMAT_VERSION,
|
|
318
|
+
transitionId: TASK_CORRECTIVE_REPLAN_TRANSITION_ID,
|
|
319
|
+
guardNamespace: 'task.corrective-replan',
|
|
320
|
+
lifecycleEpoch: LIFECYCLE_EPOCH,
|
|
321
|
+
entity: 'task',
|
|
322
|
+
scope: 'task-step',
|
|
323
|
+
command: { command: 'task corrective-replan-execute', noun: 'task', action: 'corrective-replan-execute' },
|
|
324
|
+
sourcePosture: {
|
|
325
|
+
taskStatuses: ['ready', 'needs_fix', 'blocked', 'awaiting_execution_authorization'],
|
|
326
|
+
milestoneStatuses: [],
|
|
327
|
+
stepStatuses: ['failed'],
|
|
328
|
+
correctivePostures: ['replan-required'],
|
|
329
|
+
},
|
|
330
|
+
inputContract: [
|
|
331
|
+
{ input: v1('input.task-id'), phase: 'availability', required: true, sensitivity: 'public' },
|
|
332
|
+
{ input: v1('input.expected-revision'), phase: 'availability', required: true, sensitivity: 'public' },
|
|
333
|
+
{ input: v1('input.corrective-replacement-plan'), phase: 'execution', required: true, sensitivity: 'public' },
|
|
334
|
+
{ input: v1('input.corrective-plan-validation'), phase: 'execution', required: true, sensitivity: 'public' },
|
|
335
|
+
{ input: v1('input.corrective-plan-risk-audit'), phase: 'execution', required: true, sensitivity: 'public' },
|
|
336
|
+
{ input: v1('input.corrective-execution-receipt'), phase: 'execution', required: true, sensitivity: 'public' },
|
|
337
|
+
],
|
|
338
|
+
snapshotContract: v1('projection.corrective-replan.snapshot'),
|
|
339
|
+
bindingContract: v1('projection.corrective-replan.bindings'),
|
|
340
|
+
outputContract: [
|
|
341
|
+
{ outputId: 'output.navigation', channel: 'navigation', sensitivity: 'public', required: true },
|
|
342
|
+
{ outputId: 'output.corrective-execution-receipt', channel: 'credential-receipt', sensitivity: 'redacted', required: true },
|
|
343
|
+
{ outputId: 'output.task-state', channel: 'result', sensitivity: 'public', required: true },
|
|
344
|
+
],
|
|
345
|
+
guards: guards.map((guard) => guard.metadata),
|
|
346
|
+
authority: {
|
|
347
|
+
eligibleRoles: ['delivery-coordinator', 'technical-planner'],
|
|
348
|
+
actor: {
|
|
349
|
+
identity: v1('identity.actor'),
|
|
350
|
+
principalBasis: v1('principal-basis.human-confirmation'),
|
|
351
|
+
required: true,
|
|
352
|
+
},
|
|
353
|
+
principal: {
|
|
354
|
+
identity: v1('identity.human-principal'),
|
|
355
|
+
principalBasis: v1('principal-basis.human-confirmation'),
|
|
356
|
+
issuer: v1('identity.human-principal'),
|
|
357
|
+
allowedKinds: ['human'],
|
|
358
|
+
},
|
|
359
|
+
capabilities: [{ capability: v1('capability.task.corrective-replan'), source: 'credential', required: true }],
|
|
360
|
+
credentials: [v1('credential-policy.corrective-execution-receipt')],
|
|
361
|
+
delegation: v1('delegation.forbidden'),
|
|
362
|
+
ownership: v1('ownership.corrective-replan-clear'),
|
|
363
|
+
executionBoundaries: ['human', 'coordinator', 'workflow-core'],
|
|
364
|
+
separationOfDuty: [
|
|
365
|
+
{
|
|
366
|
+
constraintId: 'sod.plan-author-plan-validator-distinct', left: v1('identity.plan-author'), right: v1('identity.plan-validator'),
|
|
367
|
+
relation: 'distinct', missingIdentity: 'block', comparator: v1('identity-comparator.corrective-replan.author-validator'),
|
|
368
|
+
enforcement: { kind: 'credential-verifier', verifier: v1('credential-verifier.corrective-replan.receipt') },
|
|
369
|
+
},
|
|
370
|
+
{
|
|
371
|
+
constraintId: 'sod.plan-author-risk-auditor-distinct', left: v1('identity.plan-author'), right: v1('identity.risk-auditor'),
|
|
372
|
+
relation: 'distinct', missingIdentity: 'block', comparator: v1('identity-comparator.corrective-replan.author-risk-auditor'),
|
|
373
|
+
enforcement: { kind: 'credential-verifier', verifier: v1('credential-verifier.corrective-replan.receipt') },
|
|
374
|
+
},
|
|
375
|
+
],
|
|
376
|
+
forbiddenCombinations: [{
|
|
377
|
+
constraintId: 'forbidden.plan-author-human-principal-self-authorize',
|
|
378
|
+
subjects: [v1('identity.plan-author'), v1('identity.human-principal')], rule: 'cannot-authorize',
|
|
379
|
+
missingIdentity: 'block', comparator: v1('identity-comparator.corrective-replan.author-human-principal'),
|
|
380
|
+
enforcement: { kind: 'credential-verifier', verifier: v1('credential-verifier.corrective-replan.receipt') },
|
|
381
|
+
}],
|
|
382
|
+
},
|
|
383
|
+
effects: {
|
|
384
|
+
graphId: 'effect-graph.task-corrective-replan-journaled',
|
|
385
|
+
graphVersion: 2,
|
|
386
|
+
taskStatus: { from: ['ready', 'needs_fix', 'blocked', 'awaiting_execution_authorization'], to: 'awaiting_execution_authorization' },
|
|
387
|
+
milestoneStatus: { from: [], to: 'preserved' },
|
|
388
|
+
stepStatus: { from: ['failed'], to: 'input-dependent' },
|
|
389
|
+
supersession: {
|
|
390
|
+
executionAuthorization: 'supersede-approved',
|
|
391
|
+
finalAcceptance: 'preserve',
|
|
392
|
+
delegationGrants: 'preserve',
|
|
393
|
+
c1Claim: 'invalidate',
|
|
394
|
+
writerLease: 'invalidate',
|
|
395
|
+
},
|
|
396
|
+
orderedNodes: [
|
|
397
|
+
{
|
|
398
|
+
effectId: 'effect.task-state-write',
|
|
399
|
+
ordinal: 0,
|
|
400
|
+
adapter: v1('effect-adapter.journaled-corrective-replan'),
|
|
401
|
+
resource: v1('resource.task-state'),
|
|
402
|
+
operation: 'write',
|
|
403
|
+
conditions: [{ kind: 'guard-passed', guardId: 'task.corrective-replan.receipt-valid' }],
|
|
404
|
+
dependsOn: [],
|
|
405
|
+
},
|
|
406
|
+
{
|
|
407
|
+
effectId: 'effect.plan-artifact-publish',
|
|
408
|
+
ordinal: 1,
|
|
409
|
+
adapter: v1('effect-adapter.journaled-corrective-replan'),
|
|
410
|
+
resource: v1('resource.plan-artifact'),
|
|
411
|
+
operation: 'publish',
|
|
412
|
+
conditions: [],
|
|
413
|
+
dependsOn: ['effect.task-state-write'],
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
effectId: 'effect.plan-validation-append',
|
|
417
|
+
ordinal: 2,
|
|
418
|
+
adapter: v1('effect-adapter.journaled-corrective-replan'),
|
|
419
|
+
resource: v1('resource.plan-validation-sidecar'),
|
|
420
|
+
operation: 'append',
|
|
421
|
+
conditions: [{ kind: 'input-present', input: v1('input.corrective-plan-validation') }],
|
|
422
|
+
dependsOn: ['effect.plan-artifact-publish'],
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
effectId: 'effect.plan-risk-audit-append',
|
|
426
|
+
ordinal: 3,
|
|
427
|
+
adapter: v1('effect-adapter.journaled-corrective-replan'),
|
|
428
|
+
resource: v1('resource.plan-risk-audit-sidecar'),
|
|
429
|
+
operation: 'append',
|
|
430
|
+
conditions: [{ kind: 'input-present', input: v1('input.corrective-plan-risk-audit') }],
|
|
431
|
+
dependsOn: ['effect.plan-validation-append'],
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
effectId: 'effect.corrective-replan-execution-append',
|
|
435
|
+
ordinal: 4,
|
|
436
|
+
adapter: v1('effect-adapter.journaled-corrective-replan'),
|
|
437
|
+
resource: v1('resource.corrective-replan-execution-sidecar'),
|
|
438
|
+
operation: 'append',
|
|
439
|
+
conditions: [{ kind: 'input-present', input: v1('input.corrective-execution-receipt') }],
|
|
440
|
+
dependsOn: ['effect.plan-risk-audit-append'],
|
|
441
|
+
},
|
|
442
|
+
],
|
|
443
|
+
postconditions: [
|
|
444
|
+
{
|
|
445
|
+
postconditionId: 'postcondition.plan-hash-equals-candidate',
|
|
446
|
+
assertion: 'hash-equals',
|
|
447
|
+
reads: [
|
|
448
|
+
slice('projection.corrective-replan.candidate', ['field.candidate-plan-hash']),
|
|
449
|
+
slice('projection.task-state.readback', ['field.task-plan-hash']),
|
|
450
|
+
],
|
|
451
|
+
effectNodeIds: ['effect.task-state-write', 'effect.plan-artifact-publish'],
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
postconditionId: 'postcondition.task-revision-advanced',
|
|
455
|
+
assertion: 'revision-advanced',
|
|
456
|
+
reads: [
|
|
457
|
+
slice('projection.corrective-replan.state', ['field.task-revision']),
|
|
458
|
+
slice('projection.task-state.readback', ['field.task-revision']),
|
|
459
|
+
],
|
|
460
|
+
effectNodeIds: ['effect.task-state-write'],
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
postconditionId: 'postcondition.task-status-awaits-authorization',
|
|
464
|
+
assertion: 'status-equals',
|
|
465
|
+
reads: [slice('projection.task-state.readback', ['field.task-status'])],
|
|
466
|
+
effectNodeIds: ['effect.task-state-write'],
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
postconditionId: 'postcondition.plan-validation-appended', assertion: 'event-appended',
|
|
470
|
+
reads: [slice('projection.corrective-replan.terminal-readback', ['field.plan-validation-hash'])],
|
|
471
|
+
effectNodeIds: ['effect.plan-validation-append'],
|
|
472
|
+
},
|
|
473
|
+
{
|
|
474
|
+
postconditionId: 'postcondition.plan-risk-audit-appended', assertion: 'event-appended',
|
|
475
|
+
reads: [slice('projection.corrective-replan.terminal-readback', ['field.risk-audit-hash'])],
|
|
476
|
+
effectNodeIds: ['effect.plan-risk-audit-append'],
|
|
477
|
+
},
|
|
478
|
+
{
|
|
479
|
+
postconditionId: 'postcondition.execution-evidence-appended', assertion: 'event-appended',
|
|
480
|
+
reads: [slice('projection.corrective-replan.terminal-readback', ['field.runtime-fingerprint', 'field.catalog-hash'])],
|
|
481
|
+
effectNodeIds: ['effect.corrective-replan-execution-append'],
|
|
482
|
+
},
|
|
483
|
+
],
|
|
484
|
+
},
|
|
485
|
+
admissionBoundary: {
|
|
486
|
+
lock: {
|
|
487
|
+
policy: v1('lock-policy.corrective-replan-task-exclusive'),
|
|
488
|
+
scope: slice('projection.corrective-replan.state', ['field.task-id']),
|
|
489
|
+
},
|
|
490
|
+
orderedRevalidations: [
|
|
491
|
+
{ revalidationId: 'revalidation.corrective-replan.state', ordinal: 0, raceId: 'race.corrective-replan.state',
|
|
492
|
+
guardId: bindingGuardMetadata.guardId, evaluator: bindingGuardMetadata.evaluator,
|
|
493
|
+
reads: [slice('projection.corrective-replan.state', manifestProjectionFields('projection.corrective-replan.state'))], underLock: true },
|
|
494
|
+
{ revalidationId: 'revalidation.corrective-replan.authority', ordinal: 1, raceId: 'race.corrective-replan.authority',
|
|
495
|
+
guardId: receiptValidGuardMetadata.guardId, evaluator: receiptValidGuardMetadata.evaluator,
|
|
496
|
+
reads: [slice('projection.corrective-replan.authority', manifestProjectionFields('projection.corrective-replan.authority'))], underLock: true },
|
|
497
|
+
{ revalidationId: 'revalidation.corrective-replan.protocol', ordinal: 2, raceId: 'race.corrective-replan.protocol',
|
|
498
|
+
guardId: noWriterLeaseGuardMetadata.guardId, evaluator: noWriterLeaseGuardMetadata.evaluator,
|
|
499
|
+
reads: [slice('projection.corrective-replan.protocol', manifestProjectionFields('projection.corrective-replan.protocol'))], underLock: true },
|
|
500
|
+
],
|
|
501
|
+
firstEffectAfterRevalidation: true,
|
|
502
|
+
release: 'finally-after-readback',
|
|
503
|
+
},
|
|
504
|
+
atomicity: {
|
|
505
|
+
classification: 'journaled-multi-write',
|
|
506
|
+
nonMutatingFailureGuarantee: 'all-guards-before-effects',
|
|
507
|
+
executionKey: {
|
|
508
|
+
policy: v1('execution-key-policy.corrective-replan-exact'),
|
|
509
|
+
fields: correctiveReplanBindingManifest.rows.map(({ projection, fieldId }) => ({ projection, fieldId })),
|
|
510
|
+
hashPolicy: v1('hash-policy.canonical-json-sha256'),
|
|
511
|
+
},
|
|
512
|
+
replay: {
|
|
513
|
+
identicalCompleted: 'return-recorded-result',
|
|
514
|
+
identicalInProgress: 'resume-journal',
|
|
515
|
+
conflicting: 'reject-state-conflict',
|
|
516
|
+
},
|
|
517
|
+
journal: {
|
|
518
|
+
mode: 'required', schema: v1('journal-schema.corrective-replan-task'),
|
|
519
|
+
adapter: v1('journal-adapter.corrective-replan-task'), digestPolicy: v1('hash-policy.canonical-json-sha256'),
|
|
520
|
+
orderedPhases: [
|
|
521
|
+
{ phaseId: 'journal-phase.corrective-replan.intent', ordinal: 0, allowedNextPhaseIds: ['journal-phase.corrective-replan.applying'], terminal: false, durable: true, effectNodeIds: [], digestFields: [] },
|
|
522
|
+
{ phaseId: 'journal-phase.corrective-replan.applying', ordinal: 1, allowedNextPhaseIds: ['journal-phase.corrective-replan.published-pending-readback'], terminal: false, durable: true,
|
|
523
|
+
effectNodeIds: ['effect.task-state-write', 'effect.plan-artifact-publish', 'effect.plan-validation-append', 'effect.plan-risk-audit-append', 'effect.corrective-replan-execution-append'],
|
|
524
|
+
digestFields: correctiveReplanBindingManifest.rows.map(({ projection, fieldId }) => ({ projection, fieldId })) },
|
|
525
|
+
{ phaseId: 'journal-phase.corrective-replan.published-pending-readback', ordinal: 2, allowedNextPhaseIds: ['journal-phase.corrective-replan.completed'], terminal: false, durable: true, effectNodeIds: [], digestFields: [] },
|
|
526
|
+
{ phaseId: 'journal-phase.corrective-replan.completed', ordinal: 3, allowedNextPhaseIds: [], terminal: true, durable: true, effectNodeIds: [], digestFields: [] },
|
|
527
|
+
],
|
|
528
|
+
},
|
|
529
|
+
recovery: {
|
|
530
|
+
mode: 'automatic-roll-forward', trigger: 'explicit-repair', adapter: v1('recovery-adapter.corrective-replan-roll-forward'),
|
|
531
|
+
fromPhaseIds: ['journal-phase.corrective-replan.intent', 'journal-phase.corrective-replan.applying', 'journal-phase.corrective-replan.published-pending-readback'],
|
|
532
|
+
orderedSteps: [
|
|
533
|
+
{ stepId: 'recovery-step.corrective-replan.intent-to-applying', ordinal: 0, fromPhaseId: 'journal-phase.corrective-replan.intent', toPhaseId: 'journal-phase.corrective-replan.applying' },
|
|
534
|
+
{ stepId: 'recovery-step.corrective-replan.applying-to-readback', ordinal: 1, fromPhaseId: 'journal-phase.corrective-replan.applying', toPhaseId: 'journal-phase.corrective-replan.published-pending-readback' },
|
|
535
|
+
{ stepId: 'recovery-step.corrective-replan.readback-to-completed', ordinal: 2, fromPhaseId: 'journal-phase.corrective-replan.published-pending-readback', toPhaseId: 'journal-phase.corrective-replan.completed' },
|
|
536
|
+
],
|
|
537
|
+
},
|
|
538
|
+
readback: {
|
|
539
|
+
adapter: v1('readback-adapter.corrective-replan-terminal'),
|
|
540
|
+
wiring: 'invoked-post-effect',
|
|
541
|
+
checkpoints: {
|
|
542
|
+
preCompletion: 'domain-readback',
|
|
543
|
+
postTerminal: 'terminal-readback',
|
|
544
|
+
},
|
|
545
|
+
projection: v1('projection.corrective-replan.terminal-readback'),
|
|
546
|
+
postconditionIds: [
|
|
547
|
+
'postcondition.plan-hash-equals-candidate',
|
|
548
|
+
'postcondition.task-revision-advanced',
|
|
549
|
+
'postcondition.task-status-awaits-authorization',
|
|
550
|
+
'postcondition.plan-validation-appended',
|
|
551
|
+
'postcondition.plan-risk-audit-appended',
|
|
552
|
+
'postcondition.execution-evidence-appended',
|
|
553
|
+
],
|
|
554
|
+
resultDigestPolicy: v1('hash-policy.canonical-json-sha256'),
|
|
555
|
+
},
|
|
556
|
+
},
|
|
557
|
+
racePreconditions: [
|
|
558
|
+
{ raceId: 'race.corrective-replan.state', reasonClass: 'external-race', guardId: bindingGuardMetadata.guardId,
|
|
559
|
+
revalidationId: 'revalidation.corrective-replan.state', reads: [slice('projection.corrective-replan.state', manifestProjectionFields('projection.corrective-replan.state'))],
|
|
560
|
+
allowedFailureCodes: ['STATE_CONFLICT', 'GIT_PRECONDITION_FAILED'] },
|
|
561
|
+
{ raceId: 'race.corrective-replan.authority', reasonClass: 'external-race', guardId: receiptValidGuardMetadata.guardId,
|
|
562
|
+
revalidationId: 'revalidation.corrective-replan.authority', reads: [slice('projection.corrective-replan.authority', manifestProjectionFields('projection.corrective-replan.authority'))],
|
|
563
|
+
allowedFailureCodes: ['STATE_CONFLICT', 'CREDENTIAL_EXPIRED'] },
|
|
564
|
+
{ raceId: 'race.corrective-replan.protocol', reasonClass: 'integrity-failure', guardId: noWriterLeaseGuardMetadata.guardId,
|
|
565
|
+
revalidationId: 'revalidation.corrective-replan.protocol', reads: [slice('projection.corrective-replan.protocol', manifestProjectionFields('projection.corrective-replan.protocol'))],
|
|
566
|
+
allowedFailureCodes: ['STATE_CONFLICT', 'STATE_CORRUPT'] },
|
|
567
|
+
],
|
|
568
|
+
navigation: { kind: 'human-gate', mode: 'exact', priority: 800 },
|
|
569
|
+
executorAdapter: v1('effect-adapter.journaled-corrective-replan'),
|
|
570
|
+
readinessManifest: {
|
|
571
|
+
developmentStage: 'candidate',
|
|
572
|
+
releaseProfile: v1('release-profile.exact-mutation'),
|
|
573
|
+
coverageProfile: v1('coverage-profile.exact-transition'),
|
|
574
|
+
limitations: [v1('limitation.release-profile.verified-evidence-external')],
|
|
575
|
+
evidence: [],
|
|
576
|
+
},
|
|
577
|
+
traceability: {
|
|
578
|
+
defectIds: ['WF-STABLE-024', 'WF-REG-005'],
|
|
579
|
+
invariantIds: ['advertised-exact-transition-is-executable', 'journaled-corrective-replan-replay-safe', 'protocol-v2-human-gate'],
|
|
580
|
+
positiveEvidence: ['tests/transition-catalog.test.ts', 'tests/corrective-yield-executor-model.test.ts'],
|
|
581
|
+
hostileEvidence: ['tests/lifecycle-model.test.ts', 'tests/corrective-replan-executor-model.test.ts'],
|
|
582
|
+
},
|
|
583
|
+
documentation: {
|
|
584
|
+
summary: 'Protocol-v2 Human-gated, receipt-bound, journaled corrective Plan replacement.',
|
|
585
|
+
},
|
|
586
|
+
};
|
|
587
|
+
|
|
588
|
+
const frozenMetadata = deepFreeze(metadata);
|
|
589
|
+
const frozenGuards = deepFreeze(guards);
|
|
590
|
+
const runtimeFingerprint = runtimeSemanticFingerprint(frozenMetadata);
|
|
591
|
+
|
|
592
|
+
export const taskCorrectiveReplanDefinition: ExecutableTransitionDefinition<
|
|
593
|
+
CorrectiveReplanSnapshot,
|
|
594
|
+
CorrectiveReplanBindings
|
|
595
|
+
> = deepFreeze({
|
|
596
|
+
metadata: frozenMetadata,
|
|
597
|
+
runtimeSemanticFingerprint: runtimeFingerprint,
|
|
598
|
+
semanticFingerprint: runtimeFingerprint,
|
|
599
|
+
definitionManifestHash: definitionManifestHash(frozenMetadata, runtimeFingerprint),
|
|
600
|
+
guards: frozenGuards,
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
export function prepareCorrectiveReplanCandidate(
|
|
604
|
+
task: Readonly<TaskState>,
|
|
605
|
+
plan: Readonly<TaskPlanInput>,
|
|
606
|
+
): PreparedCorrectiveReplanCandidate {
|
|
607
|
+
const rendered = renderPlan(task as TaskState, plan as TaskPlanInput);
|
|
608
|
+
const planBytes = rendered.endsWith('\n') ? rendered : `${rendered}\n`;
|
|
609
|
+
return {
|
|
610
|
+
planBytes,
|
|
611
|
+
planHash: hashCorrectivePlanBytes(planBytes),
|
|
612
|
+
stepIds: plan.steps.map((step) => step.id),
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
export function hashCorrectivePlanBytes(planBytes: string): string {
|
|
617
|
+
return createHash('sha256').update(planBytes).digest('hex');
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
export function evaluateTaskCorrectiveReplanAvailability(
|
|
621
|
+
task: Readonly<TaskState>,
|
|
622
|
+
posture: Readonly<CorrectiveReplanPosture>,
|
|
623
|
+
): TransitionEvaluation<CorrectiveReplanAvailabilityBindings> {
|
|
624
|
+
const bindings = availabilityBindings(task, posture);
|
|
625
|
+
return evaluateTransition(
|
|
626
|
+
taskCorrectiveReplanDefinition,
|
|
627
|
+
correctiveReplanSnapshot(task, posture, null),
|
|
628
|
+
bindings,
|
|
629
|
+
'availability',
|
|
630
|
+
) as TransitionEvaluation<CorrectiveReplanAvailabilityBindings>;
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
export function evaluateTaskCorrectiveReplanCandidate(
|
|
634
|
+
task: Readonly<TaskState>,
|
|
635
|
+
posture: Readonly<CorrectiveReplanPosture>,
|
|
636
|
+
candidate: Readonly<PreparedCorrectiveReplanCandidate>,
|
|
637
|
+
): TransitionEvaluation<CorrectiveReplanExecutionBindings> {
|
|
638
|
+
const bindings: CorrectiveReplanExecutionBindings = {
|
|
639
|
+
...availabilityBindings(task, posture),
|
|
640
|
+
candidatePlanHash: candidate.planHash,
|
|
641
|
+
candidateStepIds: [...candidate.stepIds],
|
|
642
|
+
};
|
|
643
|
+
return evaluateGuardSubset(task, posture, candidate, bindings, 7);
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
export function evaluateTaskCorrectiveReplanAdmission(
|
|
647
|
+
task: Readonly<TaskState>,
|
|
648
|
+
posture: Readonly<CorrectiveReplanPosture>,
|
|
649
|
+
candidate: Readonly<PreparedCorrectiveReplanCandidate>,
|
|
650
|
+
facts: Readonly<CorrectiveReplanAdmissionFacts>,
|
|
651
|
+
): TransitionEvaluation<CorrectiveReplanExecutionBindings> {
|
|
652
|
+
const bindings: CorrectiveReplanExecutionBindings = {
|
|
653
|
+
...availabilityBindings(task, posture), candidatePlanHash: candidate.planHash, candidateStepIds: [...candidate.stepIds],
|
|
654
|
+
};
|
|
655
|
+
return evaluateTransition(
|
|
656
|
+
taskCorrectiveReplanDefinition, correctiveReplanSnapshot(task, posture, candidate, facts), bindings, 'execution',
|
|
657
|
+
) as TransitionEvaluation<CorrectiveReplanExecutionBindings>;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
export const evaluateTaskCorrectiveReplan = evaluateTaskCorrectiveReplanAvailability;
|
|
661
|
+
|
|
662
|
+
export function correctiveReplanNavigation(
|
|
663
|
+
evaluation: TransitionEvaluation<CorrectiveReplanAvailabilityBindings>,
|
|
664
|
+
): HumanGateTransitionNavigation<CorrectiveReplanAvailabilityBindings> {
|
|
665
|
+
if (!evaluation.allowed || evaluation.phase !== 'availability') {
|
|
666
|
+
throw new TypeError(`Cannot advertise blocked transition ${evaluation.transitionId}.`);
|
|
667
|
+
}
|
|
668
|
+
return {
|
|
669
|
+
kind: 'human-gate',
|
|
670
|
+
transitionId: evaluation.transitionId,
|
|
671
|
+
semanticFingerprint: evaluation.semanticFingerprint,
|
|
672
|
+
command: 'task corrective-replan-prepare',
|
|
673
|
+
bindings: evaluation.bindings,
|
|
674
|
+
evaluationPhase: 'availability',
|
|
675
|
+
pendingInputGuardIds: evaluation.pendingGuardIds,
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
export function correctiveReplanBlockedNavigation(
|
|
680
|
+
evaluation: TransitionEvaluation<CorrectiveReplanAvailabilityBindings>,
|
|
681
|
+
): BlockedTransitionNavigation {
|
|
682
|
+
if (evaluation.allowed || evaluation.phase !== 'availability') {
|
|
683
|
+
throw new TypeError(`Transition ${evaluation.transitionId} is not blocked during availability evaluation.`);
|
|
684
|
+
}
|
|
685
|
+
return {
|
|
686
|
+
kind: 'blocked',
|
|
687
|
+
transitionId: evaluation.transitionId,
|
|
688
|
+
semanticFingerprint: evaluation.semanticFingerprint,
|
|
689
|
+
command: 'task corrective-replan-prepare',
|
|
690
|
+
blockingGuardIds: evaluation.blockingGuardIds,
|
|
691
|
+
pendingInputGuardIds: evaluation.pendingGuardIds,
|
|
692
|
+
reasonCode: evaluation.blockingGuardIds.includes(noStepInProgressGuardMetadata.guardId)
|
|
693
|
+
? 'RUNNING_STEP_BLOCKS_REPLAN'
|
|
694
|
+
: 'CORRECTIVE_REPLAN_UNAVAILABLE',
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
function correctiveReplanSnapshot(
|
|
699
|
+
task: Readonly<TaskState>,
|
|
700
|
+
posture: Readonly<CorrectiveReplanPosture>,
|
|
701
|
+
candidate: Readonly<PreparedCorrectiveReplanCandidate> | null,
|
|
702
|
+
facts: Readonly<CorrectiveReplanAdmissionFacts> = {
|
|
703
|
+
actorRoleEligible: false, c1ClaimAbsent: false, writerLeaseAbsent: false,
|
|
704
|
+
planValidationPassed: false, receiptValid: false, riskAuditPassed: false,
|
|
705
|
+
},
|
|
706
|
+
): CorrectiveReplanSnapshot {
|
|
707
|
+
return {
|
|
708
|
+
taskId: task.id,
|
|
709
|
+
taskRevision: task.revision,
|
|
710
|
+
taskStatus: task.status,
|
|
711
|
+
currentPlanHash: task.planHash,
|
|
712
|
+
stepId: posture.stepId,
|
|
713
|
+
stepStatus: task.steps.find((step) => step.id === posture.stepId)?.status ?? null,
|
|
714
|
+
inProgressStepIds: task.steps.filter((step) => step.status === 'in_progress').map((step) => step.id),
|
|
715
|
+
remediationAttemptCount: posture.attemptCount,
|
|
716
|
+
correctivePosture: posture.posture,
|
|
717
|
+
correctiveDecision: posture.correctiveDecision,
|
|
718
|
+
candidatePlanHash: candidate?.planHash ?? null,
|
|
719
|
+
candidateStepIds: candidate ? [...candidate.stepIds] : null,
|
|
720
|
+
...facts,
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
function evaluateGuardSubset(
|
|
725
|
+
task: Readonly<TaskState>, posture: Readonly<CorrectiveReplanPosture>, candidate: Readonly<PreparedCorrectiveReplanCandidate>,
|
|
726
|
+
bindings: CorrectiveReplanExecutionBindings, count: number,
|
|
727
|
+
): TransitionEvaluation<CorrectiveReplanExecutionBindings> {
|
|
728
|
+
const snapshot = correctiveReplanSnapshot(task, posture, candidate);
|
|
729
|
+
const guardResults = taskCorrectiveReplanDefinition.guards.slice(0, count).map((guard) => guard.evaluate(snapshot, bindings));
|
|
730
|
+
const blockingGuardIds = guardResults.filter((result) => result.status === 'failed').map((result) => result.guardId);
|
|
731
|
+
return {
|
|
732
|
+
transitionId: TASK_CORRECTIVE_REPLAN_TRANSITION_ID,
|
|
733
|
+
semanticFingerprint: taskCorrectiveReplanDefinition.semanticFingerprint,
|
|
734
|
+
phase: 'execution', allowed: blockingGuardIds.length === 0, bindings,
|
|
735
|
+
guardResults, blockingGuardIds, pendingGuardIds: [],
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
function availabilityBindings(
|
|
740
|
+
task: Readonly<TaskState>,
|
|
741
|
+
posture: Readonly<CorrectiveReplanPosture>,
|
|
742
|
+
): CorrectiveReplanAvailabilityBindings {
|
|
743
|
+
return {
|
|
744
|
+
taskId: task.id,
|
|
745
|
+
expectedRevision: task.revision,
|
|
746
|
+
currentPlanHash: task.planHash ?? '',
|
|
747
|
+
stepId: posture.stepId,
|
|
748
|
+
correctiveDecisionEventId: posture.correctiveDecision?.eventId ?? '',
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
|
|
752
|
+
function executionBindings(
|
|
753
|
+
bindings: Readonly<CorrectiveReplanBindings>,
|
|
754
|
+
): Readonly<CorrectiveReplanExecutionBindings> | null {
|
|
755
|
+
return 'candidatePlanHash' in bindings && 'candidateStepIds' in bindings
|
|
756
|
+
? bindings
|
|
757
|
+
: null;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
function sameStrings(left: readonly string[], right: readonly string[]): boolean {
|
|
761
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
function guardMetadata(
|
|
765
|
+
guardId: TransitionGuardMetadata['guardId'],
|
|
766
|
+
description: string,
|
|
767
|
+
phase: TransitionGuardPhase = 'availability',
|
|
768
|
+
stage: TransitionGuardStage = 'observation',
|
|
769
|
+
snapshotFieldIds: TransitionGuardMetadata['reads'][number]['fieldIds'] = [],
|
|
770
|
+
requiredInputIds: readonly TransitionGuardMetadata['requiredInputs'][number]['id'][] = [],
|
|
771
|
+
reasonClass: TransitionGuardReasonClass = 'lifecycle-blocker',
|
|
772
|
+
): TransitionGuardMetadata {
|
|
773
|
+
const suffix = guardId.slice('task.corrective-replan.'.length);
|
|
774
|
+
return {
|
|
775
|
+
guardId,
|
|
776
|
+
semanticVersion: 1,
|
|
777
|
+
phase,
|
|
778
|
+
stage,
|
|
779
|
+
reasonClass,
|
|
780
|
+
evaluator: v1(`guard-evaluator.task.corrective-replan.${suffix}`),
|
|
781
|
+
reads: [slice('projection.corrective-replan.snapshot', snapshotFieldIds)],
|
|
782
|
+
requiredInputs: requiredInputIds.map((id) => v1(id)),
|
|
783
|
+
description,
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
function v1<TId extends string>(id: TId): { readonly id: TId; readonly version: 1 } {
|
|
788
|
+
return { id, version: 1 };
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
function slice<TProjection extends `projection.${string}`>(
|
|
792
|
+
projection: TProjection,
|
|
793
|
+
fieldIds: TransitionGuardMetadata['reads'][number]['fieldIds'],
|
|
794
|
+
): {
|
|
795
|
+
readonly projection: { readonly id: TProjection; readonly version: 1 };
|
|
796
|
+
readonly fieldIds: TransitionGuardMetadata['reads'][number]['fieldIds'];
|
|
797
|
+
} {
|
|
798
|
+
return { projection: v1(projection), fieldIds };
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
function field<TProjection extends `projection.${string}`, TField extends `field.${string}`>(
|
|
802
|
+
projection: TProjection,
|
|
803
|
+
fieldId: TField,
|
|
804
|
+
): {
|
|
805
|
+
readonly projection: { readonly id: TProjection; readonly version: 1 };
|
|
806
|
+
readonly fieldId: TField;
|
|
807
|
+
} {
|
|
808
|
+
return { projection: v1(projection), fieldId };
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
function manifestProjectionFields(projectionId: string): TransitionGuardMetadata['reads'][number]['fieldIds'] {
|
|
812
|
+
return correctiveReplanBindingManifest.rows
|
|
813
|
+
.filter((entry) => entry.projection.id === projectionId)
|
|
814
|
+
.map((entry) => entry.fieldId);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
function verdict(
|
|
818
|
+
metadata: TransitionGuardMetadata,
|
|
819
|
+
passed: boolean,
|
|
820
|
+
reasonCode: string,
|
|
821
|
+
message: string,
|
|
822
|
+
): TransitionGuardResult {
|
|
823
|
+
return {
|
|
824
|
+
guardId: metadata.guardId,
|
|
825
|
+
status: passed ? 'passed' : 'failed',
|
|
826
|
+
passed,
|
|
827
|
+
reasonCode: passed ? 'PASS' : reasonCode,
|
|
828
|
+
message: passed ? 'Guard passed.' : message,
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
function booleanGuard(
|
|
833
|
+
metadata: TransitionGuardMetadata,
|
|
834
|
+
fieldName: keyof CorrectiveReplanAdmissionFacts,
|
|
835
|
+
reasonCode: string,
|
|
836
|
+
): TransitionGuard<CorrectiveReplanSnapshot, CorrectiveReplanBindings> {
|
|
837
|
+
return {
|
|
838
|
+
metadata,
|
|
839
|
+
evaluate(snapshot) {
|
|
840
|
+
return verdict(metadata, snapshot[fieldName] === true, reasonCode, `${metadata.description}`);
|
|
841
|
+
},
|
|
842
|
+
};
|
|
843
|
+
}
|