feat: add external tool audit receipts

This commit is contained in:
artale 2026-06-27 15:20:00 +02:00
parent b920691540
commit 2bf9fb6467
4 changed files with 243 additions and 1 deletions

View File

@ -0,0 +1,150 @@
{
"schema": "fable.external_tool.audit.v1",
"createdAt": "2026-06-27T00:00:00.000Z",
"source": "Dicklesworthstone agentic coding tool list",
"rows": [
{
"name": "Agent Mail",
"url": "https://github.com/Dicklesworthstone/mcp_agent_mail_rust",
"category": "agent messaging",
"fableSurface": "fable5 channel events + receipts",
"status": "partial",
"nextPatch": "Add mailbox-style receipt indexing only if channel JSONL is not enough."
},
{
"name": "Beads Viewer",
"url": "https://github.com/Dicklesworthstone/beads_viewer",
"category": "task visualization",
"fableSurface": "receipt files + docs/receipts",
"status": "gap",
"nextPatch": "Add a static receipt/task viewer after receipts become hard to navigate."
},
{
"name": "Beads Rust",
"url": "https://github.com/Dicklesworthstone/beads_rust",
"category": "task tracking",
"fableSurface": "ZTE receipts + goal audit + feedback receipts",
"status": "partial",
"nextPatch": "Map beads-style task states onto existing receipts before adding a task DB."
},
{
"name": "CASS",
"url": "https://github.com/Dicklesworthstone/coding_agent_session_search",
"category": "session search",
"fableSurface": "StateStore + persistent memory + receipt search by files",
"status": "partial",
"nextPatch": "Add indexed receipt/session search only after grep/file search is insufficient."
},
{
"name": "CASS Memory",
"url": "https://github.com/Dicklesworthstone/cass_memory_system",
"category": "memory",
"fableSurface": "persistent memory provenance + receipt-ranked recall",
"status": "covered",
"nextPatch": "Keep receipt-first ranking; no new memory backend."
},
{
"name": "NTM",
"url": "https://github.com/Dicklesworthstone/ntm",
"category": "tmux/session manager",
"fableSurface": "interactive_shell + channel receipts",
"status": "skip",
"nextPatch": "Skip until terminal session orchestration is a current bottleneck."
},
{
"name": "Flywheel Setup",
"url": "https://github.com/Dicklesworthstone/agentic_coding_flywheel_setup",
"category": "bootstrap",
"fableSurface": "COMMANDS.md + factory gate + receipts",
"status": "partial",
"nextPatch": "Turn setup steps into a checklist receipt if onboarding repeats."
},
{
"name": "Bug Scanner",
"url": "https://github.com/Dicklesworthstone/ultimate_bug_scanner",
"category": "security scan",
"fableSurface": "security scan + AST/unicode safety",
"status": "covered",
"nextPatch": "No new scanner; add repro receipts for real findings."
},
{
"name": "Destructive Command Guard",
"url": "https://github.com/Dicklesworthstone/destructive_command_guard",
"category": "safety",
"fableSurface": "factory deploy allowlist + ZTE gate + permit checks",
"status": "covered",
"nextPatch": "Keep allowlist centralized at deploy boundary."
},
{
"name": "Repo Updater",
"url": "https://github.com/Dicklesworthstone/repo_updater",
"category": "repo automation",
"fableSurface": "factory gate + deploy receipts",
"status": "covered",
"nextPatch": "No ungated updater; deploy stays receipt-gated."
},
{
"name": "SLB",
"url": "https://github.com/Dicklesworthstone/simultaneous_launch_button",
"category": "two-person rule",
"fableSurface": "explicit --yes + gate receipt + deploy token",
"status": "partial",
"nextPatch": "Add quorum metadata only when more than one approver is required."
},
{
"name": "Meta Skill",
"url": "https://github.com/Dicklesworthstone/meta_skill",
"category": "skill generation",
"fableSurface": "SkillRegistry + skill sync + ZTE receipts",
"status": "partial",
"nextPatch": "Require generated-skill test receipt before adopting meta-skill behavior."
},
{
"name": "Remote Compilation Helper",
"url": "https://github.com/Dicklesworthstone/remote_compilation_helper",
"category": "remote build",
"fableSurface": "npm build/test/docs gates + factory verify",
"status": "covered",
"nextPatch": "Use existing gates; add remote compile only for non-local toolchains."
},
{
"name": "Account Manager",
"url": "https://github.com/Dicklesworthstone/coding_agent_account_manager",
"category": "account management",
"fableSurface": "none",
"status": "skip",
"nextPatch": "Skip; secrets/accounts are outside this repo's minimal trusted surface."
},
{
"name": "WezTerm Automata",
"url": "https://github.com/Dicklesworthstone/wezterm_automata",
"category": "terminal automation",
"fableSurface": "interactive_shell",
"status": "skip",
"nextPatch": "Skip until WezTerm is a required runtime."
},
{
"name": "Brenner Bot",
"url": "https://github.com/Dicklesworthstone/brenner_bot",
"category": "bot",
"fableSurface": "none",
"status": "skip",
"nextPatch": "Skip until a concrete bot requirement exists."
}
],
"summary": {
"covered": 5,
"partial": 6,
"gap": 1,
"skip": 4
},
"next": {
"name": "Beads Viewer",
"url": "https://github.com/Dicklesworthstone/beads_viewer",
"category": "task visualization",
"fableSurface": "receipt files + docs/receipts",
"status": "gap",
"nextPatch": "Add a static receipt/task viewer after receipts become hard to navigate."
},
"policy": "map-only-no-vendored-code"
}

View File

@ -0,0 +1,28 @@
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 { createExternalToolAuditReceipt, DICKLESWORTHSTONE_TOOL_ROWS, writeExternalToolAuditReceipt } from "./external-tool-audit.js";
describe("external tool audit receipt", () => {
it("maps all listed tools without vendoring code", () => {
const receipt = createExternalToolAuditReceipt(DICKLESWORTHSTONE_TOOL_ROWS, new Date("2026-06-27T00:00:00.000Z"));
expect(receipt.schema).toBe("fable.external_tool.audit.v1");
expect(receipt.policy).toBe("map-only-no-vendored-code");
expect(receipt.rows).toHaveLength(16);
expect(receipt.rows.map((row) => row.name)).toContain("Destructive Command Guard");
expect(receipt.rows.map((row) => row.name)).toContain("Brenner Bot");
expect(receipt.summary.covered + receipt.summary.partial + receipt.summary.gap + receipt.summary.skip).toBe(16);
expect(receipt.next.status).toBe("gap");
});
it("writes the JSON receipt", () => {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "external-tool-audit-"));
const file = path.join(dir, "receipt.json");
writeExternalToolAuditReceipt(file, createExternalToolAuditReceipt());
expect(fs.readFileSync(file, "utf-8")).toContain("fable.external_tool.audit.v1");
});
});

View File

@ -0,0 +1,61 @@
import * as fs from "node:fs";
import * as path from "node:path";
export type ExternalToolAuditStatus = "covered" | "partial" | "gap" | "skip";
export interface ExternalToolAuditRow {
name: string;
url: string;
category: string;
fableSurface: string;
status: ExternalToolAuditStatus;
nextPatch: string;
}
export interface ExternalToolAuditReceipt {
schema: "fable.external_tool.audit.v1";
createdAt: string;
source: string;
rows: ExternalToolAuditRow[];
summary: Record<ExternalToolAuditStatus, number>;
next: ExternalToolAuditRow;
policy: "map-only-no-vendored-code";
}
export const DICKLESWORTHSTONE_TOOL_ROWS: ExternalToolAuditRow[] = [
{ name: "Agent Mail", url: "https://github.com/Dicklesworthstone/mcp_agent_mail_rust", category: "agent messaging", fableSurface: "fable5 channel events + receipts", status: "partial", nextPatch: "Add mailbox-style receipt indexing only if channel JSONL is not enough." },
{ name: "Beads Viewer", url: "https://github.com/Dicklesworthstone/beads_viewer", category: "task visualization", fableSurface: "receipt files + docs/receipts", status: "gap", nextPatch: "Add a static receipt/task viewer after receipts become hard to navigate." },
{ name: "Beads Rust", url: "https://github.com/Dicklesworthstone/beads_rust", category: "task tracking", fableSurface: "ZTE receipts + goal audit + feedback receipts", status: "partial", nextPatch: "Map beads-style task states onto existing receipts before adding a task DB." },
{ name: "CASS", url: "https://github.com/Dicklesworthstone/coding_agent_session_search", category: "session search", fableSurface: "StateStore + persistent memory + receipt search by files", status: "partial", nextPatch: "Add indexed receipt/session search only after grep/file search is insufficient." },
{ name: "CASS Memory", url: "https://github.com/Dicklesworthstone/cass_memory_system", category: "memory", fableSurface: "persistent memory provenance + receipt-ranked recall", status: "covered", nextPatch: "Keep receipt-first ranking; no new memory backend." },
{ name: "NTM", url: "https://github.com/Dicklesworthstone/ntm", category: "tmux/session manager", fableSurface: "interactive_shell + channel receipts", status: "skip", nextPatch: "Skip until terminal session orchestration is a current bottleneck." },
{ name: "Flywheel Setup", url: "https://github.com/Dicklesworthstone/agentic_coding_flywheel_setup", category: "bootstrap", fableSurface: "COMMANDS.md + factory gate + receipts", status: "partial", nextPatch: "Turn setup steps into a checklist receipt if onboarding repeats." },
{ name: "Bug Scanner", url: "https://github.com/Dicklesworthstone/ultimate_bug_scanner", category: "security scan", fableSurface: "security scan + AST/unicode safety", status: "covered", nextPatch: "No new scanner; add repro receipts for real findings." },
{ name: "Destructive Command Guard", url: "https://github.com/Dicklesworthstone/destructive_command_guard", category: "safety", fableSurface: "factory deploy allowlist + ZTE gate + permit checks", status: "covered", nextPatch: "Keep allowlist centralized at deploy boundary." },
{ name: "Repo Updater", url: "https://github.com/Dicklesworthstone/repo_updater", category: "repo automation", fableSurface: "factory gate + deploy receipts", status: "covered", nextPatch: "No ungated updater; deploy stays receipt-gated." },
{ name: "SLB", url: "https://github.com/Dicklesworthstone/simultaneous_launch_button", category: "two-person rule", fableSurface: "explicit --yes + gate receipt + deploy token", status: "partial", nextPatch: "Add quorum metadata only when more than one approver is required." },
{ name: "Meta Skill", url: "https://github.com/Dicklesworthstone/meta_skill", category: "skill generation", fableSurface: "SkillRegistry + skill sync + ZTE receipts", status: "partial", nextPatch: "Require generated-skill test receipt before adopting meta-skill behavior." },
{ name: "Remote Compilation Helper", url: "https://github.com/Dicklesworthstone/remote_compilation_helper", category: "remote build", fableSurface: "npm build/test/docs gates + factory verify", status: "covered", nextPatch: "Use existing gates; add remote compile only for non-local toolchains." },
{ name: "Account Manager", url: "https://github.com/Dicklesworthstone/coding_agent_account_manager", category: "account management", fableSurface: "none", status: "skip", nextPatch: "Skip; secrets/accounts are outside this repo's minimal trusted surface." },
{ name: "WezTerm Automata", url: "https://github.com/Dicklesworthstone/wezterm_automata", category: "terminal automation", fableSurface: "interactive_shell", status: "skip", nextPatch: "Skip until WezTerm is a required runtime." },
{ name: "Brenner Bot", url: "https://github.com/Dicklesworthstone/brenner_bot", category: "bot", fableSurface: "none", status: "skip", nextPatch: "Skip until a concrete bot requirement exists." },
];
export function createExternalToolAuditReceipt(rows = DICKLESWORTHSTONE_TOOL_ROWS, now = new Date()): ExternalToolAuditReceipt {
const summary = rows.reduce((acc, row) => ({ ...acc, [row.status]: acc[row.status] + 1 }), { covered: 0, partial: 0, gap: 0, skip: 0 });
return {
schema: "fable.external_tool.audit.v1",
createdAt: now.toISOString(),
source: "Dicklesworthstone agentic coding tool list",
rows,
summary,
next: rows.find((row) => row.status === "gap") ?? rows.find((row) => row.status === "partial") ?? rows[0],
policy: "map-only-no-vendored-code",
};
}
export function writeExternalToolAuditReceipt(file: string, receipt: ExternalToolAuditReceipt): string {
fs.mkdirSync(path.dirname(file), { recursive: true });
fs.writeFileSync(file, `${JSON.stringify(receipt, null, 2)}\n`);
return file;
}

View File

@ -57,6 +57,9 @@ export type { RsiReconcileOptions, RsiReconcileReceipt } from "./rsi-reconcile.j
export { FLUE_INTEROP_ROWS, formatFlueInteropReport, summarizeFlueInterop } from "./flue-interop.js"; export { FLUE_INTEROP_ROWS, formatFlueInteropReport, summarizeFlueInterop } from "./flue-interop.js";
export type { FlueInteropRow, FlueInteropStatus } from "./flue-interop.js"; export type { FlueInteropRow, FlueInteropStatus } from "./flue-interop.js";
export { createExternalToolAuditReceipt, DICKLESWORTHSTONE_TOOL_ROWS, writeExternalToolAuditReceipt } from "./external-tool-audit.js";
export type { ExternalToolAuditReceipt, ExternalToolAuditRow, ExternalToolAuditStatus } from "./external-tool-audit.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";
@ -82,7 +85,7 @@ export { auditGoalCompletion, writeGoalAuditReceipt } from "./goal-audit.js";
export type { GoalAuditCriterion, GoalAuditReceipt } from "./goal-audit.js"; export type { GoalAuditCriterion, GoalAuditReceipt } from "./goal-audit.js";
export { appendFeedbackMemory, feedbackMemoryPath, feedbackMemoryStats, writeFeedbackReceipt } from "./feedback-memory.js"; export { appendFeedbackMemory, feedbackMemoryPath, feedbackMemoryStats, writeFeedbackReceipt } from "./feedback-memory.js";
export type { FeedbackMemoryEntry, FeedbackMemoryType, FeedbackReceipt } from "./feedback-memory.js"; export type { FeedbackMemoryEntry, FeedbackMemoryType, FeedbackReceipt, FeedbackSignal } from "./feedback-memory.js";
export { generateAutoWiki, surveyFiles } from "./autowiki.js"; export { generateAutoWiki, surveyFiles } from "./autowiki.js";
export type { AutoWikiOptions, AutoWikiReceipt } from "./autowiki.js"; export type { AutoWikiOptions, AutoWikiReceipt } from "./autowiki.js";