codex-workflow-v2 2.0.0-beta.2 → 2.0.0-beta.4

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 (128) hide show
  1. package/README.md +35 -2
  2. package/dist/src/alpha6/adoption.js +13 -3
  3. package/dist/src/alpha6/adoption.js.map +1 -1
  4. package/dist/src/alpha6/handoff.d.ts +21 -0
  5. package/dist/src/alpha6/handoff.js +100 -19
  6. package/dist/src/alpha6/handoff.js.map +1 -1
  7. package/dist/src/alpha6/milestone.d.ts +2 -0
  8. package/dist/src/alpha6/milestone.js +15 -0
  9. package/dist/src/alpha6/milestone.js.map +1 -1
  10. package/dist/src/alpha6/plan-risk.d.ts +1 -0
  11. package/dist/src/alpha6/plan-risk.js +6 -0
  12. package/dist/src/alpha6/plan-risk.js.map +1 -1
  13. package/dist/src/cli.js +188 -65
  14. package/dist/src/cli.js.map +1 -1
  15. package/dist/src/contracts.d.ts +111 -5
  16. package/dist/src/contracts.js +1 -1
  17. package/dist/src/credential-output.d.ts +10 -0
  18. package/dist/src/credential-output.js +73 -0
  19. package/dist/src/credential-output.js.map +1 -0
  20. package/dist/src/errors.d.ts +1 -1
  21. package/dist/src/errors.js.map +1 -1
  22. package/dist/src/index.d.ts +1 -1
  23. package/dist/src/index.js +1 -1
  24. package/dist/src/index.js.map +1 -1
  25. package/dist/src/lifecycle/canonical-hash.d.ts +6 -0
  26. package/dist/src/lifecycle/canonical-hash.js +24 -0
  27. package/dist/src/lifecycle/canonical-hash.js.map +1 -0
  28. package/dist/src/lifecycle/catalog.d.ts +26 -0
  29. package/dist/src/lifecycle/catalog.js +208 -0
  30. package/dist/src/lifecycle/catalog.js.map +1 -0
  31. package/dist/src/lifecycle/compiler-inspection.d.ts +48 -0
  32. package/dist/src/lifecycle/compiler-inspection.js +29 -0
  33. package/dist/src/lifecycle/compiler-inspection.js.map +1 -0
  34. package/dist/src/lifecycle/core-static-readiness.d.ts +6 -0
  35. package/dist/src/lifecycle/core-static-readiness.js +130 -0
  36. package/dist/src/lifecycle/core-static-readiness.js.map +1 -0
  37. package/dist/src/lifecycle/corrective-replan-authority.d.ts +53 -0
  38. package/dist/src/lifecycle/corrective-replan-authority.js +139 -0
  39. package/dist/src/lifecycle/corrective-replan-authority.js.map +1 -0
  40. package/dist/src/lifecycle/corrective-replan-binding-manifest.d.ts +21 -0
  41. package/dist/src/lifecycle/corrective-replan-binding-manifest.js +35 -0
  42. package/dist/src/lifecycle/corrective-replan-binding-manifest.js.map +1 -0
  43. package/dist/src/lifecycle/corrective-replan-credential-core.d.ts +86 -0
  44. package/dist/src/lifecycle/corrective-replan-credential-core.js +534 -0
  45. package/dist/src/lifecycle/corrective-replan-credential-core.js.map +1 -0
  46. package/dist/src/lifecycle/corrective-replan-credential-schema.d.ts +17 -0
  47. package/dist/src/lifecycle/corrective-replan-credential-schema.js +44 -0
  48. package/dist/src/lifecycle/corrective-replan-credential-schema.js.map +1 -0
  49. package/dist/src/lifecycle/corrective-replan-credentials.d.ts +47 -0
  50. package/dist/src/lifecycle/corrective-replan-credentials.js +103 -0
  51. package/dist/src/lifecycle/corrective-replan-credentials.js.map +1 -0
  52. package/dist/src/lifecycle/corrective-replan.d.ts +64 -0
  53. package/dist/src/lifecycle/corrective-replan.js +548 -0
  54. package/dist/src/lifecycle/corrective-replan.js.map +1 -0
  55. package/dist/src/lifecycle/evaluator.d.ts +2 -0
  56. package/dist/src/lifecycle/evaluator.js +35 -0
  57. package/dist/src/lifecycle/evaluator.js.map +1 -0
  58. package/dist/src/lifecycle/fingerprint.d.ts +31 -0
  59. package/dist/src/lifecycle/fingerprint.js +388 -0
  60. package/dist/src/lifecycle/fingerprint.js.map +1 -0
  61. package/dist/src/lifecycle/immutable.d.ts +1 -0
  62. package/dist/src/lifecycle/immutable.js +10 -0
  63. package/dist/src/lifecycle/immutable.js.map +1 -0
  64. package/dist/src/lifecycle/implementation-table.d.ts +29 -0
  65. package/dist/src/lifecycle/implementation-table.js +87 -0
  66. package/dist/src/lifecycle/implementation-table.js.map +1 -0
  67. package/dist/src/lifecycle/index.d.ts +4 -0
  68. package/dist/src/lifecycle/index.js +5 -0
  69. package/dist/src/lifecycle/index.js.map +1 -0
  70. package/dist/src/lifecycle/schema-artifact.d.ts +12 -0
  71. package/dist/src/lifecycle/schema-artifact.js +214 -0
  72. package/dist/src/lifecycle/schema-artifact.js.map +1 -0
  73. package/dist/src/lifecycle/semantic-registry.d.ts +6 -0
  74. package/dist/src/lifecycle/semantic-registry.js +489 -0
  75. package/dist/src/lifecycle/semantic-registry.js.map +1 -0
  76. package/dist/src/lifecycle/types.d.ts +718 -0
  77. package/dist/src/lifecycle/types.js +4 -0
  78. package/dist/src/lifecycle/types.js.map +1 -0
  79. package/dist/src/observation.d.ts +20 -0
  80. package/dist/src/observation.js +171 -0
  81. package/dist/src/observation.js.map +1 -0
  82. package/dist/src/state/corrective-replan-executor.d.ts +64 -0
  83. package/dist/src/state/corrective-replan-executor.js +598 -0
  84. package/dist/src/state/corrective-replan-executor.js.map +1 -0
  85. package/dist/src/state/corrective-replan-public-schema.d.ts +3 -0
  86. package/dist/src/state/corrective-replan-public-schema.js +43 -0
  87. package/dist/src/state/corrective-replan-public-schema.js.map +1 -0
  88. package/dist/src/state/corrective-replan-public.d.ts +95 -0
  89. package/dist/src/state/corrective-replan-public.js +618 -0
  90. package/dist/src/state/corrective-replan-public.js.map +1 -0
  91. package/dist/src/state/corrective-replan-transaction.d.ts +158 -0
  92. package/dist/src/state/corrective-replan-transaction.js +723 -0
  93. package/dist/src/state/corrective-replan-transaction.js.map +1 -0
  94. package/dist/src/state/corrective-yield-executor.d.ts +31 -0
  95. package/dist/src/state/corrective-yield-executor.js +236 -0
  96. package/dist/src/state/corrective-yield-executor.js.map +1 -0
  97. package/dist/src/state/corrective-yield-transaction.d.ts +97 -0
  98. package/dist/src/state/corrective-yield-transaction.js +591 -0
  99. package/dist/src/state/corrective-yield-transaction.js.map +1 -0
  100. package/dist/src/state/lock.d.ts +75 -3
  101. package/dist/src/state/lock.js +582 -30
  102. package/dist/src/state/lock.js.map +1 -1
  103. package/dist/src/state/store.d.ts +3 -0
  104. package/dist/src/state/store.js +65 -12
  105. package/dist/src/state/store.js.map +1 -1
  106. package/dist/src/transition-core.d.ts +67 -0
  107. package/dist/src/transition-core.js +318 -0
  108. package/dist/src/transition-core.js.map +1 -0
  109. package/dist/src/version.d.ts +1 -1
  110. package/dist/src/version.js +1 -1
  111. package/dist/src/workflow.d.ts +34 -22
  112. package/dist/src/workflow.js +486 -141
  113. package/dist/src/workflow.js.map +1 -1
  114. package/docs/lifecycle/state-machine-stabilization.md +641 -0
  115. package/docs/release.md +4 -2
  116. package/docs/updating-existing-project.md +9 -0
  117. package/package.json +4 -1
  118. package/schemas/adoption-posture-event.schema.json +1 -1
  119. package/schemas/context-envelope.schema.json +1 -1
  120. package/schemas/corrective-replan-credentials.private.schema.json +98 -0
  121. package/schemas/corrective-replan-execution-event.private.schema.json +53 -0
  122. package/schemas/corrective-replan-public.schema.json +142 -0
  123. package/schemas/corrective-replan-task-transaction.private.schema.json +151 -0
  124. package/schemas/corrective-yield-task-transaction.private.schema.json +98 -0
  125. package/schemas/remediation-event.schema.json +11 -1
  126. package/schemas/task-handoff-event.schema.json +1 -1
  127. package/schemas/transition-definition.schema.json +2609 -0
  128. package/schemas/transition-payloads.schema.json +140 -0
@@ -9,11 +9,11 @@ import { pathAllowed, validatePlan } from './domain/validation.js';
9
9
  import { WorkflowError } from './errors.js';
10
10
  import { bindGraphEvidence, createGraphRefreshRequest, fallbackGraphBinding, inspectGraphBinding, validateGraphRequestCurrent, } from './graph.js';
11
11
  import { inspectLocalDependency, inspectWorkflowVersions } from './diagnostics.js';
12
- import { appendTaskClaim, appendTaskHandback, appendTaskHandoff, assertTaskMutationAllowedByC1, defaultTaskWorkerActor, readTaskC1Posture, } from './alpha6/handoff.js';
12
+ import { appendTaskClaim, appendTaskHandback, appendTaskHandoff, assertTaskClaimAllowed, assertTaskMutationAllowedByC1, defaultTaskWorkerActor, readTaskC1Posture, } from './alpha6/handoff.js';
13
13
  import { applyAdoptionPosture, assertAdoptionBaselinePreserved, buildAdoptionPreparation, initializeProjectRegistrationAdoption, readCurrentAdoptionPosture, } from './alpha6/adoption.js';
14
14
  import { appendCurrentPlanRiskAudit, appendReboundPlanRiskAudit, readCurrentPlanRiskAudit, validatePlanRiskAuditCandidate, } from './alpha6/plan-risk.js';
15
15
  import { appendCorrectiveDecisionEvent, appendRemediationEvent, appendRemediationModeRecovery, assertCorrectiveAuditorIndependence, assertGuardedRemediationAttemptAllowed, defaultCorrectiveAuditorActor, findCurrentGuardedRemediationPosture, findRemediationModeRecoveryCandidate, findFailedGuardedStepRequiringCorrectiveDecision, readGuardedRemediationPostureForStep, readCorrectiveDecisionEvents, readRemediationEvents, } from './alpha6/remediation.js';
