logbook/Docs

Agent workflows: the rituals

This document is written for a coding agent connected to a logbook project over MCP. It is a set of directives you can follow verbatim. It assumes the domain model in docs/guide/concepts.md — read that first if a term (log, epic, size, resolution, item_files) is unfamiliar.

The organizing principle behind every ritual here is context economy: MCP tool results stay resident in your session context for the rest of your lifetime, so the cost of a survey is paid for the whole session, not just once. Index rows are cheap; item bodies are expensive. Every ritual below is shaped to keep bodies out of context until you are actually going to work an item.

Session start: the briefing

At the start of a session, call briefing once. It returns three things in a single call — your unread inbox (and marks it read), the items assigned to you with task progress, and the team scoreboard — so you do not need separate check_inbox / get_my_work / team_summary calls.

briefing

Read the inbox first: new assignments, notes left on your items, and status changes are how work and context reach you. If you only need notifications, use check_inbox (also marks read); if you only need your queue, use get_my_work.

Surveying a backlog cheaply

Do not survey by listing full items. Follow this escalation, cheapest first:

  1. backlog_stats FIRST. For any survey or audit, call this before anything else. It returns the shape of the whole backlog in one small object with no item bodies: total, counts by status, counts by size (plus how many are unsized and need cleanup), counts by kind (log vs epic), top labels, per-assignee open counts, and the oldest in-progress item. Understand the shape here first.

  2. list_backlog, filtered, at index verbosity. Then — and only for the slice you care about — list. list_backlog returns an index (one line per item: ref, title, summary, size, status, priority, assignee names, task progress, labels) with no descriptions or bodies. Filter it: by status, assignee_email, label, size (array, OR semantics), kind, epic (an epic's children), or free-text search. It defaults to a limit of 50 and excludes done items; page with limit/offset.

  3. Pull only what you will touch. Use pull_item / pull_items (below) to read bodies for the specific items you are about to work — never as a survey.

Never run a full-verbosity survey. list_backlog accepts verbosity: "refs" | "index" | "full", default index. refs is the leanest ({ref, title, size, status} only — for very large orgs or pure lookups). full returns complete item bodies and exists only as a compatibility escape hatch for exceptional cases; its own description says it is "NOT for surveys." Do not reach for it to look around.

Picking up work

To pick up a single item, use pull_item — it gets everything in one call: problem, description, task checklist, notes, links with live PR state, assignees, recent activity, related items, and (for an epic) its children as index rows. Find it by ref (LOG-12, 12) or title text.

Pass claim: true to assign yourself and move the item from backlog to in_progress in the same call:

pull_item { query: "LOG-12", claim: true }

Claiming is idempotent — it only writes if you are not already an assignee or the status is still backlog. Do not claim an item you are only inspecting.

Batch sessions

When you are working a cluster — building out several items in one session, bulk-closing, bulk-relabeling — use the batch tools instead of dozens of single-item calls.

  • pull_items — fetch many items completely in one call. Select by explicit refs/queries (["LOG-12", "LOG-88"]) or by filter (status/label/assignee_email/size/kind/epic/search + limit). It returns the full pull_item payload per item (per-item recent activity is omitted — pull_item one ref if you need it). Pass claim: true to claim every selected backlog item at once. Pulling an epic's work queue is a filter: pull_items { epic: "LOG-12", size: ["S"] }.
  • create_items — file many structured items in one call (see Planning).
  • update_items — apply updates to many items in one call. Unlike create_items, updates are processed independently — one failure does not stop the rest, and the result reports ok/error per item. When bulk-closing, include a per-item resolution on every entry.

Planning: filing a well-formed plan

Use the plan-work prompt for anything non-trivial

logbook ships a planning interview as an MCP prompt. In Claude Code it appears as the slash command /mcp__logbook__plan-work (optionally seeded with a one-line idea). Use it whenever you are turning a rough idea into backlog items — it is the "spend time planning" surface, and it keeps a human in the loop by design: it files nothing until they approve. The prompt walks you through:

  1. Interview the human a few questions at a time, PM-style: what problem and for whom, what "done" looks like, what is explicitly out of scope, constraints and deadlines, and how the work might split.
  2. Check prior art before proposing — backlog_stats, filtered list_backlog + search, and history_for_files on any repo paths they name — and report findings in plain terms ("we did something similar in LOG-42") so you build on existing work. Call list_labels in the same pass and adopt that taxonomy; do not invent labels.
  3. Apply the decision test (see concepts.md) and state the verdict: one PR closes it → a single log; independently shippable pieces → an epic with sized children.
  4. Draft the full plan for approval as text — epic summary line; each item/child with title, one-line summary, size (treat XL as a signal to make it its own epic), labels from the existing taxonomy, and an initial task checklist. Fold acceptance criteria into descriptions; flag any long-form spec as a document to attach after the items exist.
  5. On approval, file it in one create_items call and attach any flagged specs, then report the created refs.

Filing an epic + children by hand

When you are not running the prompt, file the whole plan in one create_items call so it lands together. Put the epic entry first, then link each child with epic_index — the 0-based index of the epic entry in the same batch:

create_items { items: [
  { kind: "epic", title: "Checkout revamp",
    summary: "Rebuild checkout as independently shippable steps" },
  { title: "Cart summary panel", size: "M", epic_index: 0,
    tasks: ["wire totals", "empty state"] },
  { title: "Payment step",       size: "M", epic_index: 0 },
  { title: "Confirmation page",  size: "S", epic_index: 0 }
] }

Rules to file cleanly:

  • The epic entry carries no size (containers are not sized) and a strong summary (it is the container's one-liner). A size on an epic is an error.
  • Every log entry carries a required size (S/M/L/XL).
  • A child links its parent by epic_index (an epic in this batch) OR epic (an existing epic ref like "LOG-12") — never both.
  • Labels must already exist. Call list_labels first; create_label for any genuinely new one, reusing over near-duplicates.
  • Validation is all-or-nothing: an unknown label/email, a bad epic ref, an epic pointing at a non-epic, or an out-of-range/non-epic epic_index aborts the whole batch before anything is created — fix and retry safely.

For a single item, create_item takes the same fields. Always state the problem; a log always needs a size.

Enrichment: raising item quality

Use the enrich-log prompt

To raise the quality of an existing item, use the /mcp__logbook__enrich-log slash command (argument ref, required). It audits one item against the quality bar and, like plan-work, changes nothing until the human approves. It:

  1. Pulls the item and reads the whole thing.
  2. Audits against the quality bar — a missing size, a missing one-line summary, no problem statement, no acceptance criteria, an XL log that should be an epic, a description that is really several separable deliverables, or a stale status — and notes only the gaps actually present.
  3. Asks the human only about those gaps (not a fixed questionnaire).
  4. Applies the fixes in a single update_item call if the item stays whole.
  5. Proposes a split rather than forcing it if the item is really several deliverables — new epic + children via create_items, re-parenting the original or closing it as superseded — the human's choice.

Backfill opportunistically

The backlog carries legacy items with no size and no summary. Do not run a mass-cleanup pass, but whenever you have an item pulled anyway, backfill it: update_item accepts size (to size an unsized legacy item) and summary while you are there. backlog_stats.unsized is where unsized items surface — they never match a size filter, so sizing them is what puts them back into shape queries.

Working an item

The completion sequence matters. Follow it exactly.

  1. Claim it. pull_item { query: "LOG-12", claim: true } (or pull_items with claim: true for a cluster). You are now an assignee and it is in_progress.
  2. Work the task checklist. As you complete each step, check it off with complete_task (identify the task by exact title or a unique substring). Add steps you discover with add_task. Remember tasks are steps of this one deliverable — if a "task" is really a separate shippable piece, it belongs as an epic child, not a checkbox.
  3. Attach your branch and PR as you go. Use update_item with add_links to attach a branch link when you start and a pr link when you open the pull request: update_item { number: 12, add_links: [{ kind: "pr", value: "<pr-url>" }] }. The PR URL is what the merge webhook matches on, so this link is what wires the item to its automatic cross-off.
  4. Do NOT mark the item done when you open the PR. Leave the status in_progress. When the PR merges, the GitHub webhook crosses the item off automatically — it sets status = done, records the changed files into item_files, and leaves a merge note. Marking it done yourself at PR time is premature: the work is not shipped until it merges, and doing it by hand both races the webhook and skips the file-history capture. Let the merge be the thing that closes it.

The one exception: if there is genuinely no PR (a docs-only change, a decision recorded, work abandoned), close it yourself with update_item and a resolution — but that is the exception, not the flow.

Completing: the resolution quality bar

Whether the webhook closes an item or you do, the item must carry a real resolution. When you close something by hand, update_item requires it: "When marking done, ALWAYS write a resolution."

The bar: write the changelog entry you would want to find in six months. Be concrete — what changed, where, and which decisions were made. "Fixed it" is not a resolution. This text is not bookkeeping: it is what a future agent reads when it queries the history of a file, and what history_for_files returns as the resolution_excerpt. Resolutions accumulate into the de-facto documentation of what the project built (see concepts.md), so the quality of that documentation is exactly the quality of the resolutions you write.

The webhook's auto-stub (Resolved by PR #N … plus a file summary) is a fallback for items nobody wrote a resolution for — it never overwrites a hand-written one. Prefer to write your own; the stub is not the changelog entry you would want in six months.

Before touching unfamiliar code

Before you modify a file you did not just write, run the history_for_files ritual. Its description states it plainly: "Before modifying a file, ask what happened to it before — returns the items and resolutions that shaped these paths."

history_for_files { paths: ["lib/db/schema.ts", "app/api/mcp/"] }

Pass one or more repo-relative paths (1–20). A trailing slash makes a path a directory query (everything under it); otherwise the match is exact and case-sensitive. Each path returns the matching items newest-first, each with a resolution_excerpt and the PRs that touched the path. git log tells you what changed; only logbook holds the intent and the resolution behind it, which is what stops you from re-litigating a decision that was already made and written down.

Context economy rules

Internalize these so the rituals above make sense:

  • Index rows are cheap; bodies are expensive. An index row is roughly 40–60 tokens; a full item is 250–600+. An unfiltered 200-item survey is ~10k tokens at index, ~4k at refs, and ~80–120k at full. backlog_stats (~150 tokens) replaces most surveys entirely.
  • Everything you pull stays in context. MCP results are resident for the session's whole lifetime. Pulling bodies you will not act on is a cost you pay for the rest of the session.
  • What each verbosity level is for. refs — pure lookups and very large orgs. index — the default; scanning and selecting work. full — a compatibility escape hatch for exceptional cases, never a survey. Use pull_item / pull_items (not list_backlog { verbosity: "full" }) when you actually need bodies, because those are the "I am working this item" paths and are meant to be complete.
  • Documents and changed-files stay out of the way. Attached documents show only their filename until get_document; index rows never carry changed_files. Fetch long-form content only when you are going to read it.
  • For a very large audit, delegate the reads. Spawn a cheap subagent to do the reading and return a distillate, and keep your main session's context to index rows and conclusions.