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.
- package/README.md +35 -2
- 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 +2 -0
- package/dist/src/alpha6/milestone.js +15 -0
- 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 +188 -65
- 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/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 +34 -22
- package/dist/src/workflow.js +486 -141
- package/dist/src/workflow.js.map +1 -1
- package/docs/lifecycle/state-machine-stabilization.md +641 -0
- package/docs/release.md +4 -2
- package/docs/updating-existing-project.md +9 -0
- package/package.json +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 { 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
|
-
|
|
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,25 +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);
|
|
2157
|
-
const milestones = this.listMilestonesCurrent(identity.projectId, adoption.currentPosture);
|
|
2320
|
+
const snapshot = this.observationSnapshot(repository);
|
|
2158
2321
|
return {
|
|
2159
|
-
projectId: identity.projectId,
|
|
2160
|
-
discoveries:
|
|
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:
|
|
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
|
|
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
|
|
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
|
|
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
|
|
2201
|
-
const tasks =
|
|
2202
|
-
const milestones =
|
|
2203
|
-
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);
|
|
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 =
|
|
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(
|
|
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:
|
|
2258
|
-
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
|
|
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
|
|
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
|
|
2553
|
+
const { identity } = this.#mutationContext(repository);
|
|
2400
2554
|
return this.store.readKnowledgeMap(identity.projectId);
|
|
2401
2555
|
}
|
|
2402
2556
|
knowledgeMapStatus(repository) {
|
|
2403
|
-
const { identity } = this
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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) => ({
|
|
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 &&
|
|
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
|
|
3419
|
-
|
|
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,
|
|
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,
|