From 2bf9fb6467cf134c1cb98196f7bdff42a3777bff Mon Sep 17 00:00:00 2001 From: artale Date: Sat, 27 Jun 2026 15:20:00 +0200 Subject: [PATCH] feat: add external tool audit receipts --- .../external-tool-audit-20260627.json | 150 ++++++++++++++++++ src/fable5/external-tool-audit.test.ts | 28 ++++ src/fable5/external-tool-audit.ts | 61 +++++++ src/fable5/index.ts | 5 +- 4 files changed, 243 insertions(+), 1 deletion(-) create mode 100644 docs/receipts/external-tool-audit-20260627.json create mode 100644 src/fable5/external-tool-audit.test.ts create mode 100644 src/fable5/external-tool-audit.ts diff --git a/docs/receipts/external-tool-audit-20260627.json b/docs/receipts/external-tool-audit-20260627.json new file mode 100644 index 0000000..ffb1e20 --- /dev/null +++ b/docs/receipts/external-tool-audit-20260627.json @@ -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" +} diff --git a/src/fable5/external-tool-audit.test.ts b/src/fable5/external-tool-audit.test.ts new file mode 100644 index 0000000..17961a5 --- /dev/null +++ b/src/fable5/external-tool-audit.test.ts @@ -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"); + }); +}); diff --git a/src/fable5/external-tool-audit.ts b/src/fable5/external-tool-audit.ts new file mode 100644 index 0000000..71def3d --- /dev/null +++ b/src/fable5/external-tool-audit.ts @@ -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; + 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; +} diff --git a/src/fable5/index.ts b/src/fable5/index.ts index 827936a..19ce090 100644 --- a/src/fable5/index.ts +++ b/src/fable5/index.ts @@ -57,6 +57,9 @@ export type { RsiReconcileOptions, RsiReconcileReceipt } from "./rsi-reconcile.j export { FLUE_INTEROP_ROWS, formatFlueInteropReport, summarizeFlueInterop } 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 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 { 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 type { AutoWikiOptions, AutoWikiReceipt } from "./autowiki.js";