Story: Compass session journal
Table of Contents
This page documents a story in Sprint 19. It captures the goal, current status, acceptance criteria, and the tasks that compose it.
Goal
Up to four Claude instances run in parallel, each in its own git worktree, each working on a different story. Two recurring problems:
- Overlap risk. Before picking up a new story, a Claude needs to know whether another Claude is already touching the same area of the codebase. There is currently no lightweight way to check this without reading all worktree branches and inferring intent from them.
- Restart recovery. Restarting a Claude (to pick up settings changes, new skills, hook updates) loses conversational context. There is no persistent record of what the environment was doing so the user or Claude can resume quickly.
The solution is a .journal.org file per worktree — machine-local,
gitignored, cumulative — that records the story/task journey of that
environment as Claude picks up and completes work. Each entry is an org
heading with timestamp and org-roam links:
#+begin_example
2026-05-31 09:15 — Refactor ores.codegen C++ generation
- Task: Codegen architecture analysis and unified model roadmap — DONE
- Branch: feature/codegen-architecture-analysis
- PR: #945
#+end_example
New compass journal subcommands query the file: compass journal where
answers "where was I?"; compass journal log shows the full journey.
compass fleet is refactored to read .journal.org from each worktree
so it can show what each Claude is actively working on, not just which
branch is checked out.
What we want
- A
.journal.orgformat spec — org-mode headings with timestamp, org-roam story/task links, branch, PR, and state. Gitignored. One file per worktree directory. compass journal update— appends a new entry to.journal.orgwhen Claude picks up a task. Called automatically bycompass gotoand by the semi-autonomous developer skill when starting work.compass journal where— prints the most recent entry (last story + task + branch + PR + state). Answers "where was I?" after a restart.compass journal log— prints all entries in chronological order. Answers "what has been my journey in this environment?"compass fleetrefactored — reads.journal.orgfrom each worktree (via the existing worktree-path enumeration) so the fleet view shows story/task context rather than just branch names. Graceful fallback when no journal exists (new worktree).- Overlap detection —
compass journal where --all(orcompass fleet) shows the current story/task for every worktree. Claude checks this before picking up a new story to detect overlap. - Story-side lookup —
compass fleet --story <UUID>(orcompass journal find <UUID>) scans all worktree journals and reports which environment last worked on a given story or task. Answers "which checkout was working on story X?"
Design options considered
The capture Track which checkout last worked on a story or task identified four options:
| Option | Pros | Cons |
|---|---|---|
#+checkout: field in task org file |
Tracked in git; visible in Emacs | Pollutes every PR with machine-local info; merge conflicts across worktrees |
Git commit trailer (Checkout: local2) |
Permanent history | Requires hook; trailer added per commit not per task-pickup; not queryable |
| Compass stamp on status change | Integrated into existing flow | Still needs a backing store; same question of where |
| Worktree-local gitignored file (chosen) | Machine-local; no git noise; cumulative; queryable | Not shared; only visible locally or via fleet read |
The worktree-local gitignored file wins because it keeps machine state off the
shared branch while remaining readable by compass fleet across all local
worktrees.
What is NOT in scope
- Syncing
.journal.orgacross machines or worktrees — it is intentionally local and private. - Replacing the org-roam knowledge graph or any git-tracked agile artefacts.
- Automatic conflict resolution when two Claudes work in similar areas — the journal surfaces the overlap; the decision is human or Claude judgement.
- Reading
.journal.orgentries from the remote (it is gitignored).
Status
| Field | Value |
|---|---|
| State | DONE |
| Parent sprint | Sprint 19 |
| Now | Nothing. |
| Waiting on | Nothing. |
| Next | Done. All 5 tasks complete. |
| Last touched | 2026-05-31 |
Acceptance
.journal.orgis listed in.gitignore(project root).compass journal update --story <UUID> --task <UUID> --branch <name> [--pr <N>] [--state <state>]appends a timestamped entry with org-roam links to.journal.org.compass gotoautomatically callscompass journal updateafter branching, so the journal is updated without manual intervention.compass journal whereprints the last entry in the same concise format shown in the Goal section.compass journal logprints all entries, newest last.compass fleetreads.journal.orgfrom every worktree and shows story + task + state per worktree; falls back to branch-name display if no journal exists.- A recipe documents how Claude should use the journal: update on task pickup, check before picking up a new story.
- CI passes. No production files changed by this story.
Tasks
| Task | State | Start | End | Description |
|---|---|---|---|---|
| Design journal format and gitignore setup | DONE | 2026-05-31 | 2026-05-31 | Specify .journal.org entry schema; add to .gitignore; write format as a recipe. |
| Implement compass journal subcommand | DONE | 2026-05-31 | 2026-05-31 | Add compass journal update, where, and log subcommands to compass.py. |
| Wire compass goto to auto-update the journal | DONE | 2026-05-31 | 2026-05-31 | Call compass journal update at the end of compass goto (new-story and existing-story modes). |
| Refactor compass fleet to use journal | DONE | 2026-05-31 | 2026-05-31 | Read .journal.org from each worktree in compass fleet; show story+task+state; graceful fallback. |
| Write recipe and update semi-autonomous developer skill | DONE | 2026-05-31 | 2026-05-31 | Document journal use in a recipe; update the semi-autonomous developer skill to check/update the journal at task pickup. |
Decisions
.journal.orgnot.where_am_i.orgor.journey.org. "Journal" is the most natural term and is immediately understood. The file is per-worktree to match the per-environment isolation model.- Gitignored, not tracked. The journal is machine-local state. Checking it in would pollute every PR with irrelevant environment bookkeeping and create merge conflicts across worktrees.
- Org-roam links in entries. Story and task titles are likely to change; the UUID-based org-roam link is stable and navigable from Emacs.
compass gotoauto-updates. Requiring Claude to explicitly callcompass journal updateaftercompass gotois error-prone. Hooking intogotomakes the journal update automatic and invisible.compass fleetreads journal, not only branches. Branch names encode the story slug but not the task or state. The journal provides richer context for overlap detection and fleet visibility.
Out of scope
- GUI or web view of the journal.
- Journal entries for non-Claude users (human developers on the same worktree).
- Retention policy or pruning of old journal entries.