codex-plan-ledger

A decision ledger for Codex /plan

After coding, one command shows what changed outside the plan.

Codex agreed to a plan with you. plan-ledger check lists the changed files that plan never named, and every decision sits in the PR as a diff of decisions.json.

Hook and check output: real runs on the repo's fixtures. The Codex pane is redrawn from the fixture transcript; the code edits are written by a script.
  • Scope drift check. Lists changed files the plan never named, and planned files nobody touched. In 3 runs with planted drift: unplanned file caught 3/3, untouched planned files 3/3, no false alarm on a clean tree 3/3.
  • Decisions in the PR. The questions Codex asked you, the plan's open choices and its stated defaults are written to decisions.json, so the reviewer sees what was chosen and why.
  • Stays out of the way. Runs as a Codex Stop hook: no extra model calls, no network, never waits.
npm i -g github:miniLV/codex-plan-ledger && plan-ledger init --write

Early prototype · v0.1 Across 3 measured pairs, rounds did not improve (2 losses, 1 tie). The check reads file lists, not code: contradictions inside a planned file were caught 0/3. Details

Why you'd want this

  1. When you let Codex plan and then code, it can quietly edit files you never agreed on.
  2. plan-ledger writes down what you agreed (decisions.json, in your repo) when the plan is made.
  3. After coding, it checks the changed files against that record and tells you what fell outside it. It compares file lists; it does not read the code.

How it works

Read the diagram top to bottom: solid arrows are calls or writes, dashed arrows are replies, orange is what plan-ledger does. Keep using Codex's native Plan mode and answer its questions in Codex. Rendering never calls a model.

UML sequence diagram: Developer runs Codex /plan and answers its question; when the turn ends, the Stop hook writes the Q&A, open items, defaults and scope to decisions.json in the repo; Codex codes and may edit a file the plan never named; plan-ledger check reads decisions.json and the git diff and reports DRIFT: src/utils/analytics.ts; the PR carries the code and decisions.json, so the reviewer sees each choice and why.
Source: diagrams/how-it-works-en.html, drawn in the sketchboard-diagram style.
  1. Stop hook writes the ledger

    Parse the plan, write decisions.json and an offline HTML page, end the turn. It never blocks and never waits.

  2. Review on one page

    Open, answered, and default items are shown apart. Copy the reply JSON into your next Codex message when done.

  3. Decisions in the PR

    The ledger is reviewed next to the code: what was chosen, and why.

  4. Scope drift check

    plan-ledger check --base main compares changed files with the plan's scope.

Every decision in one reviewable place

One card per decision: options, recommendation, default, affected files. Items answered in Codex are marked; plan defaults are reviewable and not counted as open. Unanswered means default kept, not agreement.

  • Plain templates. No model, offline, one file.
  • The hook never waits for you.
  • Answers are carried across plan revisions and marked as carried.
Screenshot of the decision page: one card per decision with options, a Recommended badge, the default and the affected files, and a button that builds the reply JSON.
Headless Chrome; synthetic fixture from the repo.

Decisions in the PR

The ledger is a plain JSON file next to the code. Reviewers see the plan's scope, the question Codex asked and the answer, in the same diff as the change.

The new decisions.json in a PR: the plan's scope files, and the question Codex asked recorded with source codex-native, chosen a, status answered.
The decisions.json that plan-ledger hook-stop wrote in the same run. Excerpt; folded lines are marked.

Demo

The page the hook writes, rendered from the bundled synthetic plan (test/fixtures/send-later.message.md) plus one synthetic question answered in Codex. Pick options, then press the button at the bottom. Nothing leaves your browser.

docs/plans/demo-send-later-for-drafts/plan.html

Open the demo in a full tab · Intro video (MP4)

Scope drift check

Compares git diff with each decision's affected files and the files named in the plan. Reports unplanned files and untouched decisions. --strict exits 1 on drift, for CI or pre-commit.

Known limit

File lists only. Contradicting code inside a planned file is not caught in v0.1. That is why it is called a scope drift check, not a decision-compliance check.

Terminal: git status lists five changed files; plan-ledger check --base main reports DRIFT for src/utils/analytics.ts, a file the plan never named.
Real output, run on a scratch repo built from the repo's fixtures (path shown as ~/mail-app).

Status and measurements

Note

Directional only, n = 3 pairs over two rounds. Not evidence of an effect. No pair won on rounds (2 losses, 1 tie). The tool does not aim to cut rounds.

Each pair: one native Plan run and one plan-ledger run on vercel/ms@2.1.3, same model and settings (codex-cli 0.156.0, medium). Round 2 used a leaner profile for both arms, so tokens compare only within a round.
Round / taskRounds until final
native / ledger
Planning input tokens
native / ledger
Total tokens
native / ledger
Verify
1 / strict-option1 / 2294,933 / 372,651717,487 / 586,971both pass
2 / strict-option1 / 265,931 / 83,141180,208 / 205,644both pass
2 / month-unit1 / 179,553 / 66,354179,053 / 140,020both pass

Scope drift check on the 3 plan-ledger runs, planted after implementation: clean tree, no false positive 3/3; unplanned new file caught 3/3; planned files reverted caught 3/3; contradicting code inside a planned file caught 0/3, matching the known limit.

Round 1 · Round 2 · Measurement plan