@iowarp/clio-coder 0.3.7 → 0.3.8
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/CHANGELOG.md +31 -0
- package/README.md +7 -2
- package/dist/{acp-SK4MD6MM.js → acp-U67UHUK2.js} +13 -13
- package/dist/{agents-2FN2K6ME.js → agents-YU6SGALZ.js} +35 -33
- package/dist/assets/codewiki.json +1 -1
- package/dist/{auth-QIYZWM5I.js → auth-5ZPJOIVG.js} +32 -24
- package/dist/builtins-C6JMZVV6.js +17 -0
- package/dist/{chunk-YD734TPH.js → chunk-26LEYJZH.js} +2 -2
- package/dist/{chunk-TSHXZTOQ.js → chunk-2HEJ2F35.js} +5 -5
- package/dist/{chunk-WJHBC77E.js → chunk-2HFZQUHL.js} +5 -5
- package/dist/{chunk-FO5ZOVUY.js → chunk-3BINW3FP.js} +5 -5
- package/dist/{chunk-EOOQZZDE.js → chunk-4SPRNWDE.js} +18 -18
- package/dist/{chunk-EBEFWSGL.js → chunk-5DHKRSMQ.js} +7 -7
- package/dist/{chunk-OBMAI2DP.js → chunk-5H3GB5BO.js} +11 -9
- package/dist/{chunk-JTSEDYVQ.js → chunk-5Q2VVUKB.js} +7 -7
- package/dist/{chunk-VQNODYQ4.js → chunk-7RFXX52T.js} +294 -3704
- package/dist/{chunk-XEGB6BCN.js → chunk-7RGZWPB6.js} +5 -5
- package/dist/{chunk-GH5622CP.js → chunk-A2NJGIB3.js} +2 -2
- package/dist/{chunk-CEYBNUGC.js → chunk-A3WNZD3P.js} +323 -27
- package/dist/{chunk-D4MDIG46.js → chunk-B5CSFE7B.js} +7 -7
- package/dist/{chunk-2SFS6XQE.js → chunk-DGSYXYMX.js} +3 -2
- package/dist/{chunk-ZWLZP4ZT.js → chunk-DYIM5TJT.js} +98 -12
- package/dist/{chunk-KCMKRQX4.js → chunk-E77JEWSD.js} +42 -49
- package/dist/{chunk-J7PIKKWC.js → chunk-EMYUUSFG.js} +7 -7
- package/dist/{chunk-JRIO5UD2.js → chunk-EQ63NRB7.js} +5 -5
- package/dist/{chunk-UVDSQ6LW.js → chunk-FBVTI2TJ.js} +74 -28
- package/dist/{chunk-BMWK7ZIZ.js → chunk-FHJEP5SW.js} +13 -13
- package/dist/{chunk-FOT2FX5J.js → chunk-GN57SG4G.js} +7 -7
- package/dist/{chunk-SROCI7ZU.js → chunk-GPIEI3LY.js} +5 -5
- package/dist/{chunk-UFQ3F4FW.js → chunk-GU2UIAFZ.js} +4 -4
- package/dist/{chunk-UND3GU2L.js → chunk-H7IXIC72.js} +2 -2
- package/dist/{chunk-VREKEFLL.js → chunk-HLE42MG7.js} +3 -3
- package/dist/{chunk-465YSENW.js → chunk-IFBNV6H6.js} +3 -3
- package/dist/{chunk-LADCF22A.js → chunk-IGWKHNIQ.js} +113 -54
- package/dist/chunk-IIZWH4XA.js +172 -0
- package/dist/{chunk-UHXRNZ2J.js → chunk-IJ7RPIYJ.js} +4 -4
- package/dist/{chunk-M4AKACEO.js → chunk-J3YUBZWY.js} +2 -2
- package/dist/{chunk-AB4XIIVB.js → chunk-JOZYP4GM.js} +6 -6
- package/dist/{chunk-ZGH7FGS5.js → chunk-K4XHGFR5.js} +742 -12
- package/dist/{chunk-EELBMBT6.js → chunk-KTYTFRMB.js} +62 -5
- package/dist/chunk-LU7P4LHA.js +33 -0
- package/dist/{chunk-DQA7QLMD.js → chunk-ME6CCNFO.js} +9 -24
- package/dist/{chunk-KZ2H5X4G.js → chunk-MXKJU4JB.js} +88 -14
- package/dist/{chunk-DMD2AGVS.js → chunk-N22QMJKY.js} +20 -18
- package/dist/chunk-NMPKI6XL.js +3006 -0
- package/dist/chunk-NUGM5KR6.js +165 -0
- package/dist/{chunk-5C77SEEY.js → chunk-P43ETTHK.js} +2 -2
- package/dist/{chunk-JNXPYBB4.js → chunk-PMDBGQSJ.js} +2 -2
- package/dist/chunk-PT7HYKEM.js +165 -0
- package/dist/{chunk-GEYXPTRF.js → chunk-RWSI4YD7.js} +2 -2
- package/dist/{chunk-C4JBQ5SR.js → chunk-TANS5ZJS.js} +6 -6
- package/dist/{chunk-YTYFXUI3.js → chunk-TB5666IT.js} +9 -9
- package/dist/{chunk-UUANF5CR.js → chunk-TLQJPP24.js} +375 -687
- package/dist/{chunk-OB5HIGJY.js → chunk-TT36MB5S.js} +2 -1
- package/dist/{chunk-DJNLUABN.js → chunk-TTHACPOM.js} +135 -17
- package/dist/{chunk-6M7VS3J3.js → chunk-TYPGUK6W.js} +4 -4
- package/dist/{chunk-THKY7CD7.js → chunk-U6MBIEMB.js} +108 -24
- package/dist/{chunk-PD3MESLB.js → chunk-VAWNZU7Z.js} +4 -4
- package/dist/{chunk-IR4CFBFN.js → chunk-VCBR6CU7.js} +12 -12
- package/dist/{chunk-WHJYKASB.js → chunk-VHN4MY6O.js} +2 -2
- package/dist/{chunk-MXI6J5JF.js → chunk-VWZOAB7K.js} +10 -10
- package/dist/{chunk-QCTRSGHQ.js → chunk-WLFILSD5.js} +36 -36
- package/dist/{chunk-5UJ6ECTS.js → chunk-WNIJTQQK.js} +6 -6
- package/dist/{chunk-4DWFMQDR.js → chunk-WSB3FPX7.js} +68 -20
- package/dist/{chunk-3HAPLH5M.js → chunk-WWCZ5F23.js} +112 -10
- package/dist/{chunk-X2KV5FXT.js → chunk-WXY7KU3G.js} +2 -2
- package/dist/{chunk-PPAMZ32Z.js → chunk-XK56QHLX.js} +6 -1
- package/dist/{chunk-DOOEX22V.js → chunk-XWSF374K.js} +4 -4
- package/dist/{chunk-6TUKSZVF.js → chunk-YS5VLNH5.js} +8 -8
- package/dist/{chunk-ZZMN5OM4.js → chunk-ZNLWCMVZ.js} +2 -2
- package/dist/{chunk-WHGPSPT5.js → chunk-ZVJ5BLO2.js} +2 -2
- package/dist/cli/index.js +31 -31
- package/dist/{clio-WBVQEBKO.js → clio-QVTYJ57A.js} +8 -8
- package/dist/code-nav-FGGFIE7L.js +2 -2
- package/dist/codewiki/build-worker.js +4 -4
- package/dist/{components-F7OEATSO.js → components-ZFA3SAER.js} +8 -8
- package/dist/{config-TRBL3RCF.js → config-LW5IJFQN.js} +58 -53
- package/dist/{configure-OLCVPHNM.js → configure-7XIZCOU4.js} +26 -21
- package/dist/{context-XEWE3MOJ.js → context-L3WL3X7K.js} +47 -41
- package/dist/{context-WFPKQSM6.js → context-N52ZA626.js} +22 -22
- package/dist/{context-MJIJ6GOX.js → context-Y6Y7QPR6.js} +12 -12
- package/dist/{context-clear-KNOS2JPB.js → context-clear-MBQRLSDQ.js} +47 -41
- package/dist/{context-index-SSR5ECNE.js → context-index-HVMFQHK3.js} +5 -5
- package/dist/{context-working-set-EUXAZI6N.js → context-working-set-GS6DSO7F.js} +14 -14
- package/dist/{dispatch-runner-B7MTOVKL.js → dispatch-runner-22ZCNOM3.js} +83 -55
- package/dist/{docs-FLJTIDSE.js → docs-7LQ23DLM.js} +8 -8
- package/dist/{doctor-RN4YKO2X.js → doctor-M7YEDGAE.js} +24 -20
- package/dist/{eval-RUBJVSNQ.js → eval-BEC2WHDA.js} +71 -19
- package/dist/{evidence-JZNBUOQZ.js → evidence-REJUMSKM.js} +62 -56
- package/dist/{evolve-FJVC4KKI.js → evolve-PY5ZBA5K.js} +41 -35
- package/dist/{extensions-IQL36S7K.js → extensions-HVKU65YU.js} +6 -6
- package/dist/{fleet-BDKYJFCP.js → fleet-7WZEWRFA.js} +72 -67
- package/dist/{fleet-commands-ZFIWZSB3.js → fleet-commands-UVHWM76J.js} +10 -10
- package/dist/{fleet-graph-Y6HPXIVF.js → fleet-graph-6ULH7PES.js} +13 -13
- package/dist/{fleet-preflight-BHSNPBMH.js → fleet-preflight-J53T6CCE.js} +5 -5
- package/dist/{fleet-validate-BIYREGIK.js → fleet-validate-72PC4SLA.js} +17 -17
- package/dist/{init-LQUB5COQ.js → init-OG3TPGQG.js} +61 -56
- package/dist/{library-NJAHIGG4.js → library-CNTMPLRF.js} +18 -18
- package/dist/{memory-OG6HOYKM.js → memory-6IS7F275.js} +43 -37
- package/dist/{models-5ZG5XY7J.js → models-ENRJDA5W.js} +33 -28
- package/dist/{monitor-TJ7AMTGB.js → monitor-XLDVO7TN.js} +56 -38
- package/dist/{orchestrator-WZYB54DM.js → orchestrator-6KSPYRHA.js} +421 -303
- package/dist/{paths-XUC7GS6E.js → paths-DBXMZMDU.js} +5 -5
- package/dist/registry-LG64LTF4.js +11 -0
- package/dist/{reset-PXQT45IY.js → reset-RZ4ER727.js} +10 -10
- package/dist/{run-FQ74YF62.js → run-Y2CNK5RU.js} +79 -74
- package/dist/{share-FW7SVCL3.js → share-A55GYP6Z.js} +16 -16
- package/dist/{skills-7E7IRB3R.js → skills-ALC5J6AT.js} +19 -19
- package/dist/{skills-eval-LI75W6OK.js → skills-eval-JPBEBYQU.js} +52 -47
- package/dist/support-MIETYA5E.js +38 -0
- package/dist/{targets-4CIFKCTW.js → targets-VGNXIR3S.js} +42 -33
- package/dist/{terminal-lease-WUZY7ZV5.js → terminal-lease-WOBR64YA.js} +3 -3
- package/dist/{uninstall-7FV7IP4E.js → uninstall-ZJF5H5ZN.js} +8 -8
- package/dist/{upgrade-K2HVIVMQ.js → upgrade-FUSUAGHR.js} +27 -25
- package/dist/{usage-GTZELZQX.js → usage-N4MKVHKD.js} +51 -46
- package/dist/{verifiers-RLAHT27O.js → verifiers-YAWOJ3H2.js} +13 -13
- package/dist/{verify-BX3BRKH5.js → verify-LTDHYBGY.js} +9 -9
- package/dist/{wiki-generate-ASIFASCN.js → wiki-generate-6M7GHTBJ.js} +66 -61
- package/dist/worker/entry.js +68 -61
- package/docs/alcf-provider.md +1 -1
- package/docs/architecture.md +1 -1
- package/docs/artifact-versions.md +6 -5
- package/docs/built-in-agents.md +1 -1
- package/docs/commands-and-modes.md +2 -2
- package/docs/configuration-and-targets.md +8 -3
- package/docs/context-engine.md +1 -1
- package/docs/documentation-guide.md +1 -1
- package/docs/eval-runner.md +1 -1
- package/docs/evals-internal.md +1 -1
- package/docs/evidence-and-memory.md +70 -6
- package/docs/evolution.md +1 -1
- package/docs/extensions-and-sharing.md +1 -1
- package/docs/fleet-dispatch.md +34 -9
- package/docs/glossary.md +21 -1
- package/docs/installation-and-lifecycle.md +1 -1
- package/docs/middleware-and-components.md +1 -1
- package/docs/model-catalog.md +1 -1
- package/docs/observability.md +2 -2
- package/docs/proactive-memory.md +1 -1
- package/docs/prompt-envelope-and-tools.md +4 -2
- package/docs/provider-adapter-cookbook.md +1 -1
- package/docs/release-cut-checklist.md +41 -38
- package/docs/safety-model.md +1 -1
- package/docs/scientific-validation.md +1 -1
- package/docs/skills-marketplace.md +1 -1
- package/docs/tool-usage.md +1 -1
- package/docs/trace-store.md +1 -1
- package/docs/tui-design.md +1 -1
- package/docs/worker-dispatch-mechanics.md +1 -1
- package/package.json +1 -2
- package/src/cli/argv.ts +5 -0
- package/src/cli/configure.ts +107 -23
- package/src/cli/doctor.ts +5 -1
- package/src/cli/evidence.ts +30 -25
- package/src/cli/fleet-preflight.ts +2 -12
- package/src/cli/shared.ts +1 -0
- package/src/cli/targets.ts +4 -1
- package/src/cli/validate-model.ts +60 -5
- package/src/core/bus-events.ts +25 -0
- package/src/core/commit-attribution.ts +4 -4
- package/src/core/path-boundary.ts +100 -0
- package/src/domains/agents/extension.ts +2 -11
- package/src/domains/agents/fleet-contract.ts +30 -12
- package/src/domains/agents/recipe.ts +7 -1
- package/src/domains/agents/registry.ts +73 -5
- package/src/domains/agents/result-contract.ts +128 -17
- package/src/domains/agents/write-boundary.ts +15 -50
- package/src/domains/context/project-rules.ts +51 -1
- package/src/domains/dispatch/assignment-reconcile.ts +22 -5
- package/src/domains/dispatch/assignment-store.ts +151 -14
- package/src/domains/dispatch/contract.ts +15 -1
- package/src/domains/dispatch/delegation-plan.ts +2 -5
- package/src/domains/dispatch/execution-role.ts +9 -1
- package/src/domains/dispatch/extension.ts +156 -67
- package/src/domains/dispatch/fleet-run.ts +57 -3
- package/src/domains/dispatch/gate-role-prompts.ts +38 -0
- package/src/domains/dispatch/index.ts +3 -1
- package/src/domains/dispatch/intent-requirements.ts +40 -0
- package/src/domains/dispatch/intent.ts +84 -8
- package/src/domains/dispatch/path-scope.ts +370 -0
- package/src/domains/dispatch/receipt-integrity.ts +2 -1
- package/src/domains/dispatch/types.ts +14 -7
- package/src/domains/dispatch/validation.ts +6 -3
- package/src/domains/dispatch/write-boundary-enforcer.ts +45 -0
- package/src/domains/dispatch/write-boundary.ts +201 -22
- package/src/domains/eval/metrics/evidence.ts +79 -2
- package/src/domains/eval/runners/clio-run.ts +12 -2
- package/src/domains/evidence/build.ts +69 -11
- package/src/domains/evidence/index.ts +21 -0
- package/src/domains/evidence/provenance.ts +46 -11
- package/src/domains/evidence/trust-projection.ts +274 -0
- package/src/domains/evidence/trust-status.ts +145 -17
- package/src/domains/evidence/types.ts +4 -0
- package/src/domains/extensions/discovery.ts +88 -1
- package/src/domains/extensions/resources.ts +20 -8
- package/src/domains/extensions/state.ts +6 -2
- package/src/domains/extensions/types.ts +4 -1
- package/src/domains/lifecycle/doctor.ts +140 -1
- package/src/domains/prompts/contract.ts +3 -5
- package/src/domains/providers/extension.ts +30 -2
- package/src/domains/resources/common-loader.ts +3 -0
- package/src/domains/resources/prompts/loader.ts +119 -14
- package/src/domains/safety/policy-engine.ts +5 -5
- package/src/domains/safety/run-effects.ts +64 -1
- package/src/domains/safety/scope.ts +7 -12
- package/src/engine/acp/server.ts +4 -1
- package/src/engine/prompt-templates.ts +18 -1
- package/src/engine/worker-runtime.ts +6 -3
- package/src/interactive/dispatch-board.ts +53 -3
- package/src/interactive/interactive-event-projection.ts +14 -0
- package/src/interactive/overlays/settings.ts +141 -47
- package/src/interactive/slash-commands.ts +7 -2
- package/src/interactive/view/artifacts.ts +42 -9
- package/src/interactive/view/view-overlay.ts +15 -3
- package/src/interactive/worker-receipts.ts +14 -2
- package/src/interactive/worker-stream.ts +8 -0
- package/src/tools/dispatch-admission.ts +12 -13
- package/src/tools/dispatch-arguments.ts +27 -0
- package/src/tools/dispatch-plan.ts +29 -0
- package/src/tools/dispatch-runner.ts +48 -13
- package/src/tools/monitor.ts +13 -0
- package/src/tools/worker-evidence.ts +19 -13
- package/src/worker/spec-contract.ts +2 -1
|
@@ -29,8 +29,8 @@ import { extractRunProvenance, provenanceTranscriptLines } from "./provenance.js
|
|
|
29
29
|
import { createRedactionTally, redactSecretsDeep, redactSecretsText } from "./redact.js";
|
|
30
30
|
import { buildEvidenceTrustStatusFile } from "./run-trust.js";
|
|
31
31
|
import { EVIDENCE_FILES, evidenceDirectory, findingsFile } from "./store.js";
|
|
32
|
-
import type { TrustArtifactReference } from "./trust-status.js";
|
|
33
|
-
import { inspectRunReceiptTrustStatus } from "./trust-status.js";
|
|
32
|
+
import type { CanonicalTrustStatus, TrustArtifactReference } from "./trust-status.js";
|
|
33
|
+
import { inspectRunReceiptTrustStatus, retiredIntegrityVersionOf } from "./trust-status.js";
|
|
34
34
|
import {
|
|
35
35
|
EVIDENCE_VERSION,
|
|
36
36
|
type EvidenceAuditLinkedRow,
|
|
@@ -45,6 +45,7 @@ import {
|
|
|
45
45
|
type EvidenceReceiptFile,
|
|
46
46
|
type EvidenceRunLink,
|
|
47
47
|
type EvidenceRunSource,
|
|
48
|
+
type EvidenceSeverity,
|
|
48
49
|
type EvidenceTag,
|
|
49
50
|
type EvidenceToolEvent,
|
|
50
51
|
type EvidenceTrustStatusFile,
|
|
@@ -195,7 +196,10 @@ export async function buildEvidence(options: BuildEvidenceOptions): Promise<Evid
|
|
|
195
196
|
redactedToolEvents,
|
|
196
197
|
protectedArtifacts,
|
|
197
198
|
);
|
|
198
|
-
const transcript = redactSecretsText(
|
|
199
|
+
const transcript = redactSecretsText(
|
|
200
|
+
renderTranscript(overview, redactedRunSources, sessionLinks, trustStatusRaw),
|
|
201
|
+
tally,
|
|
202
|
+
);
|
|
199
203
|
const finalOverview: EvidenceOverview = { ...overview, redactionCount: tally.count };
|
|
200
204
|
await writeEvidenceFiles(
|
|
201
205
|
directory,
|
|
@@ -285,8 +289,14 @@ function buildFindings(
|
|
|
285
289
|
const status = trustByRun.get(source.envelope.id);
|
|
286
290
|
if (status === undefined) throw new Error(`canonical trust status missing for run ${source.envelope.id}`);
|
|
287
291
|
if (source.receiptError !== null) {
|
|
288
|
-
|
|
289
|
-
|
|
292
|
+
// The canonical axis, not the raw reason string, says whether the seal
|
|
293
|
+
// was retired: `receipt-retired` is the expected state of every run
|
|
294
|
+
// that predates an integrity bump and is an info row, while
|
|
295
|
+
// `receipt-integrity` stays the warning for a seal that was checked
|
|
296
|
+
// and rejected.
|
|
297
|
+
const retired = retiredIntegrityVersionOf(status.artifactIntegrity) !== null;
|
|
298
|
+
const tag = retired ? "receipt-retired" : source.receiptIntegrityFailed ? "receipt-integrity" : "unknown";
|
|
299
|
+
findings.push(finding(findings.length, retired ? "info" : "warn", tag, source.envelope.id, source.receiptError));
|
|
290
300
|
}
|
|
291
301
|
if (
|
|
292
302
|
isSuccessfulRun(source) &&
|
|
@@ -359,6 +369,13 @@ function buildFindings(
|
|
|
359
369
|
),
|
|
360
370
|
);
|
|
361
371
|
}
|
|
372
|
+
// The remaining axes were never read here, so a run could finish with
|
|
373
|
+
// no independent review, a contradictory context record, or a mutation
|
|
374
|
+
// nobody validated, and findings.md said nothing. Each finding is
|
|
375
|
+
// selected from the canonical state the way the validation and
|
|
376
|
+
// autonomy findings above are, with the detailed record left in
|
|
377
|
+
// trust-status.json.
|
|
378
|
+
findings.push(...axisFindings(findings.length, source, status));
|
|
362
379
|
if (source.envelope.status === "stale" || source.envelope.status === "dead") {
|
|
363
380
|
findings.push(
|
|
364
381
|
finding(findings.length, "warn", "timeout", source.envelope.id, `run status ${source.envelope.status}`),
|
|
@@ -601,7 +618,7 @@ async function writeEvidenceFiles(
|
|
|
601
618
|
await writeJson(join(directory, "overview.json"), overview);
|
|
602
619
|
await writeFile(
|
|
603
620
|
join(directory, "transcript.md"),
|
|
604
|
-
transcript ?? renderTranscript(overview, runSources, sessionLinks),
|
|
621
|
+
transcript ?? renderTranscript(overview, runSources, sessionLinks, trustStatus),
|
|
605
622
|
"utf8",
|
|
606
623
|
);
|
|
607
624
|
await writeJsonl(join(directory, "trace.raw.jsonl"), rawTraceRows(runSources));
|
|
@@ -636,9 +653,11 @@ function cleanedTraceRows(
|
|
|
636
653
|
const rows: EvidenceCleanTraceRow[] = [];
|
|
637
654
|
for (const source of runSources) {
|
|
638
655
|
const envelope = source.envelope;
|
|
639
|
-
// Clean trace rows project only provenance carried by
|
|
656
|
+
// Clean trace rows project only provenance carried by an authenticated
|
|
657
|
+
// receipt: a rejected or retired seal contributes none of its fields.
|
|
640
658
|
// Runs without receipt provenance keep the standard row shape.
|
|
641
|
-
const
|
|
659
|
+
const receipt = authenticatedReceipt(source);
|
|
660
|
+
const provenance = receipt === null ? {} : extractRunProvenance(receipt);
|
|
642
661
|
rows.push({
|
|
643
662
|
kind: "run",
|
|
644
663
|
runId: envelope.id,
|
|
@@ -1258,7 +1277,9 @@ function renderTranscript(
|
|
|
1258
1277
|
overview: EvidenceOverview,
|
|
1259
1278
|
runSources: ReadonlyArray<EvidenceRunSource>,
|
|
1260
1279
|
sessionLinks: SessionLinkResult,
|
|
1280
|
+
trustStatus: EvidenceTrustStatusFile,
|
|
1261
1281
|
): string {
|
|
1282
|
+
const trustByRun = new Map(trustStatus.runs.map((entry) => [entry.runId, entry.status]));
|
|
1262
1283
|
const lines = [
|
|
1263
1284
|
`# Evidence ${overview.evidenceId}`,
|
|
1264
1285
|
"",
|
|
@@ -1274,10 +1295,11 @@ function renderTranscript(
|
|
|
1274
1295
|
`- ${envelope.id} status=${envelope.status} exit=${exitCode ?? "?"} agent=${envelope.agentId} target=${envelope.targetId}`,
|
|
1275
1296
|
);
|
|
1276
1297
|
lines.push(` task: ${truncateText(envelope.task, MAX_TASK_CHARS)}`);
|
|
1277
|
-
// Run section rendering
|
|
1278
|
-
//
|
|
1298
|
+
// Run section rendering adds a sentence per provenance field the
|
|
1299
|
+
// canonical projection admits; a rejected or retired seal adds none.
|
|
1279
1300
|
if (source.receipt !== null) {
|
|
1280
|
-
|
|
1301
|
+
const status = trustByRun.get(envelope.id);
|
|
1302
|
+
for (const line of provenanceTranscriptLines(extractRunProvenance(source.receipt), status)) {
|
|
1281
1303
|
lines.push(` ${line}`);
|
|
1282
1304
|
}
|
|
1283
1305
|
}
|
|
@@ -1438,6 +1460,39 @@ function renderSessionTranscriptEntry(linked: LinkedSessionEntry): string[] {
|
|
|
1438
1460
|
return [`${prefix} ${String(_exhaustive)}`];
|
|
1439
1461
|
}
|
|
1440
1462
|
|
|
1463
|
+
/** Independent review, context provenance, and completion evidence, read off the canonical axes. */
|
|
1464
|
+
function axisFindings(offset: number, source: EvidenceRunSource, status: CanonicalTrustStatus): EvidenceFinding[] {
|
|
1465
|
+
const out: EvidenceFinding[] = [];
|
|
1466
|
+
const runId = source.envelope.id;
|
|
1467
|
+
const next = (severity: EvidenceSeverity, tag: EvidenceTag, message: string): void => {
|
|
1468
|
+
out.push(finding(offset + out.length, severity, tag, runId, message));
|
|
1469
|
+
};
|
|
1470
|
+
const review = status.independentReview.state;
|
|
1471
|
+
if (review === "failed") {
|
|
1472
|
+
next("warn", "independent-review", "independent review failed for this run");
|
|
1473
|
+
} else if (review === "not_independent") {
|
|
1474
|
+
next("warn", "independent-review", "review was not independent of the run it judged");
|
|
1475
|
+
} else if (review === "inconclusive") {
|
|
1476
|
+
next("info", "independent-review", "independent review was inconclusive");
|
|
1477
|
+
} else if (isSuccessfulRun(source) && (review === "absent" || review === "unknown")) {
|
|
1478
|
+
next("info", "independent-review", "run was not independently reviewed; its result rests on its own receipt");
|
|
1479
|
+
}
|
|
1480
|
+
if (status.contextProvenance.state === "invalid") {
|
|
1481
|
+
next("warn", "context-provenance", "context provenance record is contradictory; see trust-status.json");
|
|
1482
|
+
}
|
|
1483
|
+
const completion = status.completionEvidence.state;
|
|
1484
|
+
if (completion === "incomplete") {
|
|
1485
|
+
next(
|
|
1486
|
+
"warn",
|
|
1487
|
+
"completion-evidence",
|
|
1488
|
+
"run mutated the workspace and finished without validation evidence at the completion boundary",
|
|
1489
|
+
);
|
|
1490
|
+
} else if (completion === "limited") {
|
|
1491
|
+
next("info", "completion-evidence", "run finished with an explicit limitation instead of validation evidence");
|
|
1492
|
+
}
|
|
1493
|
+
return out;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1441
1496
|
function renderFindings(findings: ReadonlyArray<EvidenceFinding>): string {
|
|
1442
1497
|
if (findings.length === 0) return "# Findings\n\nNo findings.\n";
|
|
1443
1498
|
const lines = ["# Findings", ""];
|
|
@@ -1507,6 +1562,9 @@ async function readReceipt(
|
|
|
1507
1562
|
}
|
|
1508
1563
|
const integrity = inspectRunReceiptTrustStatus(receipt, envelope).integrity;
|
|
1509
1564
|
if (!integrity.ok) {
|
|
1565
|
+
// A retired seal is set aside unread, the way a missing receipt is; it
|
|
1566
|
+
// is not a failed one, and its reason already names both versions.
|
|
1567
|
+
if (integrity.retired !== undefined) return { receipt, error: integrity.reason, integrityFailed: false };
|
|
1510
1568
|
return { receipt, error: `receipt integrity: ${integrity.reason}`, integrityFailed: true };
|
|
1511
1569
|
}
|
|
1512
1570
|
return { receipt, error: null, integrityFailed: false };
|
|
@@ -5,6 +5,7 @@ export { buildEvalEvidence, evalEvidenceId } from "./eval.js";
|
|
|
5
5
|
export { FINISH_CONTRACT_EVIDENCE_TAGS, finishContractEvidenceTags } from "./finish-contract-map.js";
|
|
6
6
|
export type { RunEscalationCounts, RunProvenanceView } from "./provenance.js";
|
|
7
7
|
export {
|
|
8
|
+
admitRunProvenance,
|
|
8
9
|
extractRunProvenance,
|
|
9
10
|
hasRunProvenance,
|
|
10
11
|
PERSONA_HASH_PREFIX_CHARS,
|
|
@@ -22,6 +23,20 @@ export {
|
|
|
22
23
|
loadEvidenceRunProvenance,
|
|
23
24
|
loadEvidenceTrustStatus,
|
|
24
25
|
} from "./store.js";
|
|
26
|
+
export type { TrustSummaryProjection, TrustVerdict } from "./trust-projection.js";
|
|
27
|
+
export {
|
|
28
|
+
formatTrustAxes,
|
|
29
|
+
formatTrustSummary,
|
|
30
|
+
formatTrustSummaryLine,
|
|
31
|
+
summarizeTrustStatus,
|
|
32
|
+
TRUST_STATE_WORDS,
|
|
33
|
+
TRUST_SUMMARY_MAX_REFS,
|
|
34
|
+
TRUST_SUMMARY_VERSION,
|
|
35
|
+
TRUST_VERDICTS,
|
|
36
|
+
trustStateWord,
|
|
37
|
+
trustSummaryReferences,
|
|
38
|
+
trustVerdict,
|
|
39
|
+
} from "./trust-projection.js";
|
|
25
40
|
export type {
|
|
26
41
|
AbsentTrustStatus,
|
|
27
42
|
AdaptFinishContractTrustOptions,
|
|
@@ -35,6 +50,8 @@ export type {
|
|
|
35
50
|
GroundedEvidenceValidationInput,
|
|
36
51
|
IndependentReviewStatus,
|
|
37
52
|
PersistedRunReceiptTrustFacts,
|
|
53
|
+
ReceiptIntegrityOutcome,
|
|
54
|
+
RetiredReceiptIntegrity,
|
|
38
55
|
RunReceiptTrustInspection,
|
|
39
56
|
TrustAbsenceReason,
|
|
40
57
|
TrustArtifactKind,
|
|
@@ -63,12 +80,16 @@ export {
|
|
|
63
80
|
inspectRunReceiptTrustStatus,
|
|
64
81
|
normalizeTrustStatus,
|
|
65
82
|
projectTrustStatus,
|
|
83
|
+
retiredIntegrityVersionOf,
|
|
84
|
+
retiredReceiptIntegrity,
|
|
85
|
+
retiredReceiptIntegrityReason,
|
|
66
86
|
TRUST_STATUS_AXES,
|
|
67
87
|
TRUST_STATUS_MAX_ARTIFACT_REFERENCES,
|
|
68
88
|
TRUST_STATUS_MAX_IDENTIFIER_LENGTH,
|
|
69
89
|
TRUST_STATUS_STATES,
|
|
70
90
|
TRUST_STATUS_VERSION,
|
|
71
91
|
validateTrustStatus,
|
|
92
|
+
verifyReceiptIntegrityOutcome,
|
|
72
93
|
} from "./trust-status.js";
|
|
73
94
|
export type {
|
|
74
95
|
EvidenceAuditLinkedRow,
|
|
@@ -4,6 +4,7 @@ import type {
|
|
|
4
4
|
RunReceipt,
|
|
5
5
|
RunReceiptAutonomyEnforcement,
|
|
6
6
|
} from "../dispatch/types.js";
|
|
7
|
+
import type { CanonicalTrustStatus } from "./trust-status.js";
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* Worker permission-escalation counters projected from sealed receipt
|
|
@@ -50,6 +51,33 @@ export function extractRunProvenance(receipt: ProvenanceReceipt): RunProvenanceV
|
|
|
50
51
|
return view;
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
/**
|
|
55
|
+
* The slice of a provenance view the canonical projection admits. The
|
|
56
|
+
* projection is the only thing that decides whether an axis is reported, so
|
|
57
|
+
* the autonomy detail (`autonomy=`, `mode=`, `dangerousBypass=`) is kept only
|
|
58
|
+
* when the projection reports the autonomy axis in a recorded state, and the
|
|
59
|
+
* non-axis provenance (pipeline, persona override, escalations) only when the
|
|
60
|
+
* seal that carries it verified. With no projection at hand no axis detail is
|
|
61
|
+
* admitted at all, whatever the receipt says; the non-axis provenance passes
|
|
62
|
+
* through for a caller that prints it beside its own integrity banner.
|
|
63
|
+
*/
|
|
64
|
+
export function admitRunProvenance(view: RunProvenanceView, status?: CanonicalTrustStatus): RunProvenanceView {
|
|
65
|
+
const admitted: RunProvenanceView = {};
|
|
66
|
+
if (status === undefined || status.artifactIntegrity.state === "verified") {
|
|
67
|
+
if (view.pipeline !== undefined) admitted.pipeline = view.pipeline;
|
|
68
|
+
if (view.personaOverride !== undefined) admitted.personaOverride = view.personaOverride;
|
|
69
|
+
if (view.escalation !== undefined) admitted.escalation = view.escalation;
|
|
70
|
+
}
|
|
71
|
+
const autonomy = status?.autonomyEnforcement.state;
|
|
72
|
+
if (
|
|
73
|
+
view.autonomyEnforcement !== undefined &&
|
|
74
|
+
(autonomy === "enforced" || autonomy === "approximated" || autonomy === "bypassed")
|
|
75
|
+
) {
|
|
76
|
+
admitted.autonomyEnforcement = view.autonomyEnforcement;
|
|
77
|
+
}
|
|
78
|
+
return admitted;
|
|
79
|
+
}
|
|
80
|
+
|
|
53
81
|
/** True when at least one provenance field set is present. */
|
|
54
82
|
export function hasRunProvenance(view: RunProvenanceView): boolean {
|
|
55
83
|
return (
|
|
@@ -144,10 +172,15 @@ function formatPersonaHashPrefix(promptHash: string): string {
|
|
|
144
172
|
}
|
|
145
173
|
|
|
146
174
|
/**
|
|
147
|
-
* Human transcript sentences for each
|
|
148
|
-
*
|
|
175
|
+
* Human transcript sentences for each admitted provenance field set, gated
|
|
176
|
+
* through {@link admitRunProvenance}. The autonomy line carries only the
|
|
177
|
+
* detail the canonical projection does not: the policy name, the external
|
|
178
|
+
* mode, and the bypass flag. The axis itself (mediated, approximated,
|
|
179
|
+
* bypassed) is printed by the trust summary and nowhere else, so one
|
|
180
|
+
* autonomy fact never appears in two vocabularies on one screen.
|
|
149
181
|
*/
|
|
150
|
-
export function provenanceTranscriptLines(
|
|
182
|
+
export function provenanceTranscriptLines(source: RunProvenanceView, status?: CanonicalTrustStatus): string[] {
|
|
183
|
+
const view = admitRunProvenance(source, status);
|
|
151
184
|
const lines: string[] = [];
|
|
152
185
|
if (view.pipeline !== undefined) {
|
|
153
186
|
const { fromRunId, position, inputBytes, inputTruncated } = view.pipeline;
|
|
@@ -163,20 +196,22 @@ export function provenanceTranscriptLines(view: RunProvenanceView): string[] {
|
|
|
163
196
|
lines.push(`escalations: ${requested} requested, ${approved} approved, ${denied} denied, ${timedOut} timed out`);
|
|
164
197
|
}
|
|
165
198
|
if (view.autonomyEnforcement !== undefined) {
|
|
166
|
-
const {
|
|
199
|
+
const { autonomy, externalMode, dangerousBypass } = view.autonomyEnforcement;
|
|
167
200
|
const mode = externalMode !== undefined ? ` mode=${externalMode}` : "";
|
|
168
201
|
const bypass = dangerousBypass === true ? " dangerousBypass=true" : "";
|
|
169
|
-
lines.push(`autonomy
|
|
202
|
+
lines.push(`autonomy: ${autonomy}${mode}${bypass}`);
|
|
170
203
|
}
|
|
171
204
|
return lines;
|
|
172
205
|
}
|
|
173
206
|
|
|
174
207
|
/**
|
|
175
|
-
* Compact ` key=value` suffix for the dispatch tool's per-run and CLI lines
|
|
176
|
-
*
|
|
177
|
-
*
|
|
208
|
+
* Compact ` key=value` suffix for the dispatch tool's per-run and CLI lines,
|
|
209
|
+
* gated through {@link admitRunProvenance} on the same terms as the
|
|
210
|
+
* transcript lines. The leading space lets callers append it directly. An
|
|
211
|
+
* empty admitted view returns an empty string.
|
|
178
212
|
*/
|
|
179
|
-
export function provenanceCompactSuffix(
|
|
213
|
+
export function provenanceCompactSuffix(source: RunProvenanceView, status?: CanonicalTrustStatus): string {
|
|
214
|
+
const view = admitRunProvenance(source, status);
|
|
180
215
|
const parts: string[] = [];
|
|
181
216
|
if (view.pipeline !== undefined) {
|
|
182
217
|
const { fromRunId, position, inputBytes, inputTruncated } = view.pipeline;
|
|
@@ -191,10 +226,10 @@ export function provenanceCompactSuffix(view: RunProvenanceView): string {
|
|
|
191
226
|
parts.push(`escalations=${requested}req/${approved}appr/${denied}deny/${timedOut}timeout`);
|
|
192
227
|
}
|
|
193
228
|
if (view.autonomyEnforcement !== undefined) {
|
|
194
|
-
const {
|
|
229
|
+
const { autonomy, externalMode, dangerousBypass } = view.autonomyEnforcement;
|
|
195
230
|
const mode = externalMode !== undefined ? `/${externalMode}` : "";
|
|
196
231
|
const bypass = dangerousBypass === true ? "/bypass" : "";
|
|
197
|
-
parts.push(`
|
|
232
|
+
parts.push(`autonomy=${autonomy}${mode}${bypass}`);
|
|
198
233
|
}
|
|
199
234
|
return parts.length === 0 ? "" : ` ${parts.join(" ")}`;
|
|
200
235
|
}
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
import { RUN_RECEIPT_INTEGRITY_VERSION } from "../dispatch/receipt-integrity.js";
|
|
2
|
+
import {
|
|
3
|
+
type CanonicalTrustStatus,
|
|
4
|
+
retiredIntegrityVersionOf,
|
|
5
|
+
TRUST_STATUS_AXES,
|
|
6
|
+
type TRUST_STATUS_STATES,
|
|
7
|
+
type TrustArtifactReference,
|
|
8
|
+
type TrustStatusAxis,
|
|
9
|
+
} from "./trust-status.js";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The one projection of the canonical trust status every operator surface
|
|
13
|
+
* renders. The six-axis model stays the source of truth and is never
|
|
14
|
+
* collapsed into a score here; this module only fixes the words each state is
|
|
15
|
+
* called and the order the axes are read in, so the dispatch line, the
|
|
16
|
+
* monitor block, `evidence inspect`, `findings.md`, the Alt+W board, the
|
|
17
|
+
* receipt view, the eval bridge, and the ACP wire cannot each spell the same
|
|
18
|
+
* fact differently.
|
|
19
|
+
*
|
|
20
|
+
* Two shapes leave this module. `formatTrustSummary` is the compact human
|
|
21
|
+
* line: it answers who claims the result, what was observed, what was
|
|
22
|
+
* independently checked, and what is still unknown, without receipt
|
|
23
|
+
* internals. `summarizeTrustStatus` is the bounded, versioned machine record
|
|
24
|
+
* behind that line, carrying the axis states and typed references back to
|
|
25
|
+
* the artifacts the detailed model cites.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
export const TRUST_SUMMARY_VERSION = 1 as const;
|
|
29
|
+
|
|
30
|
+
/** Reference cap for the machine projection; the canonical model keeps up to 16 per axis. */
|
|
31
|
+
export const TRUST_SUMMARY_MAX_REFS = 8;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* How much the run's result can be leaned on, read off the axes in a fixed
|
|
35
|
+
* order. It is a presentation tier for styling and sorting, never a score:
|
|
36
|
+
* `reviewed` is the only tier that may be styled as independently verified.
|
|
37
|
+
*/
|
|
38
|
+
export type TrustVerdict = "reviewed" | "grounded" | "unverified" | "compromised" | "unknown";
|
|
39
|
+
|
|
40
|
+
export const TRUST_VERDICTS: ReadonlyArray<TrustVerdict> = [
|
|
41
|
+
"reviewed",
|
|
42
|
+
"grounded",
|
|
43
|
+
"unverified",
|
|
44
|
+
"compromised",
|
|
45
|
+
"unknown",
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
/** Bounded, versioned machine projection with references to the detailed artifacts. */
|
|
49
|
+
export interface TrustSummaryProjection {
|
|
50
|
+
version: typeof TRUST_SUMMARY_VERSION;
|
|
51
|
+
verdict: TrustVerdict;
|
|
52
|
+
/** The compact human line, byte-identical to what the text surfaces print. */
|
|
53
|
+
text: string;
|
|
54
|
+
/** Every axis state, in canonical vocabulary, for machine consumers. */
|
|
55
|
+
axes: Record<TrustStatusAxis, string>;
|
|
56
|
+
/** Who asserts the validation fact: `<authority kind>:<id>`, or `worker` when only the run's own prose exists. */
|
|
57
|
+
claimant: string;
|
|
58
|
+
/** Axes that answer nothing, so a consumer knows what remains unknown. */
|
|
59
|
+
unknown: TrustStatusAxis[];
|
|
60
|
+
/** `<artifact kind>:<id>` pointers to the detailed records, sorted, unique, capped. */
|
|
61
|
+
refs: string[];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
type AxisStates = { [Axis in TrustStatusAxis]: (typeof TRUST_STATUS_STATES)[Axis][number] };
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The standardized word for every state. A word is what an operator reads;
|
|
68
|
+
* the state id is what a machine reads. `mediated` names the `enforced`
|
|
69
|
+
* autonomy state because that is the receipt grade and the documented
|
|
70
|
+
* meaning: Clio's own safety gate mediated the run. `inferred` names an
|
|
71
|
+
* ungrounded validation claim: the worker said it validated, nothing was
|
|
72
|
+
* observed to have run.
|
|
73
|
+
*/
|
|
74
|
+
export const TRUST_STATE_WORDS: { [Axis in TrustStatusAxis]: Record<AxisStates[Axis], string> } = {
|
|
75
|
+
artifactIntegrity: {
|
|
76
|
+
verified: "sealed",
|
|
77
|
+
failed: "seal broken",
|
|
78
|
+
absent: "no receipt",
|
|
79
|
+
unknown: "seal unchecked",
|
|
80
|
+
not_applicable: "seal not applicable",
|
|
81
|
+
},
|
|
82
|
+
validationGrounding: {
|
|
83
|
+
validated: "grounded",
|
|
84
|
+
failed: "validation failed",
|
|
85
|
+
ungrounded: "inferred: validation claimed, none observed",
|
|
86
|
+
absent: "no validation observed",
|
|
87
|
+
unknown: "validation unknown",
|
|
88
|
+
not_applicable: "validation not applicable",
|
|
89
|
+
},
|
|
90
|
+
independentReview: {
|
|
91
|
+
passed: "independently reviewed: pass",
|
|
92
|
+
failed: "independently reviewed: fail",
|
|
93
|
+
inconclusive: "independent review inconclusive",
|
|
94
|
+
not_independent: "review not independent",
|
|
95
|
+
absent: "not independently reviewed",
|
|
96
|
+
unknown: "independent review unknown",
|
|
97
|
+
not_applicable: "independent review not applicable",
|
|
98
|
+
},
|
|
99
|
+
contextProvenance: {
|
|
100
|
+
recorded: "context recorded",
|
|
101
|
+
invalid: "context record invalid",
|
|
102
|
+
absent: "context not recorded",
|
|
103
|
+
unknown: "context unknown",
|
|
104
|
+
not_applicable: "context not applicable",
|
|
105
|
+
},
|
|
106
|
+
autonomyEnforcement: {
|
|
107
|
+
enforced: "mediated",
|
|
108
|
+
approximated: "approximated",
|
|
109
|
+
bypassed: "bypassed",
|
|
110
|
+
absent: "autonomy not recorded",
|
|
111
|
+
unknown: "autonomy unknown",
|
|
112
|
+
not_applicable: "autonomy not applicable",
|
|
113
|
+
},
|
|
114
|
+
completionEvidence: {
|
|
115
|
+
evidenced: "completion evidenced",
|
|
116
|
+
incomplete: "completion unevidenced",
|
|
117
|
+
limited: "completion limited",
|
|
118
|
+
absent: "completion not recorded",
|
|
119
|
+
unknown: "completion unknown",
|
|
120
|
+
not_applicable: "completion not applicable",
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/** The word for one axis state; unknown input never throws, it reads as unknown. */
|
|
125
|
+
export function trustStateWord<Axis extends TrustStatusAxis>(axis: Axis, state: string): string {
|
|
126
|
+
const words = TRUST_STATE_WORDS[axis] as Record<string, string>;
|
|
127
|
+
return words[state] ?? words.unknown ?? "unknown";
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function authorityOf(status: CanonicalTrustStatus, axis: TrustStatusAxis): string | null {
|
|
131
|
+
const entry = status[axis];
|
|
132
|
+
return entry.state === "absent" ? null : `${entry.authority.kind}:${entry.authority.id}`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* A seal this build does not verify because its version is retired names
|
|
137
|
+
* that version and the one in force, so an intact receipt from the previous
|
|
138
|
+
* release never reads as "seal broken" or as merely unchecked. The state
|
|
139
|
+
* behind the clause is `unknown`, which is what it is: nothing was checked.
|
|
140
|
+
*/
|
|
141
|
+
function integrityClause(status: CanonicalTrustStatus): string {
|
|
142
|
+
const retired = retiredIntegrityVersionOf(status.artifactIntegrity);
|
|
143
|
+
if (retired !== null) return `seal v${retired} retired (this build verifies v${RUN_RECEIPT_INTEGRITY_VERSION})`;
|
|
144
|
+
return trustStateWord("artifactIntegrity", status.artifactIntegrity.state);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* The validation clause names its claimant, because "grounded" alone hides
|
|
149
|
+
* the difference between a host-run check and a self-reported one.
|
|
150
|
+
*/
|
|
151
|
+
function validationClause(status: CanonicalTrustStatus): string {
|
|
152
|
+
const entry = status.validationGrounding;
|
|
153
|
+
const word = trustStateWord("validationGrounding", entry.state);
|
|
154
|
+
if (entry.state === "absent") return word;
|
|
155
|
+
if (entry.state === "validated" || entry.state === "failed") return `${word} by ${entry.authority.id}`;
|
|
156
|
+
// A named external system that could not observe the run is worth naming;
|
|
157
|
+
// the historical-format placeholder is not an authority anyone can ask.
|
|
158
|
+
if (entry.state === "unknown" && entry.authority.kind !== "unknown") return `${word} (${entry.authority.id})`;
|
|
159
|
+
return word;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** External runtimes name themselves on the autonomy clause so approximation is never anonymous. */
|
|
163
|
+
function autonomyClause(status: CanonicalTrustStatus): string {
|
|
164
|
+
const entry = status.autonomyEnforcement;
|
|
165
|
+
const word = trustStateWord("autonomyEnforcement", entry.state);
|
|
166
|
+
if (entry.state !== "approximated" && entry.state !== "bypassed") return word;
|
|
167
|
+
return `${word} (${entry.authority.id})`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* The compact human body. Clause order is fixed: integrity, validation with
|
|
172
|
+
* its claimant, independent review, autonomy, context, completion. Every
|
|
173
|
+
* surface prints this body verbatim, so two surfaces can only disagree by
|
|
174
|
+
* reading different canonical input.
|
|
175
|
+
*/
|
|
176
|
+
export function formatTrustSummary(status: CanonicalTrustStatus): string {
|
|
177
|
+
return [
|
|
178
|
+
integrityClause(status),
|
|
179
|
+
validationClause(status),
|
|
180
|
+
trustStateWord("independentReview", status.independentReview.state),
|
|
181
|
+
autonomyClause(status),
|
|
182
|
+
trustStateWord("contextProvenance", status.contextProvenance.state),
|
|
183
|
+
trustStateWord("completionEvidence", status.completionEvidence.state),
|
|
184
|
+
].join("; ");
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** The body under its versioned label, for surfaces that print one line per run. */
|
|
188
|
+
export function formatTrustSummaryLine(status: CanonicalTrustStatus): string {
|
|
189
|
+
return `trust v${TRUST_SUMMARY_VERSION}: ${formatTrustSummary(status)}`;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* The drill-down token line: every axis in canonical state ids. This is the
|
|
194
|
+
* one machine-readable text form; `evidence inspect` and the dispatch and
|
|
195
|
+
* monitor labels all print it from here.
|
|
196
|
+
*/
|
|
197
|
+
export function formatTrustAxes(status: CanonicalTrustStatus): string {
|
|
198
|
+
return [`trust_status=v${status.version}`, ...TRUST_STATUS_AXES.map((axis) => `${axis}:${status[axis].state}`)].join(
|
|
199
|
+
" ",
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
function isUnanswered(status: CanonicalTrustStatus, axis: TrustStatusAxis): boolean {
|
|
204
|
+
const state = status[axis].state;
|
|
205
|
+
return state === "absent" || state === "unknown";
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Read the tier off the axes in priority order. A broken seal, a bypassed
|
|
210
|
+
* gate, a failed or inferred validation, a failed or correlated review, or a
|
|
211
|
+
* contradictory context record compromises the result whatever else holds.
|
|
212
|
+
* An unchecked or missing seal leaves it unknown. Only an authenticated
|
|
213
|
+
* independent pass is `reviewed`; observed validation without one is
|
|
214
|
+
* `grounded`; a sealed receipt with nothing observed is `unverified`.
|
|
215
|
+
*/
|
|
216
|
+
export function trustVerdict(status: CanonicalTrustStatus): TrustVerdict {
|
|
217
|
+
const integrity = status.artifactIntegrity.state;
|
|
218
|
+
const validation = status.validationGrounding.state;
|
|
219
|
+
const review = status.independentReview.state;
|
|
220
|
+
const autonomy = status.autonomyEnforcement.state;
|
|
221
|
+
if (
|
|
222
|
+
integrity === "failed" ||
|
|
223
|
+
autonomy === "bypassed" ||
|
|
224
|
+
validation === "failed" ||
|
|
225
|
+
validation === "ungrounded" ||
|
|
226
|
+
review === "failed" ||
|
|
227
|
+
review === "not_independent" ||
|
|
228
|
+
status.contextProvenance.state === "invalid"
|
|
229
|
+
) {
|
|
230
|
+
return "compromised";
|
|
231
|
+
}
|
|
232
|
+
if (integrity !== "verified") return "unknown";
|
|
233
|
+
if (review === "passed") return "reviewed";
|
|
234
|
+
if (validation === "validated") return "grounded";
|
|
235
|
+
return "unverified";
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function claimantOf(status: CanonicalTrustStatus): string {
|
|
239
|
+
const state = status.validationGrounding.state;
|
|
240
|
+
if (state === "absent" || state === "ungrounded") return "worker";
|
|
241
|
+
return authorityOf(status, "validationGrounding") ?? "worker";
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function referenceKey(reference: TrustArtifactReference): string {
|
|
245
|
+
return `${reference.kind}:${reference.id}`;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Every artifact the axes cite, as bounded pointers a consumer can resolve. */
|
|
249
|
+
export function trustSummaryReferences(status: CanonicalTrustStatus): string[] {
|
|
250
|
+
const keys = new Set<string>();
|
|
251
|
+
for (const axis of TRUST_STATUS_AXES) {
|
|
252
|
+
const entry = status[axis];
|
|
253
|
+
if (entry.state === "absent") continue;
|
|
254
|
+
for (const reference of entry.artifacts) keys.add(referenceKey(reference));
|
|
255
|
+
}
|
|
256
|
+
return [...keys].sort().slice(0, TRUST_SUMMARY_MAX_REFS);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** The bounded machine projection. Flat by design so a depth-capped wire keeps it whole. */
|
|
260
|
+
export function summarizeTrustStatus(status: CanonicalTrustStatus): TrustSummaryProjection {
|
|
261
|
+
const axes = Object.fromEntries(TRUST_STATUS_AXES.map((axis) => [axis, status[axis].state])) as Record<
|
|
262
|
+
TrustStatusAxis,
|
|
263
|
+
string
|
|
264
|
+
>;
|
|
265
|
+
return {
|
|
266
|
+
version: TRUST_SUMMARY_VERSION,
|
|
267
|
+
verdict: trustVerdict(status),
|
|
268
|
+
text: formatTrustSummary(status),
|
|
269
|
+
axes,
|
|
270
|
+
claimant: claimantOf(status),
|
|
271
|
+
unknown: TRUST_STATUS_AXES.filter((axis) => isUnanswered(status, axis)),
|
|
272
|
+
refs: trustSummaryReferences(status),
|
|
273
|
+
};
|
|
274
|
+
}
|