@cassiomc1/forgeloop 1.5.0 → 1.6.0
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/DOCS_INDEX.md +13 -8
- package/EXECUTION_STATE.md +20 -0
- package/LOOP_ENGINEERING.md +81 -0
- package/LOOP_SYSTEM_DESIGN.md +32 -0
- package/PROTOCOL_INTEGRATION.md +46 -0
- package/QUALITY_SCORECARD.md +2 -0
- package/README.md +31 -9
- package/THIRD_PARTY_NOTICES.md +15 -0
- package/THREAT_MODEL.md +39 -0
- package/docs/ARTIFACT_REFERENCE.md +140 -0
- package/docs/CLI_REFERENCE.md +294 -3
- package/docs/DIAGNOSTIC_MODEL.md +181 -0
- package/docs/DOCUMENTATION_GUIDE.md +22 -13
- package/docs/EXECUTION_TRACE.md +76 -0
- package/docs/MCP.md +33 -0
- package/docs/RECIPES.md +67 -0
- package/docs/TROUBLESHOOTING.md +106 -2
- package/docs/assets/diagrams/forgeloop-engineering-flow.html +13797 -0
- package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +37 -0
- package/docs/assets/diagrams/forgeloop-engineering-flow.svg +5002 -0
- package/docs/diagrams/README.md +55 -0
- package/docs/diagrams/forgeloop-engineering-flow.workflow.json +122 -0
- package/docs/diagrams/manifest.json +42 -0
- package/docs/diagrams/reviews/forgeloop-engineering-flow.review.json +20 -0
- package/package.json +8 -6
- package/schemas/action.schema.json +100 -0
- package/schemas/approval.schema.json +51 -0
- package/schemas/capability-policy.schema.json +41 -0
- package/schemas/diagnostic-case.schema.json +85 -0
- package/schemas/execution-receipt.schema.json +16 -0
- package/schemas/hypothesis-disposition.schema.json +16 -0
- package/schemas/intervention.schema.json +27 -0
- package/schemas/policy-lock.schema.json +1 -0
- package/schemas/policy-snapshot.schema.json +2 -0
- package/schemas/trajectory-evaluation.schema.json +64 -0
- package/schemas/trajectory-scenario.schema.json +42 -0
- package/src/cli.js +94 -0
- package/src/commands/action-authorize.js +41 -0
- package/src/commands/action-propose.js +10 -0
- package/src/commands/action-reconcile.js +10 -0
- package/src/commands/action-record.js +47 -0
- package/src/commands/action-show.js +10 -0
- package/src/commands/action-verify.js +10 -0
- package/src/commands/advance.js +7 -2
- package/src/commands/approval-request.js +64 -0
- package/src/commands/approval-resolve.js +10 -0
- package/src/commands/baseline.js +3 -3
- package/src/commands/eval.js +6 -0
- package/src/commands/history.js +18 -0
- package/src/commands/init.js +2 -2
- package/src/commands/inspect.js +49 -0
- package/src/commands/metrics.js +7 -0
- package/src/commands/next.js +8 -2
- package/src/commands/policy-discover.js +2 -2
- package/src/commands/record-diagnosis.js +37 -1
- package/src/commands/record-hypothesis-disposition.js +45 -0
- package/src/commands/record-intervention.js +35 -0
- package/src/commands/reflect.js +38 -0
- package/src/commands/report.js +9 -1
- package/src/commands/run-action.js +18 -0
- package/src/commands/trace.js +34 -0
- package/src/commands/validate-protocol.js +21 -13
- package/src/core/action-authorization.js +106 -0
- package/src/core/action-constants.js +86 -0
- package/src/core/action-execution.js +105 -0
- package/src/core/action-ledger-projection.js +302 -0
- package/src/core/action-model.js +581 -0
- package/src/core/action-readiness.js +141 -0
- package/src/core/action-reconciliation-policy.js +49 -0
- package/src/core/action-reconciliation.js +66 -0
- package/src/core/action-verification.js +111 -0
- package/src/core/actions.js +462 -0
- package/src/core/approvals.js +405 -0
- package/src/core/artifact-registry.js +48 -0
- package/src/core/audit.js +25 -0
- package/src/core/bundles.js +15 -0
- package/src/core/capability-policy.js +226 -0
- package/src/core/cli-command-definitions.js +210 -1
- package/src/core/command-executors.js +171 -15
- package/src/core/command-runtime.js +12 -1
- package/src/core/completion-artifacts.js +37 -12
- package/src/core/completion-recovery-rebind.js +194 -0
- package/src/core/completion.js +70 -0
- package/src/core/continuity-reconciliation.js +24 -5
- package/src/core/diagnostic-model.js +396 -0
- package/src/core/diagnostic-projection.js +51 -0
- package/src/core/diagnostic-record.js +360 -0
- package/src/core/error-codes.js +343 -0
- package/src/core/events.js +41 -1
- package/src/core/execution-prerequisites.js +4 -1
- package/src/core/execution.js +26 -188
- package/src/core/failure-signature.js +70 -0
- package/src/core/failure-surface.js +57 -0
- package/src/core/history.js +110 -0
- package/src/core/hypothesis-projection.js +85 -0
- package/src/core/information-gain-projection.js +283 -0
- package/src/core/information-gain.js +138 -0
- package/src/core/inspect.js +105 -7
- package/src/core/integration-invocation-policy.js +47 -0
- package/src/core/integration-resources.js +51 -0
- package/src/core/next-action-model.js +35 -1
- package/src/core/next-action.js +459 -3
- package/src/core/phase.js +40 -21
- package/src/core/policy-engine.js +113 -6
- package/src/core/preflight-consistency.js +31 -5
- package/src/core/preflight.js +19 -2
- package/src/core/prepared-execution.js +227 -0
- package/src/core/progress.js +41 -4
- package/src/core/protocol-info.js +48 -0
- package/src/core/protocol.js +14 -0
- package/src/core/receipt.js +1 -0
- package/src/core/reconcile-closure.js +15 -12
- package/src/core/reflection.js +305 -0
- package/src/core/resumability.js +57 -3
- package/src/core/schema-validation.js +8 -0
- package/src/core/strategy-analysis.js +97 -0
- package/src/core/task-paths.js +28 -0
- package/src/core/task-snapshot.js +53 -0
- package/src/core/templates.js +8 -0
- package/src/core/trace.js +548 -0
- package/src/core/trajectory-evaluation.js +71 -0
- package/src/core/trajectory-metrics.js +80 -0
- package/src/core/transaction.js +8 -0
- package/src/core/work-state.js +10 -5
- package/docs/assets/forgeloop-flow.svg +0 -1
- package/docs/forgeloop-flow.mmd +0 -51
|
@@ -25,9 +25,25 @@ import { runBundle } from "../commands/bundle.js";
|
|
|
25
25
|
import { runPrepareCompletion } from "../commands/prepare-completion.js";
|
|
26
26
|
import { runRecordCheck } from "../commands/record-check.js";
|
|
27
27
|
import { runCheck } from "../commands/run-check.js";
|
|
28
|
+
import { runAction } from "../commands/run-action.js";
|
|
29
|
+
import { runActionPropose } from "../commands/action-propose.js";
|
|
30
|
+
import { runActionRecord } from "../commands/action-record.js";
|
|
31
|
+
import { runActionShow } from "../commands/action-show.js";
|
|
32
|
+
import { runActionAuthorize } from "../commands/action-authorize.js";
|
|
33
|
+
import { runActionVerify } from "../commands/action-verify.js";
|
|
34
|
+
import { runActionReconcile } from "../commands/action-reconcile.js";
|
|
35
|
+
import { runMetrics } from "../commands/metrics.js";
|
|
36
|
+
import { runEval } from "../commands/eval.js";
|
|
37
|
+
import { runApprovalRequest } from "../commands/approval-request.js";
|
|
38
|
+
import { runApprovalResolve } from "../commands/approval-resolve.js";
|
|
28
39
|
import { reconcileClosure } from "../commands/reconcile-closure.js";
|
|
29
40
|
import { runRecordTerminalResult } from "../commands/record-terminal-result.js";
|
|
30
41
|
import { runRecordDiagnosis } from "../commands/record-diagnosis.js";
|
|
42
|
+
import { runRecordIntervention } from "../commands/record-intervention.js";
|
|
43
|
+
import { runRecordHypothesisDisposition } from "../commands/record-hypothesis-disposition.js";
|
|
44
|
+
import { runHistory } from "../commands/history.js";
|
|
45
|
+
import { runTrace } from "../commands/trace.js";
|
|
46
|
+
import { runReflect } from "../commands/reflect.js";
|
|
31
47
|
import { runProgress } from "../commands/progress.js";
|
|
32
48
|
import { runRecordDecisionCriterion } from "../commands/record-decision-criterion.js";
|
|
33
49
|
import { runNext } from "../commands/next.js";
|
|
@@ -100,12 +116,19 @@ export const COMMAND_EXECUTORS = {
|
|
|
100
116
|
const result = await runPreflight({ target, packageRoot, strict: options.strict, taskId: options.taskId });
|
|
101
117
|
return { result, exitCode: result.status === "READY" ? 0 : 1 };
|
|
102
118
|
},
|
|
103
|
-
advance: async ({ target, packageRoot, options }) => ({
|
|
104
|
-
result: await runAdvance({
|
|
119
|
+
advance: async ({ target, packageRoot, options, authorityContext, runtimeContext }) => ({
|
|
120
|
+
result: await runAdvance({
|
|
121
|
+
target,
|
|
122
|
+
packageRoot,
|
|
123
|
+
to: options.to,
|
|
124
|
+
taskId: options.taskId,
|
|
125
|
+
authorityContext,
|
|
126
|
+
runtimeContext,
|
|
127
|
+
}),
|
|
105
128
|
exitCode: 0,
|
|
106
129
|
}),
|
|
107
|
-
next: async ({ target, packageRoot, options }) => ({
|
|
108
|
-
result: await runNext({ target, packageRoot, taskId: options.taskId }),
|
|
130
|
+
next: async ({ target, packageRoot, options, authorityContext, runtimeContext }) => ({
|
|
131
|
+
result: await runNext({ target, packageRoot, taskId: options.taskId, authorityContext, runtimeContext }),
|
|
109
132
|
exitCode: 0,
|
|
110
133
|
}),
|
|
111
134
|
continuity: async ({ target, packageRoot, options }) => ({
|
|
@@ -135,11 +158,11 @@ export const COMMAND_EXECUTORS = {
|
|
|
135
158
|
result: await runClearContinuity({ target, taskId: options.taskId }),
|
|
136
159
|
exitCode: 0,
|
|
137
160
|
}),
|
|
138
|
-
"prepare-completion": async ({ target, packageRoot, options }) => ({
|
|
139
|
-
result: await runPrepareCompletion({ target, packageRoot, taskId: options.taskId }),
|
|
161
|
+
"prepare-completion": async ({ target, packageRoot, options, authorityContext, runtimeContext }) => ({
|
|
162
|
+
result: await runPrepareCompletion({ target, packageRoot, taskId: options.taskId, authorityContext, runtimeContext }),
|
|
140
163
|
exitCode: 0,
|
|
141
164
|
}),
|
|
142
|
-
"run-check": async ({ target, packageRoot, options }) => {
|
|
165
|
+
"run-check": async ({ target, packageRoot, options, authorityContext, runtimeContext }) => {
|
|
143
166
|
const result = await runCheck({
|
|
144
167
|
target,
|
|
145
168
|
packageRoot,
|
|
@@ -149,9 +172,67 @@ export const COMMAND_EXECUTORS = {
|
|
|
149
172
|
details: options.checkDetails ?? undefined,
|
|
150
173
|
timeoutMs: options.timeoutMs ?? undefined,
|
|
151
174
|
taskId: options.taskId,
|
|
175
|
+
authorityContext,
|
|
176
|
+
runtimeContext,
|
|
152
177
|
});
|
|
153
178
|
return { result, exitCode: result.check.status === "passed" ? 0 : 1 };
|
|
154
179
|
},
|
|
180
|
+
"run-action": async ({ target, packageRoot, options, authorityContext, runtimeContext }) => {
|
|
181
|
+
const result = await runAction({ target, packageRoot, taskId: options.taskId,
|
|
182
|
+
actionId: options.actionId, capability: options.actionCapability,
|
|
183
|
+
effectClass: options.actionEffectClass, actionTarget: options.actionTarget,
|
|
184
|
+
idempotencyKey: options.actionIdempotencyKey, requirement: options.actionRequirement,
|
|
185
|
+
requiredForCompletion: options.actionRequiredForCompletion, argv: options.commandArgv,
|
|
186
|
+
approvalId: options.approvalId, timeoutMs: options.timeoutMs,
|
|
187
|
+
authorityContext, runtimeContext });
|
|
188
|
+
return { result, exitCode: result.action.state === "COMMITTED" ? 0 : 1 };
|
|
189
|
+
},
|
|
190
|
+
"action-propose": async ({ target, packageRoot, options }) => ({ result: await runActionPropose({
|
|
191
|
+
target, packageRoot, taskId: options.taskId, input: { actionId: options.actionId,
|
|
192
|
+
capability: options.actionCapability, effectClass: options.actionEffectClass,
|
|
193
|
+
target: options.actionTarget, operation: options.actionOperation,
|
|
194
|
+
idempotencyKey: options.actionIdempotencyKey, requirement: options.actionRequirement,
|
|
195
|
+
requiredForCompletion: options.actionRequiredForCompletion },
|
|
196
|
+
}), exitCode: 0 }),
|
|
197
|
+
"action-record": async ({ target, packageRoot, options }) => ({ result: await runActionRecord({
|
|
198
|
+
target, packageRoot, taskId: options.taskId, actionId: options.actionId,
|
|
199
|
+
state: options.actionState, provenance: options.actionProvenance,
|
|
200
|
+
evidenceRef: options.actionEvidenceRef,
|
|
201
|
+
}), exitCode: 0 }),
|
|
202
|
+
"action-show": async ({ target, packageRoot, options }) => ({ result: await runActionShow({
|
|
203
|
+
target, packageRoot, taskId: options.taskId, actionId: options.actionId,
|
|
204
|
+
}), exitCode: 0 }),
|
|
205
|
+
"action-authorize": async ({ target, packageRoot, options, authorityContext }) => ({
|
|
206
|
+
result: await runActionAuthorize({
|
|
207
|
+
target,
|
|
208
|
+
packageRoot,
|
|
209
|
+
taskId: options.taskId,
|
|
210
|
+
actionId: options.actionId,
|
|
211
|
+
approvalId: options.approvalId,
|
|
212
|
+
// Trusted authority arrives out-of-band only; never from actor input.
|
|
213
|
+
authorityContext,
|
|
214
|
+
}),
|
|
215
|
+
exitCode: 0,
|
|
216
|
+
}),
|
|
217
|
+
"action-verify": async ({ target, packageRoot, options }) => ({ result: await runActionVerify({
|
|
218
|
+
target, packageRoot, taskId: options.taskId, actionId: options.actionId,
|
|
219
|
+
evidenceRef: options.actionEvidenceRef,
|
|
220
|
+
}), exitCode: 0 }),
|
|
221
|
+
"action-reconcile": async ({ target, packageRoot, options, authorityContext }) => ({ result: await runActionReconcile({
|
|
222
|
+
target, packageRoot, taskId: options.taskId, actionId: options.actionId,
|
|
223
|
+
outcome: options.reconciliationOutcome, evidenceRefs: options.evidenceRefs ?? [],
|
|
224
|
+
observedAt: options.observedAt,
|
|
225
|
+
// Trusted settlement authority travels only through the out-of-band
|
|
226
|
+
// executor parameter; actor input can never supply it.
|
|
227
|
+
authorityContext,
|
|
228
|
+
}), exitCode: 0 }),
|
|
229
|
+
metrics: async ({ target, packageRoot, options }) => ({ result: await runMetrics({ target, packageRoot, taskId: options.taskId }), exitCode: 0 }),
|
|
230
|
+
eval: async ({ target, packageRoot, options }) => {
|
|
231
|
+
const result = await runEval({ target, packageRoot, taskId: options.taskId, scenarioPath: options.scenarioPath });
|
|
232
|
+
return { result, exitCode: result.result === "PASS" ? 0 : 1 };
|
|
233
|
+
},
|
|
234
|
+
"approval-request": async ({ target, packageRoot, options }) => ({ result: await runApprovalRequest({ target, packageRoot, taskId: options.taskId, approvalId: options.approvalId, actionId: options.actionId, reason: options.reason }), exitCode: 0 }),
|
|
235
|
+
"approval-resolve": async ({ target, packageRoot, options, authorityContext }) => ({ result: await runApprovalResolve({ target, packageRoot, taskId: options.taskId, approvalId: options.approvalId, decision: options.approvalDecision, authorityKind: options.approvalAuthorityKind, hostGrantRef: options.hostGrantRef, reason: options.reason, authorityContext }), exitCode: 0 }),
|
|
155
236
|
"record-check": async ({ target, packageRoot, options }) => ({
|
|
156
237
|
result: await runRecordCheck({
|
|
157
238
|
target,
|
|
@@ -189,6 +270,7 @@ export const COMMAND_EXECUTORS = {
|
|
|
189
270
|
result: await runRecordDiagnosis({
|
|
190
271
|
target,
|
|
191
272
|
packageRoot,
|
|
273
|
+
file: options.file ?? null,
|
|
192
274
|
hypothesis: options.hypothesis,
|
|
193
275
|
failureClass: options.failureClass,
|
|
194
276
|
evidenceRefs: options.evidenceRefs,
|
|
@@ -198,6 +280,52 @@ export const COMMAND_EXECUTORS = {
|
|
|
198
280
|
}),
|
|
199
281
|
exitCode: 0,
|
|
200
282
|
}),
|
|
283
|
+
"record-intervention": async ({ target, packageRoot, options }) => ({
|
|
284
|
+
result: await runRecordIntervention({
|
|
285
|
+
target,
|
|
286
|
+
packageRoot,
|
|
287
|
+
file: options.file ?? null,
|
|
288
|
+
taskId: options.taskId,
|
|
289
|
+
}),
|
|
290
|
+
exitCode: 0,
|
|
291
|
+
}),
|
|
292
|
+
"record-hypothesis-disposition": async ({ target, packageRoot, options }) => ({
|
|
293
|
+
result: await runRecordHypothesisDisposition({
|
|
294
|
+
target,
|
|
295
|
+
packageRoot,
|
|
296
|
+
hypothesis: options.hypothesis,
|
|
297
|
+
status: options.dispositionStatus,
|
|
298
|
+
evidenceRefs: options.evidenceRefs,
|
|
299
|
+
reason: options.reason,
|
|
300
|
+
taskId: options.taskId,
|
|
301
|
+
}),
|
|
302
|
+
exitCode: 0,
|
|
303
|
+
}),
|
|
304
|
+
history: async ({ target, packageRoot, options }) => {
|
|
305
|
+
const result = await runHistory({
|
|
306
|
+
target,
|
|
307
|
+
packageRoot,
|
|
308
|
+
taskId: options.taskId,
|
|
309
|
+
filters: {
|
|
310
|
+
type: options.historyType ?? null,
|
|
311
|
+
phase: options.historyPhase ?? null,
|
|
312
|
+
failures: Boolean(options.historyFailures),
|
|
313
|
+
checks: Boolean(options.historyChecks),
|
|
314
|
+
since: options.historySince ?? null,
|
|
315
|
+
until: options.historyUntil ?? null,
|
|
316
|
+
limit: Number.isInteger(options.historyLimit) ? options.historyLimit : null,
|
|
317
|
+
},
|
|
318
|
+
});
|
|
319
|
+
return { result, exitCode: result.integrity.valid ? 0 : 1 };
|
|
320
|
+
},
|
|
321
|
+
trace: async ({ target, packageRoot, options }) => {
|
|
322
|
+
const result = await runTrace({ target, packageRoot, taskId: options.taskId });
|
|
323
|
+
return { result, exitCode: result.integrity.valid ? 0 : 1 };
|
|
324
|
+
},
|
|
325
|
+
reflect: async ({ target, packageRoot, options }) => {
|
|
326
|
+
const result = await runReflect({ target, packageRoot, taskId: options.taskId });
|
|
327
|
+
return { result, exitCode: result.status === "STALLED" ? 1 : 0 };
|
|
328
|
+
},
|
|
201
329
|
progress: async ({ target, packageRoot, options }) => {
|
|
202
330
|
const result = await runProgress({ target, packageRoot, taskId: options.taskId });
|
|
203
331
|
return { result, exitCode: result.status === "STALLED" ? 1 : 0 };
|
|
@@ -212,16 +340,37 @@ export const COMMAND_EXECUTORS = {
|
|
|
212
340
|
}),
|
|
213
341
|
exitCode: 0,
|
|
214
342
|
}),
|
|
215
|
-
complete: async ({ target, packageRoot, options }) => {
|
|
216
|
-
const result = await runComplete({
|
|
343
|
+
complete: async ({ target, packageRoot, options, authorityContext, runtimeContext }) => {
|
|
344
|
+
const result = await runComplete({
|
|
345
|
+
target,
|
|
346
|
+
packageRoot,
|
|
347
|
+
strict: options.strict,
|
|
348
|
+
taskId: options.taskId,
|
|
349
|
+
authorityContext,
|
|
350
|
+
runtimeContext,
|
|
351
|
+
});
|
|
217
352
|
return { result, exitCode: result.status === "VALID" ? 0 : 1 };
|
|
218
353
|
},
|
|
219
|
-
audit: async ({ target, packageRoot, options }) => {
|
|
220
|
-
const result = await runAudit({
|
|
354
|
+
audit: async ({ target, packageRoot, options, authorityContext, runtimeContext }) => {
|
|
355
|
+
const result = await runAudit({
|
|
356
|
+
target,
|
|
357
|
+
packageRoot,
|
|
358
|
+
strict: options.strict,
|
|
359
|
+
taskId: options.taskId,
|
|
360
|
+
authorityContext,
|
|
361
|
+
runtimeContext,
|
|
362
|
+
});
|
|
221
363
|
return { result, exitCode: result.status === "VALID" ? 0 : 1 };
|
|
222
364
|
},
|
|
223
|
-
report: async ({ target, packageRoot, options }) => {
|
|
224
|
-
const result = await runReport({
|
|
365
|
+
report: async ({ target, packageRoot, options, authorityContext, runtimeContext }) => {
|
|
366
|
+
const result = await runReport({
|
|
367
|
+
target,
|
|
368
|
+
packageRoot,
|
|
369
|
+
strict: options.strict,
|
|
370
|
+
taskId: options.taskId,
|
|
371
|
+
authorityContext,
|
|
372
|
+
runtimeContext,
|
|
373
|
+
});
|
|
225
374
|
return { result, exitCode: result.verdict === "VALID" ? 0 : 1 };
|
|
226
375
|
},
|
|
227
376
|
policy: async ({ target, packageRoot, options }) => ({
|
|
@@ -262,8 +411,15 @@ export const COMMAND_EXECUTORS = {
|
|
|
262
411
|
result: await runBundle({ target, packageRoot, taskId: options.taskId }),
|
|
263
412
|
exitCode: 0,
|
|
264
413
|
}),
|
|
265
|
-
inspect: async ({ target, packageRoot, options }) => {
|
|
266
|
-
const result = await inspectTarget({
|
|
414
|
+
inspect: async ({ target, packageRoot, options, authorityContext, runtimeContext }) => {
|
|
415
|
+
const result = await inspectTarget({
|
|
416
|
+
target,
|
|
417
|
+
packageRoot,
|
|
418
|
+
contractFile: options.contractFile,
|
|
419
|
+
taskId: options.taskId,
|
|
420
|
+
authorityContext,
|
|
421
|
+
runtimeContext,
|
|
422
|
+
});
|
|
267
423
|
return { result, exitCode: result.ok ? 0 : 1 };
|
|
268
424
|
},
|
|
269
425
|
"validate-receipt": async ({ target, packageRoot, options }) => ({
|
|
@@ -31,6 +31,8 @@ export async function executeForgeLoopCommand({
|
|
|
31
31
|
command,
|
|
32
32
|
projectPath = ".",
|
|
33
33
|
input = {},
|
|
34
|
+
authorityContext,
|
|
35
|
+
runtimeContext,
|
|
34
36
|
} = {}) {
|
|
35
37
|
const metadata = Object.freeze({
|
|
36
38
|
protocolVersion: PROTOCOL_VERSION,
|
|
@@ -81,7 +83,16 @@ export async function executeForgeLoopCommand({
|
|
|
81
83
|
const packageRoot = getPackageRoot();
|
|
82
84
|
const packageVersion = await readPackageVersion(packageRoot);
|
|
83
85
|
|
|
84
|
-
|
|
86
|
+
// Trusted host authority and runtime context travel out-of-band, never
|
|
87
|
+
// inside actor-controlled command input (INV-AUTH-03).
|
|
88
|
+
const { result, exitCode } = await executor({
|
|
89
|
+
target,
|
|
90
|
+
packageRoot,
|
|
91
|
+
packageVersion,
|
|
92
|
+
options,
|
|
93
|
+
authorityContext,
|
|
94
|
+
runtimeContext,
|
|
95
|
+
});
|
|
85
96
|
return {
|
|
86
97
|
ok: true,
|
|
87
98
|
command,
|
|
@@ -24,6 +24,20 @@ import { readExecutionArtifact, validateExecutionBinding } from "./execution.js"
|
|
|
24
24
|
import { taskArtifactPath, taskExecutionPath } from "./task-paths.js";
|
|
25
25
|
import { assertClaimsCoverChangedPaths } from "./task-scope.js";
|
|
26
26
|
import { discoverTasks } from "./task-discovery.js";
|
|
27
|
+
import { listActions } from "./actions.js";
|
|
28
|
+
|
|
29
|
+
async function actionReceiptSummary(target, packageRoot, taskId) {
|
|
30
|
+
const actions = await listActions(target, { packageRoot, taskId });
|
|
31
|
+
return {
|
|
32
|
+
count: actions.length,
|
|
33
|
+
required: actions.filter((action) => action.requiredForCompletion).length,
|
|
34
|
+
verified: actions.filter((action) => action.state === "VERIFIED").length,
|
|
35
|
+
failed: actions.filter((action) => action.state === "FAILED").length,
|
|
36
|
+
ambiguous: actions.filter((action) => action.state === "COMMIT_UNKNOWN").length,
|
|
37
|
+
pending: actions.filter((action) => !["VERIFIED", "FAILED", "CANCELLED"].includes(action.state)).length,
|
|
38
|
+
actionRefs: actions.map((action) => action.actionId),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
27
41
|
|
|
28
42
|
/**
|
|
29
43
|
* Canonical terminal-result types shared by runtime validation, tests, and
|
|
@@ -249,6 +263,16 @@ export async function prepareCompletion({
|
|
|
249
263
|
if (existing && existingValue.stateFingerprint === undefined) {
|
|
250
264
|
throw artifactError("E_RECEIPT_STATE_MISMATCH", "Execution receipt requires the current work-state fingerprint", [receiptRel]);
|
|
251
265
|
}
|
|
266
|
+
// A checkpoint recreated after clear-state/loss starts with empty checks; a
|
|
267
|
+
// receipt still bound to the previous checkpoint fingerprint belongs to a
|
|
268
|
+
// superseded epoch and must not be adopted into the fresh one. The prior
|
|
269
|
+
// epoch remains auditable through the executions/ artifacts and the
|
|
270
|
+
// append-only ledger.
|
|
271
|
+
const receiptFromSupersededEpoch = Boolean(existing)
|
|
272
|
+
&& existingValue.stateFingerprint !== undefined
|
|
273
|
+
&& existingValue.stateFingerprint !== canonicalFingerprint(state)
|
|
274
|
+
&& (state.checks ?? []).length === 0;
|
|
275
|
+
const adoptedValue = receiptFromSupersededEpoch ? {} : existingValue;
|
|
252
276
|
assertStateIdentity({ contract, route, state });
|
|
253
277
|
|
|
254
278
|
let writeClaims = [];
|
|
@@ -286,35 +310,36 @@ export async function prepareCompletion({
|
|
|
286
310
|
|
|
287
311
|
const changedPaths = observedPaths !== null
|
|
288
312
|
? [...observedPaths]
|
|
289
|
-
: existing
|
|
313
|
+
: !receiptFromSupersededEpoch && existing
|
|
290
314
|
? [...(existingValue.changedPaths ?? [])]
|
|
291
315
|
: [];
|
|
292
|
-
const checks = existing ? [...existingValue.checks] : [...state.checks];
|
|
293
|
-
const evidence = existing ? [...(existingValue.evidence ?? [])] : [...state.verificationEvidence];
|
|
316
|
+
const checks = !receiptFromSupersededEpoch && existing ? [...existingValue.checks] : [...state.checks];
|
|
317
|
+
const evidence = !receiptFromSupersededEpoch && existing ? [...(existingValue.evidence ?? [])] : [...state.verificationEvidence];
|
|
294
318
|
const receipt = await createReceipt({
|
|
295
|
-
...
|
|
319
|
+
...adoptedValue,
|
|
296
320
|
taskId: contract.value.taskId,
|
|
297
321
|
contractFingerprint: contract.fingerprint,
|
|
298
322
|
routeFingerprint: route.fingerprint,
|
|
299
323
|
stateFingerprint: canonicalFingerprint(state),
|
|
300
324
|
verificationCycle: state.verificationCycle ?? 1,
|
|
301
|
-
status:
|
|
302
|
-
taskStatus:
|
|
303
|
-
verificationStatus:
|
|
304
|
-
publicationStatus:
|
|
305
|
-
productionReadiness:
|
|
325
|
+
status: adoptedValue.status ?? "in-progress",
|
|
326
|
+
taskStatus: adoptedValue.taskStatus ?? "in-progress",
|
|
327
|
+
verificationStatus: adoptedValue.verificationStatus ?? "not-verified",
|
|
328
|
+
publicationStatus: adoptedValue.publicationStatus ?? "local-only",
|
|
329
|
+
productionReadiness: adoptedValue.productionReadiness ?? "not-verified",
|
|
306
330
|
selectedGuides: [...route.value.guides],
|
|
307
331
|
changedPaths,
|
|
308
332
|
checks,
|
|
333
|
+
actions: await actionReceiptSummary(target, packageRoot, contract.value.taskId),
|
|
309
334
|
evidence,
|
|
310
335
|
evidenceCoverage: coverageForRequirements(requiredEvidence, checks, {
|
|
311
336
|
target,
|
|
312
337
|
taskId: contract.value.taskId,
|
|
313
338
|
options: { authorityContext, runtimeContext },
|
|
314
339
|
}),
|
|
315
|
-
review:
|
|
316
|
-
limitations: [...(
|
|
317
|
-
publication:
|
|
340
|
+
review: adoptedValue.review ?? { status: "not-run", independent: false },
|
|
341
|
+
limitations: [...(adoptedValue.limitations ?? [])],
|
|
342
|
+
publication: adoptedValue.publication ?? {
|
|
318
343
|
committed: false,
|
|
319
344
|
pushed: false,
|
|
320
345
|
pullRequest: null,
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
import { ARTIFACT_PATHS, canonicalFingerprint, readJsonArtifact, writeJsonArtifact } from "./artifacts.js";
|
|
2
|
+
import { appendProtocolEvent, validateCompletionRecoveryAuthorization, validateEventLedger } from "./events.js";
|
|
3
|
+
import { createReceipt } from "./receipt.js";
|
|
4
|
+
import { taskArtifactPath } from "./task-paths.js";
|
|
5
|
+
import { mutateWorkState, readWorkState } from "./work-state.js";
|
|
6
|
+
|
|
7
|
+
function resolveArtifactPath(key, taskId, override) {
|
|
8
|
+
if (override) return override;
|
|
9
|
+
return taskId ? taskArtifactPath(taskId, key) : ARTIFACT_PATHS[key];
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const FINGERPRINT_MISMATCH_CODES = new Set([
|
|
13
|
+
"E_COMPLETION_REJECTION_STATE_FINGERPRINT_MISMATCH",
|
|
14
|
+
"E_COMPLETION_REJECTION_RECEIPT_FINGERPRINT_MISMATCH",
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
export function isFingerprintOnlyRecoveryMismatch(errors = []) {
|
|
18
|
+
return Array.isArray(errors)
|
|
19
|
+
&& errors.length > 0
|
|
20
|
+
&& errors.every((error) => FINGERPRINT_MISMATCH_CODES.has(error?.code));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function sortedValues(values) {
|
|
24
|
+
return [...new Set(values ?? [])].sort();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function sameSortedValues(left, right) {
|
|
28
|
+
return JSON.stringify(sortedValues(left)) === JSON.stringify(sortedValues(right));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function findMatchingRejectionEvent(events, attempt, cycle) {
|
|
32
|
+
let latestReviewIndex = -1;
|
|
33
|
+
for (let index = 0; index < events.length; index += 1) {
|
|
34
|
+
const event = events[index];
|
|
35
|
+
if (event.event === "REVIEW_STARTED" && (event.details?.verificationCycle ?? 1) === cycle) {
|
|
36
|
+
latestReviewIndex = index;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
40
|
+
const event = events[index];
|
|
41
|
+
if (event.event !== "COMPLETION_REJECTED") continue;
|
|
42
|
+
if ((event.details?.verificationCycle ?? 1) !== cycle) continue;
|
|
43
|
+
if (index < latestReviewIndex) continue;
|
|
44
|
+
return { event, index };
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Rebind a persisted REJECTED completion attempt to the current work-state
|
|
51
|
+
* checkpoint when the only authorization failures are fingerprint mismatches.
|
|
52
|
+
*
|
|
53
|
+
* Repository drift or a recovery/resume cycle can mutate work-state after a
|
|
54
|
+
* completion rejection was persisted, so the ledger snapshot no longer matches
|
|
55
|
+
* the live checkpoint. Without rebinding, every sanctioned closure path refuses
|
|
56
|
+
* (reconcile-closure and REVIEWING -> VERIFYING require authorized completion
|
|
57
|
+
* recovery; complete cannot persist a fresh evidence-only rejection while the
|
|
58
|
+
* checkpoint is stale), which deadlocks the task.
|
|
59
|
+
*
|
|
60
|
+
* The rebind is append-only and logically conservative:
|
|
61
|
+
* - the rejection reasonCodes, missingRequirementIds, and verification cycle
|
|
62
|
+
* must be logically identical between work-state and the latest matching
|
|
63
|
+
* ledger rejection; any logical difference is refused,
|
|
64
|
+
* - the original COMPLETION_REJECTED event is never modified; a rebound
|
|
65
|
+
* rejection carrying the current fingerprints is appended,
|
|
66
|
+
* - the execution receipt is re-bound to the current checkpoint when present.
|
|
67
|
+
*/
|
|
68
|
+
export async function rebindCompletionRejectionSnapshot({
|
|
69
|
+
target,
|
|
70
|
+
packageRoot,
|
|
71
|
+
taskId = null,
|
|
72
|
+
statePath = null,
|
|
73
|
+
receiptPath = null,
|
|
74
|
+
eventsPath = null,
|
|
75
|
+
authorityContext,
|
|
76
|
+
runtimeContext,
|
|
77
|
+
} = {}) {
|
|
78
|
+
const statePathResolved = resolveArtifactPath("state", taskId, statePath);
|
|
79
|
+
const receiptPathResolved = resolveArtifactPath("receipt", taskId, receiptPath);
|
|
80
|
+
const state = await readWorkState(target, { packageRoot, taskId, statePath: statePathResolved });
|
|
81
|
+
if (!state || state.phase !== "REVIEWING") {
|
|
82
|
+
return { rebound: false };
|
|
83
|
+
}
|
|
84
|
+
const attempt = state.lastCompletionAttempt;
|
|
85
|
+
if (!attempt || attempt.status !== "REJECTED") {
|
|
86
|
+
return { rebound: false };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const ledger = await validateEventLedger(target, packageRoot, { taskId, eventsPath });
|
|
90
|
+
if (!ledger.valid) {
|
|
91
|
+
return { rebound: false };
|
|
92
|
+
}
|
|
93
|
+
const cycle = attempt.verificationCycle ?? state.verificationCycle ?? 1;
|
|
94
|
+
const matching = findMatchingRejectionEvent(ledger.events, attempt, cycle);
|
|
95
|
+
if (!matching) {
|
|
96
|
+
return { rebound: false };
|
|
97
|
+
}
|
|
98
|
+
const details = matching.event.details ?? {};
|
|
99
|
+
const logicalMatch = (details.verificationCycle ?? 1) === cycle
|
|
100
|
+
&& sameSortedValues(details.reasonCodes, attempt.reasonCodes)
|
|
101
|
+
&& sameSortedValues(details.missingRequirementIds, attempt.missingRequirementIds);
|
|
102
|
+
if (!logicalMatch) {
|
|
103
|
+
return { rebound: false };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const next = await mutateWorkState(target, {
|
|
107
|
+
expectedRevision: state.revision ?? 0,
|
|
108
|
+
packageRoot,
|
|
109
|
+
taskId,
|
|
110
|
+
statePath: statePathResolved,
|
|
111
|
+
}, () => ({
|
|
112
|
+
...state,
|
|
113
|
+
revision: (state.revision ?? 0) + 1,
|
|
114
|
+
lastUpdated: new Date().toISOString(),
|
|
115
|
+
}));
|
|
116
|
+
|
|
117
|
+
let reboundReceiptFingerprint;
|
|
118
|
+
try {
|
|
119
|
+
const receipt = await readJsonArtifact(target, receiptPathResolved, "execution-receipt", packageRoot);
|
|
120
|
+
const reboundReceipt = await createReceipt({
|
|
121
|
+
...receipt.value,
|
|
122
|
+
stateFingerprint: canonicalFingerprint(next),
|
|
123
|
+
verificationCycle: next.verificationCycle ?? receipt.value.verificationCycle ?? 1,
|
|
124
|
+
}, packageRoot, { target, taskId, authorityContext, runtimeContext });
|
|
125
|
+
await writeJsonArtifact(target, receiptPathResolved, reboundReceipt, "execution-receipt", packageRoot);
|
|
126
|
+
reboundReceiptFingerprint = canonicalFingerprint(reboundReceipt);
|
|
127
|
+
} catch (error) {
|
|
128
|
+
if (error.code !== "ARTIFACT_MISSING") throw error;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
await appendProtocolEvent(target, {
|
|
132
|
+
taskId: state.taskId,
|
|
133
|
+
event: "COMPLETION_REJECTED",
|
|
134
|
+
details: {
|
|
135
|
+
verificationCycle: cycle,
|
|
136
|
+
reasonCodes: sortedValues(details.reasonCodes),
|
|
137
|
+
missingRequirementIds: sortedValues(details.missingRequirementIds),
|
|
138
|
+
stateFingerprint: canonicalFingerprint(next),
|
|
139
|
+
...(reboundReceiptFingerprint ? { receiptFingerprint: reboundReceiptFingerprint } : {}),
|
|
140
|
+
...(details.stateFingerprint ? { reboundFromStateFingerprint: details.stateFingerprint } : {}),
|
|
141
|
+
},
|
|
142
|
+
}, packageRoot, { taskId, eventsPath });
|
|
143
|
+
|
|
144
|
+
return { rebound: true, state: next };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export async function authorizeCompletionRecoveryOrRebind({
|
|
148
|
+
target,
|
|
149
|
+
packageRoot,
|
|
150
|
+
taskId = null,
|
|
151
|
+
statePath = null,
|
|
152
|
+
receiptPath = null,
|
|
153
|
+
eventsPath = null,
|
|
154
|
+
authorityContext,
|
|
155
|
+
runtimeContext,
|
|
156
|
+
} = {}) {
|
|
157
|
+
const statePathResolved = resolveArtifactPath("state", taskId, statePath);
|
|
158
|
+
const receiptPathResolved = resolveArtifactPath("receipt", taskId, receiptPath);
|
|
159
|
+
const resolveArtifacts = async () => {
|
|
160
|
+
const state = await readWorkState(target, { packageRoot, taskId, statePath: statePathResolved });
|
|
161
|
+
let receipt = null;
|
|
162
|
+
try {
|
|
163
|
+
receipt = (await readJsonArtifact(target, receiptPathResolved, "execution-receipt", packageRoot))?.value ?? null;
|
|
164
|
+
} catch {
|
|
165
|
+
receipt = null;
|
|
166
|
+
}
|
|
167
|
+
const events = (await validateEventLedger(target, packageRoot, { taskId, eventsPath })).events;
|
|
168
|
+
return { state, receipt, events };
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const initial = await resolveArtifacts();
|
|
172
|
+
const recoveryAuth = validateCompletionRecoveryAuthorization(initial);
|
|
173
|
+
if (recoveryAuth.authorized || !isFingerprintOnlyRecoveryMismatch(recoveryAuth.errors)) {
|
|
174
|
+
return { ...initial, recoveryAuth, rebound: false };
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const reboundResult = await rebindCompletionRejectionSnapshot({
|
|
178
|
+
target,
|
|
179
|
+
packageRoot,
|
|
180
|
+
taskId,
|
|
181
|
+
statePath,
|
|
182
|
+
receiptPath,
|
|
183
|
+
eventsPath,
|
|
184
|
+
authorityContext,
|
|
185
|
+
runtimeContext,
|
|
186
|
+
});
|
|
187
|
+
if (!reboundResult.rebound) {
|
|
188
|
+
return { ...initial, recoveryAuth, rebound: false };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const rebounded = await resolveArtifacts();
|
|
192
|
+
const reboundAuth = validateCompletionRecoveryAuthorization(rebounded);
|
|
193
|
+
return { ...rebounded, recoveryAuth: reboundAuth, rebound: reboundAuth.authorized };
|
|
194
|
+
}
|
package/src/core/completion.js
CHANGED
|
@@ -13,6 +13,25 @@ import { isRecoverableCompletionEvidenceCode } from "./completion-recovery.js";
|
|
|
13
13
|
import { evaluateTerminalRequirements } from "./evidence-readiness.js";
|
|
14
14
|
import { PROJECT_ARTIFACT_PATHS, taskArtifactPath } from "./task-paths.js";
|
|
15
15
|
import { detectPolicyCapability, evaluateTargetPolicy } from "./policy-engine.js";
|
|
16
|
+
import { listActions } from "./actions.js";
|
|
17
|
+
|
|
18
|
+
async function actionReceiptSummary(target, packageRoot, taskId) {
|
|
19
|
+
const actions = await listActions(target, { packageRoot, taskId });
|
|
20
|
+
const { evaluateRequiredActionReadiness } = await import("./action-readiness.js");
|
|
21
|
+
const readiness = await evaluateRequiredActionReadiness({ target, packageRoot, taskId });
|
|
22
|
+
return {
|
|
23
|
+
count: actions.length,
|
|
24
|
+
required: readiness.total,
|
|
25
|
+
verified: actions.filter((action) => action.state === "VERIFIED").length,
|
|
26
|
+
// Raw state counts are observability only; trusted completion is below.
|
|
27
|
+
trustedSatisfied: readiness.satisfied,
|
|
28
|
+
unresolvedRequired: readiness.unresolved,
|
|
29
|
+
failed: actions.filter((action) => action.state === "FAILED").length,
|
|
30
|
+
ambiguous: actions.filter((action) => action.state === "COMMIT_UNKNOWN").length,
|
|
31
|
+
pending: actions.filter((action) => !["VERIFIED", "FAILED", "CANCELLED"].includes(action.state)).length,
|
|
32
|
+
actionRefs: actions.map((action) => action.actionId),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
16
35
|
|
|
17
36
|
/**
|
|
18
37
|
* Canonical completion return statuses shared by the runtime, tests, and
|
|
@@ -424,6 +443,44 @@ export async function evaluateCompletion({
|
|
|
424
443
|
}
|
|
425
444
|
}
|
|
426
445
|
|
|
446
|
+
const actionTaskId = contract?.value?.taskId ?? taskId;
|
|
447
|
+
const durableActions = actionTaskId
|
|
448
|
+
? await listActions(target, { packageRoot, taskId: actionTaskId })
|
|
449
|
+
: [];
|
|
450
|
+
const contractRequirements = new Set([
|
|
451
|
+
...(contract?.value?.verification ?? []), ...(contract?.value?.successCriteria ?? []),
|
|
452
|
+
]);
|
|
453
|
+
// Completion truth consumes the canonical action-readiness projection, not
|
|
454
|
+
// raw state labels: a forged VERIFIED label can never satisfy a required
|
|
455
|
+
// action (INV-VERIFY-02).
|
|
456
|
+
const { evaluateRequiredActionReadiness } = await import("./action-readiness.js");
|
|
457
|
+
const requiredActionReadiness = actionTaskId
|
|
458
|
+
? await evaluateRequiredActionReadiness({ target, packageRoot, taskId: actionTaskId })
|
|
459
|
+
: { total: 0, satisfied: 0, unresolved: 0, ambiguous: 0, failed: 0, untrusted: 0, actions: [] };
|
|
460
|
+
for (const readiness of requiredActionReadiness.actions) {
|
|
461
|
+
if (readiness.status === "SATISFIED") continue;
|
|
462
|
+
const action = durableActions.find((candidate) => candidate.actionId === readiness.actionId);
|
|
463
|
+
if (
|
|
464
|
+
readiness.status === "FAILED"
|
|
465
|
+
&& action?.state === "CANCELLED"
|
|
466
|
+
&& action.requirement
|
|
467
|
+
&& !contractRequirements.has(action.requirement)
|
|
468
|
+
) {
|
|
469
|
+
continue;
|
|
470
|
+
}
|
|
471
|
+
const code = readiness.status === "AMBIGUOUS"
|
|
472
|
+
? "E_ACTION_RECONCILIATION_REQUIRED"
|
|
473
|
+
: readiness.status === "UNTRUSTED"
|
|
474
|
+
? "E_ACTION_VERIFICATION_REQUIRED"
|
|
475
|
+
: "E_ACTION_STATE_MISMATCH";
|
|
476
|
+
errors.push(issue(
|
|
477
|
+
code,
|
|
478
|
+
`Required action ${readiness.actionId} is not trusted-satisfied (${readiness.status}): ${readiness.reasons[0] ?? ""}`,
|
|
479
|
+
[taskArtifactPath(action?.taskId ?? actionTaskId, "actions")],
|
|
480
|
+
{ actionId: readiness.actionId, actionState: action?.state, readiness: readiness.status },
|
|
481
|
+
));
|
|
482
|
+
}
|
|
483
|
+
|
|
427
484
|
const sortedErrors = sortIssues(errors);
|
|
428
485
|
const valid = sortedErrors.length === 0;
|
|
429
486
|
return {
|
|
@@ -440,6 +497,17 @@ export async function evaluateCompletion({
|
|
|
440
497
|
status: ledger.valid ? "valid" : "invalid",
|
|
441
498
|
events: ledger.events.length,
|
|
442
499
|
},
|
|
500
|
+
actions: {
|
|
501
|
+
count: durableActions.length,
|
|
502
|
+
required: requiredActionReadiness.total,
|
|
503
|
+
verified: durableActions.filter((action) => action.state === "VERIFIED").length,
|
|
504
|
+
trustedSatisfied: requiredActionReadiness.satisfied,
|
|
505
|
+
unresolvedRequired: requiredActionReadiness.unresolved,
|
|
506
|
+
failed: durableActions.filter((action) => action.state === "FAILED").length,
|
|
507
|
+
ambiguous: durableActions.filter((action) => action.state === "COMMIT_UNKNOWN").length,
|
|
508
|
+
pending: durableActions.filter((action) => !["VERIFIED", "FAILED", "CANCELLED"].includes(action.state)).length,
|
|
509
|
+
actionRefs: durableActions.map((action) => action.actionId),
|
|
510
|
+
},
|
|
443
511
|
};
|
|
444
512
|
}
|
|
445
513
|
|
|
@@ -528,6 +596,7 @@ export async function runComplete({
|
|
|
528
596
|
if (receipt) {
|
|
529
597
|
nextReceipt = await createReceipt({
|
|
530
598
|
...receipt.value,
|
|
599
|
+
actions: await actionReceiptSummary(target, packageRoot, state.taskId),
|
|
531
600
|
stateFingerprint: canonicalFingerprint(next),
|
|
532
601
|
verificationCycle: next.verificationCycle ?? receipt.value.verificationCycle ?? 1,
|
|
533
602
|
}, packageRoot, { target, taskId: state.taskId, authorityContext, runtimeContext });
|
|
@@ -574,6 +643,7 @@ export async function runComplete({
|
|
|
574
643
|
next.revision = (state.revision ?? 0) + 1;
|
|
575
644
|
const nextReceipt = await createReceipt({
|
|
576
645
|
...receipt.value,
|
|
646
|
+
actions: await actionReceiptSummary(target, packageRoot, state.taskId),
|
|
577
647
|
stateFingerprint: canonicalFingerprint(next),
|
|
578
648
|
verificationCycle: next.verificationCycle ?? receipt.value.verificationCycle ?? 1,
|
|
579
649
|
}, packageRoot, { target, taskId: state.taskId, authorityContext, runtimeContext });
|