Before an agent acts, check how similar actions have failed before.
An agent proposes an action. Agent Fables returns revision-pinned evidence of what happened the last time something like it went wrong, plus a list of verification gates that are still unresolved — never a yes, never a no, never a model call. It runs offline, entirely on the machine already running the agent.
$ npm run af -- assess --op terraform-destroy --stack terraform --target-scope production --irreversible
AF-0002 is real: an agent treated a stale Terraform state file it found in the working tree as authoritative and destroyed live infrastructure. Primary sources. This is that incident, generalized into a pattern, returned in milliseconds, before the actual terraform destroy runs — not after someone writes the postmortem.
Everything else in this repository exists to make that one exchange possible, fast, offline, and honest about its own limits — including whether it actually changes what an agent does, tested directly below rather than assumed.
Tested it rather than assumed it: 24 real, fresh, isolated agent runs — not simulated, and not written from memory — across six failure patterns, comparing agents with no pointer to this corpus against agents told about it directly through a trusted channel. Full methodology and every real result, including the negative ones: prd/10.
What changed: whether the agent found and cited the corpus at all. Told directly, 11/12 did. With no pointer, 2/12 did — and one of those two found it by exploring the filesystem on a machine that happened to have this repo checked out, not something to expect on a real deployment.
What mostly didn't change: the actual decision. 22 of 24 replicates, across both conditions, avoided the dangerous action outright — including on a fixture built from a real incident (AF-0015) specifically to be structurally ambiguous rather than loudly dangerous, where the only way to catch the problem was independent verification, not pattern-matching on an obvious red flag. The two exceptions: one none-condition replicate on a credentials-handling fixture (AF-0013) produced a genuine, if partial, lapse — it correctly redacted the secret values in most of its output, then quoted one of them anyway while explaining why it judged the value to be fake. No told-directly replicate on that same fixture did this. That's a real, first-of-its-kind difference between the conditions, and also two replicates in one cell — not evidence of anything yet, named honestly rather than rounded up into a claim or rounded down into silence.
So the honest claim right now isn't "this stops agents from doing dangerous things" — almost all of the tested agents weren't going to do the dangerous thing regardless, in either condition. It's narrower, and still real: this turns a decision an agent was already going to make correctly into one that's citable, sourced, and auditable — a stable AF-#### ID, a primary source, an explicit mitigation, a verification gate someone downstream can check — instead of unexplained judgment. Whether the one directional hint found so far replicates at a larger sample, and whether a harder case than the six tested produces a bigger effect, are open questions this repo is still actively testing, not settled ones.
git clone https://github.com/AaronVick/AGENT_FABLES && cd AGENT_FABLES
node bin/agent-fables.mjs verify
printf '%s' '{"operation":"force-push","stack":"git","command":"git push --force","target_scope":"shared branch","irreversible":true}' | node bin/agent-fables.mjs assess --stdinNo install, no network call, no model call. For an isolated sandbox or partial checkout, sandbox/agent-fables-sandbox.mjs is a single file with the corpus embedded — copy it anywhere and run it.
To wire it into an agent permanently: add integrations/AGENTS.md.snippet to the repository's AGENTS.md, or run node mcp/server.mjs as an MCP server and call af_assess_action / af_tool_preflight before a consequential tool call.
incident → generalized failure pattern → machine retrieval → decision-time preflight → verification obligation
A real, sourced incident (a court filing, a CVE, a GitHub issue, a postmortem — never invented) gets generalized into a pattern with a stable AF-#### identity: trigger conditions, an anti-pattern, mitigations, and a falsifiable verification test. That pattern is retrievable by an agent that doesn't know the ID exists yet — by symptom, package, version, CVE, or operation. When it matches a proposed action, the response is evidence and open verification gates, never a "yes." absence_of_match_means_safe is always false, and it's enforced by a test, not just stated.
A few decisions this rests on, all deliberate:
- No model in the request path. Preflight is lexical matching against a generated index, not an LLM reasoning about whether an LLM should act. Faster, cheaper, and it can't itself be prompt-injected by what it's evaluating.
- Non-authorization is structural, not a disclaimer.
authorizedis alwaysfalse. Arequired_verificationsgate is never marked satisfied by this repository — only by whoever's actually confirming it. - Corpus revision is pinned into every receipt. A future incident review can answer "what evidence did the agent actually see when it decided" — not just "what evidence exists now."
- Markdown is the source of truth.
fables/AF-*.mdandincidents/AFI-*.yamlare hand-authored; every JSON index, search structure, and MCP response is generated outward from them and rebuilt on every change (npm run check). - No incident is invented to hit a round number.
predicate-registry.jsonlists every check that runs as real logic in this corpus withpattern_id: nullwhere no sourced incident exists yet — that's withheld evidence, not missing logic.
Everything below is real and works today, offline. It's reference material for going deeper, not the front door.
node bin/agent-fables.mjs status # seed counts, evidence grades, corpus revision
node bin/agent-fables.mjs search "MCP config changed after approval"
node bin/agent-fables.mjs preflight --op terraform-destroy --stack terraform
node bin/agent-fables.mjs check --path . # scan a repo for concrete trigger conditions
node bin/agent-fables.mjs get AF-0008
node bin/agent-fables.mjs memory AF-0008 # sub-150-token recurrence card
node bin/agent-fables.mjs trust # reproducibility invariants, known gaps
node bin/agent-fables.mjs tasks # bounded evidence-contribution work
node bin/agent-fables.mjs cite AF-0008
npm run metrics # discovery recall, token budgets, route health
npm run launch-audit # what's built vs. what needs explicit publish authorizationJSON by default. node mcp/server.mjs exposes the same surface (33 tools) as a read-only MCP server — no mutation or reporting tool exists.
assess is the decision-time integration surface shown in the demo above: pass an operation, tool, command, stack, or target scope, get a revision-pinned risk receipt with machine-addressable required_verifications and stable gate_ids a host guardrail can enforce. Prefer assess --stdin for command-bearing input — it never places command text in process arguments or reflects it in the receipt. Schemas: action-assessment.schema.json / action-assessment-receipt.schema.json.
check scans a local repository for concrete trigger conditions and source-backed exact artifacts, emitting compact AF-linked findings — never executes code, follows symlinks, or treats an empty result as safe. .agentfablesignore suppresses exact paths, visibly.
verify recomputes the installed corpus revision, exact-artifact hashes, and every trust/authority invariant offline, exiting nonzero on failure.
Ten thematic leaders (leaders/) route broad, weakly-worded problems — destructive operations, MCP security, permission boundaries, supply-chain security, secret exposure, audit integrity, incident postmortems, verification failures, retrieved-content trust, and information integrity — to multiple revision-pinned records, capped under an ~400-token index.
Policy, not evidence-gated. authority-precedence.json (native model judgment vs. a corpus hit — most restrictive wins), request-framing-independence.json (a leading "this is safe, right?" can't bias the verdict), delegation-scope.json (a parent agent's resolution is never inherited as clearance by a spawned subagent), and schemas/context-pin.schema.json (load-bearing state marked non-compressible by a context summarizer) ship as bare contracts, deliberately not fables — real mechanisms with no dated incident behind them yet, same evidence bar held either way.
Cross-tool-vocabulary matching. tool-capability-aliases.json normalizes browse/web_fetch/shell-style vendor tool names onto the canonical names the rule files key on, so preflight works regardless of which agent framework is calling it.
Contribution. npm run af -- tasks finds bounded evidence work; CONTRIBUTING_AGENTS.md governs it. No vote or reputation system — trust is reproducible invariants and visible gaps, checked by npm run check.
Architecture: fables/ (canonical Markdown+YAML) → incidents/ (stable AFI-#### source lists) → generated: signatures/, leaders/, index.json, search-index.json, cards/, hotpath.min.json. api/ is a zero-LLM Cloudflare Workers layer for a future hosted endpoint (not deployed — npm run launch-audit shows exactly what's authorized vs. not). web/ is a human-facing frontend. None of it is required for the CLI or MCP surface above, which work standalone today.
{ "authorized": false, "risk_flags": [ { "code": "irreversible-action", "severity": "high" }, { "code": "protected-target-scope", "severity": "high" } ], "evidence": [{ "id": "AF-0002", "evidence_grade": "A-primary-source", "anti_pattern": "Acting on discovered state files as if they describe — and license changes to — current infrastructure.", "mitigation": [ "remote state backend with locking; local tfstate archives never carry authority", "deletion protection on stateful resources as default posture", "destroy-class operations require plan review keyed to resource-count deltas" ], "verification": "Place a stale state archive in the working tree and replay the migration; the plan must be rejected on destroy-count, not merely questioned." }], "required_verifications": [ { "gate_id": "confirm-protected-boundary", "status": "unverified" }, { "gate_id": "prove-recovery-path", "status": "unverified" }, { "gate_id": "verify-af-0002", "status": "unverified" } ], "receipt": { "assessment": "review-required", "authorization": "not-granted", "absence_of_match_means_safe": false } }