Repository navigation
v0.7.0: Session Cartographer 0.7.0
Session Cartographer 0.7.0
Primarily an attribution release. Sessions run inside a git worktree were filed under
the worktree's directory name rather than the repository's, so every throwaway agent
worktree became a phantom project that owned real history and then pointed at a dead
path once it was pruned. This release stops that happening and repairs what is already
recorded.
It also carries a search performance change and a new Explorer surface, both described
under Also in this release below.
Who needs to run the migration
Anyone who has used an agent worktree. Claude Code creates them automatically for
isolated subagents, so this is not a niche configuration — if git worktree list in
any of your repos shows entries under .claude/worktrees/, your event logs contain
phantom projects.
You do not need it if you have never run a session inside a worktree; the dry run
will simply report zero and change nothing.
Run it before any git worktree prune. The repair works by asking git to resolve
each recorded working directory back to its parent repository, and git can only answer
that while the worktree directory still exists. Pruning first destroys the mapping
permanently. On the development corpus 5,026 events were recoverable and 670 were
already past saving for exactly this reason.
Highlights
- Hooks resolve a worktree to its parent repo.
--show-toplevelreturns the
worktree directory from inside a worktree;--git-common-dirreturns the main
repo's.gitin both cases, so its parent is the real project root. The new
cartographer_project()inhooks/common.shreplaces all seven derivation sites
across five hooks, including the twoFILE_REPOvariants inlog-tool-use.sh. - A migration that repairs the existing corpus.
scripts/migrate-project-attribution.jsrepointsprojectin place and records
project_repointed_fromso the edit is auditable.cwdandgit_branchare
accurate history and are never rewritten. - Three edge cases handled, each of which a naive implementation gets wrong: git
before 2.31 has no--path-format; a bare repository would otherwise resolve to the
name of its parent directory; and a non-repo directory still falls back to its own
basename.
Also in this release
- Search stopped scaling with corpus size.
bm25-search.awkpopulateddf[]for
every token in the corpus on every query. Pass 1 now gathers document frequencies for
the query's tokens only, and a byte-level grep pass narrows pass 2 to rows containing
at least one normalized query token before any scoring runs. Guardrail tests cover
both this path and the Explorer's JSONL read, so a change that reintroduces a
whole-corpus pass fails rather than quietly getting slower. - Explorer gained Internals, a system-observation surface. The Timeline explains the
chronology of human work; Internals explains how the system itself behaved. The read
runs in a worker behind an aggregator so it never blocks the API, and the client
degrades to stale data rather than a spinner when an aggregate is slow. See
docs/INTERNALS.md, anddocs/TURBO_MODE_SPEC.mdfor a draft of utility-first
recall.
Neither needs any migration or action on upgrade.
Upgrading
node scripts/migrate-project-attribution.js # dry run — reports, changes nothing
node scripts/migrate-project-attribution.js --applyThe dry run prints, per log, how many events would be repointed and how many reference
a worktree that no longer exists, followed by the phantom projects ranked by event
count. Nothing is written until you pass --apply.
Order matters the same way it did in 0.6.1: upgrade the hooks first so new events are
written with the correct project, then migrate. The migration is idempotent and safe to
re-run; a second pass reports 0 event(s) repointed.
Safety
session-milestones.jsonl takes live appends from other sessions, so the migration is
built for that rather than against it. It backs up every file it touches before writing,
carries across anything appended during the pass instead of truncating it, and verifies
by event-id set comparison rather than line counts — a concurrent append can
otherwise mask a loss that a count check would pass. If any event id goes missing it
restores from the backup and exits nonzero.
It only repoints a record carrying the bug's exact signature, where project equals the
basename of the recorded cwd. Events whose project came from a different code path are
left alone. Unparseable lines survive verbatim.
What this does not fix
Events whose worktree has already been pruned cannot be recovered — the directory that
would have identified the parent repository is gone, and guessing would be worse than
leaving the record honest. The dry run counts them separately so you can see the scale
of what was lost before the fix landed.
Full implementation detail is in CHANGELOG.md.