codex-workflow-v2 2.0.0-beta.13.6 → 2.0.0-beta.13.7
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/README.md +3 -1
- package/dist/reviewer-runtime-build.json +7 -7
- package/dist/src/alpha6/root-cause-replan-carryover.d.ts +2 -1
- package/dist/src/alpha6/root-cause-replan-carryover.js +32 -8
- package/dist/src/alpha6/root-cause-replan-carryover.js.map +1 -1
- package/dist/src/cli.js +5 -2
- package/dist/src/cli.js.map +1 -1
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/workflow.d.ts +5 -1
- package/dist/src/workflow.js +222 -22
- package/dist/src/workflow.js.map +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/release.md +32 -0
- package/package.json +2 -1
- package/plugins/codex-workflow-gateway/skills/codex-workflow-gateway/SKILL.md +35 -1
package/dist/src/workflow.js
CHANGED
|
@@ -15,12 +15,12 @@ import { WorkflowError } from './errors.js';
|
|
|
15
15
|
import { withObservedRouteMetadata } from './observed-routes.js';
|
|
16
16
|
import { bindGraphEvidence, createGraphRefreshRequest, fallbackGraphBinding, inspectGraphBinding, validateGraphRequestCurrent, } from './graph.js';
|
|
17
17
|
import { inspectLocalDependency, inspectWorkflowVersions } from './diagnostics.js';
|
|
18
|
-
import { appendTaskClaim, appendTaskHandback, appendTaskHandoff, appendTaskWriterCredentialReplacement, assertTaskClaimAllowed, assertTaskMutationAllowedByC1, defaultTaskWorkerActor, prepareTaskCorrectiveYield, readTaskC1Posture, } from './alpha6/handoff.js';
|
|
18
|
+
import { appendTaskClaim, appendTaskHandback, appendTaskHandoff, appendTaskWriterCredentialReplacement, assertTaskClaimAllowed, assertTaskMutationAllowedByC1, defaultTaskWorkerActor, prepareTaskCorrectiveYield, readTaskC1Posture, readTaskHandoffEvents, } from './alpha6/handoff.js';
|
|
19
19
|
import { assessDownstreamProofCarryover, assessDownstreamProofReplanRecovery, assessDownstreamProofRecovery, downstreamProofInvalidationSidecar, downstreamProofReplanRecoverySidecar, prepareDownstreamProofInvalidation, prepareDownstreamProofReplanRecovery, readDownstreamProofInvalidations, readDownstreamProofReplanRecoveries, hashDirtyWorktree, } from './alpha6/downstream-proof.js';
|
|
20
20
|
import { applyAdoptionPosture, assertAdoptionBaselinePreserved, buildAdoptionPreparation, initializeProjectRegistrationAdoption, readCurrentAdoptionPosture, } from './alpha6/adoption.js';
|
|
21
21
|
import { appendCurrentPlanRiskAudit, appendReboundPlanRiskAudit, buildCandidatePlanRiskAuditEvent, buildPlanRiskAuditEvent, readCurrentPlanRiskAudit, readPlanRiskAuditEvents, validatePlanRiskAuditCandidate, } from './alpha6/plan-risk.js';
|
|
22
22
|
import { hashCompletedSteps, hashExecutionAuthorization, preparePreExecutionReplanEvent, preExecutionReplanSidecar, readCurrentPreExecutionReplan, } from './alpha6/preexecution-replan.js';
|
|
23
|
-
import { assessRootCauseReplanDirtyCarryover, } from './alpha6/root-cause-replan-carryover.js';
|
|
23
|
+
import { assessRootCauseReplanDirtyCarryover, matchesTerminalCorrectiveYield, } from './alpha6/root-cause-replan-carryover.js';
|
|
24
24
|
import { appendCorrectiveDecisionEvent, appendPlanIntegrityRecoveryDecision, appendRemediationModeRecovery, appendStopEscalateOverride, assertCorrectiveAuditorIndependence, assertGuardedRemediationAttemptAllowed, defaultCorrectiveAuditorActor, effectiveStepAllowedWrites, findCurrentGuardedRemediationPosture, findRemediationModeRecoveryCandidate, findFailedGuardedStepRequiringCorrectiveDecision, readGuardedRemediationPostureForStep, readCorrectiveDecisionEvents, readRemediationEvents, deriveRollingCauseDecision, deriveCurrentCauseDecisionForTask, prepareRemediationEvent, prepareStopEscalateOverride, stopEscalateOverrideDecisionEventIds, } from './alpha6/remediation.js';
|
|
25
25
|
import { assessPlanIntegrityRecovery, defaultPlanIntegrityRecoveryActor, } from './alpha6/plan-integrity.js';
|
|
26
26
|
import { mechanicalCauseBindings, normalizeMechanicalFailures, } from './alpha6/remediation-cause.js';
|
|
@@ -4182,7 +4182,8 @@ export class WorkflowService {
|
|
|
4182
4182
|
const task = snapshot.tasks.find((candidate) => candidate.id === taskId);
|
|
4183
4183
|
if (!task)
|
|
4184
4184
|
throw new WorkflowError('NOT_FOUND', `Task ${taskId} was not found in this repository.`);
|
|
4185
|
-
const
|
|
4185
|
+
const candidate = inspectBoundedWorkflowDependencyCommit(snapshot.identity.repositoryRoot, headCommit(snapshot.identity.repositoryRoot));
|
|
4186
|
+
const carryover = this.assessCurrentRootCauseReplanDirtyCarryover(snapshot.identity.repositoryRoot, task, undefined, candidate?.parentCommitSha);
|
|
4186
4187
|
const preflight = this.inspectDependencyProvenanceCandidate(snapshot, task, null, null, null, null, carryover.eligible ? carryover.compatibility : null);
|
|
4187
4188
|
if (!preflight.rootCauseReplan)
|
|
4188
4189
|
return preflight;
|
|
@@ -4201,15 +4202,16 @@ export class WorkflowService {
|
|
|
4201
4202
|
...(preflight.parentCommitSha !== preflight.rootCauseReplan.sourceHeadCommit
|
|
4202
4203
|
? ['Corrective carryover dependency parent must equal the bound source HEAD.'] : []),
|
|
4203
4204
|
...(preflight.parentCommitSha
|
|
4204
|
-
&& workflowDependencyVersionAt(snapshot.identity.repositoryRoot, preflight.parentCommitSha) !== '2.0.0-beta.13.
|
|
4205
|
-
? ['Corrective carryover dependency parent must declare exact source
|
|
4206
|
-
...(preflight.dependencyCommits.at(-1)?.packageVersion !==
|
|
4207
|
-
? [
|
|
4205
|
+
&& workflowDependencyVersionAt(snapshot.identity.repositoryRoot, preflight.parentCommitSha) !== '2.0.0-beta.13.6'
|
|
4206
|
+
? ['Corrective carryover dependency parent must declare exact source beta.13.6.'] : []),
|
|
4207
|
+
...(preflight.dependencyCommits.at(-1)?.packageVersion !== PACKAGE_VERSION
|
|
4208
|
+
? [`Corrective carryover dependency commit must declare exact target ${PACKAGE_VERSION}.`] : []),
|
|
4208
4209
|
...(!baseDependency
|
|
4209
4210
|
|| baseDependency.commitSha !== baseTip
|
|
4210
|
-
|| baseDependency.packageVersion !==
|
|
4211
|
-
|| workflowDependencyVersionAt(snapshot.identity.repositoryRoot, baseDependency.parentCommitSha)
|
|
4212
|
-
|
|
4211
|
+
|| baseDependency.packageVersion !== PACKAGE_VERSION
|
|
4212
|
+
|| workflowDependencyVersionAt(snapshot.identity.repositoryRoot, baseDependency.parentCommitSha)
|
|
4213
|
+
!== workflowDependencyVersionAt(snapshot.identity.repositoryRoot, preflight.parentCommitSha ?? '')
|
|
4214
|
+
? ['Corrective carryover Milestone-base tip must be one dependency-only commit with the same exact source and runtime target.'] : []),
|
|
4213
4215
|
];
|
|
4214
4216
|
return bridgeBlockers.length === 0
|
|
4215
4217
|
? preflight
|
|
@@ -4222,14 +4224,14 @@ export class WorkflowService {
|
|
|
4222
4224
|
blockers: [...new Set([...preflight.blockers, ...bridgeBlockers])],
|
|
4223
4225
|
};
|
|
4224
4226
|
}
|
|
4225
|
-
correctiveCarryoverUpdatePreflight(repository, taskId) {
|
|
4227
|
+
correctiveCarryoverUpdatePreflight(repository, taskId, candidate = null) {
|
|
4226
4228
|
const snapshot = this.observationSnapshot(repository);
|
|
4227
4229
|
const task = snapshot.tasks.find((candidate) => candidate.id === taskId);
|
|
4228
4230
|
if (!task)
|
|
4229
4231
|
throw new WorkflowError('NOT_FOUND', `Task ${taskId} was not found in this repository.`);
|
|
4230
|
-
const sourceVersion = '2.0.0-beta.13.5';
|
|
4231
|
-
const targetVersion = '2.0.0-beta.13.6';
|
|
4232
4232
|
const versions = inspectWorkflowVersions(snapshot.identity.repositoryRoot, [task.baseBranch]);
|
|
4233
|
+
const sourceVersion = '2.0.0-beta.13.6';
|
|
4234
|
+
const targetVersion = '2.0.0-beta.13.7';
|
|
4233
4235
|
const sourceSurfaces = {
|
|
4234
4236
|
declared: versions.declared,
|
|
4235
4237
|
locked: versions.locked,
|
|
@@ -4237,8 +4239,11 @@ export class WorkflowService {
|
|
|
4237
4239
|
currentBranch: versions.currentBranch,
|
|
4238
4240
|
baseBranch: versions.milestoneBases.find((base) => base.branch === task.baseBranch)?.version ?? null,
|
|
4239
4241
|
};
|
|
4240
|
-
const assessment = this.assessCurrentRootCauseReplanDirtyCarryover(snapshot.identity.repositoryRoot, task);
|
|
4241
4242
|
const c1 = readTaskC1Posture(this.store, task);
|
|
4243
|
+
if (c1.state !== 'claimed') {
|
|
4244
|
+
return this.consumedCarryoverSourceRecoveryPreflight(snapshot, task, sourceSurfaces, candidate);
|
|
4245
|
+
}
|
|
4246
|
+
const assessment = this.assessCurrentRootCauseReplanDirtyCarryover(snapshot.identity.repositoryRoot, task);
|
|
4242
4247
|
const now = this.now().getTime();
|
|
4243
4248
|
const activeLeases = snapshot.leases.filter((lease) => Date.parse(lease.expiresAt) > now);
|
|
4244
4249
|
const staleLeases = snapshot.leases.filter((lease) => Date.parse(lease.expiresAt) <= now);
|
|
@@ -4322,6 +4327,155 @@ export class WorkflowService {
|
|
|
4322
4327
|
blockers,
|
|
4323
4328
|
};
|
|
4324
4329
|
}
|
|
4330
|
+
consumedCarryoverSourceRecoveryPreflight(snapshot, task, sourceSurfaces, candidate) {
|
|
4331
|
+
const repositoryRoot = snapshot.identity.repositoryRoot;
|
|
4332
|
+
const sourceVersion = '2.0.0-beta.13.6';
|
|
4333
|
+
const targetVersion = '2.0.0-beta.13.7';
|
|
4334
|
+
const c1 = readTaskC1Posture(this.store, task);
|
|
4335
|
+
const recorded = [...(task.dependencyProvenanceRecoveries ?? [])].reverse().find((record) => record.rootCauseReplan && task.systemCommits.includes(record.commitSha))?.rootCauseReplan;
|
|
4336
|
+
const currentHead = headCommit(repositoryRoot);
|
|
4337
|
+
const dirtyFiles = changedFiles(repositoryRoot).sort();
|
|
4338
|
+
const dirtyWorktreeHash = hashDirtyWorktree(repositoryRoot, dirtyFiles);
|
|
4339
|
+
const navigation = this.next(repositoryRoot, task.id);
|
|
4340
|
+
const action = String(navigation.action);
|
|
4341
|
+
const stepId = typeof navigation.stepId === 'string' ? navigation.stepId : null;
|
|
4342
|
+
const step = task.steps.find((candidate) => candidate.id === stepId) ?? null;
|
|
4343
|
+
const terminal = readRemediationEvents(this.store, task)
|
|
4344
|
+
.filter((event) => event.stepId === stepId && event.failureKind === 'checks-failed').at(-1);
|
|
4345
|
+
const matchingYields = readTaskHandoffEvents(this.store, task).filter((event) => terminal && matchesTerminalCorrectiveYield(event, terminal, task.id));
|
|
4346
|
+
const yielded = matchingYields.length === 1 ? matchingYields[0] : null;
|
|
4347
|
+
const assessment = this.assessCurrentRootCauseReplanDirtyCarryover(repositoryRoot, task, stepId ?? undefined);
|
|
4348
|
+
const missingClaimOnly = assessment.blockers.length === 1
|
|
4349
|
+
&& assessment.blockers[0].startsWith('Corrective carryover requires the retained claimed C1');
|
|
4350
|
+
const rollingWave = navigation.rollingWave;
|
|
4351
|
+
const sourceActionAllowed = (action === 'task plan-set' && step?.status === 'failed'
|
|
4352
|
+
&& rollingWave?.state === 'root-cause-replan-required' && !assessment.applicable && c1.state === 'none')
|
|
4353
|
+
|| (action === 'task handoff-prepare' && step?.status === 'planned' && missingClaimOnly && c1.state === 'none')
|
|
4354
|
+
|| (action === 'task claim' && step?.status === 'planned' && missingClaimOnly
|
|
4355
|
+
&& c1.state === 'pending' && c1.targetActor === yielded?.actor);
|
|
4356
|
+
const blockers = [
|
|
4357
|
+
...(PACKAGE_VERSION !== targetVersion ? [`Runner must be the exact ${targetVersion} compatibility target.`] : []),
|
|
4358
|
+
...Object.entries(sourceSurfaces).filter(([, version]) => version !== sourceVersion)
|
|
4359
|
+
.map(([surface]) => `${surface} Workflow version must equal the exact ${sourceVersion} source.`),
|
|
4360
|
+
...observationPreflightBlockers(snapshot.observation),
|
|
4361
|
+
...(snapshot.leases.length > 0 ? ['No active or stale writer lease may exist before source recovery.'] : []),
|
|
4362
|
+
...(snapshot.observation.pendingProjectTransactions.length
|
|
4363
|
+
+ snapshot.observation.pendingMilestoneTransactions.length
|
|
4364
|
+
+ snapshot.observation.pendingTaskTransactions.length
|
|
4365
|
+
+ snapshot.observation.corruptTaskTransactions.length
|
|
4366
|
+
+ snapshot.observation.coreTaskOperations.length > 0 ? ['No pending or corrupt transaction or Core operation may exist.'] : []),
|
|
4367
|
+
...(!recorded || recorded.stepId === stepId
|
|
4368
|
+
|| task.steps.find((candidate) => candidate.id === recorded.stepId)?.status !== 'completed'
|
|
4369
|
+
? ['A different, verified completed Step must own the historical carryover binding.'] : []),
|
|
4370
|
+
...(!sourceActionAllowed ? ['No exact source Plan replacement or original-Worker handoff action is eligible.'] : []),
|
|
4371
|
+
...(!(task.status === 'ready' || (action === 'task plan-set' && task.status === 'needs_fix'))
|
|
4372
|
+
|| task.workspaceOwner !== 'local'
|
|
4373
|
+
|| currentBranch(repositoryRoot) !== task.taskBranch
|
|
4374
|
+
|| task.steps.some((candidate) => candidate.status === 'in_progress')
|
|
4375
|
+
? ['The Task must retain its local, no-running-Step source recovery boundary.'] : []),
|
|
4376
|
+
...(!step || dirtyFiles.length === 0
|
|
4377
|
+
|| dirtyFiles.some((file) => !pathAllowed(file, step.allowedWrites) || pathAllowed(file, step.forbiddenScope))
|
|
4378
|
+
? ['Retained dirty files must remain inside the exact current Step scope.'] : []),
|
|
4379
|
+
...(!terminal || terminal.failureEvidence.completionCommit !== currentHead
|
|
4380
|
+
|| terminal.failureEvidence.dirtyWorktreeHash !== dirtyWorktreeHash
|
|
4381
|
+
? ['The terminal repeated failure HEAD and dirty-byte binding must remain unchanged.'] : []),
|
|
4382
|
+
...(!yielded || !terminal || yielded.planHash === null
|
|
4383
|
+
|| !Number.isFinite(Date.parse(yielded.recordedAt))
|
|
4384
|
+
|| Date.parse(yielded.recordedAt) < Date.parse(terminal.recordedAt)
|
|
4385
|
+
? ['The terminal failure must retain its exact completed original-Worker corrective yield.'] : []),
|
|
4386
|
+
];
|
|
4387
|
+
let candidateBinding = null;
|
|
4388
|
+
if (action === 'task plan-set') {
|
|
4389
|
+
if (!candidate) {
|
|
4390
|
+
blockers.push('Source Plan replacement requires candidate Plan and independent Plan Risk Audit inputs.');
|
|
4391
|
+
}
|
|
4392
|
+
else {
|
|
4393
|
+
try {
|
|
4394
|
+
validatePlan(candidate.plan);
|
|
4395
|
+
const retained = deriveCompletedStepCarryover(task, candidate.plan.steps);
|
|
4396
|
+
if (task.steps.some((completed) => completed.status === 'completed' && !retained.has(completed.id))) {
|
|
4397
|
+
throw new Error('Candidate must preserve every completed Step definition and its execution authority exactly.');
|
|
4398
|
+
}
|
|
4399
|
+
const correctiveStep = candidate.plan.steps.find((candidateStep) => candidateStep.id === stepId);
|
|
4400
|
+
if (!correctiveStep || dirtyFiles.some((file) => !pathAllowed(file, correctiveStep.allowedWrites) || pathAllowed(file, correctiveStep.forbiddenScope))) {
|
|
4401
|
+
throw new Error('Candidate must retain the exact failed Step and confine every dirty path to its replacement scope.');
|
|
4402
|
+
}
|
|
4403
|
+
if (correctiveStep.dependencies.some((dependency) => !retained.has(dependency))) {
|
|
4404
|
+
throw new Error('Candidate corrective Step dependencies must retain completed execution authority.');
|
|
4405
|
+
}
|
|
4406
|
+
if (candidate.plan.objective !== task.planObjective
|
|
4407
|
+
|| canonicalJsonStringify(candidate.plan.requirements) !== canonicalJsonStringify(task.requirements)
|
|
4408
|
+
|| canonicalJsonStringify(candidate.plan.acceptance) !== canonicalJsonStringify(task.acceptance)) {
|
|
4409
|
+
throw new Error('Candidate cannot change Task objective, requirements or acceptance.');
|
|
4410
|
+
}
|
|
4411
|
+
const map = this.assertKnowledgeMapBinding(repositoryRoot, task.projectId, candidate.plan.knowledgeMapRevision, candidate.plan.knowledgeMapHash);
|
|
4412
|
+
this.validateKnowledgeTargets(map, candidate.plan);
|
|
4413
|
+
const prepared = prepareCorrectiveReplanCandidate(task, candidate.plan);
|
|
4414
|
+
if (!task.planHash || normalizePlanKnowledgeBinding(prepared.planBytes, 'source recovery candidate').semanticHash
|
|
4415
|
+
=== normalizePlanKnowledgeBinding(readVerifiedPlanArtifact(this.store.taskRoot(task.projectId, task.id), task.planHash, task.planHash), 'current failed Plan').semanticHash) {
|
|
4416
|
+
throw new Error('Candidate must change the failed Plan semantics before source recovery.');
|
|
4417
|
+
}
|
|
4418
|
+
if (!['approved', 'approved-with-rationale'].includes(candidate.planRiskAudit.decision)) {
|
|
4419
|
+
throw new Error('Source recovery candidate requires a positive independent Plan Risk Audit.');
|
|
4420
|
+
}
|
|
4421
|
+
const auditTask = {
|
|
4422
|
+
...task, status: 'awaiting_execution_authorization', planHash: prepared.planHash,
|
|
4423
|
+
knowledgeMapRevision: candidate.plan.knowledgeMapRevision,
|
|
4424
|
+
knowledgeMapHash: candidate.plan.knowledgeMapHash,
|
|
4425
|
+
steps: candidate.plan.steps.map((candidateStep) => retained.get(candidateStep.id) ?? {
|
|
4426
|
+
...candidateStep, status: 'planned', evidence: null, failurePause: null,
|
|
4427
|
+
}),
|
|
4428
|
+
};
|
|
4429
|
+
validatePlanRiskAuditCandidate(this.store, auditTask, candidate.planRiskAudit);
|
|
4430
|
+
candidateBinding = {
|
|
4431
|
+
candidatePlanHash: prepared.planHash,
|
|
4432
|
+
candidateInputHash: sha256Hex(canonicalJsonStringify(candidate.plan)),
|
|
4433
|
+
riskAuditInputHash: sha256Hex(canonicalJsonStringify(candidate.planRiskAudit)),
|
|
4434
|
+
};
|
|
4435
|
+
}
|
|
4436
|
+
catch (error) {
|
|
4437
|
+
blockers.push(error instanceof Error ? error.message : String(error));
|
|
4438
|
+
}
|
|
4439
|
+
}
|
|
4440
|
+
}
|
|
4441
|
+
try {
|
|
4442
|
+
validateTaskHistory(repositoryRoot, task);
|
|
4443
|
+
this.assertTaskKnowledgeBinding(repositoryRoot, task);
|
|
4444
|
+
const sourceAudit = [...readPlanRiskAuditEvents(this.store, task)].reverse().find((event) => event.taskRevision <= (terminal?.failureEvidence.executionTaskRevision ?? -1));
|
|
4445
|
+
if (!sourceAudit || !yielded?.planHash || !task.planHash
|
|
4446
|
+
|| normalizePlanKnowledgeBinding(readVerifiedPlanArtifact(this.store.taskRoot(task.projectId, task.id), sourceAudit.planHash, task.planHash), 'terminal source').semanticHash
|
|
4447
|
+
!== normalizePlanKnowledgeBinding(readVerifiedPlanArtifact(this.store.taskRoot(task.projectId, task.id), yielded.planHash, task.planHash), 'terminal corrective yield').semanticHash) {
|
|
4448
|
+
throw new Error('Terminal failure and exact corrective yield must share a verified source Plan.');
|
|
4449
|
+
}
|
|
4450
|
+
}
|
|
4451
|
+
catch (error) {
|
|
4452
|
+
blockers.push(error instanceof Error ? error.message : String(error));
|
|
4453
|
+
}
|
|
4454
|
+
const binding = {
|
|
4455
|
+
taskId: task.id, taskRevision: task.revision, stepId, briefHash: task.briefHash,
|
|
4456
|
+
planHash: task.planHash, planRiskAuditEventHash: readCurrentPlanRiskAudit(this.store, task)?.audit.eventHash ?? null,
|
|
4457
|
+
sourceHeadCommit: currentHead, dirtyFiles, dirtyWorktreeHash,
|
|
4458
|
+
completedStepsHash: hashCompletedSteps(task), historicalCarryoverHash: recorded?.compatibilityHash ?? null,
|
|
4459
|
+
terminalRemediationEventHash: terminal?.eventHash ?? null,
|
|
4460
|
+
correctiveYieldEventHash: yielded?.eventHash ?? null, originalWorker: yielded?.actor ?? null,
|
|
4461
|
+
c1Posture: c1.state,
|
|
4462
|
+
candidateBinding,
|
|
4463
|
+
};
|
|
4464
|
+
return {
|
|
4465
|
+
readOnly: true, eligible: false, sourceVersion, targetVersion, ...binding,
|
|
4466
|
+
action: null, transportAllowed: false,
|
|
4467
|
+
...(action === 'task plan-set' ? { taskPlanContract: navigation.taskPlanContract } : {}),
|
|
4468
|
+
compatibilityHash: sha256Hex(canonicalJsonStringify(binding)),
|
|
4469
|
+
sourceRecovery: blockers.length === 0 ? {
|
|
4470
|
+
eligible: true, action,
|
|
4471
|
+
...(action === 'task plan-set' ? { ...candidateBinding } : {}),
|
|
4472
|
+
...(action === 'task handoff-prepare' ? { targetActor: yielded.actor } : {}),
|
|
4473
|
+
...(action === 'task claim' ? { actor: yielded.actor } : {}),
|
|
4474
|
+
instruction: 'Execute only this exact action with the installed source package; run source status then next and repeat this read-only preflight. Ordinary Plan Risk Audit and execution authorization retain their normal gates.',
|
|
4475
|
+
} : null,
|
|
4476
|
+
blockers,
|
|
4477
|
+
};
|
|
4478
|
+
}
|
|
4325
4479
|
historicalStepProvenanceRecoveryPreflight(repository, taskId) {
|
|
4326
4480
|
const snapshot = this.observationSnapshot(repository);
|
|
4327
4481
|
if (snapshot.observation.kind !== 'assessment') {
|
|
@@ -7512,21 +7666,67 @@ export class WorkflowService {
|
|
|
7512
7666
|
},
|
|
7513
7667
|
}, activeDownstreamProof, downstreamProofReplan, historicalStepProvenance, planIntegrity, rootCauseReplan);
|
|
7514
7668
|
}
|
|
7515
|
-
assessCurrentRootCauseReplanDirtyCarryover(repositoryRoot, task, stepId) {
|
|
7516
|
-
const
|
|
7517
|
-
&& task.systemCommits.includes(record.commitSha))
|
|
7669
|
+
assessCurrentRootCauseReplanDirtyCarryover(repositoryRoot, task, stepId, historyHead) {
|
|
7670
|
+
const recordedRecovery = [...(task.dependencyProvenanceRecoveries ?? [])].reverse().find((record) => record.rootCauseReplan
|
|
7671
|
+
&& task.systemCommits.includes(record.commitSha)) ?? null;
|
|
7672
|
+
const recorded = recordedRecovery?.rootCauseReplan ?? null;
|
|
7518
7673
|
if (recorded) {
|
|
7519
|
-
|
|
7674
|
+
const recordedStep = task.steps.find((candidate) => candidate.id === recorded.stepId) ?? null;
|
|
7675
|
+
if (!recordedStep) {
|
|
7520
7676
|
return {
|
|
7521
7677
|
applicable: true,
|
|
7522
7678
|
eligible: false,
|
|
7523
7679
|
compatibility: null,
|
|
7524
|
-
blockers: [`Recorded corrective carryover
|
|
7680
|
+
blockers: [`Recorded corrective carryover Step ${recorded.stepId} is missing from the current Plan.`],
|
|
7525
7681
|
};
|
|
7526
7682
|
}
|
|
7527
|
-
|
|
7528
|
-
|
|
7529
|
-
|
|
7683
|
+
if (recordedStep.status === 'completed') {
|
|
7684
|
+
const blockers = [];
|
|
7685
|
+
if (!recordedStep.evidence) {
|
|
7686
|
+
blockers.push(`Completed corrective carryover Step ${recorded.stepId} has no execution evidence.`);
|
|
7687
|
+
}
|
|
7688
|
+
try {
|
|
7689
|
+
validateTaskHistory(repositoryRoot, task, historyHead);
|
|
7690
|
+
const sourceAudit = readPlanRiskAuditEvents(this.store, task).find((event) => event.eventId === recorded.replacementPlanRiskAuditEventId
|
|
7691
|
+
&& event.eventHash === recorded.replacementPlanRiskAuditEventHash) ?? null;
|
|
7692
|
+
const classification = sourceAudit?.stepClassifications.find((candidate) => candidate.stepId === recorded.stepId) ?? null;
|
|
7693
|
+
if (!sourceAudit || !classification) {
|
|
7694
|
+
blockers.push('Completed corrective carryover Step lost its exact replacement Plan Risk Audit binding.');
|
|
7695
|
+
}
|
|
7696
|
+
else if (classification.reviewRequired
|
|
7697
|
+
&& recordedStep.evidence
|
|
7698
|
+
&& !verifiedHistoricalStepReviewBindings(this.store, task).some((binding) => binding.stepId === recorded.stepId
|
|
7699
|
+
&& binding.commitSha === recordedStep.evidence.commitSha)) {
|
|
7700
|
+
blockers.push(`Completed corrective carryover Step ${recorded.stepId} lacks verified strict-review proof.`);
|
|
7701
|
+
}
|
|
7702
|
+
}
|
|
7703
|
+
catch (error) {
|
|
7704
|
+
blockers.push(error instanceof Error ? error.message : String(error));
|
|
7705
|
+
}
|
|
7706
|
+
if (blockers.length > 0) {
|
|
7707
|
+
return {
|
|
7708
|
+
applicable: true,
|
|
7709
|
+
eligible: false,
|
|
7710
|
+
compatibility: null,
|
|
7711
|
+
blockers: [...new Set(blockers)],
|
|
7712
|
+
};
|
|
7713
|
+
}
|
|
7714
|
+
// A reviewed, history-valid completed Step consumed this carryover. Keep its
|
|
7715
|
+
// immutable recovery record, but let later Steps use their own lifecycle route.
|
|
7716
|
+
}
|
|
7717
|
+
else {
|
|
7718
|
+
if (stepId && stepId !== recorded.stepId) {
|
|
7719
|
+
return {
|
|
7720
|
+
applicable: true,
|
|
7721
|
+
eligible: false,
|
|
7722
|
+
compatibility: null,
|
|
7723
|
+
blockers: [`Recorded corrective carryover is bound to ${recorded.stepId}, not requested ${stepId}.`],
|
|
7724
|
+
};
|
|
7725
|
+
}
|
|
7726
|
+
return assessRootCauseReplanDirtyCarryover(this.store, repositoryRoot, task, recorded.stepId, {
|
|
7727
|
+
binding: recorded,
|
|
7728
|
+
});
|
|
7729
|
+
}
|
|
7530
7730
|
}
|
|
7531
7731
|
const candidates = task.steps
|
|
7532
7732
|
.filter((step) => step.status === 'planned' && (!stepId || step.id === stepId))
|