codex-workflow-v2 2.0.0-beta.13.13 → 2.0.0-beta.13.15

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 (39) hide show
  1. package/README.md +17 -0
  2. package/dist/reviewer-runtime-build.json +19 -7
  3. package/dist/src/alpha6/captured-check-evidence.d.ts +49 -0
  4. package/dist/src/alpha6/captured-check-evidence.js +146 -0
  5. package/dist/src/alpha6/captured-check-evidence.js.map +1 -0
  6. package/dist/src/alpha6/corrective-decision-boundary.d.ts +4 -0
  7. package/dist/src/alpha6/corrective-decision-boundary.js +47 -0
  8. package/dist/src/alpha6/corrective-decision-boundary.js.map +1 -0
  9. package/dist/src/alpha6/root-cause-replan-carryover.d.ts +3 -1
  10. package/dist/src/alpha6/root-cause-replan-carryover.js +4 -0
  11. package/dist/src/alpha6/root-cause-replan-carryover.js.map +1 -1
  12. package/dist/src/contracts.d.ts +5 -1
  13. package/dist/src/dependency-provenance.js +10 -0
  14. package/dist/src/dependency-provenance.js.map +1 -1
  15. package/dist/src/version.d.ts +1 -1
  16. package/dist/src/version.js +1 -1
  17. package/dist/src/workflow-blocker-route.d.ts +8 -0
  18. package/dist/src/workflow-blocker-route.js +106 -0
  19. package/dist/src/workflow-blocker-route.js.map +1 -0
  20. package/dist/src/workflow.d.ts +1 -0
  21. package/dist/src/workflow.js +140 -18
  22. package/dist/src/workflow.js.map +1 -1
  23. package/docs/pdf/codex-workflow-v2-architecture-ru.pdf +0 -0
  24. package/docs/pdf/codex-workflow-v2-chat-only-guide-ru.pdf +0 -0
  25. package/docs/pdf/codex-workflow-v2-technical-reference-ru.pdf +0 -0
  26. package/docs/pdf/sources/codex-workflow-v2-architecture-ru.md +11 -1
  27. package/docs/pdf/sources/codex-workflow-v2-chat-only-guide-ru.md +12 -2
  28. package/docs/pdf/sources/codex-workflow-v2-technical-reference-ru.md +12 -2
  29. package/docs/release.md +63 -0
  30. package/docs/stable-release-defect-register.md +16 -0
  31. package/docs/updating-existing-project.md +9 -0
  32. package/package.json +1 -1
  33. package/plugins/codex-workflow-gateway/.codex-plugin/plugin.json +1 -1
  34. package/plugins/codex-workflow-gateway/references/chat-dispatch.md +40 -11
  35. package/plugins/codex-workflow-gateway/references/codebase-memory-routing.md +52 -0
  36. package/plugins/codex-workflow-gateway/scripts/chat-dispatch.mjs +7 -1
  37. package/plugins/codex-workflow-gateway/scripts/chat-model-policy.mjs +10 -8
  38. package/plugins/codex-workflow-gateway/scripts/chat-registry.mjs +23 -5
  39. package/plugins/codex-workflow-gateway/skills/codex-workflow-gateway/SKILL.md +84 -13
@@ -1,2 +1,2 @@
1
1
  export declare const PACKAGE_NAME = "codex-workflow-v2";
2
- export declare const PACKAGE_VERSION = "2.0.0-beta.13.13";
2
+ export declare const PACKAGE_VERSION = "2.0.0-beta.13.15";
@@ -1,3 +1,3 @@
1
1
  export const PACKAGE_NAME = 'codex-workflow-v2';
2
- export const PACKAGE_VERSION = '2.0.0-beta.13.13';
2
+ export const PACKAGE_VERSION = '2.0.0-beta.13.15';
3
3
  //# sourceMappingURL=version.js.map
