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>`
|
- `--output <path>`
|
||||||
- `fable5 falsification`
|
- `fable5 falsification`
|
||||||
- `--output <path>`
|
- `--output <path>`
|
||||||
|
- `fable5 external-rsi-evidence`
|
||||||
|
- `--output <path>`
|
||||||
- `fable5 receipt-health`
|
- `fable5 receipt-health`
|
||||||
- `--root <path>`
|
- `--root <path>`
|
||||||
- `--window-minutes <n>`
|
- `--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 { createFactoryMemoryFalsificationReceipt, createFalsificationReceipt, writeFalsificationReceipt } from "./falsification-receipt.js";
|
||||||
export type { FalsificationCase, FalsificationReceipt } 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 { channelPath, emitChannelEvent, readChannelEvents } from "./channel.js";
|
||||||
export type { ChannelEvent, ChannelEventSource, ChannelEventType, NewChannelEvent } 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);
|
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
|
fable
|
||||||
.command("receipt-health")
|
.command("receipt-health")
|
||||||
.description("Check receipt consumer health over a recent time window")
|
.description("Check receipt consumer health over a recent time window")
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue