@cassiomc1/forgeloop 1.3.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/.github/copilot-instructions.md +1 -0
- package/AGENTS.md +1 -0
- package/CLAUDE.md +1 -0
- package/DOCS_INDEX.md +20 -8
- package/EXECUTION_STATE.md +60 -0
- package/LOOP_ENGINEERING.md +135 -5
- package/LOOP_SYSTEM_DESIGN.md +54 -1
- package/PROTOCOL_INTEGRATION.md +87 -0
- package/QUALITY_SCORECARD.md +2 -0
- package/README.md +69 -9
- package/TERMINOLOGY.md +15 -0
- package/THIRD_PARTY_NOTICES.md +30 -0
- package/THREAT_MODEL.md +59 -1
- package/docs/ARTIFACT_REFERENCE.md +183 -0
- package/docs/CLI_REFERENCE.md +391 -6
- package/docs/CROSS_HARNESS_CONTINUITY.md +23 -0
- package/docs/DIAGNOSTIC_MODEL.md +181 -0
- package/docs/DOCUMENTATION_GUIDE.md +36 -13
- package/docs/EXECUTION_TRACE.md +76 -0
- package/docs/GETTING_STARTED.md +1 -0
- package/docs/MCP.md +159 -0
- package/docs/RECIPES.md +149 -0
- package/docs/RELEASE_CHECKLIST_1_4.md +38 -0
- package/docs/RELEASE_CHECKLIST_1_5_MCP.md +78 -0
- package/docs/TROUBLESHOOTING.md +217 -3
- package/docs/UNIVERSAL_INTEGRATION.md +48 -0
- 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 +21 -8
- 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/task-recovery.schema.json +61 -0
- package/schemas/trajectory-evaluation.schema.json +64 -0
- package/schemas/trajectory-scenario.schema.json +42 -0
- package/src/cli.js +267 -347
- 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/audit.js +5 -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 +55 -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/progress.js +6 -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/status.js +17 -0
- package/src/commands/task-create.js +39 -1
- package/src/commands/task-list.js +14 -1
- package/src/commands/task-lock-status.js +2 -2
- package/src/commands/task-recover.js +202 -0
- package/src/commands/task-repair-legacy-recovery.js +417 -0
- package/src/commands/task-resume.js +172 -0
- package/src/commands/task-scope.js +23 -4
- package/src/commands/task-show.js +18 -4
- package/src/commands/trace.js +34 -0
- package/src/commands/validate-protocol.js +40 -15
- 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 +60 -0
- package/src/core/audit.js +45 -4
- package/src/core/bundles.js +30 -0
- package/src/core/capability-policy.js +226 -0
- package/src/core/cli-command-definitions.js +260 -5
- package/src/core/command-executors.js +543 -0
- package/src/core/command-input.js +107 -0
- package/src/core/command-runtime.js +117 -0
- package/src/core/completion-artifacts.js +39 -15
- package/src/core/completion-ownership.js +88 -0
- 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 +461 -1
- package/src/core/events.js +171 -2
- 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/filesystem.js +55 -6
- 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 +132 -7
- package/src/core/integration-invocation-policy.js +217 -0
- package/src/core/integration-limits.js +20 -0
- package/src/core/integration-resources.js +178 -0
- package/src/core/next-action-model.js +94 -0
- package/src/core/next-action.js +490 -3
- package/src/core/phase.js +42 -22
- 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/project-root.js +21 -0
- package/src/core/protocol-info.js +61 -0
- package/src/core/protocol.js +14 -0
- package/src/core/receipt.js +1 -0
- package/src/core/reconcile-closure.js +35 -10
- package/src/core/recovery-history.js +116 -0
- package/src/core/reflection.js +305 -0
- package/src/core/resumability.js +57 -3
- package/src/core/schema-validation.js +9 -0
- package/src/core/strategy-analysis.js +97 -0
- package/src/core/task-claim-state.js +272 -0
- package/src/core/task-command.js +5 -1
- package/src/core/task-conflict-inspection.js +321 -0
- package/src/core/task-context.js +32 -29
- package/src/core/task-discovery.js +14 -1
- package/src/core/task-lock.js +216 -22
- package/src/core/task-paths.js +31 -2
- package/src/core/task-recovery-migration.js +192 -0
- package/src/core/task-recovery.js +205 -0
- package/src/core/task-scope.js +33 -1
- package/src/core/task-snapshot.js +53 -0
- package/src/core/templates.js +9 -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 +36 -2
- package/src/core/work-state.js +10 -5
- package/src/integration.js +47 -0
- package/docs/assets/forgeloop-flow.svg +0 -1
- package/docs/forgeloop-flow.mmd +0 -51
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { readEvents } from "./events.js";
|
|
2
|
+
import { assertActionTransition, assertActionAuthorizationDetails } from "./action-model.js";
|
|
3
|
+
import { E_ACTION_EVIDENCE_INVALID } from "./error-codes.js";
|
|
4
|
+
|
|
5
|
+
function hasModernAuthorizationEvidence(details) {
|
|
6
|
+
if (!details || typeof details !== "object") return false;
|
|
7
|
+
if (!["ALLOW", "REQUIRE_AUTHORITY", "REQUIRE_APPROVAL"].includes(details.capabilityDecision)) return false;
|
|
8
|
+
if (typeof details.capabilityPolicyFingerprint !== "string" || !/^[a-f0-9]{64}$/.test(details.capabilityPolicyFingerprint)) return false;
|
|
9
|
+
if (typeof details.policyLockDigest !== "string" || !details.policyLockDigest.startsWith("sha256:")) return false;
|
|
10
|
+
if (typeof details.taskPolicyDigest !== "string" || !details.taskPolicyDigest.startsWith("sha256:")) return false;
|
|
11
|
+
if (details.capabilityDecision === "ALLOW") return true;
|
|
12
|
+
if (details.authorityKind !== "HOST_ATTESTED" || typeof details.authorityRef !== "string" || !details.authorityRef) return false;
|
|
13
|
+
if (details.capabilityDecision === "REQUIRE_AUTHORITY") return true;
|
|
14
|
+
if (details.capabilityDecision === "REQUIRE_APPROVAL") {
|
|
15
|
+
return typeof details.approvalId === "string" && !!details.approvalId
|
|
16
|
+
&& typeof details.approvalFingerprint === "string" && /^[a-f0-9]{64}$/.test(details.approvalFingerprint);
|
|
17
|
+
}
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function hasCanonicalVerificationEvidence(details) {
|
|
22
|
+
return Boolean(
|
|
23
|
+
details
|
|
24
|
+
&& typeof details.evidenceRef === "string" && details.evidenceRef.length > 0 && details.evidenceRef.length <= 256
|
|
25
|
+
&& details.evidenceKind === "FORGELOOP_EXECUTION"
|
|
26
|
+
&& typeof details.verifiedAt === "string",
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function issue(code, message) {
|
|
31
|
+
return { code, message };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Deterministic replay of one durable action's chronology from the canonical
|
|
36
|
+
* task-scoped events.ndjson. Detects forged or incomplete history: illegal
|
|
37
|
+
* transitions, revision skips, fingerprint drift, STARTED without trusted
|
|
38
|
+
* authorization, unverifiable VERIFIED claims, out-of-order reconciliation,
|
|
39
|
+
* and artifact/projection divergence (T-DURABLE-07).
|
|
40
|
+
*
|
|
41
|
+
* Compatibility: legacy pre-hardening ACTION_AUTHORIZED events remain
|
|
42
|
+
* ledger-valid for historical readability but are never trusted
|
|
43
|
+
* authorization evidence for new required-action completion.
|
|
44
|
+
*/
|
|
45
|
+
export async function projectActionLedger({
|
|
46
|
+
target,
|
|
47
|
+
packageRoot,
|
|
48
|
+
taskId,
|
|
49
|
+
actionId,
|
|
50
|
+
artifact = null,
|
|
51
|
+
}) {
|
|
52
|
+
const events = await readEvents(target, packageRoot, { taskId });
|
|
53
|
+
const chronology = events.filter((event) => event.details?.actionId === actionId);
|
|
54
|
+
const errors = [];
|
|
55
|
+
|
|
56
|
+
const proposed = chronology.find((event) => event.event === "ACTION_PROPOSED");
|
|
57
|
+
if (!proposed) {
|
|
58
|
+
return {
|
|
59
|
+
actionId,
|
|
60
|
+
valid: false,
|
|
61
|
+
state: null,
|
|
62
|
+
revision: null,
|
|
63
|
+
actionFingerprint: artifact?.actionFingerprint ?? null,
|
|
64
|
+
authorization: { valid: false, details: null },
|
|
65
|
+
verification: { valid: false, evidenceRef: null },
|
|
66
|
+
reconciliation: { count: 0, latestOutcome: null },
|
|
67
|
+
errors: [issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId} has no matching ACTION_PROPOSED ledger event`)],
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const actionFingerprint = proposed.details.actionFingerprint;
|
|
72
|
+
let state = "PROPOSED";
|
|
73
|
+
let revision = 0;
|
|
74
|
+
let authorization = { valid: false, details: null };
|
|
75
|
+
let verification = { valid: false, evidenceRef: null };
|
|
76
|
+
const reconciliation = { count: 0, latestOutcome: null };
|
|
77
|
+
// A trusted COMMITTED reconciliation emits two events for one logical
|
|
78
|
+
// transition: ACTION_RECONCILED(outcome=COMMITTED) owns the transition and
|
|
79
|
+
// ACTION_COMMIT_RECORDED(reconciled=true) is a same-revision informational
|
|
80
|
+
// mirror. Replay must apply exactly one transition (INV-FINAL-REPLAY-01).
|
|
81
|
+
let pendingReconciledCommitMirror = null;
|
|
82
|
+
|
|
83
|
+
for (const event of chronology) {
|
|
84
|
+
const details = event.details ?? {};
|
|
85
|
+
|
|
86
|
+
if (details.actionFingerprint !== actionFingerprint) {
|
|
87
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: event ${event.event} carries a different action fingerprint`));
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const isReconciledMirror = event.event === "ACTION_COMMIT_RECORDED" && details.reconciled === true;
|
|
91
|
+
if (
|
|
92
|
+
Number.isInteger(details.revision)
|
|
93
|
+
&& details.revision !== revision + 1
|
|
94
|
+
&& event.event !== "ACTION_RECONCILED"
|
|
95
|
+
&& !isReconciledMirror
|
|
96
|
+
) {
|
|
97
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: ${event.event} skipped revision (expected ${revision + 1}, got ${details.revision})`));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
switch (event.event) {
|
|
101
|
+
case "ACTION_PROPOSED":
|
|
102
|
+
break;
|
|
103
|
+
case "ACTION_AUTHORIZED": {
|
|
104
|
+
try {
|
|
105
|
+
assertActionTransition(state, "AUTHORIZED");
|
|
106
|
+
} catch {
|
|
107
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: illegal transition ${state} -> AUTHORIZED`));
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
// Structural validation always applies; legacy events are readable
|
|
111
|
+
// but never satisfy trusted authorization.
|
|
112
|
+
const isModern = hasModernAuthorizationEvidence(details);
|
|
113
|
+
try {
|
|
114
|
+
assertActionAuthorizationDetails({ ...details }, { legacyAllowed: true });
|
|
115
|
+
} catch (error) {
|
|
116
|
+
errors.push(issue(error.code ?? E_ACTION_EVIDENCE_INVALID, `action ${actionId}: invalid ACTION_AUTHORIZED details: ${error.message}`));
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
state = "AUTHORIZED";
|
|
120
|
+
revision += 1;
|
|
121
|
+
authorization = { valid: isModern, details: isModern ? details : null };
|
|
122
|
+
break;
|
|
123
|
+
}
|
|
124
|
+
case "ACTION_STARTED": {
|
|
125
|
+
try {
|
|
126
|
+
assertActionTransition(state, "STARTED");
|
|
127
|
+
} catch {
|
|
128
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: ACTION_STARTED without a legal predecessor state (${state})`));
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
if (!authorization.valid) {
|
|
132
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: ACTION_STARTED occurred without valid modern authorization evidence`));
|
|
133
|
+
}
|
|
134
|
+
state = "STARTED";
|
|
135
|
+
revision += 1;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
case "ACTION_COMMIT_RECORDED": {
|
|
139
|
+
// Reconciled commit mirrors corroborate a preceding trusted COMMITTED
|
|
140
|
+
// reconciliation; they never transition state again.
|
|
141
|
+
if (details.reconciled === true) {
|
|
142
|
+
const mirror = pendingReconciledCommitMirror;
|
|
143
|
+
if (
|
|
144
|
+
!mirror
|
|
145
|
+
|| details.revision !== mirror.revision
|
|
146
|
+
|| details.actionFingerprint !== mirror.actionFingerprint
|
|
147
|
+
|| details.fromState !== mirror.fromState
|
|
148
|
+
|| details.toState !== mirror.toState
|
|
149
|
+
) {
|
|
150
|
+
errors.push(issue(
|
|
151
|
+
E_ACTION_EVIDENCE_INVALID,
|
|
152
|
+
`action ${actionId}: reconciled commit mirror does not match the preceding COMMITTED reconciliation`,
|
|
153
|
+
));
|
|
154
|
+
break;
|
|
155
|
+
}
|
|
156
|
+
pendingReconciledCommitMirror = null;
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
try {
|
|
160
|
+
assertActionTransition(state, "COMMITTED");
|
|
161
|
+
} catch {
|
|
162
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: illegal transition ${state} -> COMMITTED`));
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
state = "COMMITTED";
|
|
166
|
+
revision += 1;
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
case "ACTION_VERIFIED": {
|
|
170
|
+
try {
|
|
171
|
+
assertActionTransition(state, "VERIFIED");
|
|
172
|
+
} catch {
|
|
173
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: illegal transition ${state} -> VERIFIED`));
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
if (!hasCanonicalVerificationEvidence(details)) {
|
|
177
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: ACTION_VERIFIED lacks canonical evidence`));
|
|
178
|
+
state = "VERIFIED";
|
|
179
|
+
revision += 1;
|
|
180
|
+
verification = { valid: false, evidenceRef: details.evidenceRef ?? null };
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
state = "VERIFIED";
|
|
184
|
+
revision += 1;
|
|
185
|
+
verification = { valid: true, evidenceRef: details.evidenceRef };
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
case "ACTION_FAILED": {
|
|
189
|
+
try {
|
|
190
|
+
assertActionTransition(state, "FAILED");
|
|
191
|
+
} catch {
|
|
192
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: illegal transition ${state} -> FAILED`));
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
state = "FAILED";
|
|
196
|
+
revision += 1;
|
|
197
|
+
break;
|
|
198
|
+
}
|
|
199
|
+
case "ACTION_CANCELLED": {
|
|
200
|
+
try {
|
|
201
|
+
assertActionTransition(state, "CANCELLED");
|
|
202
|
+
} catch {
|
|
203
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: illegal transition ${state} -> CANCELLED`));
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
state = "CANCELLED";
|
|
207
|
+
revision += 1;
|
|
208
|
+
break;
|
|
209
|
+
}
|
|
210
|
+
case "ACTION_COMMIT_UNKNOWN": {
|
|
211
|
+
try {
|
|
212
|
+
assertActionTransition(state, "COMMIT_UNKNOWN");
|
|
213
|
+
} catch {
|
|
214
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: illegal transition ${state} -> COMMIT_UNKNOWN`));
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
state = "COMMIT_UNKNOWN";
|
|
218
|
+
revision += 1;
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
case "ACTION_RECONCILED": {
|
|
222
|
+
reconciliation.count += 1;
|
|
223
|
+
reconciliation.latestOutcome = details.outcome ?? null;
|
|
224
|
+
if (state !== "COMMIT_UNKNOWN") {
|
|
225
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: ACTION_RECONCILED recorded while state was ${state}, not COMMIT_UNKNOWN`));
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const outcome = details.outcome;
|
|
229
|
+
if (outcome === "UNKNOWN") {
|
|
230
|
+
revision += 1;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
const nextState = outcome === "COMMITTED" ? "COMMITTED" : outcome === "NOT_COMMITTED" ? "PROPOSED" : null;
|
|
234
|
+
if (!nextState) {
|
|
235
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: unknown reconciliation outcome ${String(outcome)}`));
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
try {
|
|
239
|
+
assertActionTransition("COMMIT_UNKNOWN", nextState);
|
|
240
|
+
} catch {
|
|
241
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: reconciliation outcome ${outcome} maps to an illegal transition`));
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (
|
|
245
|
+
typeof details.authorityKind !== "string"
|
|
246
|
+
|| details.authorityKind !== "HOST_ATTESTED"
|
|
247
|
+
|| typeof details.authorityRef !== "string"
|
|
248
|
+
) {
|
|
249
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: settling reconciliation lacks trusted authority binding`));
|
|
250
|
+
}
|
|
251
|
+
state = nextState;
|
|
252
|
+
revision += 1;
|
|
253
|
+
if (outcome === "COMMITTED") {
|
|
254
|
+
pendingReconciledCommitMirror = {
|
|
255
|
+
revision: details.revision,
|
|
256
|
+
actionFingerprint,
|
|
257
|
+
fromState: "COMMIT_UNKNOWN",
|
|
258
|
+
toState: "COMMITTED",
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
break;
|
|
262
|
+
}
|
|
263
|
+
default:
|
|
264
|
+
break;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
if (artifact) {
|
|
269
|
+
if (artifact.state !== state) {
|
|
270
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: artifact state ${artifact.state} differs from projected ledger state ${state}`));
|
|
271
|
+
}
|
|
272
|
+
if (artifact.revision !== revision) {
|
|
273
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: artifact revision ${artifact.revision} differs from projected revision ${revision}`));
|
|
274
|
+
}
|
|
275
|
+
if (artifact.actionFingerprint !== actionFingerprint) {
|
|
276
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: artifact fingerprint differs from the immutable proposal fingerprint`));
|
|
277
|
+
}
|
|
278
|
+
// The artifact's evidence pointer must match the canonical verification
|
|
279
|
+
// event when both claim a verified postcondition.
|
|
280
|
+
if (
|
|
281
|
+
artifact.lastEvidenceRef !== undefined
|
|
282
|
+
&& artifact.lastEvidenceRef !== null
|
|
283
|
+
&& verification.valid
|
|
284
|
+
&& state === "VERIFIED"
|
|
285
|
+
&& artifact.lastEvidenceRef !== verification.evidenceRef
|
|
286
|
+
) {
|
|
287
|
+
errors.push(issue(E_ACTION_EVIDENCE_INVALID, `action ${actionId}: artifact lastEvidenceRef does not match the ACTION_VERIFIED evidence reference`));
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return {
|
|
292
|
+
actionId,
|
|
293
|
+
valid: errors.length === 0,
|
|
294
|
+
state,
|
|
295
|
+
revision,
|
|
296
|
+
actionFingerprint,
|
|
297
|
+
authorization,
|
|
298
|
+
verification,
|
|
299
|
+
reconciliation,
|
|
300
|
+
errors,
|
|
301
|
+
};
|
|
302
|
+
}
|