# A ledger of which of your own AI architectural decisions you could defend

> Source: <https://github.com/Dupflo/decision-ledger>
> Published: 2026-08-13 12:26:05+00:00

*Three agent skills for Claude Code: /ledger, /ledger-audit, /ledger-report.*

**Two developers ship the same feature this week. In three weeks, only one of them can still explain it.**

Nothing in the diff tells you which one you are. Decision Ledger is three agent skills for Claude Code that keep the record: at each structural decision they ask you to take a position, note whether you could hold it, and hand it back as a map of your project. You shipped twelve thousand lines this quarter. How many of those decisions could you defend in review?

Status: early.This is an experiment, not a product. There is no usage data yet. I'm running it on my own projects first and will publish what I find, including if the answer is that nobody tolerates the friction.

They install as normal Claude Code skills, in `~/.claude/skills`

. One promise each, and only the first one loads on its own. The other two you invoke by name, like any slash command.

| Skill | When |
|---|---|
`/ledger` |
At a structural decision. Asks once, records, writes the code regardless. The only skill with a broad description, because interception has to fire without being asked for. |
`/ledger-audit` |
Once per project, on a codebase that predates the ledger. The one moment a batch of questions is welcome, because you sat down for it. |
`/ledger-report` |
At a commit or a pull request. The map, and nothing appended to it. |

You never run a command yourself. State lives behind a small script the skills call for you; there is no CLI to learn.

You're adding auth to a Next.js app. Before writing anything:

Session in an httpOnly cookie or a token held client-side? And why that one?

You say the cookie is safer. That's the right call, but it's half the reasoning, so it asks once more:

Agreed on the XSS angle. What happens when a mobile client hits the same API?

You hadn't thought about it. It writes the cookie version anyway, because it never blocks you, and records `auth/session-storage: partial`

.

Three weeks later, at a pull request:

```
Mastery map: 24 decisions tracked, 21 days

  auth/          ██████░░░░  2 of 5 defended     <- thinnest area
  data-model/    █████████░  4 of 5, 1 decayed
  ui/            ██████████  7 of 7
  jobs/          ████░░░░░░  2 of 5, 1 unknown

  Transfers observed: 2 (session-storage, cache-invalidation)
```

That example is illustrative. This one is not. It is [ .mastery/index.json](/Dupflo/decision-ledger/blob/main/.mastery/index.json), committed here, produced by running the tool on itself:

```
skills/skill-granularity      justified   one skill or three
state/personal-layer-split    justified   project file vs local file
state/decay-scope             partial     per-area half-life, unresolved
distribution/install-scope    justified   global default vs per project
```

`state/decay-scope: partial`

is the point. The half-life is computed per area, so recording an `unknown`

in `auth/`

refreshes the confidence of its `justified`

neighbours. That is wrong, and I have not fixed it or argued it away. It sits in the file, in public, on the author's own project.

A tool that measures undefended decisions and ships without any of its own would be worth nothing.

The native path, for Claude Code users. Add this repo as a plugin marketplace, then install the plugin; the three skills arrive together, since they are one product with one shared personal layer.

```
/plugin marketplace add Dupflo/decision-ledger
/plugin install decision-ledger@dupflo
```

`/plugin`

is where Claude Code users actually look for skills, so this is the path that gets it seen. The manifests live in [ .claude-plugin/](/Dupflo/decision-ledger/blob/main/.claude-plugin): one

`plugin.json`

that points at the three skill directories, one `marketplace.json`

