madelogdocs

Summaries

Weekly paragraphs from a local model or a template. How the provider is chosen, what madelog sends to Ollama and what it accepts back, the fallback notices, editing, and the marker on the page.

madelog summarize writes one summary per week of recorded work. Weeks run Monday to Sunday in your local time zone, counted from the week of the first commit. A summary is a summary event like everything else: regenerating a week appends a new one, editing appends another, and the page shows the newest per week. madelog publish runs summarize before the preview, so weeks without a summary get one at publish time.

What the model sees

Only structured facts, never a diff and never a prompt: the first line of each commit message with its date, file count and line counts; the decisions you wrote down that week, private ones excluded; and the patterns recorded that week. At most 40 commit lines go in; the rest are counted in one line. This is the whole input, whichever provider writes the text. The exact text for one week, as madelog sent it:

Week 1: 2026-08-31 to 2026-09-06
Commits (9) on 4 days:
- 2026-08-31: feat: project skeleton (3 files, +29/-0)
- 2026-08-31: feat: sqlite store and first migration (4 files, +83/-1)
- 2026-09-01: feat: csv import with BOM handling (2 files, +62/-0)
- 2026-09-01: fix: importer dropped the last row when the file has no trailing newline (1 file, +6/-2)
- 2026-09-02: wip: matching by amount (1 file, +31/-0)
- 2026-09-02: wip: matching by amount and date (1 file, +12/-4)
- 2026-09-02: wip: matching by amount and reference (1 file, +9/-3)
- 2026-09-03: feat: report command (2 files, +46/-1)
- 2026-09-03: Revert "feat: report command" (2 files, +1/-46)
Files touched most: src/main.rs, src/matcher.rs, Cargo.toml
Decisions (2):
- chose SQLite over Postgres: one binary, no service to run
- dropped the report command for now: the client only needs the matched list as CSV

A week with patterns gets a Patterns (n): block with one - <kind>: <detail> line each. A week without commits says Commits: none. "Files touched most" names up to three paths, ranked by how many of the week's commits touched them, then by the lines those commits changed in that file alone, then by name.

Provider order

llm.provider in .madelog/config.toml decides who writes:

ValueBehaviour
auto (default)Ollama, if something answers at the Ollama host and the configured model is pulled; otherwise the template, with a notice saying why
ollamaOllama, or an error that names what is missing and exits 1. Use this when a template summary would be a mistake
noneThe template, without touching the network

llm.remote = true overrides all three: it prints remote models are not supported in this version and uses the template.

The host is the llm.host config key when it is set, then OLLAMA_HOST, then http://localhost:11434. localhost:11434 and http://localhost:11434/ mean the same thing; a value without a scheme gets http://. A blank llm.host counts as unset.

What madelog asks Ollama

Two endpoints, nothing else:

RequestPurposeTimeout
GET <host>/api/tagsIs Ollama there, and is the model pulled? The models[].name list is compared with llm.model; a bare name matches its :latest tag1 second
POST <host>/api/generateOne request per week60 seconds

The body of the generate request, exactly as sent, minus the prompt shown above:

{
  "format": "json",
  "model": "qwen3:4b",
  "prompt": "Week 1: 2026-08-31 to 2026-09-06\nCommits (9) on 4 days:\n...",
  "stream": false,
  "system": "You summarise one week of a single developer's work for a page a company will read. You are given commit messages, decisions the developer wrote down, and patterns a program detected. Use only those facts. Never invent a reason, a feeling or a result. Do not judge the work. Answer with JSON only, of the form {\"summary\": \"...\"}, where the summary is two or three short paragraphs of plain English separated by blank lines: what happened, what was decided, what stood out."
}

madelog reads the response field of the answer and expects it to parse as {"summary": "..."}. The summary is trimmed and accepted only if it is not empty and at most 2,000 characters. Anything else, a refused connection, a timeout, a non-JSON body, an empty or runaway summary, counts as no answer: the template writes that week instead and the command says so before the week. A model that starts summarising badly cannot put made-up text on the page.

With the model answering well:

$ madelog summarize --force --week 1
week 1  2026-08-31 to 2026-09-06  (generated)  ollama:qwen3:4b  01a0b389-3f60-7636-8279-4991571f246c
You started the ledger CLI on Monday and had a working SQLite store and a CSV importer by Tuesday, with one fix the same afternoon.

You wrote down two decisions: SQLite over Postgres, and dropping the report command until the client asks for it.

The importer commit carried 51 AI-written lines; the fix that followed touched the same file two hours later.

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

With the model answering prose instead of JSON:

$ madelog summarize --force --week 2
week 2: qwen3:4b did not return a usable summary (the model did not answer with {"summary": ...} JSON: expected value at line 1 column 1); wrote the template instead
week 2  2026-09-07 to 2026-09-13  (generated)  heuristic  01a0b389-4561-75f8-b96d-0f0501a4c571
In the week of 2026-09-07 you made 1 commit over 1 day. Most of the changes landed in README.md, src/import.rs and src/main.rs.