16
- import { assessMilestoneInitialAssembly, applyMilestoneScopeChangeTransaction, assertMilestoneMembershipIntegrity, assertMilestoneScopeChangeStatusAllowed, normalizeMilestonePlan, prepareMilestoneScopeChangeCandidate, prepareMilestoneScopeChangeBundle, readMilestoneForScopeChange, readMilestoneWithIntegrity, } from './alpha6/milestone.js';
16
+ import { assessMilestoneInitialAssembly, applyMilestoneScopeChangeTransaction, assertMilestoneMembershipIntegrity, assertMilestoneScopeChangeStatusAllowed, inspectMilestoneWithIntegrity, normalizeMilestonePlan, prepareMilestoneScopeChangeCandidate, prepareMilestoneScopeChangeBundle, readMilestoneForScopeChange, readMilestoneWithIntegrity, } from './alpha6/milestone.js';
17
17
  import { applyProjectTransaction, recoverProjectTransactions, runRollbackProjectTransaction, } from './beta1/project-transaction.js';
18
18
  import { buildCurrentStrictStepReviewCycle, ensurePendingStrictStepReview, isStepReviewRequired, recordStrictStepReview, } from './alpha6/review.js';
19
19
  import { applyMilestoneAutonomyContract, assertAutonomousPlanEvolution, prepareMilestoneAutonomyContract, readMilestoneAutonomyEvents, requireActiveMilestoneAutonomy, } from './alpha7/autonomy.js';
@@ -23,10 +23,16 @@ import { canonicalJsonStringify, sha256Hex } from './alpha6/store-sidecars.js';
23
23
  import { commitStep, mergeTaskBranch, runChecks, startLocalTaskBranch, syncBaseIntoTask, validateTaskHistory, } from './git.js';
24
24
  import { changedFiles, currentBranch, gitIsAncestor, headCommit, isClean, resolveRepositoryIdentity, runGit, } from './repository.js';
25
25
  import { launchStrictStepReviewer } from './reviewer.js';
26
- import { WriterLockManager } from './state/lock.js';
26
+ import { listCoreTaskOperationMutexes, inspectWriterLease as inspectRawWriterLease, toPublicWriterLease, WriterLockManager, } from './state/lock.js';
27
27
  import { FileStateStore } from './state/store.js';
28
+ import { inspectCorrectiveReplanTaskTransactions } from './state/corrective-replan-transaction.js';
29
+ import { inspectCorrectiveYieldTaskTransactions } from './state/corrective-yield-transaction.js';
30
+ import { createCorrectiveYieldExecutor } from './state/corrective-yield-executor.js';
31
+ import { createCorrectiveReplanPublicController, } from './state/corrective-replan-public.js';
32
+ import { inspectWorkflowObservationBoundary, } from './observation.js';
28
33
  import { createEntityId } from './ulid.js';
29
34
  import { PACKAGE_VERSION } from './version.js';