that lists the plugin.Cross-agent, via the [skills.sh](https://skills.sh) directory (Claude Code, Codex, Cursor, and the rest). This is the other place skills get seen: the directory indexes automatically from install telemetry, so every one of these adds it a little higher.

```
npx skills add Dupflo/decision-ledger
```

The three skill directories sit at the repo root, self-contained, exactly the layout this CLI expects; `npx skills add . --list`

finds all three.

Or, without either directory, the same three skills by script:

```
curl -fsSL https://raw.githubusercontent.com/Dupflo/decision-ledger/main/install.sh | bash
```

Global by default, in `~/.claude/skills`

, available in every repo. That is deliberate: the personal layer spans projects, so a concept you defended on one codebase is not re-asked on the next, and a per-project install throws that away.

```
./install.sh --project     # this repo only, ./.claude/skills
./install.sh --uninstall   # removes the symlinks, keeps every ledger
```

Re-running it updates in place. The three skills are symlinked into one checkout, so they never drift apart. Requires `python3`

on your PATH, nothing else, no packages.

Then, for the interception to persist across sessions, add to your project's `CLAUDE.md`

:

```
## decision-ledger
Before any structurally significant decision (data model, state ownership,
auth strategy, module boundaries, a dependency that will spread, caching,
retry semantics), load the ledger skill and follow it. Skip entirely
when the session is passed --ship.
```

State lives in `.mastery/index.json`

at your repo root. Commit it: the ledger belongs to the project, and it travels with it. There is no setup step, since the first decision recorded creates the file.

One caution follows from committing it. A note describing an undefended security decision also describes where the project is weak. On a private repo that is exactly what you want written down. On a public one, write the note so it names the decision without drawing the map. "All writes bypass row-level security, never arbitrated" says enough, and says it to the right people.

We have a word for code that works but costs us later: technical debt. We don't have one for the decisions we shipped without understanding them.

It accrues quietly. You ask an agent for auth, you get auth, it works. Three weeks later someone asks why the session lives in a cookie and you find you have no answer. Not because the choice was wrong, but because you were never the one who made it.

The bet behind this repo is that feeling productive and being capable can drift apart silently, and that the drift is measurable if you ask at the right moment: not in a quiz, but on a real choice, in your own project, before the code exists.

These are the constraints, not features. Most of them exist because the obvious version of this tool gets uninstalled on day one.

**It never blocks your work.** No quizzes, no lessons, no gates. The code always gets written in the same turn. `--ship`

disables everything for a session.

**It asks at the decision, not out of context.** Only on choices that are expensive to reverse: data model, state ownership, auth strategy, module boundaries, a dependency that will spread, caching, retry semantics. At most one question per session, across every tool you have installed, not one per tool.

**"I don't know" is a valid answer.** It gets recorded, the code gets written, and the reasoning is explained in the doing. The concept comes back later on a neighbouring decision. If you answer it then unaided, that's a *transfer*, the strongest signal in the file.

**It's a map, not a curve.** A rising line invites optimisation and becomes an engagement metric, which is the thing this exists to reject. Confidence also decays: 90-day half-life on areas you haven't touched.

**It describes the project, not you.** No global score, no timing data, no ranking. Those fields are deliberately absent. See [ references/index-schema.md](/Dupflo/decision-ledger/blob/main/references/index-schema.md).

**Your history follows you; it doesn't follow you around.** A concept you defended on one project isn't re-asked from scratch on the next. That lives in `~/.decision-ledger/personal.json`

, which is local only, mode `0600`

, never committed, with no export command. The project ledger is shareable and says nothing about who understood what; the personal one never leaves your machine, and asking for it to be forgotten deletes the one file it lives in.

Once a concept has gone stale, it gets asked once in the new project, and answering it correctly in a *different codebase* is the strongest transfer evidence the tool can collect.

A tool about defending structural decisions owes you its own. Each of these is in the ledger above, and each cost something.

**Three skills, not one.** Interception must fire without being asked for, so it needs a broad description, and broad descriptions compete with each other for routing. Giving the audit and the report terse, command-style descriptions keeps them out of that competition, and keeps the file loaded at every decision down to what is actually needed there. The cost is real: "one skill, one promise" was a cleaner story, and three skills are three things to keep in sync.

**The personal layer is a second file.** A single ledger would have been simpler. But the project file has to be readable by a team without saying who understood what, and a personal history has to survive across projects without ever being exportable. Those two requirements cannot live in one file, so they don't.

**Confidence decays.** An index that only rises is a badge, and badges are engagement metrics, the exact thing this rejects. Someone who hasn't opened a module in three months understands it less well than they did. A decayed concept becomes askable again, which is spaced retrieval, and re-answering correctly is stronger evidence than the first answer was.

**Nothing here is a gate.** Blocking someone on their own project because a model judged their answer thin is a guaranteed uninstall on the first false negative. A warning at a pull request ("340 lines added, two decisions in `auth/`

with nothing on record") is more honest and more effective than a closed door.

Which is every project, the first time. `/ledger-audit`

reconstructs the questions the skill *would* have asked while you were building, grounded in your actual schema, modules and dependencies, so you don't start from an empty file.

Where `docs/decisions/`

or `docs/adr/`

exists, it starts there: those decisions are already isolated, scoped and dated, and an ADR written by an agent is the sharpest possible question, because it documents what the project decided while proving nothing about whether you can defend it.

It also reads `CLAUDE.md`

and `AGENTS.md`

. Conventions stated there without a reason are marked `inherited`

: applied across the whole codebase, defended by nobody. Often they were copied from another project, which is comprehension debt that never even met a moment of decision.

Inherited isn't wrong. Adopting a good convention is usually the right call. It only means you couldn't currently defend it in review.

Interrogation skills already exist, and they ask better questions than this one does:

[rodbv/socratic-skills](https://github.com/rodbv/socratic-skills):`quiz-me`

questions you on a diff or spec one question at a time;`guide-me`

walks you through implementing a spec without writing the code for you.[mattpocock/skills](https://github.com/mattpocock/skills):`grill-me`

interviews you until every branch of the design tree is resolved.`grill-with-docs`

is stateful, keeping what it learns in`CONTEXT.md`

and ADRs.`teach`

runs across sessions in a stateful workspace.[garrytan/gstack](https://github.com/garrytan/gstack):`/office-hours`

and the`plan-*-review`

family interrogate before code;`/learn`

persists what the agent learned about a codebase; the design taste profile decays weekly. And`plan-tune`

keeps a**developer profile**, a dual-track model of what you declared you want versus what your behaviour suggests, used to decide which questions to stop asking you.**ADR pipelines**: MADR files in`docs/decisions/`

, written by hand or generated.[MikeCodeur/killer-saas](https://github.com/MikeCodeur/killer-saas)records every imposed structural decision that way, with the options considered and why they were rejected.

Most of these keep state, so the honest question is not whether something already stores something. It is what the state is *about*.

`grill-with-docs`

records the project's domain model. `/learn`

records what the agent knows about the codebase. An ADR records what the **project** decided, and is usually written by the agent, which is precisely why it proves nothing: a repo can hold a perfect `docs/decisions/`

directory whose every rationale was generated by a model and read by nobody. `plan-tune`

comes closest, since it does model the developer, but it models *preferences*, in order to ask you less, which is close to the opposite goal.

None of them records what the developer could and could not defend, how that changed over time, or whether an answer ever transferred to a new case. That gap is the entire contribution here, and if one of those projects closes it, this one stops being necessary.

The overlap is worth using rather than avoiding. Where `docs/decisions/`

exists, `/ledger-audit`

reads it first: it is a list of structural decisions already isolated and dated, and asking you to defend one of them is a far better question than anything scraped out of `CLAUDE.md`

.

And if you already run one of these, this skill stays quiet. When another skill has just made you reason about a decision, it listens and records instead of asking again. One interruption per session is the budget for the whole stack, not for each tool in it.

Distinguishing genuine understanding from a fluent-sounding sentence is unsolved, and this only approximates it. A confident writer will score higher than a careful thinker who expresses themselves poorly. The ledger is a conversation starter about where a project is thin, not a measurement instrument. The transfer signal is the only part that comes close to evidence.

**An early map is not a measurement, and the first one is the worst offender.** An audit goes looking for what nobody arbitrated: decisions with no entry, conventions with no reason. So it finds them, and a first report can read "0 of 5 defended" on a project its author knows perfectly well. The decisions that were already settled were never asked about, so they are not in the file. Entries carry a `source`

, and `report`

says out loud when the map is made of audit findings. Read a first run as a list of places to look, never as a score.

**The convention scanner is a keyword heuristic**, now tuned to keep only lines that read as normative. That was a correction. Pointed at a real `AGENTS.md`

documenting a pipeline, the first version reported 47 "undefended conventions", of which roughly none were conventions. They were command reference and flow diagrams. It now under-reports on purpose. A missed rule costs one question nobody asks; a phantom one costs the credibility of the whole report.

The likely failure mode isn't accuracy, it's adoption: a tool that slows you down for your own good runs into the same wall as a meditation app. The people who actually install it are narrower than the pitch suggests: someone with a code review or an interview dated in their calendar, or a lead who wants to know what their team understands of the code it merges. If broad adoption turns out to be the wrong expectation, that will be written up here rather than quietly dropped.

Nothing in the mechanism is specific to code. It needs decisions that are dated, trade-offs that are real, and consequences that come back. Contracts and financial models qualify. Writing and design largely don't.

MIT.