No decisions were written down that week, and the analysis found no patterns.

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

The notices

Each situation prints one line before any week is written. With auto:

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

With ollama, the same two situations are errors with exit code 1 and nothing written:

madelog: llm.provider is `ollama`, but nothing answers at http://localhost:11434. Start Ollama, or run `madelog config set llm.provider auto`.
madelog: llm.provider is `ollama`, but http://localhost:11434 has no model `qwen3:4b`. Run `ollama pull qwen3:4b`, or run `madelog config set llm.provider auto`.

llm.model

The model madelog asks Ollama for. The default is qwen3:4b, which runs on a laptop. Any model Ollama has pulled works; a blank value means the default.

$ ollama pull qwen3:4b
$ madelog config set llm.model qwen3:4b
llm.model = qwen3:4b

The template

The fallback is deterministic and cannot invent anything. It assembles two or three paragraphs from counts and from text you wrote yourself: what happened, what was decided, what stood out.

In the week of 2026-08-31 you made 9 commits over 4 days. Most of the changes landed in src/main.rs, src/matcher.rs and Cargo.toml.

You wrote down 2 decisions: chose SQLite over Postgres: one binary, no service to run; dropped the report command for now: the client only needs the matched list as CSV.

The analysis found 4 patterns that week: a fix after an AI session (d366a14 reworked AI-written code in src/import.rs two hours after fb7eeec); repeated attempts at the same files (Three commits in 50 minutes reworked the same file); a revert (Reverted 2 files changed in ea23143 one hour earlier); a stall (After six days without commits, 72ddd54 changed 12 files (444 lines)).

A week with no decisions and no patterns ends with "No decisions were written down that week, and the analysis found no patterns." A week with no commits starts with "In the week of <date> there were no commits." Pattern kinds are spelled out: "a revert", "repeated attempts at the same files", "a fix after an AI session", "a stall". The same week always produces the same paragraphs.

Selecting weeks

madelog summarize writes only weeks that have no summary yet. When every week has one it prints every week already has a summary — run `madelog summarize --force` to write them again and exits 0. Two flags change what is written:

  • --week N limits the run to week N, counted from 1 at the week of the first commit. A week with no commits, decisions or patterns is not a valid target: madelog: this log has no week 5. Weeks with anything in them: 1, 2, 3, exit 1.
  • --force writes a new summary even for weeks that already have one.

A log with no commits prints nothing to summarise yet — madelog counts weeks from your first commit.

Patterns land in the week of the work they describe, not the week madelog analyze ran: a pattern event is bucketed by the timestamp of the commit it is anchored to, which is the last commit it covers. Running analyze at the end of a project therefore still files each pattern under the week it happened in. A pattern with no commit anchor falls back to its own timestamp.

Editing

Every summary is shown in the preview before publishing, and any of them can be rewritten:

$ 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.

The text is written to a temporary file (madelog-summary-<uuid>.md in the system temp directory) and opened in your editor, chosen in this order: MADELOG_EDITOR, EDITOR, VISUAL, then vi. A value with arguments, such as code -w, works; the first word is the program and the rest are passed before the file path. When the editor exits 0 the file is read back and deleted.

  • Saving a changed, non-empty text appends a new summary event with generated_by: user and edited: true, and prints the new id.
  • Leaving the text unchanged prints unchanged — nothing appended.
  • Emptying the text is refused: madelog: a summary cannot be empty — nothing was changed, exit 1.
  • An editor that exits non-zero: madelog: your editor (`false`) exited without saving; nothing was changed, exit 1.
  • Any id that is not a summary: madelog: `madelog summary edit` takes a summary event; event `01a0…` is a commit, exit 1.

MADELOG_EDITOR=stdin reads the new text from standard input instead of opening an editor, which is how scripts and the tests edit without a terminal. Pressing e on a summary in the publish preview runs the same command on the newest summary of that week.

The generated marker

The page is honest about its own AI use. Each summary paragraph ends with a marker that names its source:

generated_byMarker on the page
ollama:<model>generated locally (<model>), with ", edited" when edited is true
heuristicgenerated from the log, with ", edited" when edited is true
userno marker: these are your words
remote:<model>summary generated by remote model (<model>); nothing writes this in this version

The footer of the page adds: "Paragraphs marked generated were written by a model and reviewed by the author." In the preview a summary is headed <start> to <end> (generated, ollama:qwen3:4b) or (edited by you).

llm.remote

Remote APIs are never called by default, and in this version they are not called at all. llm.remote exists in the config and defaults to false. Setting it to true makes madelog summarize print remote models are not supported in this version and write the template, whatever llm.provider says. When a later version adds a remote provider, the page will carry the "summary generated by remote model" marker on every paragraph it wrote, and true will remain the only way to turn it on.