feat: add external tool audit receipts
This commit is contained in:
parent
b920691540
commit
2bf9fb6467
|
|
@ -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"
|
||||||
|
}
|
||||||
|
|
@ -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");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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";
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue