madelogdocs

Getting started

From an installed binary to a published page, on a repository you already have. Every step shows the output the current binary prints.

This walk-through uses a small repository called ledger with one commit in it. The output shown is what madelog 0.1.0 prints; only the home directory was shortened to /Users/ana. Timestamps are UTC, because that is what the log stores.

1. Check the machine

madelog onboard explains what is recorded, checks for the tools madelog works with, and offers to set up the repository you are in. It is safe to run again at any time.

$ cd ~/projects/ledger
$ madelog onboard
  +---+
  | +-+-+   madelog - Proof of Process
  +-+-+ |   Records how your project came together.
    +---+

madelog records three things: the commits you make, the AI sessions Git AI attributes to them, and the one-sentence decisions you write with `madelog note`.
All of it stays on this machine, in .madelog/log.db, until you run `madelog publish`.
No keystrokes, no screenshots, no files git ignores. `madelog export` hands you everything back as JSON.

git      found    /opt/homebrew/bin/git
git-ai   missing  Without it, AI sessions stay empty. Install it with: curl -sSL https://usegitai.com/install.sh | bash
ollama   found    /usr/local/bin/ollama

Set up madelog in this repo now? [Y/n] n
Nothing changed. Run `madelog init` when you are ready.

Answer y and it runs madelog init for you, questions included. Outside a git repository it prints git init and madelog init as the two things to do next and stops. In a repository that already has .madelog/log.db it says so and lists madelog log and madelog verify.

Before madelog init, every command that needs the log stops with the same sentence:

$ madelog log
madelog: no madelog log in this repository yet — run `madelog init` first (/Users/ana/projects/ledger/.madelog/log.db is missing)

2. Set up the repository

madelog init asks three questions and one yes/no, then starts recording.

$ madelog init
What are you building? A CLI that reconciles bank CSV exports against invoices
Who is it for? A small accounting firm that closes its books by hand
Which AI tools do you use? (comma separated) claude-code, cursor
Record AI sessions from Git AI? [Y/n] y
madelog is recording in /Users/ana/projects/ledger/
  .madelog/intent.md      what you are building, in your own words
  .madelog/log.db         the event log, hash chained
  .madelog/config.toml    settings, including privacy.redact
Your commits are recorded from now on. Nothing leaves this machine.

Next: `madelog note "why you did it that way"` after a decision.
Git AI is not on your PATH, so AI sessions stay empty. Install it with: curl -sSL https://usegitai.com/install.sh | bash

What happened, in order:

  1. .madelog/ was created with intent.md (your answers as Markdown), config.toml (defaults plus project.name = "ledger", the directory name) and log.db.
  2. The first event, intent, was appended, anchored to the commit HEAD points at.
  3. .madelog/ was added to .git/info/exclude, so it never shows up in git status and never enters a commit.
  4. Two lines were appended to .git/hooks/post-commit. An existing hook is kept and madelog's lines are added once, however many times you run init:
#!/bin/sh
echo "hello from an older hook"

# madelog-cli:post-commit - records this commit in .madelog/log.db
madelog hook post-commit || true
  1. A device key was created at ~/.config/madelog/device.key (65 bytes, mode 0600) if there was none. It signs every publish.

All four answers can be passed as flags for scripts: --what, --for-whom, --tools, and --record-ai-sessions or --no-record-ai-sessions. --yes skips every question and takes the flags given and empty answers for the rest. Running init a second time appends another intent event and rewrites intent.md; the page reads the newest event, so pass all four answers again when you do.

$ madelog log
   1  2026-09-18T07:53:48.617Z  intent     9659671  A CLI that reconciles bank CSV exports against invoices

3. Commit, and write down a decision

The hook records every commit from now on. It prints nothing on success. Without Git AI it says so once per process, on standard error, and the commit goes through:

$ git commit -m "feat: reconcile skeleton"
hello from an older hook
madelog: Git AI is not installed, so no AI sessions are recorded.
madelog: install it with: curl -sSL https://usegitai.com/install.sh | bash
[main ec66d42] feat: reconcile skeleton
 2 files changed, 7 insertions(+), 1 deletion(-)
 create mode 100644 src/reconcile.rs

When you choose something, say why. One sentence, no editor, anchored to the commit you are on:

$ madelog note "chose SQLite over Postgres: one binary, no service to run"
noted at ec66d42
$ madelog note --private "the client sends CSVs with a BOM; strip it before parsing"
noted at ec66d42 (private, never published)

A private note stays in the chain and never reaches the page, the preview or the summaries. An empty note is refused: madelog: a note needs a sentence: madelog note "why you did it that way".

4. An AI session, and a fix

With Git AI installed, a commit that carries AI-written lines also gets an ai_session event, and for Claude Code the session's first prompt. This is what the log looks like after such a commit and a fix to the same file:

$ madelog log
   1  2026-09-18T07:53:48.617Z  intent     9659671  A CLI that reconciles bank CSV exports against invoices
   2  2026-09-18T07:53:48.915Z  commit     ec66d42  feat: reconcile skeleton
   3  2026-09-18T07:53:48.935Z  decision   ec66d42  chose SQLite over Postgres: one binary, no service to run
   4  2026-09-18T07:53:48.943Z  decision   ec66d42  the client sends CSVs with a BOM; strip it before parsing (private)
   5  2026-09-18T07:53:49.023Z  commit     77433d9  feat: match bank rows to invoices by amount and reference
   6  2026-09-18T07:53:49.023Z  ai_session 77433d9  claude-code: Write src/reconcile.rs: load two CSVs (amount, reference), index invoices by amount in cents, and count bank rows that match an invoice by amount and reference.
   7  2026-09-18T07:53:49.076Z  commit     8a322f5  fix: equal amounts with a different reference were counted as matched
   8  2026-09-18T07:53:49.128Z  commit     8960705  chore: assets, api token placeholder and gitignore

The columns are the sequence number, the timestamp, the kind, the short sha the event is anchored to, and a one-line title. madelog log --json prints the same events as JSON with every field, including each event's id, which madelog hide and madelog summary edit take. AI sessions explains where event 6 comes from.

5. Check the chain

$ madelog verify
chain ok
8 events, root fe97dbc626e14dc52a58c6f49352bf0467bed76c7ae4dd4aadc02729ae4f9265

The root is the hash of the last event. It changes with every event you add, and it is what the server stores when you publish. Concepts shows what a broken chain looks like.

6. Look for patterns

madelog analyze runs four rules over the commits and prints the moments the page would show. It records each pattern once; running it again records nothing new.

$ madelog analyze
1 pattern in this log:
  fix_after_ai  77433d9 8a322f5          8a322f5 reworked AI-written code in src/reconcile.rs less than a minute after 77433d9.
recorded 1 new pattern event

1 moment for the page:
  1. Fixing AI-written code in src/reconcile.rs (fix_after_ai)
     77433d9 ("feat: match bank rows to invoices by amount and reference") carried AI-written lines in src/reconcile.rs. Less than a minute later, 8a322f5 went back to the same code: "fix: equal amounts with a different reference were counted a...". Git AI attributed 27 lines in 77433d9 to the model.

A log with nothing to find prints no patterns yet — keep committing and no moments yet — a moment needs a fix after AI, a decision that changed the shape of the project, or a long gap. The four rules and their thresholds are in Concepts.

7. Write the summaries

madelog summarize buckets the log into weeks, Monday to Sunday from the first commit, and writes two or three paragraphs per week. With Ollama running and the configured model pulled, the model writes them; otherwise a template does, and the command says why:

$ madelog summarize
Ollama at http://localhost:11434 has no model `qwen3:4b`; writing the summaries from the template (run `ollama pull qwen3:4b` to use it)

week 1  2026-09-14 to 2026-09-20  (generated)  heuristic  01a0b381-e41b-73af-9c25-190f485d0fbd
In the week of 2026-09-14 you made 4 commits over 1 day. Most of the changes landed in src/reconcile.rs, src/main.rs and .gitignore.

You wrote down 1 decision: chose SQLite over Postgres: one binary, no service to run.

The analysis found 1 pattern that week: a fix after an AI session (8a322f5 reworked AI-written code in src/reconcile.rs less than a minute after 77433d9).

edit any of them with `madelog summary edit <id>`

The last value on the week 1 line is the summary event's id. Editing opens the text in your editor and appends a new summary event in your words:

$ madelog summary edit 01a0b381-e41b-73af-9c25-190f485d0fbd
week of 2026-09-14 to 2026-09-20  (edited by you)  01a0b381-e442-718d-91c3-12e4160b580d
This week I built the matcher and fixed the equal-amount bug the same afternoon.

I wrote down why SQLite: one binary, nothing to operate.

See Summaries for the provider order and the marker the page shows.

8. Render the page locally

madelog publish --local does everything publish does except talk to a server: it refreshes patterns and summaries, opens the preview, and writes .madelog/site/index.html. With --yes there is no preview.

$ madelog publish --local --yes
1 pattern in this log:
  fix_after_ai  77433d9 8a322f5          8a322f5 reworked AI-written code in src/reconcile.rs less than a minute after 77433d9.
nothing new to record

1 moment for the page:
  1. Fixing AI-written code in src/reconcile.rs (fix_after_ai)
     77433d9 ("feat: match bank rows to invoices by amount and reference") carried AI-written lines in src/reconcile.rs. Less than a minute later, 8a322f5 went back to the same code: "fix: equal amounts with a different reference were counted a...". Git AI attributed 27 lines in 77433d9 to the model.

every week already has a summary — run `madelog summarize --force` to write them again

page   /Users/ana/projects/ledger/.madelog/site/index.html
not published. Run `madelog publish` without --local to put it online.

Without --yes, and with a terminal, the preview opens first. It is one screen with three panes: the timeline on the left, the selected item on the right, the summaries at the bottom. Keys: j/k or the arrows move, h hides the AI session under the cursor, e opens the selected summary in your editor, Tab/n and Shift-Tab/p step through summaries, PageUp/PageDown scroll the detail pane, Enter confirms, q or Esc aborts with exit code 1 and the message publish aborted. Without a terminal, publish proceeds without the preview.

Open the file in a browser. The badge at the bottom reads "Not yet published". With fewer than 15 events the command prints a warning first and goes on:

warning: this project has 13 events so far, and a page built from fewer than 15 events says very little. It will still publish. Keep committing and run `madelog publish` again later.

9. Publish

The same command without --local signs the chain root with your device key and sends the page to the server.

$ madelog publish --yes
...
page   /Users/ana/projects/ledger/.madelog/site/index.html
page   /Users/ana/projects/ledger/.madelog/site/index.html
verify https://madelog-server-production.up.railway.app/v1/verify/north-fable-4xt3

published  https://madelog-server-production.up.railway.app/p/north-fable-4xt3
Send that link. `madelog unpublish` takes it down again.

page is printed twice because the page is rendered twice: once with "Not yet published" in the badge, uploaded to get a slug, then once more with the server's confirmation and uploaded again to the same slug. What was sent, both times: the rendered HTML, the root, the first and last event timestamps, the publish timestamp, and an ed25519 signature over root, timestamp and slug. The server answers with a slug, which madelog stores in .madelog/config.toml as server.slug, next to the server.device_id it gave your public key:

[server]
url = "https://madelog-server-production.up.railway.app"
device_id = "5ea56900-b0d3-4683-aa32-fe1e3eca821c"
slug = "north-fable-4xt3"

The next madelog publish replaces the same page instead of minting a second link. Anyone can compare the badge with the server's record:

$ curl -sS https://madelog-server-production.up.railway.app/v1/verify/north-fable-4xt3
{"slug":"north-fable-4xt3","root":"6cc1406d82499fc6f69483a603f1f5efe04ac8767edd31674a4f1afaf3b18535","first_ts":"2026-09-18T07:53:48.617Z","last_ts":"2026-09-18T07:53:49.507Z","received_at":"2026-09-18T07:53:50.471573Z","device_id":"5ea56900-b0d3-4683-aa32-fe1e3eca821c"}

The hosted server answers at madelog-server-production.up.railway.app today and moves to a madelog.app address once that domain is attached; the pages published before keep working at the address they were given.

10. Take it down

madelog unpublish deletes the page from the server. The chain on your machine is untouched, and madelog publish can put the page back at the same address.

$ madelog unpublish
north-fable-4xt3 is gone from https://madelog-server-production.up.railway.app. The chain is untouched, and `madelog publish` can put it back.
$ curl -sS https://madelog-server-production.up.railway.app/v1/verify/north-fable-4xt3
{"error":"not_found","message":"no such page"}

From here: Workflow follows a project over three weeks, and Commands has every flag, exit code and edge case.