@@ -0,0 +1,8 @@
1
+ type ObjectValue = Record<string, unknown>;
2
+ export declare function nextBetaPatchVersion(version: string): string;
3
+ /**
4
+ * Describes a cross-project incident when Core has no executable, repair, or Human route.
5
+ * The descriptor grants no authority: gateway/App orchestration owns chat creation and release work.
6
+ */
7
+ export declare function withWorkflowBlockerRoute(next: ObjectValue, packageVersion: string): ObjectValue;
8
+ export {};
@@ -0,0 +1,106 @@
1
+ import { isCliCommand } from './cli-actions.js';
2
+ import { hashCanonical } from './lifecycle/fingerprint.js';
3
+ const object = (value) => value !== null && typeof value === 'object' && !Array.isArray(value) ? value : {};
4
+ export function nextBetaPatchVersion(version) {
5
+ const beta = /^(.*-beta(?:\.\d+)*\.)(\d+)$/u.exec(version);
6
+ if (beta)
7
+ return `${beta[1]}${Number(beta[2]) + 1}`;
8
+ const stable = /^(\d+)\.(\d+)\.(\d+)$/u.exec(version);
9
+ if (stable)
10
+ return `${stable[1]}.${stable[2]}.${Number(stable[3]) + 1}`;
11
+ throw new TypeError(`Workflow package version has no incrementable patch: ${version}`);
12
+ }
13
+ function requiresHuman(next) {
14
+ const visit = (value) => {
15
+ if (Array.isArray(value))
16
+ return value.some(visit);
17
+ const record = object(value);
18
+ return Object.entries(record).some(([key, child]) => (key === 'humanApprovalRequired' && child === true)
19
+ || (key === 'requiredHumanGate' && child !== null && child !== undefined)
20
+ || visit(child));
21
+ };
22
+ return visit(next);
23
+ }
24
+ /**
25
+ * Describes a cross-project incident when Core has no executable, repair, or Human route.
26
+ * The descriptor grants no authority: gateway/App orchestration owns chat creation and release work.
27
+ */
28
+ export function withWorkflowBlockerRoute(next, packageVersion) {
29
+ const observation = object(next.observation);
30
+ const route = object(next.route);
31
+ const blockedAction = next.blockedAction;
32
+ if (next.action !== 'doctor'
33
+ || typeof blockedAction !== 'string'
34
+ || !isCliCommand(blockedAction)
35
+ || observation.kind !== 'assessment'
36
+ || observation.availableRepairTransitionId !== null
37
+ || observation.repair !== null
38
+ || route.actionSupport !== 'unwitnessed'
39
+ || requiresHuman(next))
40
+ return next;
41
+ const patchVersion = nextBetaPatchVersion(packageVersion);
42
+ const source = {
43
+ scope: typeof next.scope === 'string' ? next.scope : null,
44
+ id: typeof next.id === 'string' ? next.id : null,
45
+ revision: Number.isInteger(next.revision) ? next.revision : null,
46
+ status: typeof next.status === 'string' ? next.status : null,
47
+ stepId: typeof next.stepId === 'string' ? next.stepId : null,
48
+ action: 'doctor',
49
+ blockedAction,
50
+ routeId: typeof route.routeId === 'string' ? route.routeId : null,
51
+ routeFingerprint: typeof route.routeFingerprint === 'string' ? route.routeFingerprint : null,
52
+ reasonCodes: Array.isArray(observation.reasonCodes)
53
+ ? observation.reasonCodes.filter((value) => typeof value === 'string')
54
+ : [],
55
+ };
56
+ const blockerId = hashCanonical({ domain: 'codex-workflow/workflow-blocker-route/v1', packageVersion, source });
57
+ return {
58
+ ...next,
59
+ workflowBlockerRoute: {
60
+ schemaVersion: 1,
61
+ state: 'gateway-confirmation-required',
62
+ blockerId,
63
+ packageVersion,
64
+ source,
65
+ confirmation: {
66
+ required: true,
67
+ checks: [
68
+ 'Refresh status and next without mutation.',
69
+ 'Confirm that no executable Core, repair, or Human route exists.',
70
+ 'Confirm that the responsible product chat is not actively progressing the blocked action.',
71
+ ],
72
+ },
73
+ dispatch: {
74
+ owner: 'originating-coordinator-via-gateway',
75
+ projectName: 'codex-workflow-v2',
76
+ chatType: 'workflow-blocker',
77
+ title: patchVersion,
78
+ modelRequest: { phase: 'implementation', complexity: 'complex', highRisk: false },
79
+ requiredPacketFields: [
80
+ 'blockerId', 'packageVersion', 'sourceProject', 'sourceThreadId', 'source',
81
+ 'freshStatusHash', 'freshNextHash', 'repositoryState', 'expectedContinuation',
82
+ ],
83
+ },
84
+ patchChatResponsibilities: [
85
+ 'Reproduce and identify the state-machine cause.',
86
+ 'Implement the smallest general fix and regression coverage.',
87
+ 'Run release checks and publish the exact patch version.',
88
+ 'Send a closed callback report to the originating Coordinator without being polled.',
89
+ ],
90
+ callback: {
91
+ target: 'originating-coordinator-thread',
92
+ requiredFields: [
93
+ 'blockerId', 'publishedVersion', 'verification', 'downstreamUpdateRoute',
94
+ 'expectedFreshNext', 'residualBlockers',
95
+ ],
96
+ },
97
+ authority: {
98
+ core: ['describe', 'bind-current-navigation', 'suggest-patch-version'],
99
+ coordinator: ['confirm', 'dispatch', 'verify-created-instructions', 'receive-callback', 'resume-product'],
100
+ patchChat: ['analyze', 'implement', 'verify', 'publish', 'send-callback'],
101
+ coreEffects: 'none',
102
+ },
103
+ },
104
+ };
105
+ }
106
+ //# sourceMappingURL=workflow-blocker-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workflow-blocker-route.js","sourceRoot":"","sources":["../../src/workflow-blocker-route.ts"],"names":[],"mappings":"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"}
@@ -424,6 +424,7 @@ export declare class WorkflowService {
424
424
  private listMilestonesObserved;
425
425
  private taskGitNavigationOverride;
426
426
  private carriedPlanIntegrityUpdateCompatibility;
427
+ private navigationFixDirtyCarryoverCompatibility;
427
428
  private postRebindCheckSupportCompatibility;
428
429
  private carriedPostRebindCheckSupportDirtyCarryover;
429
430
  private assessPostRebindCheckSupportDirtyCarryover;
@@ -1,3 +1,5 @@
1
+ import { correctiveDecisionCheckoutEligible } from './alpha6/corrective-decision-boundary.js';
2
+ import { prepareCapturedCheckEvidence, assertCapturedCheckEvidence } from './alpha6/captured-check-evidence.js';
1
3
  import { readCorrectivePlanningAudit, readFailedStepPlanningRecovery } from './alpha6/failed-step-planning-recovery.js';
2
4
  import { explainChange } from './change-explanation.js';
3
5
  import { pendingReviewRunnerFingerprint, PENDING_REVIEW_UPDATE_SIDECAR, preparePendingReviewUpdateEvent, PENDING_REVIEW_SOURCE_VERSION, pendingReviewSourceBinding, pendingReviewBindingHash, assertPendingReviewTransport, verifiedPendingReviewTransport } from './pending-review-update.js';
@@ -15,6 +17,7 @@ import { deriveCompletedStepCarryover, normalizePlanStepDefinition, } from './do
15
17
  import { pathAllowed, validatePlan } from './domain/validation.js';
16
18
  import { WorkflowError } from './errors.js';
17
19
  import { withObservedRouteMetadata } from './observed-routes.js';
20
+ import { withWorkflowBlockerRoute } from './workflow-blocker-route.js';
18
21
  import { bindGraphEvidence, createGraphRefreshRequest, fallbackGraphBinding, inspectGraphBinding, validateGraphRequestCurrent, } from './graph.js';
19
22
  import { inspectLocalDependency, inspectWorkflowVersions } from './diagnostics.js';
20
23
  import { appendTaskClaim, appendTaskHandback, appendTaskHandoff, appendTaskWriterCredentialReplacement, assertTaskClaimAllowed, assertTaskMutationAllowedByC1, defaultTaskWorkerActor, prepareTaskCorrectiveYield, readTaskC1Posture, readTaskHandoffEvents, } from './alpha6/handoff.js';
@@ -22,7 +25,7 @@ import { assessDownstreamProofCarryover, assessDownstreamProofReplanRecovery, as
22
25
  import { applyAdoptionPosture, assertAdoptionBaselinePreserved, buildAdoptionPreparation, initializeProjectRegistrationAdoption, readCurrentAdoptionPosture, } from './alpha6/adoption.js';
23
26
  import { appendCurrentPlanRiskAudit, appendReboundPlanRiskAudit, buildCandidatePlanRiskAuditEvent, buildPlanRiskAuditEvent, readCurrentPlanRiskAudit, readCurrentPlanningObstructionAudit, readPlanRiskAuditEvents, validatePlanRiskAuditCandidate, } from './alpha6/plan-risk.js';
24
27
  import { hashCompletedSteps, hashExecutionAuthorization, preparePreExecutionReplanEvent, preExecutionReplanSidecar, readCurrentPreExecutionReplan, } from './alpha6/preexecution-replan.js';
25
- import { assessRootCauseReplanDirtyCarryover, matchesTerminalCorrectiveYield, } from './alpha6/root-cause-replan-carryover.js';
28
+ import { assessRootCauseReplanDirtyCarryover, isRootCauseReplacementStart, matchesTerminalCorrectiveYield, } from './alpha6/root-cause-replan-carryover.js';
26
29
  import { appendCorrectiveDecisionEvent, appendPlanIntegrityRecoveryDecision, appendRemediationModeRecovery, appendStopEscalateOverride, assertCorrectiveAuditorIndependence, assertGuardedRemediationAttemptAllowed, defaultCorrectiveAuditorActor, effectiveStepAllowedWrites, isKnowledgeReboundSupportDecision, findCurrentGuardedRemediationPosture, findRemediationModeRecoveryCandidate, findFailedGuardedStepRequiringCorrectiveDecision, readGuardedRemediationPostureForStep, readCorrectiveDecisionEvents, readRemediationEvents, deriveRollingCauseDecision, deriveCurrentCauseDecisionForTask, prepareRemediationEvent, prepareStopEscalateOverride, stopEscalateOverrideDecisionEventIds, } from './alpha6/remediation.js';
27
30
  import { assessPlanIntegrityRecovery, defaultPlanIntegrityRecoveryActor, } from './alpha6/plan-integrity.js';
28
31
  import { eligiblePlanIntegrityUpdateSource } from './alpha6/plan-integrity-update.js';
@@ -51,7 +54,7 @@ import { PACKAGE_NAME, PACKAGE_VERSION } from './version.js';
51
54
  import { correctiveReplanBlockedNavigation, correctiveReplanNavigation, evaluateTaskCorrectiveReplanAvailability, evaluateTaskCorrectiveReplanCandidate, identifyCorrectiveReplanPosture, prepareCorrectiveReplanCandidate, TASK_CORRECTIVE_REPLAN_TRANSITION_ID, } from './lifecycle/corrective-replan.js';
52
55
  const MAX_STRICT_REVIEW_INFRASTRUCTURE_FAILURES = 2;
53
56
  // Release-reviewed exact compatibility target; never infer support from PACKAGE_VERSION.
54
- const CONSUMED_CARRYOVER_TARGET_VERSION = '2.0.0-beta.13.13';
57
+ const CONSUMED_CARRYOVER_TARGET_VERSION = '2.0.0-beta.13.15';
55
58
  export class WorkflowService {
56
59
  store;
57
60
  now;
@@ -2847,7 +2850,7 @@ export class WorkflowService {
2847
2850
  const planIntegrityDirtyCarryover = step.status === 'planned' && dirty.length > 0
2848
2851
  ? this.assessPostRebindCheckSupportDirtyCarryover(context.identity.repositoryRoot, task, stepId)
2849
2852
  : null;
2850
- const rootCauseReplanDirtyCarryover = step.status === 'planned' && dirty.length > 0
2853
+ const rootCauseReplanDirtyCarryover = isRootCauseReplacementStart(step.status, dirty.length > 0)
2851
2854
  ? this.assessCurrentRootCauseReplanDirtyCarryover(context.identity.repositoryRoot, task, stepId)
2852
2855
  : null;
2853
2856
  if (step.status === 'planned'
@@ -3099,12 +3102,14 @@ export class WorkflowService {
3099
3102
  // Bind useful uncommitted work to this failure without treating it as
3100
3103
  // completed-Step authority.
3101
3104
  const dirtyFiles = changedFiles(context.identity.repositoryRoot).sort();
3105
+ const capturedEvidence = prepareCapturedCheckEvidence(this.store, task, checks);
3102
3106
  const failurePause = {
3103
3107
  recordedAt: now.toISOString(),
3104
3108
  dirtyFiles,
3105
3109
  dirtyWorktreeHash: hashDirtyWorktree(context.identity.repositoryRoot, dirtyFiles),
3106
3110
  checksHash: sha256Hex(canonicalJsonStringify(checks)),
3107
3111
  failedChecks: normalizedFailures,
3112
+ checksEvidence: capturedEvidence.reference,
3108
3113
  };
3109
3114
  const preparedRemediation = reviewRequired && planRiskAudit
3110
3115
  ? prepareRemediationEvent(this.store, task, stepId, 'checks-failed', {
@@ -3161,6 +3166,7 @@ export class WorkflowService {
3161
3166
  : 'failed-step-continuation',
3162
3167
  now,
3163
3168
  targets: [
3169
+ ...(capturedEvidence.target ? [capturedEvidence.target] : []),
3164
3170
  ...(preparedRemediation?.postimage
3165
3171
  ? [{
3166
3172
  path: relative(path.join(taskRoot, 'remediation-events.jsonl')),
@@ -3176,6 +3182,7 @@ export class WorkflowService {
3176
3182
  validate: () => {
3177
3183
  const saved = this.store.readTask(task.projectId, task.id);
3178
3184
  const savedStep = requireStep(saved, stepId);
3185
+ assertCapturedCheckEvidence(this.store, saved, savedStep.failurePause, checks);
3179
3186
  const readbackDirtyFiles = changedFiles(context.identity.repositoryRoot).sort();
3180
3187
  const remediationHash = preparedRemediation
3181
3188
  ? readRemediationEvents(this.store, saved).find((event) => event.eventId === preparedRemediation.event.eventId)?.eventHash ?? null
@@ -3779,6 +3786,9 @@ export class WorkflowService {
3779
3786
  resolution: currentCycle,
3780
3787
  });
3781
3788
  }
3789
+ if (!correctiveDecisionCheckoutEligible(this.store, identity.repositoryRoot, task, stepId)) {
3790
+ throw new WorkflowError('TRANSITION_BLOCKED', 'Corrective decision requires the Task checkout to be clean or match its exact in-scope failed checkpoint.', { taskId, stepId });
3791
+ }
3782
3792
  assertCorrectiveAuditorIndependence(this.store, task, stepId, correctiveAudit.auditor);
3783
3793
  return appendCorrectiveDecisionEvent(this.store, task, stepId, correctiveAudit, planRiskAudit, task.planHash, this.now());
3784
3794
  }
@@ -4863,7 +4873,12 @@ export class WorkflowService {
4863
4873
  mode: 'post-rebind-dirty-carryover',
4864
4874
  }
4865
4875
  : null;
4866
- const deferredKnowledge = this.deferredPlanIntegrityKnowledge(snapshot.identity.repositoryRoot, task, assessment?.stepId ?? adoptedCompatibility?.stepId ?? dirtyCarryoverCompatibility?.stepId ?? null, assessment?.evidence?.changedFiles ?? adoptedCompatibility?.dirtyFiles ?? dirtyCarryoverCompatibility?.dirtyFiles ?? []);
4876
+ const navigationFixCompatibility = assessment || adoptedCompatibility || dirtyCarryoverCompatibility
4877
+ ? null
4878
+ : this.navigationFixDirtyCarryoverCompatibility(snapshot.identity.repositoryRoot, task);
4879
+ const deferredKnowledge = this.deferredPlanIntegrityKnowledge(snapshot.identity.repositoryRoot, task, assessment?.stepId ?? adoptedCompatibility?.stepId ?? dirtyCarryoverCompatibility?.stepId
4880
+ ?? navigationFixCompatibility?.stepId ?? null, assessment?.evidence?.changedFiles ?? adoptedCompatibility?.dirtyFiles ?? dirtyCarryoverCompatibility?.dirtyFiles
4881
+ ?? navigationFixCompatibility?.dirtyFiles ?? []);
4867
4882
  const versions = inspectWorkflowVersions(snapshot.identity.repositoryRoot, [task.baseBranch]);
4868
4883
  const versionSurfaces = {
4869
4884
  declared: versions.declared,
@@ -4876,7 +4891,9 @@ export class WorkflowService {
4876
4891
  const sourceBetaVersion = parseWorkflowBetaVersion(sourceVersion);
4877
4892
  const targetBetaVersion = parseWorkflowBetaVersion(PACKAGE_VERSION);
4878
4893
  const hasRunningStep = snapshot.tasks.some((candidate) => candidate.steps.some((step) => step.status === 'in_progress'));
4879
- const sourceIsEligible = eligiblePlanIntegrityUpdateSource(sourceBetaVersion, assessment, hasRunningStep);
4894
+ const sourceIsEligible = navigationFixCompatibility
4895
+ ? sourceBetaVersion?.train === 13 && sourceBetaVersion.patch === 14 && !hasRunningStep
4896
+ : eligiblePlanIntegrityUpdateSource(sourceBetaVersion, assessment, hasRunningStep);
4880
4897
  const targetIsNewer = sourceBetaVersion !== null
4881
4898
  && targetBetaVersion !== null
4882
4899
  && (targetBetaVersion.train > sourceBetaVersion.train
@@ -4884,10 +4901,23 @@ export class WorkflowService {
4884
4901
  && targetBetaVersion.patch !== null
4885
4902
  && sourceBetaVersion.patch !== null
4886
4903
  && targetBetaVersion.patch > sourceBetaVersion.patch));
4904
+ const c1 = readTaskC1Posture(this.store, task);
4905
+ const now = this.now().getTime();
4906
+ const activeLeases = snapshot.leases.filter((lease) => Date.parse(lease.expiresAt) > now);
4907
+ const staleLeases = snapshot.leases.filter((lease) => Date.parse(lease.expiresAt) <= now);
4908
+ const repairableNavigationLease = navigationFixCompatibility
4909
+ && c1.state === 'claimed'
4910
+ && activeLeases.length === 0
4911
+ && staleLeases.length === 1
4912
+ && staleLeases[0].entityId === task.id
4913
+ && staleLeases[0].owner === c1.claimant
4914
+ && createHash('sha256').update(staleLeases[0].token).digest('hex') === c1.writerLeaseTokenHash
4915
+ ? staleLeases[0]
4916
+ : null;
4887
4917
  const blockers = [
4888
4918
  ...deferredKnowledge.blockers,
4889
4919
  ...(assessments.length > 1 ? ['Exactly one failed Step may expose a Plan-integrity conflict.'] : []),
4890
- ...(assessment?.blockers ?? (adoptedCompatibility || dirtyCarryoverCompatibility
4920
+ ...(assessment?.blockers ?? (adoptedCompatibility || dirtyCarryoverCompatibility || navigationFixCompatibility
4891
4921
  ? []
4892
4922
  : (dirtyCarryoverAssessment?.blockers.length
4893
4923
  ? dirtyCarryoverAssessment.blockers
@@ -4901,19 +4931,29 @@ export class WorkflowService {
4901
4931
  ...Object.entries(versionSurfaces)
4902
4932
  .filter(([, version]) => version !== sourceVersion)
4903
4933
  .map(([surface, version]) => `${surface} Workflow version ${version ?? 'missing'} must equal the declared source ${sourceVersion ?? 'missing'}.`),
4904
- ...(snapshot.leases.length > 0 ? ['No active or stale Task writer lease may exist.'] : []),
4934
+ ...(!navigationFixCompatibility && snapshot.leases.length > 0
4935
+ ? ['No active or stale Task writer lease may exist.'] : []),
4936
+ ...(navigationFixCompatibility && activeLeases.length > 0
4937
+ ? ['No active writer lease may exist during navigation-fix transport.'] : []),
4938
+ ...(navigationFixCompatibility && staleLeases.length > 0 && !repairableNavigationLease
4939
+ ? ['Only the exact expired claimed Worker lease may be repaired before navigation-fix transport.'] : []),
4940
+ ...(repairableNavigationLease
4941
+ ? ['Repair the exact expired claimant lease with source locks repair, then repeat this preflight.'] : []),
4905
4942
  ...observationPreflightBlockers(snapshot.observation),
4906
4943
  ];
4907
4944
  return {
4908
4945
  readOnly: true,
4909
- eligible: Boolean(assessment?.eligible || adoptedCompatibility || dirtyCarryoverCompatibility) && blockers.length === 0,
4910
- action: (assessment?.eligible || adoptedCompatibility || dirtyCarryoverCompatibility) && blockers.length === 0
4946
+ eligible: Boolean(assessment?.eligible || adoptedCompatibility || dirtyCarryoverCompatibility || navigationFixCompatibility)
4947
+ && blockers.length === 0,
4948
+ action: (assessment?.eligible || adoptedCompatibility || dirtyCarryoverCompatibility || navigationFixCompatibility)
4949
+ && blockers.length === 0
4911
4950
  ? 'update plan-integrity-transport'
4912
4951
  : null,
4913
4952
  projectId: task.projectId,
4914
4953
  taskId: task.id,
4915
4954
  taskRevision: task.revision,
4916
- stepId: assessment?.stepId ?? adoptedCompatibility?.stepId ?? dirtyCarryoverCompatibility?.stepId ?? null,
4955
+ stepId: assessment?.stepId ?? adoptedCompatibility?.stepId ?? dirtyCarryoverCompatibility?.stepId
4956
+ ?? navigationFixCompatibility?.stepId ?? null,
4917
4957
  sourceVersion,
4918
4958
  targetVersion: PACKAGE_VERSION,
4919
4959
  taskBranch: task.taskBranch,
@@ -4921,19 +4961,28 @@ export class WorkflowService {
4921
4961
  dirtyFiles: assessment?.evidence?.changedFiles
4922
4962
  ?? adoptedCompatibility?.dirtyFiles
4923
4963
  ?? dirtyCarryoverCompatibility?.dirtyFiles
4964
+ ?? navigationFixCompatibility?.dirtyFiles
4924
4965
  ?? [],
4925
4966
  conflictHash: assessment?.evidence?.conflictHash
4926
4967
  ?? adoptedCompatibility?.conflictHash
4927
4968
  ?? dirtyCarryoverCompatibility?.conflictHash
4969
+ ?? navigationFixCompatibility?.conflictHash
4928
4970
  ?? null,
4929
4971
  compatibilityMode: adoptedCompatibility?.mode
4930
4972
  ?? dirtyCarryoverCompatibility?.mode
4973
+ ?? navigationFixCompatibility?.mode
4931
4974
  ?? 'failed-step-conflict',
4932
4975
  deferredKnowledgeRefresh: deferredKnowledge.deferred ? {
4933
4976
  action: 'task context-refresh', mode: 'delegated-content-only',
4934
4977
  productPaths: deferredKnowledge.productPaths, delegatedApprovalOptions: deferredKnowledge.options,
4935
4978
  application: 'after-provenance-and-scope-recovery', approvalDeferred: true,
4936
4979
  } : null,
4980
+ staleLeaseRepair: repairableNavigationLease ? [{
4981
+ action: 'locks repair',
4982
+ entityId: task.id,
4983
+ owner: repairableNavigationLease.owner,
4984
+ expiresAt: repairableNavigationLease.expiresAt,
4985
+ }] : [],
4937
4986
  requiredTransport: [
4938
4987
  'Commit only package.json and package-lock.json on the active Milestone base.',
4939
4988
  'Fast-forward or apply only that dependency commit to the Task branch while preserving all product bytes.',
@@ -4978,10 +5027,15 @@ export class WorkflowService {
4978
5027
  const carriedCompatibility = !assessment?.evidence && task.planHash
4979
5028
  ? this.carriedPlanIntegrityUpdateCompatibility(snapshot, task)
4980
5029
  : null;
5030
+ const navigationFixCompatibility = assessment?.evidence || adoptedCompatibility
5031
+ || dirtyCarryoverCompatibility || carriedCompatibility
5032
+ ? null
5033
+ : this.navigationFixDirtyCarryoverCompatibility(snapshot.identity.repositoryRoot, task);
4981
5034
  if ((!assessment?.evidence
4982
5035
  && !carriedCompatibility
4983
5036
  && !adoptedCompatibility
4984
- && !dirtyCarryoverCompatibility) || !task.planHash) {
5037
+ && !dirtyCarryoverCompatibility
5038
+ && !navigationFixCompatibility) || !task.planHash) {
4985
5039
  const ordinary = this.inspectDependencyProvenanceCandidate(snapshot, task);
4986
5040
  return {
4987
5041
  ...ordinary,
@@ -4998,6 +5052,7 @@ export class WorkflowService {
4998
5052
  const compatibility = carriedCompatibility
4999
5053
  ?? adoptedCompatibility
5000
5054
  ?? dirtyCarryoverCompatibility
5055
+ ?? navigationFixCompatibility
5001
5056
  ?? {
5002
5057
  stepId: assessment.stepId,
5003
5058
  planHash: task.planHash,
@@ -5499,7 +5554,7 @@ export class WorkflowService {
5499
5554
  return explainChange(input, repository, effectiveTask, navigation);
5500
5555
  }
5501
5556
  next(repository, taskId = null) {
5502
- return withObservedRouteMetadata(this.nextFromStatus(repository, taskId, this.statusFromSnapshot(this.observationSnapshot(repository))));
5557
+ return withWorkflowBlockerRoute(withObservedRouteMetadata(this.nextFromStatus(repository, taskId, this.statusFromSnapshot(this.observationSnapshot(repository)))), PACKAGE_VERSION);
5503
5558
  }
5504
5559
  nextFromStatus(repository, taskId, status) {
5505
5560
  if (status.observation.kind === 'blocked') {
@@ -6570,7 +6625,8 @@ export class WorkflowService {
6570
6625
  : null;
6571
6626
  let rootCauseReplanDirtyCarryover = ['task run', 'task handoff-prepare', 'task claim'].includes(String(next.action))
6572
6627
  && rootCauseStepId
6573
- && changedFiles(repositoryRoot).length > 0
6628
+ // Match runStep: historical carryover admits a replacement start, never a failed retry.
6629
+ && isRootCauseReplacementStart(task.steps.find(step => step.id === rootCauseStepId)?.status, changedFiles(repositoryRoot).length > 0)
6574
6630
  ? this.assessCurrentRootCauseReplanDirtyCarryover(repositoryRoot, task, rootCauseStepId)
6575
6631
  : null;
6576
6632
  if (rootCauseReplanDirtyCarryover?.eligible) {
@@ -7329,7 +7385,7 @@ export class WorkflowService {
7329
7385
  && failedStepRemediations.length >= 2
7330
7386
  && handoffPosture.state !== 'pending'
7331
7387
  && !readCorrectiveDecisionEvents(this.store, task).some(event => event.stepId === failedStep.id && event.triggeringAttemptCount === correctiveDecisionOrdinal)
7332
- && currentBranch(repositoryRoot) === task.taskBranch && isClean(repositoryRoot)
7388
+ && correctiveDecisionCheckoutEligible(this.store, repositoryRoot, task, failedStep.id)
7333
7389
  ? {
7334
7390
  command: 'task corrective-decision',
7335
7391
  state: 'eligible',
@@ -7725,6 +7781,64 @@ export class WorkflowService {
7725
7781
  });
7726
7782
  return candidates.length === 1 ? structuredClone(candidates[0].planIntegrity) : null;
7727
7783
  }
7784
+ navigationFixDirtyCarryoverCompatibility(repositoryRoot, task) {
7785
+ if (task.status !== 'ready' || !task.planHash
7786
+ || task.steps.some((step) => step.status === 'in_progress'))
7787
+ return null;
7788
+ const dirtyFiles = changedFiles(repositoryRoot).sort();
7789
+ if (dirtyFiles.length === 0)
7790
+ return null;
7791
+ const steps = task.steps.filter((step) => step.status === 'planned'
7792
+ && step.evidence === null
7793
+ && dirtyFiles.every((file) => pathAllowed(file, step.allowedWrites)
7794
+ && !pathAllowed(file, step.forbiddenScope)));
7795
+ if (steps.length !== 1)
7796
+ return null;
7797
+ const step = steps[0];
7798
+ const failures = readRemediationEvents(this.store, task)
7799
+ .filter((event) => event.stepId === step.id && event.failureKind === 'checks-failed');
7800
+ const terminal = failures.at(-1);
7801
+ if (!terminal)
7802
+ return null;
7803
+ const yields = readTaskHandoffEvents(this.store, task)
7804
+ .filter((event) => matchesTerminalCorrectiveYield(event, terminal, task.id));
7805
+ if (yields.length !== 0 || readTaskC1Posture(this.store, task).state !== 'claimed')
7806
+ return null;
7807
+ const dirtyWorktreeHash = hashDirtyWorktree(repositoryRoot, dirtyFiles);
7808
+ if (!terminal.failureEvidence.dirtyWorktreeHash
7809
+ || terminal.failureEvidence.dirtyWorktreeHash !== dirtyWorktreeHash)
7810
+ return null;
7811
+ const currentHead = headCommit(repositoryRoot);
7812
+ const dependencyCommit = inspectBoundedWorkflowDependencyCommit(repositoryRoot, currentHead);
7813
+ const authorization = [...task.authorizations].reverse().find((event) => event.kind === 'execution'
7814
+ && event.decision === 'approved'
7815
+ && event.briefHash === task.briefHash
7816
+ && event.planHash === task.planHash
7817
+ && event.mechanicalFeasibility);
7818
+ if (!authorization || (authorization.headCommit !== currentHead
7819
+ && dependencyCommit?.parentCommitSha !== authorization.headCommit))
7820
+ return null;
7821
+ const conflictHash = createHash('sha256').update(canonicalJsonStringify({
7822
+ domain: 'codex-workflow/navigation-fix-dirty-carryover/v1',
7823
+ taskId: task.id,
7824
+ taskRevision: task.revision,
7825
+ stepId: step.id,
7826
+ planHash: task.planHash,
7827
+ terminalFailureEventId: terminal.eventId,
7828
+ terminalFailureEventHash: terminal.eventHash,
7829
+ dirtyFiles,
7830
+ dirtyWorktreeHash,
7831
+ })).digest('hex');
7832
+ return {
7833
+ stepId: step.id,
7834
+ planHash: task.planHash,
7835
+ conflictHash,
7836
+ dirtyFiles,
7837
+ dirtyWorktreeHash,
7838
+ remediationEventIds: failures.map((event) => event.eventId),
7839
+ mode: 'navigation-fix-dirty-carryover',
7840
+ };
7841
+ }
7728
7842
  postRebindCheckSupportCompatibility(snapshot, task) {
7729
7843
  if (task.status !== 'awaiting_execution_authorization'
7730
7844
  || !task.planHash
@@ -7985,10 +8099,18 @@ export class WorkflowService {
7985
8099
  }
7986
8100
  const candidates = task.steps
7987
8101
  .filter((step) => step.status === 'planned' && (!stepId || step.id === stepId))
7988
- .flatMap((step) => [...new Set(readRemediationEvents(this.store, task)
7989
- .filter((event) => event.stepId === step.id && event.failureKind === 'checks-failed')
7990
- .map((event) => event.failureEvidence.completionCommit))]
7991
- .map((historyHead) => assessRootCauseReplanDirtyCarryover(this.store, repositoryRoot, task, step.id, { historyHead })))
8102
+ .flatMap((step) => {
8103
+ const failures = readRemediationEvents(this.store, task)
8104
+ .filter((event) => event.stepId === step.id && event.failureKind === 'checks-failed');
8105
+ const terminal = failures.at(-1);
8106
+ if (!terminal)
8107
+ return [];
8108
+ const yields = readTaskHandoffEvents(this.store, task)
8109
+ .filter((event) => matchesTerminalCorrectiveYield(event, terminal, task.id));
8110
+ if (yields.length !== 1)
8111
+ return [];
8112
+ return [assessRootCauseReplanDirtyCarryover(this.store, repositoryRoot, task, step.id, { historyHead: terminal.failureEvidence.completionCommit })];
8113
+ })
7992
8114
  .filter((assessment) => assessment.applicable);
7993
8115
  const eligible = candidates.filter((assessment) => assessment.eligible && assessment.compatibility);
7994
8116
  if (eligible.length === 1)