35
+ import { correctiveReplanBlockedNavigation, correctiveReplanNavigation, evaluateTaskCorrectiveReplanAvailability, evaluateTaskCorrectiveReplanCandidate, identifyCorrectiveReplanPosture, prepareCorrectiveReplanCandidate, TASK_CORRECTIVE_REPLAN_TRANSITION_ID, } from './lifecycle/corrective-replan.js';
30
36
  export class WorkflowService {
31
37
  store;
32
38
  now;
@@ -34,9 +40,33 @@ export class WorkflowService {
34
40
  this.store = store;
35
41
  this.now = now;
36
42
  }
37
- context(repository) {
43
+ #mutationContext(repository) {
38
44
  const identity = resolveRepositoryIdentity(repository);
39
45
  const projectRoot = this.store.projectRoot(identity.projectId);
46
+ const replanTransactions = inspectCorrectiveReplanTaskTransactions(projectRoot, this.store.root);
47
+ const yieldTransactions = inspectCorrectiveYieldTaskTransactions(projectRoot, this.store.root);
48
+ const taskTransactions = {
49
+ corrupt: [...replanTransactions.corrupt, ...yieldTransactions.corrupt],
50
+ pending: [...replanTransactions.pending, ...yieldTransactions.pending],
51
+ };
52
+ if (taskTransactions.corrupt.length > 0) {
53
+ throw new WorkflowError('STATE_CORRUPT', 'A Task transaction journal failed integrity validation.', {
54
+ transactions: taskTransactions.corrupt,
55
+ });
56
+ }
57
+ if (taskTransactions.pending.length > 0) {
58
+ throw new WorkflowError('TASK_RECOVERY_REQUIRED', 'A Task transaction requires explicit recovery.', {
59
+ taskIds: [...new Set(taskTransactions.pending.map((transaction) => transaction.taskId))].sort(),
60
+ transactionIds: taskTransactions.pending.map((transaction) => transaction.transactionId).sort(),
61
+ });
62
+ }
63
+ const coreTaskOperations = listCoreTaskOperationMutexes(this.store.lockRoot(identity.projectId), this.store.root);
64
+ if (coreTaskOperations.length > 0) {
65
+ throw new WorkflowError('LOCKED', 'A Core Task operation owns the project mutation boundary.', {
66
+ taskIds: coreTaskOperations.map((operation) => operation.entityId).sort(),
67
+ operationIds: coreTaskOperations.map((operation) => operation.operationId).sort(),
68
+ });
69
+ }
40
70
  recoverProjectTransactions(projectRoot, identity.projectId);
41
71
  const projectFile = path.join(projectRoot, 'project.json');
42
72
  const projectAlreadyRegistered = existsSync(projectFile);
@@ -54,17 +84,103 @@ export class WorkflowService {
54
84
  return {
55
85
  identity,
56
86
  store: this.store,
57
- locks: new WriterLockManager(this.store.lockRoot(identity.projectId), this.now),
87
+ locks: new WriterLockManager(this.store.lockRoot(identity.projectId), this.now, this.store.root),
88
+ };
89
+ }
90
+ inspectWriterLease(repository, entityId) {
91
+ const identity = resolveRepositoryIdentity(repository);
92
+ const lease = inspectRawWriterLease(this.store.lockRoot(identity.projectId), entityId, this.store.root);
93
+ return lease ? this.publicWriterLeaseDiagnostic(lease) : null;
94
+ }
95
+ repairStaleWriterLease(repository, entityId) {
96
+ const context = this.#mutationContext(repository);
97
+ const lease = context.locks.repairStale(entityId);
98
+ return {
99
+ ...this.publicWriterLeaseDiagnostic(lease),
100
+ repaired: true,
101
+ };
102
+ }
103
+ prepareCorrectiveReplan(repository, taskId, candidate) {
104
+ return this.correctiveReplanController(repository).prepare({ taskId, candidate });
105
+ }
106
+ confirmCorrectiveReplan(repository, taskId, candidate, gate, confirmationCode) {
107
+ return this.correctiveReplanController(repository).confirm({ taskId, candidate, gate, confirmationCode });
108
+ }
109
+ executeCorrectiveReplan(repository, taskId, candidate, credential) {
110
+ return this.correctiveReplanController(repository).execute({ taskId, candidate, credential });
111
+ }
112
+ recoverCorrectiveReplan(repository, taskId, transactionId) {
113
+ return this.correctiveReplanController(repository).recover({ taskId, transactionId });
114
+ }
115
+ executeCorrectiveYield(repository, taskId, expectedTaskRevision, actor, writerToken, reason) {
116
+ const identity = resolveRepositoryIdentity(repository);
117
+ return createCorrectiveYieldExecutor({
118
+ store: this.store,
119
+ projectId: identity.projectId,
120
+ now: this.now,
121
+ }).execute({
122
+ projectId: identity.projectId,
123
+ taskId,
124
+ expectedTaskRevision,
125
+ actor,
126
+ writerToken,
127
+ reason,
128
+ });
129
+ }
130
+ recoverCorrectiveYield(repository, taskId, transactionId) {
131
+ const identity = resolveRepositoryIdentity(repository);
132
+ return createCorrectiveYieldExecutor({
133
+ store: this.store,
134
+ projectId: identity.projectId,
135
+ now: this.now,
136
+ }).recover({ taskId, transactionId });
137
+ }
138
+ correctiveReplanController(repository) {
139
+ const identity = resolveRepositoryIdentity(repository);
140
+ return createCorrectiveReplanPublicController({
141
+ store: this.store,
142
+ projectId: identity.projectId,
143
+ repositoryRoot: identity.repositoryRoot,
144
+ now: this.now,
145
+ });
146
+ }
147
+ observationSnapshot(repository) {
148
+ const identity = resolveRepositoryIdentity(repository);
149
+ const boundary = inspectWorkflowObservationBoundary({
150
+ store: this.store,
151
+ identity,
152
+ now: this.now(),
153
+ });
154
+ if (boundary.observation.kind !== 'assessment') {
155
+ return {
156
+ ...boundary,
157
+ discoveries: [],
158
+ tasks: [],
159
+ milestones: [],
160
+ delegations: [],
161
+ adoption: null,
162
+ };
163
+ }
164
+ const tasks = this.store.listTasks(identity.projectId);
165
+ const rawMilestones = this.store.listMilestones(identity.projectId);
166
+ const adoption = this.inspectAdoptionPreparation(identity, tasks, rawMilestones, boundary.leases, boundary.currentAdoptionPosture);
167
+ return {
168
+ ...boundary,
169
+ discoveries: this.store.listDiscoveries(identity.projectId),
170
+ tasks,
171
+ milestones: this.listMilestonesObserved(identity.projectId, rawMilestones, adoption.currentPosture),
172
+ delegations: this.store.listDelegations(identity.projectId),
173
+ adoption,
58
174
  };
59
175
  }
60
176
  prepareDelegation(repository, input) {
61
- const { identity } = this.context(repository);
177
+ const { identity } = this.#mutationContext(repository);
62
178
  const gate = prepareDelegationGrantGate(identity.projectId, input, this.now());
63
179
  this.assertDelegationScopeExists(identity.projectId, gate.policy);
64
180
  return gate;
65
181
  }
66
182
  grantDelegation(repository, input, confirmationCode) {
67
- const { identity } = this.context(repository);
183
+ const { identity } = this.#mutationContext(repository);
68
184
  const gate = prepareDelegationGrantGate(identity.projectId, input, this.now());
69
185
  this.assertDelegationScopeExists(identity.projectId, gate.policy);
70
186
  if (confirmationCode.trim() !== gate.confirmationCode) {
@@ -100,7 +216,7 @@ export class WorkflowService {
100
216
  }, null);
101
217
  }
102
218
  revokeDelegation(repository, grantId, expectedRevision, actor, reason) {
103
- const { identity } = this.context(repository);
219
+ const { identity } = this.#mutationContext(repository);
104
220
  const grant = this.store.readDelegation(identity.projectId, grantId);
105
221
  assertExpectedRevision(grant, expectedRevision);
106
222
  if (grant.status !== 'active') {
@@ -124,7 +240,7 @@ export class WorkflowService {
124
240
  startDiscovery(repository, input) {
125
241
  if (!input.goal.trim())
126
242
  throw new WorkflowError('INVALID_ARGUMENT', 'Discovery requires a substantive goal.');
127
- const { identity } = this.context(repository);
243
+ const { identity } = this.#mutationContext(repository);
128
244
  const timestamp = this.now().toISOString();
129
245
  const initial = {
130
246
  schemaVersion: STATE_SCHEMA_VERSION,
@@ -152,7 +268,7 @@ export class WorkflowService {
152
268
  return this.store.writeDiscovery(initial, null);
153
269
  }
154
270
  updateDiscovery(repository, discoveryId, expectedRevision, patch) {
155
- const { identity } = this.context(repository);
271
+ const { identity } = this.#mutationContext(repository);
156
272
  const current = this.store.readDiscovery(identity.projectId, discoveryId);
157
273
  if (current.status === 'materialized' || current.status === 'abandoned') {
158
274
  throw new WorkflowError('TRANSITION_BLOCKED', `Discovery ${discoveryId} is ${current.status}.`);
@@ -176,7 +292,7 @@ export class WorkflowService {
176
292
  return this.store.writeDiscovery(updated, expectedRevision);
177
293
  }
178
294
  materializeTask(repository, discoveryId, expectedRevision, title, baseBranch = 'main', milestoneId = null) {
179
- const { identity } = this.context(repository);
295
+ const { identity } = this.#mutationContext(repository);
180
296
  const discovery = this.store.readDiscovery(identity.projectId, discoveryId);
181
297
  assertExpectedRevision(discovery, expectedRevision);
182
298
  const readiness = assessDiscovery(discovery);
@@ -253,7 +369,7 @@ export class WorkflowService {
253
369
  return { discovery: savedDiscovery, task: savedTask, taskRoot };
254
370
  }
255
371
  replaceSplitRequiredTask(repository, oldTaskId, expectedOldTaskRevision, discoveryId, expectedDiscoveryRevision, expectedMilestoneRevision, title, actor) {
256
- const { identity, locks } = this.context(repository);
372
+ const { identity, locks } = this.#mutationContext(repository);
257
373
  const oldTask = this.store.readTask(identity.projectId, oldTaskId);
258
374
  assertExpectedRevision(oldTask, expectedOldTaskRevision);
259
375
  if (!oldTask.milestoneId) {
@@ -504,7 +620,7 @@ export class WorkflowService {
504
620
  };
505
621
  }
506
622
  materializeMilestone(repository, discoveryId, expectedRevision, title, baseBranch = 'main') {
507
- const { identity } = this.context(repository);
623
+ const { identity } = this.#mutationContext(repository);
508
624
  const discovery = this.store.readDiscovery(identity.projectId, discoveryId);
509
625
  assertExpectedRevision(discovery, expectedRevision);
510
626
  const readiness = assessDiscovery(discovery);
@@ -552,7 +668,7 @@ export class WorkflowService {
552
668
  };
553
669
  }
554
670
  setMilestonePlan(repository, milestoneId, expectedRevision, plan) {
555
- const { identity } = this.context(repository);
671
+ const { identity } = this.#mutationContext(repository);
556
672
  const milestone = this.readMilestoneScopeChangeCurrent(identity.projectId, milestoneId);
557
673
  assertExpectedRevision(milestone, expectedRevision);
558
674
  if (!['planning', 'awaiting_execution_authorization', 'active', 'blocked'].includes(milestone.status)) {
@@ -624,7 +740,7 @@ export class WorkflowService {
624
740
  return saved;
625
741
  }
626
742
  prepareMilestoneScopeChange(repository, milestoneId, expectedRevision, plan, actor) {
627
- const { identity } = this.context(repository);
743
+ const { identity } = this.#mutationContext(repository);
628
744
  const milestone = this.readMilestoneScopeChangeCurrent(identity.projectId, milestoneId);
629
745
  assertExpectedRevision(milestone, expectedRevision);
630
746
  assertMilestoneScopeChangeStatusAllowed(milestone);
@@ -641,7 +757,7 @@ export class WorkflowService {
641
757
  };
642
758
  }
643
759
  applyMilestoneScopeChange(repository, milestoneId, expectedRevision, plan, actor, confirmationCode) {
644
- const { identity } = this.context(repository);
760
+ const { identity } = this.#mutationContext(repository);
645
761
  const milestone = this.readMilestoneScopeChangeCurrent(identity.projectId, milestoneId);
646
762
  assertExpectedRevision(milestone, expectedRevision);
647
763
  assertMilestoneScopeChangeStatusAllowed(milestone);
@@ -664,7 +780,7 @@ export class WorkflowService {
664
780
  });
665
781
  }
666
782
  prepareMilestoneAutonomy(repository, milestoneId, expectedRevision, principal, delegate, expiresAt) {
667
- const { identity } = this.context(repository);
783
+ const { identity } = this.#mutationContext(repository);
668
784
  const milestone = this.readMilestoneCurrent(identity.projectId, milestoneId);
669
785
  assertExpectedRevision(milestone, expectedRevision);
670
786
  return prepareMilestoneAutonomyContract({
@@ -677,7 +793,7 @@ export class WorkflowService {
677
793
  });
678
794
  }
679
795
  grantMilestoneAutonomy(repository, milestoneId, expectedRevision, principal, delegate, expiresAt, confirmationCode) {
680
- const { identity } = this.context(repository);
796
+ const { identity } = this.#mutationContext(repository);
681
797
  const milestone = this.readMilestoneCurrent(identity.projectId, milestoneId);
682
798
  assertExpectedRevision(milestone, expectedRevision);
683
799
  return applyMilestoneAutonomyContract({
@@ -692,7 +808,7 @@ export class WorkflowService {
692
808
  });
693
809
  }
694
810
  evolveMilestonePlanAutonomously(repository, milestoneId, expectedRevision, plan, actor) {
695
- const { identity } = this.context(repository);
811
+ const { identity } = this.#mutationContext(repository);
696
812
  const milestone = this.readMilestoneScopeChangeCurrent(identity.projectId, milestoneId);
697
813
  assertExpectedRevision(milestone, expectedRevision);
698
814
  assertMilestoneScopeChangeStatusAllowed(milestone);
@@ -722,7 +838,7 @@ export class WorkflowService {
722
838
  });
723
839
  }
724
840
  authorizeMilestone(repository, milestoneId, expectedRevision, actor, reason = '', delegationGrantId = null) {
725
- const { identity } = this.context(repository);
841
+ const { identity } = this.#mutationContext(repository);
726
842
  const milestone = this.readMilestoneCurrent(identity.projectId, milestoneId);
727
843
  assertExpectedRevision(milestone, expectedRevision);
728
844
  if (milestone.status !== 'awaiting_execution_authorization' || !milestone.planHash) {
@@ -751,7 +867,7 @@ export class WorkflowService {
751
867
  return this.store.writeMilestone({ ...milestone, status: 'active', planHash, authorizations: [...milestone.authorizations, event] }, expectedRevision);
752
868
  }
753
869
  validateMilestone(repository, milestoneId, expectedRevision) {
754
- const { identity } = this.context(repository);
870
+ const { identity } = this.#mutationContext(repository);
755
871
  const milestone = this.readMilestoneCurrent(identity.projectId, milestoneId);
756
872
  assertExpectedRevision(milestone, expectedRevision);
757
873
  if (!['active', 'awaiting_final_acceptance'].includes(milestone.status)) {
@@ -814,7 +930,7 @@ export class WorkflowService {
814
930
  return saved;
815
931
  }
816
932
  acceptMilestone(repository, milestoneId, expectedRevision, actor, confirmationCode, delegationGrantId = null) {
817
- const { identity } = this.context(repository);
933
+ const { identity } = this.#mutationContext(repository);
818
934
  const milestone = this.readMilestoneCurrent(identity.projectId, milestoneId);
819
935
  assertExpectedRevision(milestone, expectedRevision);
820
936
  if (milestone.status !== 'awaiting_final_acceptance' || !milestone.resultHash || !milestone.evidenceHash) {
@@ -869,7 +985,7 @@ export class WorkflowService {
869
985
  cancelMilestone(repository, milestoneId, expectedRevision, reason) {
870
986
  if (!reason.trim())
871
987
  throw new WorkflowError('INVALID_ARGUMENT', 'Milestone cancellation requires a reason.');
872
- const { identity } = this.context(repository);
988
+ const { identity } = this.#mutationContext(repository);
873
989
  const milestone = this.readMilestoneCurrent(identity.projectId, milestoneId);
874
990
  assertExpectedRevision(milestone, expectedRevision);
875
991
  if (['accepted', 'cancelled'].includes(milestone.status)) {
@@ -879,21 +995,54 @@ export class WorkflowService {
879
995
  }
880
996
  setTaskPlan(repository, taskId, expectedRevision, plan, correctiveAudit = null, planRiskAudit = null) {
881
997
  validatePlan(plan);
882
- const { identity } = this.context(repository);
998
+ const { identity } = this.#mutationContext(repository);
883
999
  const knowledgeMap = this.assertKnowledgeMapBinding(identity.repositoryRoot, identity.projectId, plan.knowledgeMapRevision, plan.knowledgeMapHash);
884
1000
  this.validateKnowledgeTargets(knowledgeMap, plan);
885
1001
  const task = this.store.readTask(identity.projectId, taskId);
886
1002
  assertExpectedRevision(task, expectedRevision);
887
- if (!['planning', 'awaiting_execution_authorization', 'needs_fix', 'blocked'].includes(task.status)) {
888
- throw new WorkflowError('TRANSITION_BLOCKED', `Cannot update Plan while Task is ${task.status}.`);
889
- }
1003
+ const correctiveDecisionEvents = task.planHash ? readCorrectiveDecisionEvents(this.store, task) : [];
890
1004
  const currentPlanCorrectiveDecisions = task.planHash
891
- ? readCorrectiveDecisionEvents(this.store, task).filter((event) => event.planHash === task.planHash)
1005
+ ? correctiveDecisionEvents.filter((event) => event.planHash === task.planHash)
892
1006
  : [];
893
1007
  const currentReplanDecisions = currentPlanCorrectiveDecisions.filter((event) => event.decision === 'replan-required');
894
1008
  const currentContinueFixDecisions = currentPlanCorrectiveDecisions.filter((event) => event.decision === 'continue-fix');
895
1009
  const currentHardBlockingDecisions = currentPlanCorrectiveDecisions.filter((event) => event.decision === 'split-required' || event.decision === 'stop-escalate');
896
- const currentReplanRequiredStepIds = [...new Set(currentReplanDecisions.map((event) => event.stepId))];
1010
+ const currentPlanRiskAudit = task.status === 'needs_fix' || currentReplanDecisions.length > 0
1011
+ ? readCurrentPlanRiskAudit(this.store, task)
1012
+ : null;
1013
+ const classifiedCorrectivePosture = currentReplanDecisions.length > 0 && currentPlanRiskAudit
1014
+ ? findCurrentGuardedRemediationPosture(this.store, task, currentPlanRiskAudit)
1015
+ : null;
1016
+ const correctiveReplanPosture = classifiedCorrectivePosture?.posture === 'replan-required'
1017
+ ? classifiedCorrectivePosture
1018
+ : currentPlanRiskAudit
1019
+ ? identifyCorrectiveReplanPosture(task, currentPlanRiskAudit.reviewRequiredStepIds, correctiveDecisionEvents, readRemediationEvents(this.store, task))
1020
+ : null;
1021
+ const correctiveReplanAvailability = correctiveReplanPosture?.posture === 'replan-required'
1022
+ ? evaluateTaskCorrectiveReplanAvailability(task, correctiveReplanPosture)
1023
+ : null;
1024
+ if (currentReplanDecisions.length > 0) {
1025
+ if (!correctiveReplanAvailability?.allowed) {
1026
+ throw new WorkflowError('TRANSITION_BLOCKED', `Transition ${TASK_CORRECTIVE_REPLAN_TRANSITION_ID} is blocked for Task ${task.id}.`, {
1027
+ transitionId: TASK_CORRECTIVE_REPLAN_TRANSITION_ID,
1028
+ posture: correctiveReplanPosture?.posture ?? null,
1029
+ evaluationPhase: correctiveReplanAvailability?.phase ?? 'availability',
1030
+ blockingGuardIds: correctiveReplanAvailability?.blockingGuardIds ?? [],
1031
+ pendingGuardIds: correctiveReplanAvailability?.pendingGuardIds ?? [],
1032
+ guardResults: correctiveReplanAvailability?.guardResults ?? [],
1033
+ });
1034
+ }
1035
+ throw new WorkflowError('TRANSITION_BLOCKED', `Legacy task plan-set cannot execute ${TASK_CORRECTIVE_REPLAN_TRANSITION_ID}.`, {
1036
+ taskId: task.id,
1037
+ transitionId: TASK_CORRECTIVE_REPLAN_TRANSITION_ID,
1038
+ requiredAction: readTaskC1Posture(this.store, task).state === 'claimed'
1039
+ ? 'Run task corrective-yield first, then use task corrective-replan-prepare.'
1040
+ : 'Use task corrective-replan-prepare, confirm, and execute.',
1041
+ });
1042
+ }
1043
+ else if (!['planning', 'awaiting_execution_authorization', 'needs_fix', 'blocked'].includes(task.status)) {
1044
+ throw new WorkflowError('TRANSITION_BLOCKED', `Cannot update Plan while Task is ${task.status}.`);
1045
+ }
897
1046
  if (currentHardBlockingDecisions.length > 0) {
898
1047
  throw new WorkflowError('TRANSITION_BLOCKED', `Task ${task.id} has current corrective decisions that prohibit normal plan updates.`, {
899
1048
  taskId: task.id,
@@ -923,9 +1072,6 @@ export class WorkflowService {
923
1072
  })),
924
1073
  });
925
1074
  }
926
- const currentPlanRiskAudit = task.status === 'needs_fix'
927
- ? readCurrentPlanRiskAudit(this.store, task)
928
- : null;
929
1075
  const failedReviewAttempts = countFailedReviewAttempts(this.store.taskRoot(identity.projectId, taskId));
930
1076
  if (task.status === 'needs_fix' && task.review.status === 'failed' && failedReviewAttempts >= 2) {
931
1077
  validateCorrectivePlanAudit(task, correctiveAudit, failedReviewAttempts);
@@ -968,7 +1114,7 @@ export class WorkflowService {
968
1114
  }
969
1115
  }
970
1116
  const root = this.store.taskRoot(identity.projectId, taskId);
971
- const stagedPlan = this.store.stageArtifact(root, 'plan.md', renderPlan(task, plan));
1117
+ const preparedPlan = prepareCorrectiveReplanCandidate(task, plan);
972
1118
  const authorizations = task.authorizations.map((authorization) => authorization.kind === 'execution' && authorization.decision === 'approved'
973
1119
  ? { ...authorization, decision: 'superseded' }
974
1120
  : authorization);
@@ -985,12 +1131,17 @@ export class WorkflowService {
985
1131
  plannedSteps.every((step) => step.status === 'completed')) {
986
1132
  throw new WorkflowError('TRANSITION_BLOCKED', 'A failed review requires at least one new or changed remediation Step.');
987
1133
  }
988
- if (currentReplanRequiredStepIds.some((stepId) => !plannedSteps.some((step) => step.id === stepId))) {
989
- throw new WorkflowError('TRANSITION_BLOCKED', `Replacement Plan under replan-required must retain every guarded Step targeted by the current corrective decision; removing or renaming one would reset remediation history.`, {
990
- taskId: task.id,
991
- requiredStepIds: currentReplanRequiredStepIds,
992
- plannedStepIds: plannedSteps.map((step) => step.id),
993
- });
1134
+ if (correctiveReplanPosture?.posture === 'replan-required') {
1135
+ const executionEvaluation = evaluateTaskCorrectiveReplanCandidate(task, correctiveReplanPosture, preparedPlan);
1136
+ if (!executionEvaluation.allowed) {
1137
+ throw new WorkflowError('TRANSITION_BLOCKED', `Transition ${TASK_CORRECTIVE_REPLAN_TRANSITION_ID} rejected the candidate Plan for Task ${task.id}.`, {
1138
+ transitionId: TASK_CORRECTIVE_REPLAN_TRANSITION_ID,
1139
+ evaluationPhase: executionEvaluation.phase,
1140
+ blockingGuardIds: executionEvaluation.blockingGuardIds,
1141
+ pendingGuardIds: executionEvaluation.pendingGuardIds,
1142
+ guardResults: executionEvaluation.guardResults,
1143
+ });
1144
+ }
994
1145
  }
995
1146
  if (guardedRemediationGate && currentReplanDecisions.length === 0 && !plannedSteps.some((step) => step.id === guardedRemediationGate.stepId)) {
996
1147
  throw new WorkflowError('TRANSITION_BLOCKED', `Continue-fix must retain guarded Step ${guardedRemediationGate.stepId}; removing or renaming it would reset the remediation breaker.`, {
@@ -998,14 +1149,10 @@ export class WorkflowService {
998
1149
  stepId: guardedRemediationGate.stepId,
999
1150
  });
1000
1151
  }
1001
- if (correctiveAudit && guardedRemediationGate && currentReplanDecisions.length === 0 && currentPlanRiskAudit) {
1002
- assertCorrectiveAuditorIndependence(this.store, task, guardedRemediationGate.stepId, correctiveAudit.auditor);
1003
- appendCorrectiveDecisionEvent(this.store, task, guardedRemediationGate.stepId, correctiveAudit, currentPlanRiskAudit, stagedPlan.hash, this.now());
1004
- }
1005
1152
  const candidate = {
1006
1153
  ...task,
1007
1154
  status: 'awaiting_execution_authorization',
1008
- planHash: stagedPlan.hash,
1155
+ planHash: preparedPlan.planHash,
1009
1156
  planObjective: plan.objective,
1010
1157
  requirements: plan.requirements,
1011
1158
  acceptance: plan.acceptance,
@@ -1025,6 +1172,18 @@ export class WorkflowService {
1025
1172
  };
1026
1173
  if (planRiskAudit)
1027
1174
  validatePlanRiskAuditCandidate(this.store, candidate, planRiskAudit);
1175
+ const stagedPlan = this.store.stageArtifact(root, 'plan.md', preparedPlan.planBytes);
1176
+ if (stagedPlan.hash !== preparedPlan.planHash) {
1177
+ throw new WorkflowError('STATE_CORRUPT', 'Prepared candidate Plan hash changed during staging.', {
1178
+ taskId: task.id,
1179
+ preparedPlanHash: preparedPlan.planHash,
1180
+ stagedPlanHash: stagedPlan.hash,
1181
+ });
1182
+ }
1183
+ if (correctiveAudit && guardedRemediationGate && currentReplanDecisions.length === 0 && currentPlanRiskAudit) {
1184
+ assertCorrectiveAuditorIndependence(this.store, task, guardedRemediationGate.stepId, correctiveAudit.auditor);
1185
+ appendCorrectiveDecisionEvent(this.store, task, guardedRemediationGate.stepId, correctiveAudit, currentPlanRiskAudit, preparedPlan.planHash, this.now());
1186
+ }
1028
1187
  const saved = this.store.writeTask(candidate, expectedRevision);
1029
1188
  this.store.publishArtifact(stagedPlan);
1030
1189
  if (planRiskAudit)
@@ -1035,7 +1194,7 @@ export class WorkflowService {
1035
1194
  return saved;
1036
1195
  }
1037
1196
  rebindTaskKnowledge(repository, taskId, expectedRevision) {
1038
- const { identity } = this.context(repository);
1197
+ const { identity } = this.#mutationContext(repository);
1039
1198
  const task = this.store.readTask(identity.projectId, taskId);
1040
1199
  assertExpectedRevision(task, expectedRevision);
1041
1200
  this.assertTaskContextChangeAllowed(task);
@@ -1120,7 +1279,7 @@ export class WorkflowService {
1120
1279
  if (!delegationGrantId) {
1121
1280
  throw new WorkflowError('TRANSITION_BLOCKED', 'Task context refresh combines approval transitions and therefore requires an explicit delegated approval grant.');
1122
1281
  }
1123
- const { identity } = this.context(repository);
1282
+ const { identity } = this.#mutationContext(repository);
1124
1283
  const task = this.store.readTask(identity.projectId, taskId);
1125
1284
  const currentMap = this.store.readKnowledgeMap(identity.projectId);
1126
1285
  assertExpectedRevision(task, expectedTaskRevision);
@@ -1211,7 +1370,7 @@ export class WorkflowService {
1211
1370
  });
1212
1371
  }
1213
1372
  authorizeTask(repository, taskId, expectedRevision, actor, reason = '', delegationGrantId = null) {
1214
- const { identity } = this.context(repository);
1373
+ const { identity } = this.#mutationContext(repository);
1215
1374
  this.readCurrentAdoptionPostureStrict(identity.projectId);
1216
1375
  const task = this.store.readTask(identity.projectId, taskId);
1217
1376
  assertExpectedRevision(task, expectedRevision);
@@ -1241,7 +1400,7 @@ export class WorkflowService {
1241
1400
  return this.store.writeTask({ ...task, status: 'ready', briefHash, planHash, authorizations: [...task.authorizations, event] }, expectedRevision);
1242
1401
  }
1243
1402
  recordPlanRiskAudit(repository, taskId, expectedRevision, input) {
1244
- const { identity } = this.context(repository);
1403
+ const { identity } = this.#mutationContext(repository);
1245
1404
  this.readCurrentAdoptionPostureStrict(identity.projectId);
1246
1405
  const task = this.store.readTask(identity.projectId, taskId);
1247
1406
  assertExpectedRevision(task, expectedRevision);
@@ -1249,7 +1408,7 @@ export class WorkflowService {
1249
1408
  return appendCurrentPlanRiskAudit(this.store, task, input, this.now());
1250
1409
  }
1251
1410
  showTaskHandoff(repository, taskId) {
1252
- const { identity } = this.context(repository);
1411
+ const { identity } = this.#mutationContext(repository);
1253
1412
  const task = this.store.readTask(identity.projectId, taskId);
1254
1413
  return {
1255
1414
  task,
@@ -1258,7 +1417,7 @@ export class WorkflowService {
1258
1417
  };
1259
1418
  }
1260
1419
  handoffTask(repository, taskId, expectedRevision, actor, targetActor, reason, writerToken = null, grantId = null, expiresAt = null) {
1261
- const context = this.context(repository);
1420
+ const context = this.#mutationContext(repository);
1262
1421
  const task = this.store.readTask(context.identity.projectId, taskId);
1263
1422
  assertExpectedRevision(task, expectedRevision);
1264
1423
  const normalizedActor = actor.trim();
@@ -1327,15 +1486,15 @@ export class WorkflowService {
1327
1486
  }
1328
1487
  }
1329
1488
  claimTask(repository, taskId, expectedRevision, actor, claimToken, writerToken = null) {
1330
- const context = this.context(repository);
1489
+ const context = this.#mutationContext(repository);
1331
1490
  const task = this.store.readTask(context.identity.projectId, taskId);
1332
1491
  assertExpectedRevision(task, expectedRevision);
1333
1492
  const normalizedActor = actor.trim();
1334
1493
  if (!normalizedActor) {
1335
1494
  throw new WorkflowError('INVALID_ARGUMENT', 'Claim actor is required.');
1336
1495
  }
1337
- const pendingHandoff = readTaskC1Posture(this.store, task);
1338
- if (pendingHandoff.state === 'pending' && pendingHandoff.latestEvent.grantId) {
1496
+ const pendingHandoff = assertTaskClaimAllowed(this.store, task, normalizedActor, claimToken, this.now());
1497
+ if (pendingHandoff.latestEvent.grantId) {
1339
1498
  this.assertHandoffGrant(context.identity.projectId, task, normalizedActor, pendingHandoff.latestEvent.grantId);
1340
1499
  }
1341
1500
  const existingLease = context.locks.inspect(taskId);
@@ -1368,7 +1527,14 @@ export class WorkflowService {
1368
1527
  }
1369
1528
  try {
1370
1529
  const event = appendTaskClaim(this.store, task, actor, claimToken, lease.token, this.now());
1371
- return { task, event, lease };
1530
+ return {
1531
+ task,
1532
+ event,
1533
+ lease: toPublicWriterLease(lease, this.now()),
1534
+ writerCredential: acquiredHere
1535
+ ? { disposition: 'issued', token: lease.token }
1536
+ : { disposition: 'renewed' },
1537
+ };
1372
1538
  }
1373
1539
  catch (error) {
1374
1540
  if (acquiredHere)
@@ -1377,7 +1543,7 @@ export class WorkflowService {
1377
1543
  }
1378
1544
  }
1379
1545
  handbackTask(repository, taskId, expectedRevision, actor, reason, writerToken = null, limitations = [], recommendedNext = 'Coordinator reviews the terminal Task result and recorded evidence.') {
1380
- const context = this.context(repository);
1546
+ const context = this.#mutationContext(repository);
1381
1547
  const task = this.store.readTask(context.identity.projectId, taskId);
1382
1548
  assertExpectedRevision(task, expectedRevision);
1383
1549
  const lease = task.status === 'merged' || task.status === 'cancelled'
@@ -1389,7 +1555,7 @@ export class WorkflowService {
1389
1555
  return { task, event };
1390
1556
  }
1391
1557
  startTask(repository, taskId, expectedRevision, workspaceOwner) {
1392
- const { identity } = this.context(repository);
1558
+ const { identity } = this.#mutationContext(repository);
1393
1559
  const task = this.store.readTask(identity.projectId, taskId);
1394
1560
  assertExpectedRevision(task, expectedRevision);
1395
1561
  this.requireExecutionAdoptionPosture(identity, taskId);
@@ -1441,7 +1607,7 @@ export class WorkflowService {
1441
1607
  return this.store.writeTask({ ...task, baseCommit, taskBranch, workspaceOwner, blockReason: null }, expectedRevision);
1442
1608
  }
1443
1609
  runStep(repository, taskId, stepId, expectedRevision, actor, writerToken) {
1444
- const context = this.context(repository);
1610
+ const context = this.#mutationContext(repository);
1445
1611
  const task = this.store.readTask(context.identity.projectId, taskId);
1446
1612
  assertExpectedRevision(task, expectedRevision);
1447
1613
  this.requireExecutionAdoptionPosture(context.identity, taskId);
@@ -1555,7 +1721,8 @@ export class WorkflowService {
1555
1721
  }
1556
1722
  return {
1557
1723
  task: saved,
1558
- lease,
1724
+ lease: toPublicWriterLease(lease, this.now()),
1725
+ writerCredential: { disposition: acquiredHere ? 'issued' : 'renewed' },
1559
1726
  envelope: {
1560
1727
  protocolVersion: PROTOCOL_VERSION,
1561
1728
  packageVersion: PACKAGE_VERSION,
@@ -1591,14 +1758,14 @@ export class WorkflowService {
1591
1758
  taskId,
1592
1759
  stepId,
1593
1760
  expectedRevision: saved.revision,
1594
- writerToken: lease.token,
1761
+ writerToken: acquiredHere ? lease.token : null,
1595
1762
  knowledgeMap: knowledgeSummary(knowledgeMap),
1596
1763
  knowledgeSources: selectKnowledgeSources(knowledgeMap, 'worker', task.knowledgeTargets.map((target) => target.path)),
1597
1764
  },
1598
1765
  };
1599
1766
  }
1600
1767
  taskContext(repository, taskId, role) {
1601
- const { identity } = this.context(repository);
1768
+ const { identity } = this.#mutationContext(repository);
1602
1769
  const task = this.store.readTask(identity.projectId, taskId);
1603
1770
  const historicalStrictReview = role === 'independent-reviewer'
1604
1771
  && task.steps.some((step) => step.status === 'in_progress' && step.evidence !== null);
@@ -1677,7 +1844,7 @@ export class WorkflowService {
1677
1844
  };
1678
1845
  }
1679
1846
  completeStep(repository, taskId, stepId, expectedRevision, writerToken, notes, actor) {
1680
- const context = this.context(repository);
1847
+ const context = this.#mutationContext(repository);
1681
1848
  const lease = context.locks.heartbeat(taskId, writerToken);
1682
1849
  this.readCurrentAdoptionPostureStrict(context.identity.projectId);
1683
1850
  const task = this.store.readTask(context.identity.projectId, taskId);
@@ -1763,7 +1930,7 @@ export class WorkflowService {
1763
1930
  return this.store.writeTask({ ...task, status: 'in_progress', steps, blockReason: null }, expectedRevision);
1764
1931
  }
1765
1932
  reviewStep(repository, taskId, stepId, expectedRevision, runner, actor, writerToken = null) {
1766
- const context = this.context(repository);
1933
+ const context = this.#mutationContext(repository);
1767
1934
  this.readCurrentAdoptionPostureStrict(context.identity.projectId);
1768
1935
  const task = this.store.readTask(context.identity.projectId, taskId);
1769
1936
  assertExpectedRevision(task, expectedRevision);
@@ -1868,7 +2035,7 @@ export class WorkflowService {
1868
2035
  return this.store.writeTask(applyStrictStepReviewResolution(task, stepId, recorded.resolution, launch.review.summary), expectedRevision);
1869
2036
  }
1870
2037
  submitTask(repository, taskId, expectedRevision, writerToken, actor) {
1871
- const context = this.context(repository);
2038
+ const context = this.#mutationContext(repository);
1872
2039
  const lease = context.locks.heartbeat(taskId, writerToken);
1873
2040
  const task = this.store.readTask(context.identity.projectId, taskId);
1874
2041
  assertExpectedRevision(task, expectedRevision);
@@ -1900,11 +2067,11 @@ export class WorkflowService {
1900
2067
  return saved;
1901
2068
  }
1902
2069
  recordReview(repository, taskId, expectedRevision, review, actor, writerToken = null) {
1903
- const { identity } = this.context(repository);
2070
+ const { identity } = this.#mutationContext(repository);
1904
2071
  this.readCurrentAdoptionPostureStrict(identity.projectId);
1905
2072
  const task = this.store.readTask(identity.projectId, taskId);
1906
2073
  assertExpectedRevision(task, expectedRevision);
1907
- this.proveOptionalWriterLeaseToken(this.context(repository), taskId, writerToken);
2074
+ this.proveOptionalWriterLeaseToken(this.#mutationContext(repository), taskId, writerToken);
1908
2075
  this.assertTaskMutationAllowedByHandoff(task, actor ?? null, writerToken);
1909
2076
  const retryingUnverified = task.status === 'blocked' && task.review.status === 'unverified';
1910
2077
  if (task.status !== 'validating' && !retryingUnverified) {
@@ -1942,7 +2109,7 @@ export class WorkflowService {
1942
2109
  return saved;
1943
2110
  }
1944
2111
  recoverStepCorrectiveDecision(repository, taskId, stepId, expectedRevision, actor, writerToken) {
1945
- const context = this.context(repository);
2112
+ const context = this.#mutationContext(repository);
1946
2113
  const task = this.store.readTask(context.identity.projectId, taskId);
1947
2114
  assertExpectedRevision(task, expectedRevision);
1948
2115
  this.requireExecutionAdoptionPosture(context.identity, taskId);
@@ -1954,18 +2121,18 @@ export class WorkflowService {
1954
2121
  return appendCorrectiveDecisionRecovery(this.store, task, stepId, expectedRevision, actor, writerToken, this.now());
1955
2122
  }
1956
2123
  recoverRemediationMode(repository, taskId, stepId, expectedRevision, actor, writerToken) {
1957
- const context = this.context(repository);
2124
+ const context = this.#mutationContext(repository);
1958
2125
  const task = this.store.readTask(context.identity.projectId, taskId);
1959
2126
  assertExpectedRevision(task, expectedRevision);
1960
2127
  this.requireExecutionAdoptionPosture(context.identity, taskId);
1961
2128
  return appendRemediationModeRecovery(this.store, task, stepId, expectedRevision, actor, writerToken, this.now());
1962
2129
  }
1963
2130
  recordStepCorrectiveDecision(repository, taskId, stepId, expectedRevision, correctiveAudit, actor, writerToken = null) {
1964
- const { identity } = this.context(repository);
2131
+ const { identity } = this.#mutationContext(repository);
1965
2132
  this.readCurrentAdoptionPostureStrict(identity.projectId);
1966
2133
  const task = this.store.readTask(identity.projectId, taskId);
1967
2134
  assertExpectedRevision(task, expectedRevision);
1968
- this.proveOptionalWriterLeaseToken(this.context(repository), taskId, writerToken);
2135
+ this.proveOptionalWriterLeaseToken(this.#mutationContext(repository), taskId, writerToken);
1969
2136
  this.assertTaskMutationAllowedByHandoff(task, actor ?? null, writerToken);
1970
2137
  if (!['ready', 'needs_fix', 'blocked'].includes(task.status)) {
1971
2138
  throw new WorkflowError('TRANSITION_BLOCKED', `Task ${taskId} cannot record a corrective decision while ${task.status}.`);
@@ -2007,7 +2174,7 @@ export class WorkflowService {
2007
2174
  return appendCorrectiveDecisionEvent(this.store, task, stepId, correctiveAudit, planRiskAudit, task.planHash, this.now());
2008
2175
  }
2009
2176
  setTaskResult(repository, taskId, expectedRevision, summary, limitations) {
2010
- const { identity } = this.context(repository);
2177
+ const { identity } = this.#mutationContext(repository);
2011
2178
  const task = this.store.readTask(identity.projectId, taskId);
2012
2179
  assertExpectedRevision(task, expectedRevision);
2013
2180
  if (task.status !== 'validating' || task.review.status !== 'passed') {
@@ -2021,7 +2188,7 @@ export class WorkflowService {
2021
2188
  return saved;
2022
2189
  }
2023
2190
  acceptTask(repository, taskId, expectedRevision, actor, delegationGrantId = null) {
2024
- const { identity } = this.context(repository);
2191
+ const { identity } = this.#mutationContext(repository);
2025
2192
  const task = this.store.readTask(identity.projectId, taskId);
2026
2193
  assertExpectedRevision(task, expectedRevision);
2027
2194
  if (task.status !== 'awaiting_final_acceptance' || !task.resultHash || !task.evidenceHash) {
@@ -2059,7 +2226,7 @@ export class WorkflowService {
2059
2226
  }, expectedRevision);
2060
2227
  }
2061
2228
  syncTaskBase(repository, taskId, expectedRevision) {
2062
- const { identity } = this.context(repository);
2229
+ const { identity } = this.#mutationContext(repository);
2063
2230
  const task = this.store.readTask(identity.projectId, taskId);
2064
2231
  assertExpectedRevision(task, expectedRevision);
2065
2232
  if (task.status !== 'merging' || task.workspaceOwner !== 'local' || !task.taskBranch || !task.baseCommit) {
@@ -2111,7 +2278,7 @@ export class WorkflowService {
2111
2278
  return saved;
2112
2279
  }
2113
2280
  mergeTask(repository, taskId, expectedRevision, writerToken, actor) {
2114
- const context = this.context(repository);
2281
+ const context = this.#mutationContext(repository);
2115
2282
  const task = this.store.readTask(context.identity.projectId, taskId);
2116
2283
  assertExpectedRevision(task, expectedRevision);
2117
2284
  const lease = context.locks.heartbeat(taskId, writerToken);
@@ -2133,7 +2300,7 @@ export class WorkflowService {
2133
2300
  return saved;
2134
2301
  }
2135
2302
  confirmExternalMerge(repository, taskId, expectedRevision, writerToken, actor) {
2136
- const context = this.context(repository);
2303
+ const context = this.#mutationContext(repository);
2137
2304
  const task = this.store.readTask(context.identity.projectId, taskId);
2138
2305
  assertExpectedRevision(task, expectedRevision);
2139
2306
  const lease = context.locks.heartbeat(taskId, writerToken);
@@ -2150,25 +2317,22 @@ export class WorkflowService {
2150
2317
  return saved;
2151
2318
  }
2152
2319
  status(repository) {
2153
- const { identity } = this.context(repository);
2154
- const tasks = this.store.listTasks(identity.projectId);
2155
- const rawMilestones = this.store.listMilestones(identity.projectId);
2156
- const adoption = this.inspectAdoptionPreparation(identity, tasks, rawMilestones);
2157
- const milestones = this.listMilestonesCurrent(identity.projectId, adoption.currentPosture);
2320
+ const snapshot = this.observationSnapshot(repository);
2158
2321
  return {
2159
- projectId: identity.projectId,
2160
- discoveries: this.store.listDiscoveries(identity.projectId),
2161
- tasks,
2162
- milestones,
2163
- milestoneInitialAssembly: milestones
2164
- .map((milestone) => this.describeMilestoneInitialAssembly(identity.projectId, milestone, tasks))
2322
+ projectId: snapshot.identity.projectId,
2323
+ discoveries: snapshot.discoveries,
2324
+ tasks: snapshot.tasks,
2325
+ milestones: snapshot.milestones,
2326
+ milestoneInitialAssembly: snapshot.milestones
2327
+ .map((milestone) => this.describeMilestoneInitialAssembly(snapshot.identity.projectId, milestone, snapshot.tasks))
2165
2328
  .filter((value) => value !== null),
2166
- delegations: this.store.listDelegations(identity.projectId),
2167
- adoption,
2329
+ delegations: snapshot.delegations,
2330
+ adoption: snapshot.adoption,
2331
+ observation: snapshot.observation,
2168
2332
  };
2169
2333
  }
2170
2334
  recoverMilestoneTransactions(repository) {
2171
- const { identity } = this.context(repository);
2335
+ const { identity } = this.#mutationContext(repository);
2172
2336
  const milestones = this.listMilestonesCurrent(identity.projectId, readCurrentAdoptionPosture(this.store, identity.projectId));
2173
2337
  return {
2174
2338
  projectId: identity.projectId,
@@ -2176,11 +2340,11 @@ export class WorkflowService {
2176
2340
  };
2177
2341
  }
2178
2342
  prepareAdoption(repository) {
2179
- const { identity } = this.context(repository);
2343
+ const { identity } = this.#mutationContext(repository);
2180
2344
  return this.inspectAdoptionPreparation(identity, this.store.listTasks(identity.projectId), this.store.listMilestones(identity.projectId));
2181
2345
  }
2182
2346
  applyAdoption(repository, actor, confirmationCode) {
2183
- const context = this.context(repository);
2347
+ const context = this.#mutationContext(repository);
2184
2348
  const tasks = context.store.listTasks(context.identity.projectId);
2185
2349
  const milestones = context.store.listMilestones(context.identity.projectId);
2186
2350
  return applyAdoptionPosture(this.store, {
@@ -2197,19 +2361,19 @@ export class WorkflowService {
2197
2361
  }, actor, confirmationCode);
2198
2362
  }
2199
2363
  updatePreflight(repository) {
2200
- const context = this.context(repository);
2201
- const tasks = context.store.listTasks(context.identity.projectId);
2202
- const milestones = context.store.listMilestones(context.identity.projectId);
2203
- const dirty = changedFiles(context.identity.repositoryRoot);
2364
+ const snapshot = this.observationSnapshot(repository);
2365
+ const tasks = snapshot.tasks;
2366
+ const milestones = snapshot.milestones;
2367
+ const dirty = changedFiles(snapshot.identity.repositoryRoot);
2204
2368
  const runningSteps = tasks.flatMap((task) => task.steps
2205
2369
  .filter((step) => step.status === 'in_progress')
2206
2370
  .map((step) => ({ taskId: task.id, stepId: step.id })));
2207
- const leases = context.locks.list();
2371
+ const leases = snapshot.leases;
2208
2372
  const now = this.now().getTime();
2209
2373
  const activeLeases = leases.filter((lease) => Date.parse(lease.expiresAt) > now);
2210
2374
  const staleLeases = leases.filter((lease) => Date.parse(lease.expiresAt) <= now);
2211
2375
  const activeMilestones = milestones.filter((milestone) => !['accepted', 'cancelled'].includes(milestone.status));
2212
- const inspectedVersions = inspectWorkflowVersions(context.identity.repositoryRoot, activeMilestones.map((milestone) => milestone.baseBranch));
2376
+ const inspectedVersions = inspectWorkflowVersions(snapshot.identity.repositoryRoot, activeMilestones.map((milestone) => milestone.baseBranch));
2213
2377
  const milestoneBases = inspectedVersions.milestoneBases.map((base) => ({
2214
2378
  ...base,
2215
2379
  milestoneIds: activeMilestones.filter((milestone) => milestone.baseBranch === base.branch).map((milestone) => milestone.id),
@@ -2236,31 +2400,20 @@ export class WorkflowService {
2236
2400
  ...(activeLeases.length > 0 ? ['At least one writer lease is active.'] : []),
2237
2401
  ...(staleLeases.length > 0 ? ['At least one stale writer lease requires explicit repair.'] : []),
2238
2402
  ...versionBlockers,
2403
+ ...observationPreflightBlockers(snapshot.observation),
2239
2404
  ];
2240
- const adoption = buildAdoptionPreparation({
2241
- projectId: context.identity.projectId,
2242
- repositoryIdentity: context.identity,
2243
- packageVersion: PACKAGE_VERSION,
2244
- protocolVersion: PROTOCOL_VERSION,
2245
- stateSchemaVersion: STATE_SCHEMA_VERSION,
2246
- tasks,
2247
- milestones,
2248
- sidecarInventory: this.collectAdoptionSidecarInventory(context.identity.projectId, tasks, milestones),
2249
- blockers,
2250
- now: this.now(),
2251
- }, readCurrentAdoptionPosture(this.store, context.identity.projectId));
2252
2405
  return {
2253
2406
  safe: blockers.length === 0,
2254
2407
  packageVersion: PACKAGE_VERSION,
2255
2408
  protocolVersion: PROTOCOL_VERSION,
2256
2409
  stateSchemaVersion: STATE_SCHEMA_VERSION,
2257
- projectId: context.identity.projectId,
2258
- repositoryRoot: context.identity.repositoryRoot,
2410
+ projectId: snapshot.identity.projectId,
2411
+ repositoryRoot: snapshot.identity.repositoryRoot,
2259
2412
  clean: dirty.length === 0,
2260
2413
  changedFiles: dirty,
2261
2414
  runningSteps,
2262
- activeLeases,
2263
- staleLeases,
2415
+ activeLeases: activeLeases.map((lease) => toPublicWriterLease(lease, new Date(now))),
2416
+ staleLeases: staleLeases.map((lease) => toPublicWriterLease(lease, new Date(now))),
2264
2417
  blockers,
2265
2418
  workflowVersions: {
2266
2419
  runtime: inspectedVersions.runtime,
@@ -2276,11 +2429,12 @@ export class WorkflowService {
2276
2429
  ? null
2277
2430
  : 'Land only package.json/package-lock.json dependency changes on every active Milestone base; do not transport unrelated product commits.',
2278
2431
  },
2279
- adoption,
2432
+ adoption: snapshot.adoption,
2433
+ observation: snapshot.observation,
2280
2434
  };
2281
2435
  }
2282
2436
  alpha6StrictReviewRescuePreflight(repository) {
2283
- const context = this.context(repository);
2437
+ const context = this.#mutationContext(repository);
2284
2438
  const dependency = inspectLocalDependency(context.identity.repositoryRoot);
2285
2439
  const tasks = context.store.listTasks(context.identity.projectId);
2286
2440
  const leases = context.locks.list();
@@ -2375,15 +2529,15 @@ export class WorkflowService {
2375
2529
  projectId: context.identity.projectId,
2376
2530
  repositoryRoot: context.identity.repositoryRoot,
2377
2531
  clean: dirty.length === 0,
2378
- activeLeases,
2379
- staleLeases,
2532
+ activeLeases: activeLeases.map((lease) => toPublicWriterLease(lease, new Date(now))),
2533
+ staleLeases: staleLeases.map((lease) => toPublicWriterLease(lease, new Date(now))),
2380
2534
  candidate,
2381
2535
  blockers,
2382
2536
  requiredActions,
2383
2537
  };
2384
2538
  }
2385
2539
  scanKnowledgeMap(repository) {
2386
- const { identity } = this.context(repository);
2540
+ const { identity } = this.#mutationContext(repository);
2387
2541
  const scanned = scanProjectKnowledge(identity.repositoryRoot, identity.projectId, this.now());
2388
2542
  try {
2389
2543
  const current = this.store.readKnowledgeMap(identity.projectId);
@@ -2396,11 +2550,11 @@ export class WorkflowService {
2396
2550
  }
2397
2551
  }
2398
2552
  showKnowledgeMap(repository) {
2399
- const { identity } = this.context(repository);
2553
+ const { identity } = this.#mutationContext(repository);
2400
2554
  return this.store.readKnowledgeMap(identity.projectId);
2401
2555
  }
2402
2556
  knowledgeMapStatus(repository) {
2403
- const { identity } = this.context(repository);
2557
+ const { identity } = this.#mutationContext(repository);
2404
2558
  const current = this.store.readKnowledgeMap(identity.projectId);
2405
2559
  const scanned = scanProjectKnowledge(identity.repositoryRoot, identity.projectId, this.now());
2406
2560
  return inspectKnowledgeMap(current, scanned);
@@ -2409,7 +2563,7 @@ export class WorkflowService {
2409
2563
  return this.approveKnowledgeMapForTaskRefresh(repository, expectedRevision, selections, actor, delegationGrantId, null);
2410
2564
  }
2411
2565
  approveKnowledgeMapForTaskRefresh(repository, expectedRevision, selections, actor, delegationGrantId, autonomyTaskId) {
2412
- const { identity } = this.context(repository);
2566
+ const { identity } = this.#mutationContext(repository);
2413
2567
  const current = this.store.readKnowledgeMap(identity.projectId);
2414
2568
  assertExpectedRevision(current, expectedRevision);
2415
2569
  const inspected = inspectKnowledgeMap(current, scanProjectKnowledge(identity.repositoryRoot, identity.projectId, this.now()));
@@ -2426,7 +2580,7 @@ export class WorkflowService {
2426
2580
  return this.store.writeKnowledgeMap(approved, expectedRevision);
2427
2581
  }
2428
2582
  reconcileKnowledgeMap(repository, expectedRevision, allowAtomicTaskRefresh = false) {
2429
- const { identity } = this.context(repository);
2583
+ const { identity } = this.#mutationContext(repository);
2430
2584
  const current = this.store.readKnowledgeMap(identity.projectId);
2431
2585
  assertExpectedRevision(current, expectedRevision);
2432
2586
  const scanned = scanProjectKnowledge(identity.repositoryRoot, identity.projectId, this.now());
@@ -2452,17 +2606,17 @@ export class WorkflowService {
2452
2606
  return this.store.writeKnowledgeMap(reconcileKnowledgeMap(current, scanned), expectedRevision);
2453
2607
  }
2454
2608
  graphRefreshRequest(repository, graphKind, mode = 'moderate') {
2455
- const { identity } = this.context(repository);
2609
+ const { identity } = this.#mutationContext(repository);
2456
2610
  return createGraphRefreshRequest(identity.repositoryRoot, identity.projectId, graphKind, mode, this.now());
2457
2611
  }
2458
2612
  graphStatus(repository, graphKind, mode = 'moderate') {
2459
- const { identity } = this.context(repository);
2613
+ const { identity } = this.#mutationContext(repository);
2460
2614
  const request = createGraphRefreshRequest(identity.repositoryRoot, identity.projectId, graphKind, mode, this.now());
2461
2615
  const binding = this.readGraphBindingOrNull(identity.projectId, graphKind);
2462
2616
  return { request, binding: binding ? inspectGraphBinding(binding, request) : null };
2463
2617
  }
2464
2618
  bindGraph(repository, expectedRevision, request, evidence) {
2465
- const { identity } = this.context(repository);
2619
+ const { identity } = this.#mutationContext(repository);
2466
2620
  const currentRequest = createGraphRefreshRequest(identity.repositoryRoot, identity.projectId, request.graphKind, request.mode, this.now());
2467
2621
  validateGraphRequestCurrent(request, currentRequest);
2468
2622
  const existing = this.readGraphBindingOrNull(identity.projectId, request.graphKind);
@@ -2470,13 +2624,78 @@ export class WorkflowService {
2470
2624
  return this.store.writeGraphBinding(binding, expectedRevision);
2471
2625
  }
2472
2626
  recordGraphFallback(repository, graphKind, mode, expectedRevision, reason) {
2473
- const { identity } = this.context(repository);
2627
+ const { identity } = this.#mutationContext(repository);
2474
2628
  const request = createGraphRefreshRequest(identity.repositoryRoot, identity.projectId, graphKind, mode, this.now());
2475
2629
  const existing = this.readGraphBindingOrNull(identity.projectId, graphKind);
2476
2630
  return this.store.writeGraphBinding(fallbackGraphBinding(identity.projectId, request, reason, existing, this.now()), expectedRevision);
2477
2631
  }
2478
2632
  next(repository) {
2479
2633
  const status = this.status(repository);
2634
+ if (status.observation.kind === 'blocked') {
2635
+ return {
2636
+ scope: 'project',
2637
+ projectId: status.projectId,
2638
+ status: 'observation-blocked',
2639
+ action: 'blocked',
2640
+ observation: status.observation,
2641
+ navigation: {
2642
+ kind: 'blocked',
2643
+ reasonCodes: status.observation.reasonCodes,
2644
+ availableRepairTransitionId: null,
2645
+ },
2646
+ };
2647
+ }
2648
+ if (status.observation.kind === 'repair') {
2649
+ const repair = status.observation.repair;
2650
+ return {
2651
+ scope: 'project',
2652
+ projectId: status.projectId,
2653
+ status: 'repair-required',
2654
+ action: repair?.command ?? 'blocked',
2655
+ observation: status.observation,
2656
+ navigation: {
2657
+ kind: repair ? 'repair' : 'blocked',
2658
+ transitionId: null,
2659
+ availableRepairTransitionId: null,
2660
+ ...(repair ? {
2661
+ command: repair.command,
2662
+ cataloged: repair.cataloged,
2663
+ ...('available' in repair ? { available: repair.available } : {}),
2664
+ bindings: repair.command === 'milestone recover'
2665
+ ? {
2666
+ projectId: repair.projectId,
2667
+ milestoneIds: repair.milestoneIds,
2668
+ transactionIds: repair.transactionIds,
2669
+ }
2670
+ : 'taskIds' in repair
2671
+ ? {
2672
+ projectId: repair.projectId,
2673
+ taskIds: repair.taskIds,
2674
+ transactionIds: repair.transactionIds,
2675
+ }
2676
+ : {
2677
+ projectId: repair.projectId,
2678
+ entityIds: repair.entityIds,
2679
+ },
2680
+ } : {}),
2681
+ reasonCodes: status.observation.reasonCodes,
2682
+ },
2683
+ };
2684
+ }
2685
+ if (!status.adoption) {
2686
+ return {
2687
+ scope: 'project',
2688
+ projectId: status.projectId,
2689
+ status: 'observation-blocked',
2690
+ action: 'blocked',
2691
+ observation: status.observation,
2692
+ navigation: {
2693
+ kind: 'blocked',
2694
+ reasonCodes: status.observation.reasonCodes,
2695
+ availableRepairTransitionId: null,
2696
+ },
2697
+ };
2698
+ }
2480
2699
  const adoptionInfo = {
2481
2700
  status: status.adoption.status,
2482
2701
  safe: status.adoption.safe,
@@ -2494,9 +2713,34 @@ export class WorkflowService {
2494
2713
  status: status.adoption.status,
2495
2714
  action: status.adoption.safe ? 'state adoption-apply' : 'restore safe boundary, then state adoption-prepare',
2496
2715
  adoption: adoptionInfo,
2716
+ observation: status.observation,
2717
+ navigation: status.adoption.safe
2718
+ ? {
2719
+ kind: 'human-gate',
2720
+ transitionId: null,
2721
+ cataloged: false,
2722
+ command: 'state adoption-apply',
2723
+ bindings: {
2724
+ projectId: status.projectId,
2725
+ confirmationCode: status.adoption.confirmationCode,
2726
+ confirmationCodeBindingHash: status.adoption.confirmationCodeBindingHash,
2727
+ actorRequired: true,
2728
+ },
2729
+ }
2730
+ : {
2731
+ kind: 'blocked',
2732
+ transitionId: null,
2733
+ availableRepairTransitionId: null,
2734
+ reasonCodes: ['ADOPTION_MISSING'],
2735
+ blockers: status.adoption.blockers,
2736
+ },
2497
2737
  };
2498
2738
  }
2499
- const withAdoption = (value) => ({ ...value, adoption: adoptionInfo });
2739
+ const withAdoption = (value) => ({
2740
+ ...value,
2741
+ adoption: adoptionInfo,
2742
+ observation: status.observation,
2743
+ });
2500
2744
  const repositoryRoot = resolveRepositoryIdentity(repository).repositoryRoot;
2501
2745
  const milestonesById = new Map(status.milestones.map((milestone) => [milestone.id, milestone]));
2502
2746
  const activeTask = status.tasks.find((task) => taskIsActionableFromRepositoryNext(task, milestonesById));
@@ -2823,6 +3067,19 @@ export class WorkflowService {
2823
3067
  throw error;
2824
3068
  }
2825
3069
  }
3070
+ let catalogCorrectiveReplanPosture = guardedCorrectivePosture?.posture === 'replan-required'
3071
+ ? guardedCorrectivePosture
3072
+ : null;
3073
+ if (!remediationModeRecovery && planRiskAudit && guardedCorrectivePosture === null) {
3074
+ try {
3075
+ catalogCorrectiveReplanPosture = identifyCorrectiveReplanPosture(task, planRiskAudit.reviewRequiredStepIds, readCorrectiveDecisionEvents(this.store, task), readRemediationEvents(this.store, task));
3076
+ }
3077
+ catch (error) {
3078
+ remediationModeRecovery = findRemediationModeRecoveryCandidate(this.store, task);
3079
+ if (!remediationModeRecovery)
3080
+ throw error;
3081
+ }
3082
+ }
2826
3083
  const next = this.taskGitNavigationOverride(task) ?? nextForTask(task);
2827
3084
  const handoffPosture = readTaskC1Posture(this.store, task);
2828
3085
  const strictStepReviewStep = planRiskAudit
@@ -2865,6 +3122,63 @@ export class WorkflowService {
2865
3122
  },
2866
3123
  }
2867
3124
  : {};
3125
+ const correctiveReplanOverlay = (posture) => {
3126
+ const evaluation = evaluateTaskCorrectiveReplanAvailability(task, posture);
3127
+ const writerLease = inspectRawWriterLease(this.store.lockRoot(task.projectId), task.id, this.store.root);
3128
+ const command = handoffPosture.state === 'claimed'
3129
+ ? 'task corrective-yield'
3130
+ : writerLease
3131
+ ? 'doctor'
3132
+ : 'task corrective-replan-prepare';
3133
+ if (evaluation.allowed && handoffPosture.state === 'none' && writerLease) {
3134
+ const navigation = correctiveReplanNavigation(evaluation);
3135
+ return {
3136
+ action: command,
3137
+ blockedAction: 'task corrective-replan-prepare',
3138
+ stepId: posture.stepId,
3139
+ navigation: {
3140
+ kind: 'blocked',
3141
+ transitionId: navigation.transitionId,
3142
+ semanticFingerprint: navigation.semanticFingerprint,
3143
+ command: navigation.command,
3144
+ blockingGuardIds: ['task.corrective-replan.no-writer-lease'],
3145
+ pendingInputGuardIds: navigation.pendingInputGuardIds,
3146
+ reasonCode: 'ACTIVE_WRITER_LEASE',
3147
+ },
3148
+ correctiveDecisionGate: {
3149
+ attemptCount: posture.attemptCount,
3150
+ decision: posture.correctiveDecision?.decision ?? null,
3151
+ decisionEventId: posture.correctiveDecision?.eventId ?? null,
3152
+ requiredAction: 'The active non-C1 writer lease must expire and be explicitly repaired before corrective replan preparation.',
3153
+ writerLease: {
3154
+ owner: writerLease.owner,
3155
+ expiresAt: writerLease.expiresAt,
3156
+ },
3157
+ },
3158
+ };
3159
+ }
3160
+ return evaluation.allowed
3161
+ ? {
3162
+ action: command,
3163
+ blockedAction: 'task plan-set',
3164
+ stepId: posture.stepId,
3165
+ navigation: { ...correctiveReplanNavigation(evaluation), command },
3166
+ correctiveDecisionGate: {
3167
+ attemptCount: posture.attemptCount,
3168
+ decision: posture.correctiveDecision?.decision ?? null,
3169
+ decisionEventId: posture.correctiveDecision?.eventId ?? null,
3170
+ requiredAction: handoffPosture.state === 'claimed'
3171
+ ? 'Yield the exact C1 claim and writer lease before preparing replacement authority.'
3172
+ : 'Prepare a state-bound candidate, confirm the Human gate, then execute its exact receipt.',
3173
+ },
3174
+ }
3175
+ : {
3176
+ action: 'doctor',
3177
+ blockedAction: 'task plan-set',
3178
+ stepId: posture.stepId,
3179
+ navigation: correctiveReplanBlockedNavigation(evaluation),
3180
+ };
3181
+ };
2868
3182
  let guardedCorrectiveOverlay = {};
2869
3183
  if (remediationModeRecovery) {
2870
3184
  guardedCorrectiveOverlay = {
@@ -2883,7 +3197,12 @@ export class WorkflowService {
2883
3197
  },
2884
3198
  };
2885
3199
  }
2886
- if (!remediationModeRecovery && !strictStepReviewStep && guardedCorrectivePosture) {
3200
+ if (!remediationModeRecovery && guardedCorrectivePosture === null && catalogCorrectiveReplanPosture) {
3201
+ guardedCorrectiveOverlay = correctiveReplanOverlay(catalogCorrectiveReplanPosture);
3202
+ }
3203
+ if (!remediationModeRecovery
3204
+ && guardedCorrectivePosture
3205
+ && (!strictStepReviewStep || guardedCorrectivePosture.posture === 'replan-required')) {
2887
3206
  switch (guardedCorrectivePosture.posture) {
2888
3207
  case 'decision-required':
2889
3208
  if (guardedCorrectivePosture.correctiveDecision
@@ -2958,16 +3277,7 @@ export class WorkflowService {
2958
3277
  };
2959
3278
  break;
2960
3279
  case 'replan-required':
2961
- guardedCorrectiveOverlay = {
2962
- action: 'task plan-set',
2963
- stepId: guardedCorrectivePosture.stepId,
2964
- correctiveDecisionGate: {
2965
- attemptCount: guardedCorrectivePosture.attemptCount,
2966
- decision: guardedCorrectivePosture.correctiveDecision?.decision ?? null,
2967
- decisionEventId: guardedCorrectivePosture.correctiveDecision?.eventId ?? null,
2968
- requiredAction: 'Record an actual new Plan posture before another remediation run.',
2969
- },
2970
- };
3280
+ guardedCorrectiveOverlay = correctiveReplanOverlay(guardedCorrectivePosture);
2971
3281
  break;
2972
3282
  case 'split-required':
2973
3283
  {
@@ -3252,6 +3562,14 @@ export class WorkflowService {
3252
3562
  ? milestone
3253
3563
  : readMilestoneWithIntegrity(this.store, projectId, milestone.id, (taskId) => this.store.readTask(projectId, taskId), () => this.store.listTasks(projectId))));
3254
3564
  }
3565
+ listMilestonesObserved(projectId, milestones, posture) {
3566
+ if (!posture)
3567
+ return milestones;
3568
+ const grandfatheredMilestones = new Set(posture.terminalMilestones.map((milestone) => `${milestone.milestoneId}:${milestone.revision}:${milestone.status}`));
3569
+ return milestones.map((milestone) => (grandfatheredMilestones.has(`${milestone.id}:${milestone.revision}:${milestone.status}`)
3570
+ ? milestone
3571
+ : inspectMilestoneWithIntegrity(this.store, projectId, milestone.id, (taskId) => this.store.readTask(projectId, taskId), () => this.store.listTasks(projectId))));
3572
+ }
3255
3573
  taskGitNavigationOverride(task) {
3256
3574
  if (task.status !== 'merging' || task.workspaceOwner !== 'local' || !task.baseCommit || !task.taskBranch) {
3257
3575
  return null;
@@ -3325,6 +3643,12 @@ export class WorkflowService {
3325
3643
  actor,
3326
3644
  });
3327
3645
  }
3646
+ publicWriterLeaseDiagnostic(lease) {
3647
+ return {
3648
+ ...toPublicWriterLease(lease, this.now()),
3649
+ tokenFingerprint: createHash('sha256').update(lease.token).digest('hex').slice(0, 16),
3650
+ };
3651
+ }
3328
3652
  proveWriterLeaseTokenForHandoff(context, taskId, writerToken) {
3329
3653
  const currentLease = context.locks.inspect(taskId);
3330
3654
  if (!currentLease) {
@@ -3414,9 +3738,12 @@ export class WorkflowService {
3414
3738
  storedKnowledgeMapHash: storedHash,
3415
3739
  });
3416
3740
  }
3417
- inspectAdoptionPreparation(identity, tasks, milestones) {
3418
- const locks = new WriterLockManager(this.store.lockRoot(identity.projectId), this.now);
3419
- const currentPosture = readCurrentAdoptionPosture(this.store, identity.projectId);
3741
+ inspectAdoptionPreparation(identity, tasks, milestones, observedLeases, observedPosture) {
3742
+ const leases = observedLeases
3743
+ ?? new WriterLockManager(this.store.lockRoot(identity.projectId), this.now, this.store.root).list();
3744
+ const currentPosture = observedPosture === undefined
3745
+ ? readCurrentAdoptionPosture(this.store, identity.projectId)
3746
+ : observedPosture;
3420
3747
  if (currentPosture) {
3421
3748
  assertAdoptionBaselinePreserved(currentPosture, tasks, milestones);
3422
3749
  }
@@ -3429,7 +3756,7 @@ export class WorkflowService {
3429
3756
  tasks,
3430
3757
  milestones,
3431
3758
  sidecarInventory: this.collectAdoptionSidecarInventory(identity.projectId, tasks, milestones),
3432
- blockers: this.computeAdoptionBoundaryBlockers(identity, tasks, locks.list()),
3759
+ blockers: this.computeAdoptionBoundaryBlockers(identity, tasks, leases),
3433
3760
  now: this.now(),
3434
3761
  }, currentPosture);
3435
3762
  }
@@ -4051,6 +4378,7 @@ function writerTokenContractForAction(action, claimedC1Posture = false) {
4051
4378
  'task corrective-decision-recover',
4052
4379
  'task remediation-mode-recover',
4053
4380
  'task corrective-decision',
4381
+ 'task corrective-yield',
4054
4382
  'task handoff',
4055
4383
  'task handoff-prepare',
4056
4384
  'task handback',
@@ -4066,6 +4394,23 @@ function writerTokenContractForAction(action, claimedC1Posture = false) {
4066
4394
  sensitive: true,
4067
4395
  };
4068
4396
  }
4397
+ function observationPreflightBlockers(observation) {
4398
+ const messages = {
4399
+ PROJECT_REGISTRATION_MISSING: 'Workflow project registration is missing.',
4400
+ PROJECT_REPOSITORY_ROOT_UNBOUND: 'The current repository root is not bound to the Workflow project.',
4401
+ PROJECT_RECOVERY_REQUIRED: 'A project transaction requires explicit recovery.',
4402
+ MILESTONE_RECOVERY_REQUIRED: 'A Milestone transaction requires explicit recovery.',
4403
+ TASK_RECOVERY_REQUIRED: 'A Task corrective-replan transaction requires explicit recovery.',
4404
+ TASK_TRANSACTION_INTEGRITY_FAILED: 'A Task transaction journal failed integrity validation.',
4405
+ TASK_OPERATION_IN_PROGRESS: 'A Core Task operation is in progress.',
4406
+ STALE_TASK_OPERATION: 'A stale Core Task operation requires bounded repair.',
4407
+ ADOPTION_MISSING: 'Workflow lifecycle adoption is missing.',
4408
+ };
4409
+ return observation.reasonCodes.flatMap((reasonCode) => {
4410
+ const message = messages[reasonCode];
4411
+ return message ? [message] : [];
4412
+ });
4413
+ }
4069
4414
  function knowledgeSummary(map) {
4070
4415
  return {
4071
4416
  revision: map.revision,