@cassiomc1/forgeloop 1.2.1 → 1.2.2
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/.cursor/rules/project-loop.mdc +1 -1
- package/.github/copilot-instructions.md +1 -1
- package/AGENTS.md +1 -1
- package/CLAUDE.md +1 -1
- package/DOCS_INDEX.md +3 -0
- package/ENG/design-code-eng.md +59 -0
- package/ENG/premium-sites-studio-eng.md +28 -0
- package/LOOP_ENGINEERING.md +23 -0
- package/LOOP_SYSTEM_DESIGN.md +9 -5
- package/ORCHESTRATOR_INTEGRATION.md +37 -4
- package/PROTOCOL_INTEGRATION.md +13 -0
- package/README.md +34 -2
- package/TERMINOLOGY.md +10 -0
- package/THIRD_PARTY_NOTICES.md +34 -0
- package/THREAT_MODEL.md +12 -1
- package/docs/ARTIFACT_REFERENCE.md +150 -0
- package/docs/CLI_REFERENCE.md +263 -30
- package/docs/CROSS_HARNESS_CONTINUITY.md +1 -0
- package/docs/DOCUMENTATION_GUIDE.md +41 -4
- package/docs/GETTING_STARTED.md +9 -4
- package/docs/RECIPES.md +31 -1
- package/docs/TROUBLESHOOTING.md +191 -0
- package/package.json +1 -1
- package/schemas/policy-baseline.schema.json +26 -0
- package/schemas/policy-discovery.schema.json +45 -0
- package/schemas/policy-lock.schema.json +16 -0
- package/schemas/policy-rules.schema.json +48 -0
- package/schemas/policy-snapshot.schema.json +16 -0
- package/src/cli.js +69 -1
- package/src/commands/baseline.js +120 -0
- package/src/commands/init.js +304 -6
- package/src/commands/policy-diff.js +51 -0
- package/src/commands/policy-discover.js +42 -0
- package/src/commands/policy-status.js +33 -0
- package/src/commands/profile-interview.js +50 -0
- package/src/commands/reconcile-closure.js +49 -0
- package/src/commands/rule-verify.js +36 -0
- package/src/commands/validate-receipt.js +38 -3
- package/src/core/artifact-registry.js +60 -0
- package/src/core/audit.js +24 -0
- package/src/core/cli-command-definitions.js +114 -7
- package/src/core/cli-metadata.js +1 -1
- package/src/core/completion-artifacts.js +29 -3
- package/src/core/completion.js +101 -10
- package/src/core/error-codes.js +227 -0
- package/src/core/events.js +22 -0
- package/src/core/execution-prerequisites.js +38 -20
- package/src/core/execution.js +20 -3
- package/src/core/native-adapters.js +14 -4
- package/src/core/next-action-model.js +9 -0
- package/src/core/next-action.js +128 -82
- package/src/core/policy-adapters.js +276 -0
- package/src/core/policy-baseline.js +144 -0
- package/src/core/policy-diff.js +133 -0
- package/src/core/policy-discovery.js +225 -0
- package/src/core/policy-engine.js +533 -0
- package/src/core/policy-mutation.js +139 -0
- package/src/core/preflight-consistency.js +23 -15
- package/src/core/preflight.js +65 -1
- package/src/core/reconcile-closure.js +173 -0
- package/src/core/schema-validation.js +6 -0
- package/src/core/task-context.js +11 -0
- package/src/core/task-discovery.js +67 -1
- package/src/core/task-paths.js +9 -0
- package/src/core/templates.js +5 -0
package/src/core/next-action.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ARTIFACT_PATHS, readJsonArtifact } from "./artifacts.js";
|
|
2
|
+
import { taskArtifactPath } from "./task-paths.js";
|
|
2
3
|
import { completionIdentityErrors, evaluateCompletion } from "./completion.js";
|
|
3
4
|
import { readContract } from "./contract.js";
|
|
4
5
|
import { evaluatePreflight, validatePersistedPreflight, validateReadyProtocolConsistency } from "./preflight.js";
|
|
@@ -39,72 +40,106 @@ import { readEvents } from "./events.js";
|
|
|
39
40
|
|
|
40
41
|
export { NEXT_ACTIONS } from "./next-action-model.js";
|
|
41
42
|
|
|
43
|
+
export function policyRecoveryAction(errors = []) {
|
|
44
|
+
if (errors.some((e) => e.code === "E_POLICY_WEAKENING" || e.code === "E_POLICY_LOCK_MISMATCH")) {
|
|
45
|
+
return NEXT_ACTIONS.RESTORE_POLICY;
|
|
46
|
+
}
|
|
47
|
+
if (errors.some((e) => e.code === "E_CHECK_MUTATION_EXECUTION_ERROR" || e.code === "E_CHECK_MUTATION_NOT_DETECTED")) {
|
|
48
|
+
return NEXT_ACTIONS.REPAIR_CHECKER;
|
|
49
|
+
}
|
|
50
|
+
if (errors.some((e) => e.code === "E_POLICY_INVALID" || e.code === "E_POLICY_EVALUATION_FAILED")) {
|
|
51
|
+
return NEXT_ACTIONS.REPAIR_POLICY;
|
|
52
|
+
}
|
|
53
|
+
if (errors.some((e) => e.code === "E_POLICY_DRIFT_UNKNOWN")) {
|
|
54
|
+
return NEXT_ACTIONS.REVERIFY_AFTER_POLICY_CHANGE;
|
|
55
|
+
}
|
|
56
|
+
if (errors.some((e) => e.code === "E_BASELINE_EXPANSION")) {
|
|
57
|
+
return NEXT_ACTIONS.RESTORE_BASELINE;
|
|
58
|
+
}
|
|
59
|
+
if (errors.some((e) => e.code === "E_BASELINE_RECORD_DURING_ACTIVE_TASK")) {
|
|
60
|
+
return NEXT_ACTIONS.CONTINUE_WITH_EXISTING_BASELINE;
|
|
61
|
+
}
|
|
62
|
+
if (errors.some((e) => e.code === "E_CHECK_INERT")) {
|
|
63
|
+
return NEXT_ACTIONS.RESOLVE_INERT_CHECK;
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
42
68
|
async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
43
69
|
const normalized = typeof targetOrOptions === "string"
|
|
44
70
|
? { target: targetOrOptions, packageRoot: packageRootOption }
|
|
45
71
|
: targetOrOptions;
|
|
46
|
-
const { target, packageRoot, authorityContext, runtimeContext } = normalized ?? {};
|
|
72
|
+
const { target, packageRoot, taskId: explicitTaskId, authorityContext, runtimeContext } = normalized ?? {};
|
|
73
|
+
|
|
74
|
+
const stateRel = explicitTaskId ? taskArtifactPath(explicitTaskId, "state") : ARTIFACT_PATHS.state;
|
|
75
|
+
const preflightRel = explicitTaskId ? taskArtifactPath(explicitTaskId, "preflight") : ARTIFACT_PATHS.preflight;
|
|
76
|
+
const contractRel = explicitTaskId ? taskArtifactPath(explicitTaskId, "contract") : ARTIFACT_PATHS.contract;
|
|
77
|
+
const routeRel = explicitTaskId ? taskArtifactPath(explicitTaskId, "route") : ARTIFACT_PATHS.route;
|
|
78
|
+
const receiptRel = explicitTaskId ? taskArtifactPath(explicitTaskId, "receipt") : ARTIFACT_PATHS.receipt;
|
|
79
|
+
const eventsRel = explicitTaskId ? taskArtifactPath(explicitTaskId, "events") : ARTIFACT_PATHS.events;
|
|
80
|
+
|
|
47
81
|
const workState = await loadArtifact(
|
|
48
|
-
() => readWorkState(target, packageRoot),
|
|
49
|
-
|
|
82
|
+
() => readWorkState(target, { packageRoot, taskId: explicitTaskId }),
|
|
83
|
+
stateRel,
|
|
50
84
|
);
|
|
51
85
|
if (workState.error) {
|
|
52
86
|
return decision(
|
|
53
87
|
{},
|
|
54
88
|
NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
55
|
-
artifactError("WORK_STATE_INVALID", workState.error.message, [
|
|
56
|
-
[
|
|
89
|
+
artifactError("WORK_STATE_INVALID", workState.error.message, [stateRel]),
|
|
90
|
+
[stateRel],
|
|
57
91
|
workState.missingArtifacts,
|
|
58
92
|
);
|
|
59
93
|
}
|
|
60
94
|
if (!workState.value) {
|
|
61
95
|
const persistedPreflight = await loadArtifact(
|
|
62
|
-
() => readJsonArtifact(target,
|
|
63
|
-
|
|
96
|
+
() => readJsonArtifact(target, preflightRel, "preflight", packageRoot),
|
|
97
|
+
preflightRel,
|
|
64
98
|
);
|
|
65
99
|
if (!persistedPreflight.error && persistedPreflight.value?.value?.status === "READY") {
|
|
66
100
|
try {
|
|
67
101
|
const consistencyErrors = await validateReadyProtocolConsistency({
|
|
68
102
|
target,
|
|
69
103
|
packageRoot,
|
|
104
|
+
taskId: explicitTaskId,
|
|
70
105
|
persisted: persistedPreflight.value.value,
|
|
71
106
|
});
|
|
72
107
|
if (consistencyErrors.length > 0) {
|
|
73
108
|
return result({
|
|
74
|
-
taskId: persistedPreflight.value.value.taskId ?? "unknown",
|
|
109
|
+
taskId: persistedPreflight.value.value.taskId ?? explicitTaskId ?? "unknown",
|
|
75
110
|
currentPhase: "ROUTED",
|
|
76
111
|
nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
77
112
|
reasons: consistencyErrors,
|
|
78
113
|
requiredArtifacts: [
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
114
|
+
stateRel,
|
|
115
|
+
contractRel,
|
|
116
|
+
routeRel,
|
|
117
|
+
preflightRel,
|
|
118
|
+
eventsRel,
|
|
84
119
|
],
|
|
85
|
-
missingArtifacts: [
|
|
120
|
+
missingArtifacts: [stateRel],
|
|
86
121
|
});
|
|
87
122
|
}
|
|
88
123
|
} catch (error) {
|
|
89
124
|
return result({
|
|
90
|
-
taskId: persistedPreflight.value.value.taskId ?? "unknown",
|
|
125
|
+
taskId: persistedPreflight.value.value.taskId ?? explicitTaskId ?? "unknown",
|
|
91
126
|
currentPhase: "ROUTED",
|
|
92
127
|
nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
93
128
|
reasons: [artifactError(
|
|
94
129
|
error.code ?? "E_PREFLIGHT_READY_INCONSISTENT",
|
|
95
130
|
error.message,
|
|
96
|
-
error.artifacts ?? [
|
|
131
|
+
error.artifacts ?? [preflightRel, eventsRel],
|
|
97
132
|
)],
|
|
98
|
-
requiredArtifacts: [
|
|
133
|
+
requiredArtifacts: [preflightRel, eventsRel],
|
|
99
134
|
});
|
|
100
135
|
}
|
|
101
136
|
}
|
|
102
137
|
return decision(
|
|
103
138
|
{},
|
|
104
139
|
NEXT_ACTIONS.DISCOVER,
|
|
105
|
-
artifactError("WORK_STATE_ABSENT", "No work-state checkpoint is present", [
|
|
106
|
-
[
|
|
107
|
-
[
|
|
140
|
+
artifactError("WORK_STATE_ABSENT", "No work-state checkpoint is present", [stateRel]),
|
|
141
|
+
[stateRel],
|
|
142
|
+
[stateRel],
|
|
108
143
|
);
|
|
109
144
|
}
|
|
110
145
|
|
|
@@ -115,7 +150,7 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
115
150
|
context,
|
|
116
151
|
NEXT_ACTIONS.DISCOVER,
|
|
117
152
|
artifactError("PHASE_RECEIVED", "Discovery has not started"),
|
|
118
|
-
[
|
|
153
|
+
[stateRel],
|
|
119
154
|
);
|
|
120
155
|
}
|
|
121
156
|
if (state.phase === "DISCOVERING") {
|
|
@@ -123,14 +158,14 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
123
158
|
context,
|
|
124
159
|
NEXT_ACTIONS.CREATE_CONTRACT,
|
|
125
160
|
artifactError("PHASE_DISCOVERING", "Create and validate the task contract"),
|
|
126
|
-
[
|
|
161
|
+
[stateRel],
|
|
127
162
|
);
|
|
128
163
|
}
|
|
129
164
|
const contractResult = await loadArtifact(
|
|
130
|
-
() => readContract(target, packageRoot),
|
|
131
|
-
|
|
165
|
+
() => readContract(target, packageRoot, { taskId: explicitTaskId }),
|
|
166
|
+
contractRel,
|
|
132
167
|
);
|
|
133
|
-
const contractArtifacts = [
|
|
168
|
+
const contractArtifacts = [stateRel, contractRel];
|
|
134
169
|
|
|
135
170
|
if (contractResult.error) {
|
|
136
171
|
return result({
|
|
@@ -159,7 +194,7 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
159
194
|
const freshness = await classifyLoadedWorkState({
|
|
160
195
|
target,
|
|
161
196
|
state,
|
|
162
|
-
contractFile:
|
|
197
|
+
contractFile: contractRel,
|
|
163
198
|
});
|
|
164
199
|
if (freshness.status === "REVALIDATION_REQUIRED") {
|
|
165
200
|
return result({
|
|
@@ -167,8 +202,8 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
167
202
|
nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
168
203
|
reasons: freshnessReasons(state, freshness),
|
|
169
204
|
requiredArtifacts: uniqueSorted([
|
|
170
|
-
|
|
171
|
-
|
|
205
|
+
stateRel,
|
|
206
|
+
contractRel,
|
|
172
207
|
...(state.requiredArtifacts?.map((artifact) => artifact.path) ?? []),
|
|
173
208
|
]),
|
|
174
209
|
});
|
|
@@ -179,14 +214,14 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
179
214
|
NEXT_ACTIONS.ROUTE,
|
|
180
215
|
artifactError("PHASE_CONTRACT_READY", "Persist deterministic routing for the validated contract"),
|
|
181
216
|
contractArtifacts,
|
|
182
|
-
[
|
|
217
|
+
[routeRel],
|
|
183
218
|
);
|
|
184
219
|
}
|
|
185
220
|
const routeResult = await loadArtifact(
|
|
186
|
-
() => readPersistedRoute(target, packageRoot),
|
|
187
|
-
|
|
221
|
+
() => readPersistedRoute(target, packageRoot, { taskId: explicitTaskId }),
|
|
222
|
+
routeRel,
|
|
188
223
|
);
|
|
189
|
-
const requiredArtifacts = [...contractArtifacts,
|
|
224
|
+
const requiredArtifacts = [...contractArtifacts, routeRel];
|
|
190
225
|
const missingArtifacts = [...routeResult.missingArtifacts];
|
|
191
226
|
|
|
192
227
|
if (routeResult.error) {
|
|
@@ -218,7 +253,7 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
218
253
|
let executionPrerequisites = null;
|
|
219
254
|
if (phaseNeedsChronology) {
|
|
220
255
|
try {
|
|
221
|
-
executionPrerequisites = await evaluateStartExecutionPrerequisites({ target, state, packageRoot });
|
|
256
|
+
executionPrerequisites = await evaluateStartExecutionPrerequisites({ target, state, packageRoot, taskId: explicitTaskId });
|
|
222
257
|
} catch (error) {
|
|
223
258
|
return result({
|
|
224
259
|
...context,
|
|
@@ -226,14 +261,14 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
226
261
|
reasons: [artifactError(
|
|
227
262
|
error?.code ?? "E_PHASE_CHRONOLOGY_INVALID",
|
|
228
263
|
`Unable to evaluate post-execution prerequisites: ${error?.message ?? String(error)}`,
|
|
229
|
-
Array.isArray(error?.artifacts) ? error.artifacts : [
|
|
264
|
+
Array.isArray(error?.artifacts) ? error.artifacts : [eventsRel],
|
|
230
265
|
)],
|
|
231
266
|
requiredArtifacts: [
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
267
|
+
stateRel,
|
|
268
|
+
contractRel,
|
|
269
|
+
routeRel,
|
|
270
|
+
preflightRel,
|
|
271
|
+
eventsRel,
|
|
237
272
|
],
|
|
238
273
|
});
|
|
239
274
|
}
|
|
@@ -247,15 +282,15 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
247
282
|
}
|
|
248
283
|
}
|
|
249
284
|
|
|
250
|
-
const preflight = executionPrerequisites?.preflight ?? await evaluatePreflight({ target, packageRoot });
|
|
285
|
+
const preflight = executionPrerequisites?.preflight ?? await evaluatePreflight({ target, packageRoot, taskId: explicitTaskId });
|
|
251
286
|
const preflightArtifact = phaseNeedsChronology
|
|
252
287
|
? null
|
|
253
288
|
: await loadArtifact(
|
|
254
|
-
() => readJsonArtifact(target,
|
|
255
|
-
|
|
289
|
+
() => readJsonArtifact(target, preflightRel, "preflight", packageRoot),
|
|
290
|
+
preflightRel,
|
|
256
291
|
);
|
|
257
292
|
const missingGates = preflight.requiredGates.filter((gate) => !preflight.satisfiedGates.includes(gate));
|
|
258
|
-
const preflightArtifacts = [...requiredArtifacts,
|
|
293
|
+
const preflightArtifacts = [...requiredArtifacts, preflightRel];
|
|
259
294
|
const persistedPreflightErrors = phaseNeedsChronology
|
|
260
295
|
? []
|
|
261
296
|
: validatePersistedPreflight(preflightArtifact.value?.value, preflight);
|
|
@@ -290,7 +325,7 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
290
325
|
return decision(context, NEXT_ACTIONS.PLAN, artifactError("PHASE_DESIGNING", "Required gates are ready for planning"));
|
|
291
326
|
}
|
|
292
327
|
if (state.phase === "PLANNED") {
|
|
293
|
-
const prerequisites = await evaluateStartExecutionPrerequisites({ target, state, packageRoot });
|
|
328
|
+
const prerequisites = await evaluateStartExecutionPrerequisites({ target, state, packageRoot, taskId: explicitTaskId });
|
|
294
329
|
if (prerequisites.errors.length > 0) {
|
|
295
330
|
const preflightOnly = prerequisites.errors.every((error) => error.code.startsWith("E_PREFLIGHT_")
|
|
296
331
|
|| (error.code === "E_PHASE_CHRONOLOGY_INVALID"
|
|
@@ -350,7 +385,7 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
350
385
|
return decision(
|
|
351
386
|
context,
|
|
352
387
|
NEXT_ACTIONS.DIAGNOSE,
|
|
353
|
-
artifactError("E_CHECK_FAILED", `Observed check failed: ${failed.check.id}`, [
|
|
388
|
+
artifactError("E_CHECK_FAILED", `Observed check failed: ${failed.check.id}`, [stateRel]),
|
|
354
389
|
);
|
|
355
390
|
}
|
|
356
391
|
const blocked = authoritative.find(({ check }) => check?.status === "blocked");
|
|
@@ -358,28 +393,28 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
358
393
|
return decision(
|
|
359
394
|
context,
|
|
360
395
|
NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
361
|
-
artifactError("E_CHECK_BLOCKED", `Observed check is blocked: ${blocked.check.id}`, [
|
|
396
|
+
artifactError("E_CHECK_BLOCKED", `Observed check is blocked: ${blocked.check.id}`, [stateRel]),
|
|
362
397
|
);
|
|
363
398
|
}
|
|
364
399
|
const receipt = await loadArtifact(
|
|
365
|
-
() => readJsonArtifact(target,
|
|
366
|
-
|
|
400
|
+
() => readJsonArtifact(target, receiptRel, "execution-receipt", packageRoot),
|
|
401
|
+
receiptRel,
|
|
367
402
|
);
|
|
368
403
|
if (receipt.error) {
|
|
369
404
|
if (receipt.error.code === "ARTIFACT_MISSING") {
|
|
370
405
|
return decision(
|
|
371
406
|
context,
|
|
372
407
|
NEXT_ACTIONS.PREPARE_COMPLETION,
|
|
373
|
-
artifactError("E_RECEIPT_MISSING", "Prepare the execution receipt before recording verification checks", [
|
|
374
|
-
[...requiredArtifacts,
|
|
408
|
+
artifactError("E_RECEIPT_MISSING", "Prepare the execution receipt before recording verification checks", [receiptRel]),
|
|
409
|
+
[...requiredArtifacts, receiptRel],
|
|
375
410
|
receipt.missingArtifacts,
|
|
376
411
|
);
|
|
377
412
|
}
|
|
378
413
|
return decision(
|
|
379
414
|
context,
|
|
380
415
|
NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
381
|
-
artifactError("E_RECEIPT_INVALID", `Repair or remove the invalid execution receipt before continuing: ${receipt.error.message}`, [
|
|
382
|
-
[...requiredArtifacts,
|
|
416
|
+
artifactError("E_RECEIPT_INVALID", `Repair or remove the invalid execution receipt before continuing: ${receipt.error.message}`, [receiptRel]),
|
|
417
|
+
[...requiredArtifacts, receiptRel],
|
|
383
418
|
);
|
|
384
419
|
}
|
|
385
420
|
try {
|
|
@@ -393,8 +428,8 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
393
428
|
return decision(
|
|
394
429
|
context,
|
|
395
430
|
NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
396
|
-
artifactError("E_RECEIPT_INVALID", `Repair or remove the invalid execution receipt before continuing: ${error.message}`, [
|
|
397
|
-
[...requiredArtifacts,
|
|
431
|
+
artifactError("E_RECEIPT_INVALID", `Repair or remove the invalid execution receipt before continuing: ${error.message}`, [receiptRel]),
|
|
432
|
+
[...requiredArtifacts, receiptRel],
|
|
398
433
|
);
|
|
399
434
|
}
|
|
400
435
|
const readiness = evaluateRequiredEvidence({
|
|
@@ -427,15 +462,15 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
427
462
|
return decision(
|
|
428
463
|
context,
|
|
429
464
|
NEXT_ACTIONS.PREPARE_COMPLETION,
|
|
430
|
-
artifactError("E_RECEIPT_STATE_MISMATCH", "Run forgeloop prepare-completion to refresh the execution receipt with current state", [
|
|
431
|
-
[...requiredArtifacts,
|
|
465
|
+
artifactError("E_RECEIPT_STATE_MISMATCH", "Run forgeloop prepare-completion to refresh the execution receipt with current state", [receiptRel]),
|
|
466
|
+
[...requiredArtifacts, receiptRel],
|
|
432
467
|
);
|
|
433
468
|
}
|
|
434
469
|
return result({
|
|
435
470
|
...context,
|
|
436
471
|
nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
437
472
|
reasons: receiptRelationships,
|
|
438
|
-
requiredArtifacts: [...requiredArtifacts,
|
|
473
|
+
requiredArtifacts: [...requiredArtifacts, receiptRel],
|
|
439
474
|
});
|
|
440
475
|
}
|
|
441
476
|
if (readiness.ready) {
|
|
@@ -455,7 +490,7 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
455
490
|
? "E_EVIDENCE_PARTIAL"
|
|
456
491
|
: "E_EVIDENCE_REQUIRED",
|
|
457
492
|
"Run the required check and record observed evidence through the structured command specification.",
|
|
458
|
-
[
|
|
493
|
+
[stateRel],
|
|
459
494
|
)),
|
|
460
495
|
requiredArtifacts: requiredArtifacts,
|
|
461
496
|
});
|
|
@@ -472,11 +507,11 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
472
507
|
artifactError(
|
|
473
508
|
"E_DIAGNOSIS_REQUIRED",
|
|
474
509
|
"Current correction cycle has no append-only diagnosis record.",
|
|
475
|
-
[
|
|
510
|
+
[eventsRel],
|
|
476
511
|
),
|
|
477
512
|
],
|
|
478
513
|
commandSpecs: [recordDiagnosisCommandSpec()],
|
|
479
|
-
requiredArtifacts: [...requiredArtifacts,
|
|
514
|
+
requiredArtifacts: [...requiredArtifacts, eventsRel],
|
|
480
515
|
});
|
|
481
516
|
}
|
|
482
517
|
const progress = evaluateProgress({ state, events: ledger.events });
|
|
@@ -488,7 +523,7 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
488
523
|
artifactError(
|
|
489
524
|
"E_PROGRESS_STALLED",
|
|
490
525
|
"The current correction strategy has no new diagnostic information.",
|
|
491
|
-
[
|
|
526
|
+
[stateRel, eventsRel],
|
|
492
527
|
),
|
|
493
528
|
],
|
|
494
529
|
progress,
|
|
@@ -540,10 +575,10 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
540
575
|
let recoveryAuthorized = false;
|
|
541
576
|
if (state.lastCompletionAttempt?.status === "REJECTED") {
|
|
542
577
|
try {
|
|
543
|
-
const ledger = await validateEventLedger(target, packageRoot);
|
|
578
|
+
const ledger = await validateEventLedger(target, packageRoot, { taskId: explicitTaskId, eventsPath: eventsRel });
|
|
544
579
|
let currentReceipt = null;
|
|
545
580
|
try {
|
|
546
|
-
const receiptArtifact = await readJsonArtifact(target,
|
|
581
|
+
const receiptArtifact = await readJsonArtifact(target, receiptRel, "execution-receipt", packageRoot);
|
|
547
582
|
currentReceipt = receiptArtifact?.value;
|
|
548
583
|
} catch {}
|
|
549
584
|
const recoveryAuth = validateCompletionRecoveryAuthorization({
|
|
@@ -570,22 +605,22 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
570
605
|
? "E_EVIDENCE_REQUIRED"
|
|
571
606
|
: item.reasonCode ?? "E_EVIDENCE_PARTIAL",
|
|
572
607
|
`Evidence is not ready: ${item.text}`,
|
|
573
|
-
[
|
|
608
|
+
[stateRel],
|
|
574
609
|
)),
|
|
575
610
|
requiredArtifacts,
|
|
576
611
|
});
|
|
577
612
|
}
|
|
578
613
|
const receipt = await loadArtifact(
|
|
579
|
-
() => readJsonArtifact(target,
|
|
580
|
-
|
|
614
|
+
() => readJsonArtifact(target, receiptRel, "execution-receipt", packageRoot),
|
|
615
|
+
receiptRel,
|
|
581
616
|
);
|
|
582
617
|
if (receipt.error) {
|
|
583
618
|
if (receipt.error.code !== "ARTIFACT_MISSING") {
|
|
584
619
|
return decision(
|
|
585
620
|
context,
|
|
586
621
|
NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
587
|
-
artifactError("E_RECEIPT_INVALID", `Repair or remove the invalid execution receipt before continuing: ${receipt.error.message}`, [
|
|
588
|
-
[...requiredArtifacts,
|
|
622
|
+
artifactError("E_RECEIPT_INVALID", `Repair or remove the invalid execution receipt before continuing: ${receipt.error.message}`, [receiptRel]),
|
|
623
|
+
[...requiredArtifacts, receiptRel],
|
|
589
624
|
);
|
|
590
625
|
}
|
|
591
626
|
return decision(
|
|
@@ -594,9 +629,9 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
594
629
|
artifactError(
|
|
595
630
|
receipt.error.code === "ARTIFACT_MISSING" ? "E_RECEIPT_MISSING" : "E_RECEIPT_INVALID",
|
|
596
631
|
receipt.error.message,
|
|
597
|
-
[
|
|
632
|
+
[receiptRel],
|
|
598
633
|
),
|
|
599
|
-
[...requiredArtifacts,
|
|
634
|
+
[...requiredArtifacts, receiptRel],
|
|
600
635
|
receipt.missingArtifacts,
|
|
601
636
|
);
|
|
602
637
|
}
|
|
@@ -611,8 +646,8 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
611
646
|
return decision(
|
|
612
647
|
context,
|
|
613
648
|
NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
614
|
-
artifactError("E_RECEIPT_INVALID", `Repair or remove the invalid execution receipt before continuing: ${error.message}`, [
|
|
615
|
-
[...requiredArtifacts,
|
|
649
|
+
artifactError("E_RECEIPT_INVALID", `Repair or remove the invalid execution receipt before continuing: ${error.message}`, [receiptRel]),
|
|
650
|
+
[...requiredArtifacts, receiptRel],
|
|
616
651
|
);
|
|
617
652
|
}
|
|
618
653
|
const receiptRelationships = completionRelationshipErrors({
|
|
@@ -637,18 +672,19 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
637
672
|
return decision(
|
|
638
673
|
context,
|
|
639
674
|
NEXT_ACTIONS.PREPARE_COMPLETION,
|
|
640
|
-
artifactError("E_RECEIPT_STATE_MISMATCH", "Run forgeloop prepare-completion to refresh the execution receipt with current state", [
|
|
641
|
-
[...requiredArtifacts,
|
|
675
|
+
artifactError("E_RECEIPT_STATE_MISMATCH", "Run forgeloop prepare-completion to refresh the execution receipt with current state", [receiptRel]),
|
|
676
|
+
[...requiredArtifacts, receiptRel],
|
|
642
677
|
);
|
|
643
678
|
}
|
|
644
679
|
return result({
|
|
645
680
|
...context,
|
|
646
681
|
nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
647
682
|
reasons: receiptRelationships,
|
|
648
|
-
requiredArtifacts: [...requiredArtifacts,
|
|
683
|
+
requiredArtifacts: [...requiredArtifacts, receiptRel],
|
|
649
684
|
});
|
|
650
685
|
}
|
|
651
|
-
|
|
686
|
+
|
|
687
|
+
const completion = await evaluateCompletion({ target, packageRoot, taskId: explicitTaskId, authorityContext, runtimeContext });
|
|
652
688
|
if (completion.status !== "VALID") {
|
|
653
689
|
const terminalPendingErrors = completion.errors.filter((err) => (
|
|
654
690
|
err.code === "E_PUBLICATION_REQUIREMENT_PENDING" || err.code === "E_PRODUCTION_REQUIREMENT_PENDING"
|
|
@@ -667,28 +703,38 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
667
703
|
commands: ["forgeloop record-terminal-result"],
|
|
668
704
|
commandSpecs: terminalPendingReqs.map(recordTerminalResultCommandSpec),
|
|
669
705
|
reasons: completion.errors,
|
|
670
|
-
requiredArtifacts: [...requiredArtifacts,
|
|
706
|
+
requiredArtifacts: [...requiredArtifacts, receiptRel, eventsRel],
|
|
707
|
+
});
|
|
708
|
+
}
|
|
709
|
+
const policyAction = policyRecoveryAction(completion.errors);
|
|
710
|
+
if (policyAction) {
|
|
711
|
+
return result({
|
|
712
|
+
...context,
|
|
713
|
+
nextAction: policyAction,
|
|
714
|
+
reasons: completion.errors,
|
|
715
|
+
requiredArtifacts: [...requiredArtifacts, receiptRel, eventsRel],
|
|
671
716
|
});
|
|
672
717
|
}
|
|
673
718
|
return result({
|
|
674
719
|
...context,
|
|
675
720
|
nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
676
721
|
reasons: completion.errors,
|
|
677
|
-
requiredArtifacts: [...requiredArtifacts,
|
|
722
|
+
requiredArtifacts: [...requiredArtifacts, receiptRel, eventsRel],
|
|
678
723
|
});
|
|
679
724
|
}
|
|
680
725
|
return decision(context, NEXT_ACTIONS.RUN_COMPLETE, artifactError("COMPLETION_READY", "Completion artifacts and cross-artifact validation are valid"));
|
|
681
726
|
}
|
|
682
727
|
if (state.phase === "COMPLETE") {
|
|
683
|
-
const completion = await evaluateCompletion({ target, packageRoot, authorityContext, runtimeContext });
|
|
728
|
+
const completion = await evaluateCompletion({ target, packageRoot, taskId: explicitTaskId, authorityContext, runtimeContext });
|
|
684
729
|
if (completion.status === "VALID") {
|
|
685
730
|
return decision(context, NEXT_ACTIONS.NONE, artifactError("PHASE_COMPLETE", "Completion is validator-backed and terminal"));
|
|
686
731
|
}
|
|
732
|
+
const policyAction = policyRecoveryAction(completion.errors);
|
|
687
733
|
return result({
|
|
688
734
|
...context,
|
|
689
|
-
nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
735
|
+
nextAction: policyAction ?? NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
690
736
|
reasons: completion.errors,
|
|
691
|
-
requiredArtifacts: [...requiredArtifacts,
|
|
737
|
+
requiredArtifacts: [...requiredArtifacts, receiptRel, eventsRel],
|
|
692
738
|
});
|
|
693
739
|
}
|
|
694
740
|
if (state.phase === "BLOCKED") {
|
|
@@ -698,7 +744,7 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
|
|
|
698
744
|
reasons: state.blockers.map((blocker) => artifactError(
|
|
699
745
|
"WORK_STATE_BLOCKED",
|
|
700
746
|
blocker.reason ?? "Work state has a recorded blocker",
|
|
701
|
-
[
|
|
747
|
+
[stateRel],
|
|
702
748
|
)),
|
|
703
749
|
requiredArtifacts,
|
|
704
750
|
});
|