codex-workflow-v2 2.0.0-beta.1 → 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.
- package/README.md +47 -3
- package/dist/src/alpha6/adoption.js +13 -3
- package/dist/src/alpha6/adoption.js.map +1 -1
- package/dist/src/alpha6/handoff.d.ts +21 -0
- package/dist/src/alpha6/handoff.js +100 -19
- package/dist/src/alpha6/handoff.js.map +1 -1
- package/dist/src/alpha6/milestone.d.ts +12 -0
- package/dist/src/alpha6/milestone.js +113 -1
- package/dist/src/alpha6/milestone.js.map +1 -1
- package/dist/src/alpha6/plan-risk.d.ts +1 -0
- package/dist/src/alpha6/plan-risk.js +6 -0
- package/dist/src/alpha6/plan-risk.js.map +1 -1
- package/dist/src/cli.js +383 -43
- package/dist/src/cli.js.map +1 -1
- package/dist/src/contracts.d.ts +111 -5
- package/dist/src/contracts.js +1 -1
- package/dist/src/credential-output.d.ts +10 -0
- package/dist/src/credential-output.js +73 -0
- package/dist/src/credential-output.js.map +1 -0
- package/dist/src/errors.d.ts +1 -1
- package/dist/src/errors.js.map +1 -1
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +1 -1
- package/dist/src/index.js.map +1 -1
- package/dist/src/lifecycle/canonical-hash.d.ts +6 -0
- package/dist/src/lifecycle/canonical-hash.js +24 -0
- package/dist/src/lifecycle/canonical-hash.js.map +1 -0
- package/dist/src/lifecycle/catalog.d.ts +26 -0
- package/dist/src/lifecycle/catalog.js +208 -0
- package/dist/src/lifecycle/catalog.js.map +1 -0
- package/dist/src/lifecycle/compiler-inspection.d.ts +48 -0
- package/dist/src/lifecycle/compiler-inspection.js +29 -0
- package/dist/src/lifecycle/compiler-inspection.js.map +1 -0
- package/dist/src/lifecycle/core-static-readiness.d.ts +6 -0
- package/dist/src/lifecycle/core-static-readiness.js +130 -0
- package/dist/src/lifecycle/core-static-readiness.js.map +1 -0
- package/dist/src/lifecycle/corrective-replan-authority.d.ts +53 -0
- package/dist/src/lifecycle/corrective-replan-authority.js +139 -0
- package/dist/src/lifecycle/corrective-replan-authority.js.map +1 -0
- package/dist/src/lifecycle/corrective-replan-binding-manifest.d.ts +21 -0
- package/dist/src/lifecycle/corrective-replan-binding-manifest.js +35 -0
- package/dist/src/lifecycle/corrective-replan-binding-manifest.js.map +1 -0
- package/dist/src/lifecycle/corrective-replan-credential-core.d.ts +86 -0
- package/dist/src/lifecycle/corrective-replan-credential-core.js +534 -0
- package/dist/src/lifecycle/corrective-replan-credential-core.js.map +1 -0
- package/dist/src/lifecycle/corrective-replan-credential-schema.d.ts +17 -0
- package/dist/src/lifecycle/corrective-replan-credential-schema.js +44 -0
- package/dist/src/lifecycle/corrective-replan-credential-schema.js.map +1 -0
- package/dist/src/lifecycle/corrective-replan-credentials.d.ts +47 -0
- package/dist/src/lifecycle/corrective-replan-credentials.js +103 -0
- package/dist/src/lifecycle/corrective-replan-credentials.js.map +1 -0
- package/dist/src/lifecycle/corrective-replan.d.ts +64 -0
- package/dist/src/lifecycle/corrective-replan.js +548 -0
- package/dist/src/lifecycle/corrective-replan.js.map +1 -0
- package/dist/src/lifecycle/evaluator.d.ts +2 -0
- package/dist/src/lifecycle/evaluator.js +35 -0
- package/dist/src/lifecycle/evaluator.js.map +1 -0
- package/dist/src/lifecycle/fingerprint.d.ts +31 -0
- package/dist/src/lifecycle/fingerprint.js +388 -0
- package/dist/src/lifecycle/fingerprint.js.map +1 -0
- package/dist/src/lifecycle/immutable.d.ts +1 -0
- package/dist/src/lifecycle/immutable.js +10 -0
- package/dist/src/lifecycle/immutable.js.map +1 -0
- package/dist/src/lifecycle/implementation-table.d.ts +29 -0
- package/dist/src/lifecycle/implementation-table.js +87 -0
- package/dist/src/lifecycle/implementation-table.js.map +1 -0
- package/dist/src/lifecycle/index.d.ts +4 -0
- package/dist/src/lifecycle/index.js +5 -0
- package/dist/src/lifecycle/index.js.map +1 -0
- package/dist/src/lifecycle/schema-artifact.d.ts +12 -0
- package/dist/src/lifecycle/schema-artifact.js +214 -0
- package/dist/src/lifecycle/schema-artifact.js.map +1 -0
- package/dist/src/lifecycle/semantic-registry.d.ts +6 -0
- package/dist/src/lifecycle/semantic-registry.js +489 -0
- package/dist/src/lifecycle/semantic-registry.js.map +1 -0
- package/dist/src/lifecycle/types.d.ts +718 -0
- package/dist/src/lifecycle/types.js +4 -0
- package/dist/src/lifecycle/types.js.map +1 -0
- package/dist/src/observation.d.ts +20 -0
- package/dist/src/observation.js +171 -0
- package/dist/src/observation.js.map +1 -0
- package/dist/src/reviewer.d.ts +4 -0
- package/dist/src/reviewer.js +24 -7
- package/dist/src/reviewer.js.map +1 -1
- package/dist/src/state/corrective-replan-executor.d.ts +64 -0
- package/dist/src/state/corrective-replan-executor.js +598 -0
- package/dist/src/state/corrective-replan-executor.js.map +1 -0
- package/dist/src/state/corrective-replan-public-schema.d.ts +3 -0
- package/dist/src/state/corrective-replan-public-schema.js +43 -0
- package/dist/src/state/corrective-replan-public-schema.js.map +1 -0
- package/dist/src/state/corrective-replan-public.d.ts +95 -0
- package/dist/src/state/corrective-replan-public.js +618 -0
- package/dist/src/state/corrective-replan-public.js.map +1 -0
- package/dist/src/state/corrective-replan-transaction.d.ts +158 -0
- package/dist/src/state/corrective-replan-transaction.js +723 -0
- package/dist/src/state/corrective-replan-transaction.js.map +1 -0
- package/dist/src/state/corrective-yield-executor.d.ts +31 -0
- package/dist/src/state/corrective-yield-executor.js +236 -0
- package/dist/src/state/corrective-yield-executor.js.map +1 -0
- package/dist/src/state/corrective-yield-transaction.d.ts +97 -0
- package/dist/src/state/corrective-yield-transaction.js +591 -0
- package/dist/src/state/corrective-yield-transaction.js.map +1 -0
- package/dist/src/state/lock.d.ts +75 -3
- package/dist/src/state/lock.js +582 -30
- package/dist/src/state/lock.js.map +1 -1
- package/dist/src/state/store.d.ts +3 -0
- package/dist/src/state/store.js +65 -12
- package/dist/src/state/store.js.map +1 -1
- package/dist/src/transition-core.d.ts +67 -0
- package/dist/src/transition-core.js +318 -0
- package/dist/src/transition-core.js.map +1 -0
- package/dist/src/version.d.ts +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/workflow.d.ts +51 -21
- package/dist/src/workflow.js +667 -144
- package/dist/src/workflow.js.map +1 -1
- package/docs/autonomy-guardrails.md +8 -1
- package/docs/beta2-initial-assembly-navigation-brief.md +616 -0
- package/docs/development-flow.md +17 -2
- package/docs/lifecycle/state-machine-stabilization.md +641 -0
- package/docs/release.md +11 -2
- package/docs/split-required-recovery.md +5 -4
- package/docs/stable-release-defect-register.md +330 -1
- package/docs/updating-existing-project.md +11 -0
- package/package.json +4 -1
- package/plugins/codex-workflow-gateway/references/protocol.md +56 -1
- package/plugins/codex-workflow-gateway/skills/codex-workflow-gateway/SKILL.md +44 -0
- package/references/state-machine.md +4 -1
- package/schemas/adoption-posture-event.schema.json +1 -1
- package/schemas/context-envelope.schema.json +1 -1
- package/schemas/corrective-replan-credentials.private.schema.json +98 -0
- package/schemas/corrective-replan-execution-event.private.schema.json +53 -0
- package/schemas/corrective-replan-public.schema.json +142 -0
- package/schemas/corrective-replan-task-transaction.private.schema.json +151 -0
- package/schemas/corrective-yield-task-transaction.private.schema.json +98 -0
- package/schemas/remediation-event.schema.json +11 -1
- package/schemas/task-handoff-event.schema.json +1 -1
- package/schemas/transition-definition.schema.json +2609 -0
- package/schemas/transition-payloads.schema.json +140 -0
package/dist/src/workflow.js
CHANGED
|
@@ -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 { 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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
?
|
|
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
|
|
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
|
|
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 (
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
1338
|
-
if (pendingHandoff.
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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,21 +2317,22 @@ export class WorkflowService {
|
|
|
2150
2317
|
return saved;
|
|
2151
2318
|
}
|
|
2152
2319
|
status(repository) {
|
|
2153
|
-
const
|
|
2154
|
-
const tasks = this.store.listTasks(identity.projectId);
|
|
2155
|
-
const rawMilestones = this.store.listMilestones(identity.projectId);
|
|
2156
|
-
const adoption = this.inspectAdoptionPreparation(identity, tasks, rawMilestones);
|
|
2320
|
+
const snapshot = this.observationSnapshot(repository);
|
|
2157
2321
|
return {
|
|
2158
|
-
projectId: identity.projectId,
|
|
2159
|
-
discoveries:
|
|
2160
|
-
tasks,
|
|
2161
|
-
milestones:
|
|
2162
|
-
|
|
2163
|
-
|
|
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))
|
|
2328
|
+
.filter((value) => value !== null),
|
|
2329
|
+
delegations: snapshot.delegations,
|
|
2330
|
+
adoption: snapshot.adoption,
|
|
2331
|
+
observation: snapshot.observation,
|
|
2164
2332
|
};
|
|
2165
2333
|
}
|
|
2166
2334
|
recoverMilestoneTransactions(repository) {
|
|
2167
|
-
const { identity } = this
|
|
2335
|
+
const { identity } = this.#mutationContext(repository);
|
|
2168
2336
|
const milestones = this.listMilestonesCurrent(identity.projectId, readCurrentAdoptionPosture(this.store, identity.projectId));
|
|
2169
2337
|
return {
|
|
2170
2338
|
projectId: identity.projectId,
|
|
@@ -2172,11 +2340,11 @@ export class WorkflowService {
|
|
|
2172
2340
|
};
|
|
2173
2341
|
}
|
|
2174
2342
|
prepareAdoption(repository) {
|
|
2175
|
-
const { identity } = this
|
|
2343
|
+
const { identity } = this.#mutationContext(repository);
|
|
2176
2344
|
return this.inspectAdoptionPreparation(identity, this.store.listTasks(identity.projectId), this.store.listMilestones(identity.projectId));
|
|
2177
2345
|
}
|
|
2178
2346
|
applyAdoption(repository, actor, confirmationCode) {
|
|
2179
|
-
const context = this
|
|
2347
|
+
const context = this.#mutationContext(repository);
|
|
2180
2348
|
const tasks = context.store.listTasks(context.identity.projectId);
|
|
2181
2349
|
const milestones = context.store.listMilestones(context.identity.projectId);
|
|
2182
2350
|
return applyAdoptionPosture(this.store, {
|
|
@@ -2193,19 +2361,19 @@ export class WorkflowService {
|
|
|
2193
2361
|
}, actor, confirmationCode);
|
|
2194
2362
|
}
|
|
2195
2363
|
updatePreflight(repository) {
|
|
2196
|
-
const
|
|
2197
|
-
const tasks =
|
|
2198
|
-
const milestones =
|
|
2199
|
-
const dirty = changedFiles(
|
|
2364
|
+
const snapshot = this.observationSnapshot(repository);
|
|
2365
|
+
const tasks = snapshot.tasks;
|
|
2366
|
+
const milestones = snapshot.milestones;
|
|
2367
|
+
const dirty = changedFiles(snapshot.identity.repositoryRoot);
|
|
2200
2368
|
const runningSteps = tasks.flatMap((task) => task.steps
|
|
2201
2369
|
.filter((step) => step.status === 'in_progress')
|
|
2202
2370
|
.map((step) => ({ taskId: task.id, stepId: step.id })));
|
|
2203
|
-
const leases =
|
|
2371
|
+
const leases = snapshot.leases;
|
|
2204
2372
|
const now = this.now().getTime();
|
|
2205
2373
|
const activeLeases = leases.filter((lease) => Date.parse(lease.expiresAt) > now);
|
|
2206
2374
|
const staleLeases = leases.filter((lease) => Date.parse(lease.expiresAt) <= now);
|
|
2207
2375
|
const activeMilestones = milestones.filter((milestone) => !['accepted', 'cancelled'].includes(milestone.status));
|
|
2208
|
-
const inspectedVersions = inspectWorkflowVersions(
|
|
2376
|
+
const inspectedVersions = inspectWorkflowVersions(snapshot.identity.repositoryRoot, activeMilestones.map((milestone) => milestone.baseBranch));
|
|
2209
2377
|
const milestoneBases = inspectedVersions.milestoneBases.map((base) => ({
|
|
2210
2378
|
...base,
|
|
2211
2379
|
milestoneIds: activeMilestones.filter((milestone) => milestone.baseBranch === base.branch).map((milestone) => milestone.id),
|
|
@@ -2232,31 +2400,20 @@ export class WorkflowService {
|
|
|
2232
2400
|
...(activeLeases.length > 0 ? ['At least one writer lease is active.'] : []),
|
|
2233
2401
|
...(staleLeases.length > 0 ? ['At least one stale writer lease requires explicit repair.'] : []),
|
|
2234
2402
|
...versionBlockers,
|
|
2403
|
+
...observationPreflightBlockers(snapshot.observation),
|
|
2235
2404
|
];
|
|
2236
|
-
const adoption = buildAdoptionPreparation({
|
|
2237
|
-
projectId: context.identity.projectId,
|
|
2238
|
-
repositoryIdentity: context.identity,
|
|
2239
|
-
packageVersion: PACKAGE_VERSION,
|
|
2240
|
-
protocolVersion: PROTOCOL_VERSION,
|
|
2241
|
-
stateSchemaVersion: STATE_SCHEMA_VERSION,
|
|
2242
|
-
tasks,
|
|
2243
|
-
milestones,
|
|
2244
|
-
sidecarInventory: this.collectAdoptionSidecarInventory(context.identity.projectId, tasks, milestones),
|
|
2245
|
-
blockers,
|
|
2246
|
-
now: this.now(),
|
|
2247
|
-
}, readCurrentAdoptionPosture(this.store, context.identity.projectId));
|
|
2248
2405
|
return {
|
|
2249
2406
|
safe: blockers.length === 0,
|
|
2250
2407
|
packageVersion: PACKAGE_VERSION,
|
|
2251
2408
|
protocolVersion: PROTOCOL_VERSION,
|
|
2252
2409
|
stateSchemaVersion: STATE_SCHEMA_VERSION,
|
|
2253
|
-
projectId:
|
|
2254
|
-
repositoryRoot:
|
|
2410
|
+
projectId: snapshot.identity.projectId,
|
|
2411
|
+
repositoryRoot: snapshot.identity.repositoryRoot,
|
|
2255
2412
|
clean: dirty.length === 0,
|
|
2256
2413
|
changedFiles: dirty,
|
|
2257
2414
|
runningSteps,
|
|
2258
|
-
activeLeases,
|
|
2259
|
-
staleLeases,
|
|
2415
|
+
activeLeases: activeLeases.map((lease) => toPublicWriterLease(lease, new Date(now))),
|
|
2416
|
+
staleLeases: staleLeases.map((lease) => toPublicWriterLease(lease, new Date(now))),
|
|
2260
2417
|
blockers,
|
|
2261
2418
|
workflowVersions: {
|
|
2262
2419
|
runtime: inspectedVersions.runtime,
|
|
@@ -2272,11 +2429,12 @@ export class WorkflowService {
|
|
|
2272
2429
|
? null
|
|
2273
2430
|
: 'Land only package.json/package-lock.json dependency changes on every active Milestone base; do not transport unrelated product commits.',
|
|
2274
2431
|
},
|
|
2275
|
-
adoption,
|
|
2432
|
+
adoption: snapshot.adoption,
|
|
2433
|
+
observation: snapshot.observation,
|
|
2276
2434
|
};
|
|
2277
2435
|
}
|
|
2278
2436
|
alpha6StrictReviewRescuePreflight(repository) {
|
|
2279
|
-
const context = this
|
|
2437
|
+
const context = this.#mutationContext(repository);
|
|
2280
2438
|
const dependency = inspectLocalDependency(context.identity.repositoryRoot);
|
|
2281
2439
|
const tasks = context.store.listTasks(context.identity.projectId);
|
|
2282
2440
|
const leases = context.locks.list();
|
|
@@ -2371,15 +2529,15 @@ export class WorkflowService {
|
|
|
2371
2529
|
projectId: context.identity.projectId,
|
|
2372
2530
|
repositoryRoot: context.identity.repositoryRoot,
|
|
2373
2531
|
clean: dirty.length === 0,
|
|
2374
|
-
activeLeases,
|
|
2375
|
-
staleLeases,
|
|
2532
|
+
activeLeases: activeLeases.map((lease) => toPublicWriterLease(lease, new Date(now))),
|
|
2533
|
+
staleLeases: staleLeases.map((lease) => toPublicWriterLease(lease, new Date(now))),
|
|
2376
2534
|
candidate,
|
|
2377
2535
|
blockers,
|
|
2378
2536
|
requiredActions,
|
|
2379
2537
|
};
|
|
2380
2538
|
}
|
|
2381
2539
|
scanKnowledgeMap(repository) {
|
|
2382
|
-
const { identity } = this
|
|
2540
|
+
const { identity } = this.#mutationContext(repository);
|
|
2383
2541
|
const scanned = scanProjectKnowledge(identity.repositoryRoot, identity.projectId, this.now());
|
|
2384
2542
|
try {
|
|
2385
2543
|
const current = this.store.readKnowledgeMap(identity.projectId);
|
|
@@ -2392,11 +2550,11 @@ export class WorkflowService {
|
|
|
2392
2550
|
}
|
|
2393
2551
|
}
|
|
2394
2552
|
showKnowledgeMap(repository) {
|
|
2395
|
-
const { identity } = this
|
|
2553
|
+
const { identity } = this.#mutationContext(repository);
|
|
2396
2554
|
return this.store.readKnowledgeMap(identity.projectId);
|
|
2397
2555
|
}
|
|
2398
2556
|
knowledgeMapStatus(repository) {
|
|
2399
|
-
const { identity } = this
|
|
2557
|
+
const { identity } = this.#mutationContext(repository);
|
|
2400
2558
|
const current = this.store.readKnowledgeMap(identity.projectId);
|
|
2401
2559
|
const scanned = scanProjectKnowledge(identity.repositoryRoot, identity.projectId, this.now());
|
|
2402
2560
|
return inspectKnowledgeMap(current, scanned);
|
|
@@ -2405,7 +2563,7 @@ export class WorkflowService {
|
|
|
2405
2563
|
return this.approveKnowledgeMapForTaskRefresh(repository, expectedRevision, selections, actor, delegationGrantId, null);
|
|
2406
2564
|
}
|
|
2407
2565
|
approveKnowledgeMapForTaskRefresh(repository, expectedRevision, selections, actor, delegationGrantId, autonomyTaskId) {
|
|
2408
|
-
const { identity } = this
|
|
2566
|
+
const { identity } = this.#mutationContext(repository);
|
|
2409
2567
|
const current = this.store.readKnowledgeMap(identity.projectId);
|
|
2410
2568
|
assertExpectedRevision(current, expectedRevision);
|
|
2411
2569
|
const inspected = inspectKnowledgeMap(current, scanProjectKnowledge(identity.repositoryRoot, identity.projectId, this.now()));
|
|
@@ -2422,7 +2580,7 @@ export class WorkflowService {
|
|
|
2422
2580
|
return this.store.writeKnowledgeMap(approved, expectedRevision);
|
|
2423
2581
|
}
|
|
2424
2582
|
reconcileKnowledgeMap(repository, expectedRevision, allowAtomicTaskRefresh = false) {
|
|
2425
|
-
const { identity } = this
|
|
2583
|
+
const { identity } = this.#mutationContext(repository);
|
|
2426
2584
|
const current = this.store.readKnowledgeMap(identity.projectId);
|
|
2427
2585
|
assertExpectedRevision(current, expectedRevision);
|
|
2428
2586
|
const scanned = scanProjectKnowledge(identity.repositoryRoot, identity.projectId, this.now());
|
|
@@ -2448,17 +2606,17 @@ export class WorkflowService {
|
|
|
2448
2606
|
return this.store.writeKnowledgeMap(reconcileKnowledgeMap(current, scanned), expectedRevision);
|
|
2449
2607
|
}
|
|
2450
2608
|
graphRefreshRequest(repository, graphKind, mode = 'moderate') {
|
|
2451
|
-
const { identity } = this
|
|
2609
|
+
const { identity } = this.#mutationContext(repository);
|
|
2452
2610
|
return createGraphRefreshRequest(identity.repositoryRoot, identity.projectId, graphKind, mode, this.now());
|
|
2453
2611
|
}
|
|
2454
2612
|
graphStatus(repository, graphKind, mode = 'moderate') {
|
|
2455
|
-
const { identity } = this
|
|
2613
|
+
const { identity } = this.#mutationContext(repository);
|
|
2456
2614
|
const request = createGraphRefreshRequest(identity.repositoryRoot, identity.projectId, graphKind, mode, this.now());
|
|
2457
2615
|
const binding = this.readGraphBindingOrNull(identity.projectId, graphKind);
|
|
2458
2616
|
return { request, binding: binding ? inspectGraphBinding(binding, request) : null };
|
|
2459
2617
|
}
|
|
2460
2618
|
bindGraph(repository, expectedRevision, request, evidence) {
|
|
2461
|
-
const { identity } = this
|
|
2619
|
+
const { identity } = this.#mutationContext(repository);
|
|
2462
2620
|
const currentRequest = createGraphRefreshRequest(identity.repositoryRoot, identity.projectId, request.graphKind, request.mode, this.now());
|
|
2463
2621
|
validateGraphRequestCurrent(request, currentRequest);
|
|
2464
2622
|
const existing = this.readGraphBindingOrNull(identity.projectId, request.graphKind);
|
|
@@ -2466,13 +2624,78 @@ export class WorkflowService {
|
|
|
2466
2624
|
return this.store.writeGraphBinding(binding, expectedRevision);
|
|
2467
2625
|
}
|
|
2468
2626
|
recordGraphFallback(repository, graphKind, mode, expectedRevision, reason) {
|
|
2469
|
-
const { identity } = this
|
|
2627
|
+
const { identity } = this.#mutationContext(repository);
|
|
2470
2628
|
const request = createGraphRefreshRequest(identity.repositoryRoot, identity.projectId, graphKind, mode, this.now());
|
|
2471
2629
|
const existing = this.readGraphBindingOrNull(identity.projectId, graphKind);
|
|
2472
2630
|
return this.store.writeGraphBinding(fallbackGraphBinding(identity.projectId, request, reason, existing, this.now()), expectedRevision);
|
|
2473
2631
|
}
|
|
2474
2632
|
next(repository) {
|
|
2475
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
|
+
}
|
|
2476
2699
|
const adoptionInfo = {
|
|
2477
2700
|
status: status.adoption.status,
|
|
2478
2701
|
safe: status.adoption.safe,
|
|
@@ -2490,9 +2713,34 @@ export class WorkflowService {
|
|
|
2490
2713
|
status: status.adoption.status,
|
|
2491
2714
|
action: status.adoption.safe ? 'state adoption-apply' : 'restore safe boundary, then state adoption-prepare',
|
|
2492
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
|
+
},
|
|
2493
2737
|
};
|
|
2494
2738
|
}
|
|
2495
|
-
const withAdoption = (value) => ({
|
|
2739
|
+
const withAdoption = (value) => ({
|
|
2740
|
+
...value,
|
|
2741
|
+
adoption: adoptionInfo,
|
|
2742
|
+
observation: status.observation,
|
|
2743
|
+
});
|
|
2496
2744
|
const repositoryRoot = resolveRepositoryIdentity(repository).repositoryRoot;
|
|
2497
2745
|
const milestonesById = new Map(status.milestones.map((milestone) => [milestone.id, milestone]));
|
|
2498
2746
|
const activeTask = status.tasks.find((task) => taskIsActionableFromRepositoryNext(task, milestonesById));
|
|
@@ -2588,6 +2836,31 @@ export class WorkflowService {
|
|
|
2588
2836
|
}
|
|
2589
2837
|
const activeMilestone = status.milestones.find((milestone) => !['accepted', 'cancelled'].includes(milestone.status));
|
|
2590
2838
|
if (activeMilestone) {
|
|
2839
|
+
const initialAssembly = status.milestoneInitialAssembly
|
|
2840
|
+
.find((candidate) => candidate.milestoneId === activeMilestone.id) ?? null;
|
|
2841
|
+
if (initialAssembly) {
|
|
2842
|
+
const activeDiscovery = status.discoveries.find((discovery) => !['materialized', 'abandoned'].includes(discovery.status));
|
|
2843
|
+
if (activeDiscovery) {
|
|
2844
|
+
return withAdoption({
|
|
2845
|
+
scope: 'discovery',
|
|
2846
|
+
id: activeDiscovery.id,
|
|
2847
|
+
status: activeDiscovery.status,
|
|
2848
|
+
action: activeDiscovery.status === 'ready_to_materialize' ? 'discovery materialize' : 'discovery update',
|
|
2849
|
+
readiness: assessDiscovery(activeDiscovery),
|
|
2850
|
+
milestoneInitialAssembly: initialAssembly,
|
|
2851
|
+
});
|
|
2852
|
+
}
|
|
2853
|
+
return withAdoption({
|
|
2854
|
+
scope: 'milestone',
|
|
2855
|
+
id: activeMilestone.id,
|
|
2856
|
+
status: activeMilestone.status,
|
|
2857
|
+
action: 'milestone initial-assembly',
|
|
2858
|
+
revision: activeMilestone.revision,
|
|
2859
|
+
linkedTaskIds: initialAssembly.linkedTaskIds,
|
|
2860
|
+
executionAvailable: initialAssembly.executionAvailable,
|
|
2861
|
+
milestoneInitialAssembly: initialAssembly,
|
|
2862
|
+
});
|
|
2863
|
+
}
|
|
2591
2864
|
if (activeMilestone.status === 'awaiting_final_acceptance') {
|
|
2592
2865
|
const root = this.store.milestoneRoot(activeMilestone.projectId, activeMilestone.id);
|
|
2593
2866
|
const evidence = JSON.parse(readFileSync(path.join(root, 'evidence.json'), 'utf8'));
|
|
@@ -2794,7 +3067,20 @@ export class WorkflowService {
|
|
|
2794
3067
|
throw error;
|
|
2795
3068
|
}
|
|
2796
3069
|
}
|
|
2797
|
-
|
|
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
|
+
}
|
|
3083
|
+
const next = this.taskGitNavigationOverride(task) ?? nextForTask(task);
|
|
2798
3084
|
const handoffPosture = readTaskC1Posture(this.store, task);
|
|
2799
3085
|
const strictStepReviewStep = planRiskAudit
|
|
2800
3086
|
? task.steps.find((step) => {
|
|
@@ -2836,6 +3122,63 @@ export class WorkflowService {
|
|
|
2836
3122
|
},
|
|
2837
3123
|
}
|
|
2838
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
|
+
};
|
|
2839
3182
|
let guardedCorrectiveOverlay = {};
|
|
2840
3183
|
if (remediationModeRecovery) {
|
|
2841
3184
|
guardedCorrectiveOverlay = {
|
|
@@ -2854,7 +3197,12 @@ export class WorkflowService {
|
|
|
2854
3197
|
},
|
|
2855
3198
|
};
|
|
2856
3199
|
}
|
|
2857
|
-
if (!remediationModeRecovery &&
|
|
3200
|
+
if (!remediationModeRecovery && guardedCorrectivePosture === null && catalogCorrectiveReplanPosture) {
|
|
3201
|
+
guardedCorrectiveOverlay = correctiveReplanOverlay(catalogCorrectiveReplanPosture);
|
|
3202
|
+
}
|
|
3203
|
+
if (!remediationModeRecovery
|
|
3204
|
+
&& guardedCorrectivePosture
|
|
3205
|
+
&& (!strictStepReviewStep || guardedCorrectivePosture.posture === 'replan-required')) {
|
|
2858
3206
|
switch (guardedCorrectivePosture.posture) {
|
|
2859
3207
|
case 'decision-required':
|
|
2860
3208
|
if (guardedCorrectivePosture.correctiveDecision
|
|
@@ -2929,16 +3277,7 @@ export class WorkflowService {
|
|
|
2929
3277
|
};
|
|
2930
3278
|
break;
|
|
2931
3279
|
case 'replan-required':
|
|
2932
|
-
guardedCorrectiveOverlay =
|
|
2933
|
-
action: 'task plan-set',
|
|
2934
|
-
stepId: guardedCorrectivePosture.stepId,
|
|
2935
|
-
correctiveDecisionGate: {
|
|
2936
|
-
attemptCount: guardedCorrectivePosture.attemptCount,
|
|
2937
|
-
decision: guardedCorrectivePosture.correctiveDecision?.decision ?? null,
|
|
2938
|
-
decisionEventId: guardedCorrectivePosture.correctiveDecision?.eventId ?? null,
|
|
2939
|
-
requiredAction: 'Record an actual new Plan posture before another remediation run.',
|
|
2940
|
-
},
|
|
2941
|
-
};
|
|
3280
|
+
guardedCorrectiveOverlay = correctiveReplanOverlay(guardedCorrectivePosture);
|
|
2942
3281
|
break;
|
|
2943
3282
|
case 'split-required':
|
|
2944
3283
|
{
|
|
@@ -3051,13 +3390,31 @@ export class WorkflowService {
|
|
|
3051
3390
|
...(delegatedApprovalOptions.length > 0 ? { delegatedApprovalOptions } : {}),
|
|
3052
3391
|
};
|
|
3053
3392
|
if (handoffPosture.state === 'pending') {
|
|
3054
|
-
return {
|
|
3393
|
+
return this.withTaskNavigationContracts(task, {
|
|
3055
3394
|
...result,
|
|
3056
3395
|
action: 'task claim',
|
|
3057
3396
|
blockedAction: next.action,
|
|
3058
|
-
};
|
|
3397
|
+
});
|
|
3059
3398
|
}
|
|
3060
|
-
|
|
3399
|
+
const navigableResult = ['task plan-set', 'task plan-set with independent corrective audit']
|
|
3400
|
+
.includes(String(result.action))
|
|
3401
|
+
? {
|
|
3402
|
+
...result,
|
|
3403
|
+
taskPlanContract: this.taskPlanContract(projectId, task),
|
|
3404
|
+
}
|
|
3405
|
+
: result;
|
|
3406
|
+
return this.withTaskNavigationContracts(task, navigableResult);
|
|
3407
|
+
}
|
|
3408
|
+
taskPlanContract(projectId, task) {
|
|
3409
|
+
const map = this.store.readKnowledgeMap(projectId);
|
|
3410
|
+
return {
|
|
3411
|
+
requiredRequirementIds: [...task.requirements],
|
|
3412
|
+
requiredAcceptanceIds: [...task.acceptance],
|
|
3413
|
+
currentRisks: [...task.risks],
|
|
3414
|
+
exactInclusionRequired: true,
|
|
3415
|
+
briefHash: task.briefHash,
|
|
3416
|
+
knowledgeMap: knowledgeSummary(map),
|
|
3417
|
+
};
|
|
3061
3418
|
}
|
|
3062
3419
|
assertTaskContextChangeAllowed(task) {
|
|
3063
3420
|
const posture = this.taskTerminalCorrectivePosture(task);
|
|
@@ -3205,6 +3562,78 @@ export class WorkflowService {
|
|
|
3205
3562
|
? milestone
|
|
3206
3563
|
: readMilestoneWithIntegrity(this.store, projectId, milestone.id, (taskId) => this.store.readTask(projectId, taskId), () => this.store.listTasks(projectId))));
|
|
3207
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
|
+
}
|
|
3573
|
+
taskGitNavigationOverride(task) {
|
|
3574
|
+
if (task.status !== 'merging' || task.workspaceOwner !== 'local' || !task.baseCommit || !task.taskBranch) {
|
|
3575
|
+
return null;
|
|
3576
|
+
}
|
|
3577
|
+
const currentBase = runGit(task.repositoryRoot, ['rev-parse', task.baseBranch]);
|
|
3578
|
+
if (currentBase === task.baseCommit)
|
|
3579
|
+
return null;
|
|
3580
|
+
return {
|
|
3581
|
+
scope: 'task',
|
|
3582
|
+
id: task.id,
|
|
3583
|
+
status: task.status,
|
|
3584
|
+
action: 'task sync-base',
|
|
3585
|
+
revision: task.revision,
|
|
3586
|
+
baseSyncRequired: {
|
|
3587
|
+
baseBranch: task.baseBranch,
|
|
3588
|
+
recordedBaseCommit: task.baseCommit,
|
|
3589
|
+
currentBaseCommit: currentBase,
|
|
3590
|
+
},
|
|
3591
|
+
};
|
|
3592
|
+
}
|
|
3593
|
+
withTaskNavigationContracts(task, next) {
|
|
3594
|
+
const action = typeof next.action === 'string' ? next.action : null;
|
|
3595
|
+
if (!action)
|
|
3596
|
+
return next;
|
|
3597
|
+
const policy = gitMutationPolicyForAction(task, action);
|
|
3598
|
+
const claimedC1Posture = readTaskC1Posture(this.store, task).state === 'claimed';
|
|
3599
|
+
const writerTokenContract = writerTokenContractForAction(action, claimedC1Posture);
|
|
3600
|
+
return {
|
|
3601
|
+
...next,
|
|
3602
|
+
...(policy ? { gitMutationPolicy: policy } : {}),
|
|
3603
|
+
...(writerTokenContract ? { writerTokenContract } : {}),
|
|
3604
|
+
};
|
|
3605
|
+
}
|
|
3606
|
+
describeMilestoneInitialAssembly(projectId, milestone, tasks) {
|
|
3607
|
+
const assessment = assessMilestoneInitialAssembly({
|
|
3608
|
+
store: this.store,
|
|
3609
|
+
projectId,
|
|
3610
|
+
milestone,
|
|
3611
|
+
listTasks: () => tasks,
|
|
3612
|
+
});
|
|
3613
|
+
if (!assessment.eligible)
|
|
3614
|
+
return null;
|
|
3615
|
+
const options = [
|
|
3616
|
+
{
|
|
3617
|
+
action: 'linked-task-assembly',
|
|
3618
|
+
commands: ['discovery start', 'discovery materialize'],
|
|
3619
|
+
description: 'Start or materialize the next linked Task Discovery for this Milestone before closing the initial membership Plan.',
|
|
3620
|
+
},
|
|
3621
|
+
];
|
|
3622
|
+
if (assessment.orphanTaskIds.length > 0) {
|
|
3623
|
+
options.push({
|
|
3624
|
+
action: 'milestone plan-set',
|
|
3625
|
+
commands: ['milestone plan-set'],
|
|
3626
|
+
description: 'Record one complete initial Milestone Plan that classifies every currently linked Task.',
|
|
3627
|
+
});
|
|
3628
|
+
}
|
|
3629
|
+
return {
|
|
3630
|
+
milestoneId: milestone.id,
|
|
3631
|
+
revision: milestone.revision,
|
|
3632
|
+
linkedTaskIds: assessment.orphanTaskIds,
|
|
3633
|
+
executionAvailable: false,
|
|
3634
|
+
options,
|
|
3635
|
+
};
|
|
3636
|
+
}
|
|
3208
3637
|
prepareMilestoneScopeChangeCandidate(projectId, milestone, plan, actor) {
|
|
3209
3638
|
validateMilestonePlan(plan);
|
|
3210
3639
|
return prepareMilestoneScopeChangeCandidate({
|
|
@@ -3214,6 +3643,12 @@ export class WorkflowService {
|
|
|
3214
3643
|
actor,
|
|
3215
3644
|
});
|
|
3216
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
|
+
}
|
|
3217
3652
|
proveWriterLeaseTokenForHandoff(context, taskId, writerToken) {
|
|
3218
3653
|
const currentLease = context.locks.inspect(taskId);
|
|
3219
3654
|
if (!currentLease) {
|
|
@@ -3303,9 +3738,12 @@ export class WorkflowService {
|
|
|
3303
3738
|
storedKnowledgeMapHash: storedHash,
|
|
3304
3739
|
});
|
|
3305
3740
|
}
|
|
3306
|
-
inspectAdoptionPreparation(identity, tasks, milestones) {
|
|
3307
|
-
const
|
|
3308
|
-
|
|
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;
|
|
3309
3747
|
if (currentPosture) {
|
|
3310
3748
|
assertAdoptionBaselinePreserved(currentPosture, tasks, milestones);
|
|
3311
3749
|
}
|
|
@@ -3318,7 +3756,7 @@ export class WorkflowService {
|
|
|
3318
3756
|
tasks,
|
|
3319
3757
|
milestones,
|
|
3320
3758
|
sidecarInventory: this.collectAdoptionSidecarInventory(identity.projectId, tasks, milestones),
|
|
3321
|
-
blockers: this.computeAdoptionBoundaryBlockers(identity, tasks,
|
|
3759
|
+
blockers: this.computeAdoptionBoundaryBlockers(identity, tasks, leases),
|
|
3322
3760
|
now: this.now(),
|
|
3323
3761
|
}, currentPosture);
|
|
3324
3762
|
}
|
|
@@ -3860,13 +4298,13 @@ function nextForTask(task) {
|
|
|
3860
4298
|
awaiting_execution_authorization: 'task authorize',
|
|
3861
4299
|
ready: task.workspaceOwner ? (stepsComplete ? 'task submit' : 'task run') : 'task start',
|
|
3862
4300
|
in_progress: activeStep ? 'task step-complete' : stepsComplete ? 'task submit' : 'task run',
|
|
3863
|
-
validating: task.review.status === 'passed' ? 'task result-set' : 'task review-
|
|
4301
|
+
validating: task.review.status === 'passed' ? 'task result-set' : 'task review-launch',
|
|
3864
4302
|
needs_fix: task.steps.some((step) => step.status === 'failed') ? 'task run' : 'task plan-set',
|
|
3865
4303
|
awaiting_final_acceptance: 'task accept',
|
|
3866
4304
|
merging: 'task merge',
|
|
3867
4305
|
ready_to_merge: 'task merge-confirm',
|
|
3868
4306
|
merged: 'none',
|
|
3869
|
-
blocked: task.review.status === 'unverified' ? 'task review-
|
|
4307
|
+
blocked: task.review.status === 'unverified' ? 'task review-launch' : 'doctor',
|
|
3870
4308
|
cancelled: 'none',
|
|
3871
4309
|
};
|
|
3872
4310
|
const selectedStep = activeStep ?? failedStep ?? runnableStep;
|
|
@@ -3888,6 +4326,91 @@ function nextForTask(task) {
|
|
|
3888
4326
|
: {}),
|
|
3889
4327
|
};
|
|
3890
4328
|
}
|
|
4329
|
+
function gitMutationPolicyForAction(task, action) {
|
|
4330
|
+
const metadataWrites = ['.git/index', '.git/index.lock', '.git/HEAD', '.git/refs', '.git/logs'];
|
|
4331
|
+
switch (action) {
|
|
4332
|
+
case 'task start':
|
|
4333
|
+
return {
|
|
4334
|
+
permission: 'git-metadata-write',
|
|
4335
|
+
requiredBeforeFirstCall: true,
|
|
4336
|
+
boundedToTransition: true,
|
|
4337
|
+
transition: 'task start',
|
|
4338
|
+
appliesWhen: { workspaceOwner: 'local' },
|
|
4339
|
+
metadataWrites,
|
|
4340
|
+
};
|
|
4341
|
+
case 'task step-complete':
|
|
4342
|
+
return {
|
|
4343
|
+
permission: 'git-metadata-write',
|
|
4344
|
+
requiredBeforeFirstCall: true,
|
|
4345
|
+
boundedToTransition: true,
|
|
4346
|
+
transition: 'task step-complete',
|
|
4347
|
+
metadataWrites,
|
|
4348
|
+
};
|
|
4349
|
+
case 'task merge':
|
|
4350
|
+
if (task.workspaceOwner !== 'local')
|
|
4351
|
+
return null;
|
|
4352
|
+
return {
|
|
4353
|
+
permission: 'git-metadata-write',
|
|
4354
|
+
requiredBeforeFirstCall: true,
|
|
4355
|
+
boundedToTransition: true,
|
|
4356
|
+
transition: 'task merge',
|
|
4357
|
+
metadataWrites,
|
|
4358
|
+
};
|
|
4359
|
+
case 'task sync-base':
|
|
4360
|
+
return {
|
|
4361
|
+
permission: 'git-metadata-write',
|
|
4362
|
+
requiredBeforeFirstCall: true,
|
|
4363
|
+
boundedToTransition: true,
|
|
4364
|
+
transition: 'task sync-base',
|
|
4365
|
+
metadataWrites,
|
|
4366
|
+
};
|
|
4367
|
+
default:
|
|
4368
|
+
return null;
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4371
|
+
function writerTokenContractForAction(action, claimedC1Posture = false) {
|
|
4372
|
+
const alwaysLeaseBound = ['task step-complete', 'task submit', 'task merge', 'task merge-confirm'];
|
|
4373
|
+
const claimedC1LeaseBound = [
|
|
4374
|
+
'task run',
|
|
4375
|
+
'task step-review',
|
|
4376
|
+
'task review-record',
|
|
4377
|
+
'task review-launch',
|
|
4378
|
+
'task corrective-decision-recover',
|
|
4379
|
+
'task remediation-mode-recover',
|
|
4380
|
+
'task corrective-decision',
|
|
4381
|
+
'task corrective-yield',
|
|
4382
|
+
'task handoff',
|
|
4383
|
+
'task handoff-prepare',
|
|
4384
|
+
'task handback',
|
|
4385
|
+
'task handback-create',
|
|
4386
|
+
];
|
|
4387
|
+
if (!alwaysLeaseBound.includes(action) && !(claimedC1Posture && claimedC1LeaseBound.includes(action))) {
|
|
4388
|
+
return null;
|
|
4389
|
+
}
|
|
4390
|
+
return {
|
|
4391
|
+
required: true,
|
|
4392
|
+
option: '--writer-token',
|
|
4393
|
+
source: 'active-writer-lease',
|
|
4394
|
+
sensitive: true,
|
|
4395
|
+
};
|
|
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
|
+
}
|
|
3891
4414
|
function knowledgeSummary(map) {
|
|
3892
4415
|
return {
|
|
3893
4416
|
revision: map.revision,
|