feat: add external evidence receipts
This commit is contained in:
parent
712d94c689
commit
93c9697dbb
|
|
@ -395,6 +395,8 @@ fable-agent plinius godmode "improve explanation quality"
|
|||
- `--output <path>`
|
||||
- `fable5 falsification`
|
||||
- `--output <path>`
|
||||
- `fable5 external-rsi-evidence`
|
||||
- `--output <path>`
|
||||
- `fable5 receipt-health`
|
||||
- `--root <path>`
|
||||
- `--window-minutes <n>`
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { createExternalRsiEvidenceReceipt, writeExternalEvidenceReceipt } from "./external-evidence.js";
|
||||
|
||||
describe("external evidence receipts", () => {
|
||||
it("records external RSI proof as advisory, not local deploy authority", () => {
|
||||
const receipt = createExternalRsiEvidenceReceipt(new Date("2026-07-01T00:00:00.000Z"));
|
||||
|
||||
expect(receipt).toMatchObject({
|
||||
schema: "fable.external_evidence.receipt.v1",
|
||||
source: "conversation memory citing pi-real-engineering RSI proof",
|
||||
decision: "advisory",
|
||||
deployAttempted: false,
|
||||
});
|
||||
expect(receipt.evidence.every((entry) => entry.scope === "external")).toBe(true);
|
||||
expect(receipt.notUsableFor).toContain("local fable-agent deploy authority");
|
||||
expect(receipt.notUsableFor).toContain("auto_patch_proven=true without fresh local degraded-skill recovery receipt");
|
||||
expect(receipt.notUsableFor).toContain("8098/deploy-webhook trust");
|
||||
});
|
||||
|
||||
it("writes external evidence receipts", () => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "external-evidence-"));
|
||||
const file = path.join(dir, "receipt.json");
|
||||
|
||||
writeExternalEvidenceReceipt(file, createExternalRsiEvidenceReceipt());
|
||||
|
||||
expect(JSON.parse(fs.readFileSync(file, "utf-8"))).toMatchObject({ schema: "fable.external_evidence.receipt.v1", decision: "advisory" });
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import * as fs from "node:fs";
|
||||
import * as path from "node:path";
|
||||
|
||||
export interface ExternalEvidenceReceipt {
|
||||
schema: "fable.external_evidence.receipt.v1";
|
||||
createdAt: string;
|
||||
source: string;
|
||||
evidence: Array<{ claim: string; reference: string; scope: "external" | "local" }>;
|
||||
usableFor: string[];
|
||||
notUsableFor: string[];
|
||||
decision: "advisory" | "local-authority";
|
||||
deployAttempted: false;
|
||||
}
|
||||
|
||||
export function createExternalRsiEvidenceReceipt(now = new Date()): ExternalEvidenceReceipt {
|
||||
return {
|
||||
schema: "fable.external_evidence.receipt.v1",
|
||||
createdAt: now.toISOString(),
|
||||
source: "conversation memory citing pi-real-engineering RSI proof",
|
||||
evidence: [
|
||||
{
|
||||
claim: "hermes rsi_canary recovered after repair",
|
||||
reference: "pi-real-engineering:/tmp/rsi-proof-20260619T093724Z.md",
|
||||
scope: "external",
|
||||
},
|
||||
{
|
||||
claim: "worker_skill_repair.sh copied skill_health.py and verified rsi_canary",
|
||||
reference: "pi-real-engineering:/tmp/rsi-proof-20260619T093724Z.md",
|
||||
scope: "external",
|
||||
},
|
||||
],
|
||||
usableFor: ["advisory RSI recovery history", "future local proof import checklist", "red-team/safety context"],
|
||||
notUsableFor: [
|
||||
"local fable-agent deploy authority",
|
||||
"auto_patch_proven=true without fresh local degraded-skill recovery receipt",
|
||||
"8098/deploy-webhook trust",
|
||||
"container count truth without live reconciliation",
|
||||
],
|
||||
decision: "advisory",
|
||||
deployAttempted: false,
|
||||
};
|
||||
}
|
||||
|
||||
export function writeExternalEvidenceReceipt(file: string, receipt: ExternalEvidenceReceipt): string {
|
||||
fs.mkdirSync(path.dirname(file), { recursive: true });
|
||||
fs.writeFileSync(file, `${JSON.stringify(receipt, null, 2)}\n`);
|
||||
return file;
|
||||
}
|
||||
|
|
@ -72,6 +72,9 @@ export type { StaleMemoryClaim, StaleMemoryQuarantineReceipt, StaleMemorySeverit
|
|||
export { createFactoryMemoryFalsificationReceipt, createFalsificationReceipt, writeFalsificationReceipt } from "./falsification-receipt.js";
|
||||
export type { FalsificationCase, FalsificationReceipt } from "./falsification-receipt.js";
|
||||
|
||||
export { createExternalRsiEvidenceReceipt, writeExternalEvidenceReceipt } from "./external-evidence.js";
|
||||
export type { ExternalEvidenceReceipt } from "./external-evidence.js";
|
||||
|
||||
export { channelPath, emitChannelEvent, readChannelEvents } from "./channel.js";
|
||||
export type { ChannelEvent, ChannelEventSource, ChannelEventType, NewChannelEvent } from "./channel.js";
|
||||
|
||||
|
|
|
|||
15
src/index.ts
15
src/index.ts
|
|
@ -2335,6 +2335,21 @@ fable
|
|||
if (receipt.decision === "failed") process.exit(1);
|
||||
});
|
||||
|
||||
fable
|
||||
.command("external-rsi-evidence")
|
||||
.description("Write advisory external RSI evidence receipt without local deploy authority")
|
||||
.option("--output <path>", "Receipt output path", path.join(".fable", "evidence", "external-rsi-evidence-live.json"))
|
||||
.action(async (opts: { output?: string }) => {
|
||||
const { createExternalRsiEvidenceReceipt, writeExternalEvidenceReceipt } = await import("./fable5/external-evidence.js");
|
||||
const receipt = createExternalRsiEvidenceReceipt();
|
||||
const out = opts.output ?? path.join(".fable", "evidence", "external-rsi-evidence-live.json");
|
||||
writeExternalEvidenceReceipt(out, receipt);
|
||||
console.log(JSON.stringify(receipt, null, 2));
|
||||
console.log(`
|
||||
Receipt: ${out}
|
||||
`);
|
||||
});
|
||||
|
||||
fable
|
||||
.command("receipt-health")
|
||||
.description("Check receipt consumer health over a recent time window")
|
||||
|
|
|
|||
Loading…
Reference in New Issue