Repository navigation
trail init: share one hook and the code map in .trail/, keep the rest personal - #619
Conversation
🌱 graft blast radius5 areas changed → 13 areas can be affected. 63 dependent symbols, depth 2. flowchart TB
A0(("Dependency Graph<br/>22 symbols"))
A1(("Host Integration<br/>10 symbols"))
A2(("Build Orchestration<br/>7 symbols"))
A3(("Engine API<br/>5 symbols"))
A4(("MCP Server<br/>4 symbols"))
AX(("8 smaller areas<br/>15 symbols"))
classDef reached fill:#D9EDF3,stroke:#3AA7C9,stroke-width:1.5px,color:#0E313C;
class A0,A1,A2,A3,A4 reached;
classDef tail fill:#EEF2F3,stroke:#9AA4A9,stroke-width:1px,color:#3A4247;
class AX tail;
Who knows this code — 4 people across 18 areas
Ownership is git history over each area's own files, weighted towards recent work (120-day half-life). Merge commits and bots are dropped, and you are dropped from your own PR. A name with no All 63 dependent symbols, grouped by areaDependency Graph — 22 symbols in 8 files
Host Integration — 10 symbols in 6 files
Build Orchestration — 7 symbols in 5 files
Engine API — 5 symbols in 1 file
MCP Server — 4 symbols in 2 files
Pull Request Review — 4 symbols in 4 files
Context Building — 3 symbols in 2 files
Findings Cache — 3 symbols in 3 files
Viewer Build — 1 symbol in 1 file
Repository Identity — 1 symbol in 1 file
…3 further symbols not listed. Test signal per changed area — 5 ✓Reached = a node under a test path has a resolved edge into the changed symbol. It undercounts anything called indirectly — through a CLI, a spawned process or a dynamic import — so read a low ratio as “look here”, never as a coverage gate.
56 test suites also reference this code65 symbols, kept out of the diagram and the table so they cannot crowd out the areas a reviewer has to look at.
Open the interactive graph → — click an area to see its dependent symbols at file:line. |
* fix(init): make .trail/learnings before the first telemetry event The past-session read sends cold_start before init wired anything, so the repo didn't count as on trail yet and the event's repo id went to graft/.cache/. Since #619 nothing ignores graft/, so every fresh init with telemetry on left an untracked graft/ folder in git status. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore(release): 0.23.0 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Shrish Dwivedi <shrish@nanonets.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Moves:
learnings_committed, the number of clones whose committed learnings went up in the last 7 days (PostHog query indaily/project.md). I didn't check today's value, because this session has no Metabase access. Today, a team that commits.trail/but not the hook script gets nothing for teammates who haven't installed trail. In a repo like NanoNets/assign, whose.gitignoreexcludes.claude/*, the hook script can't be committed at all. This change makes the shared part one folder and one settings line, and aftertrail initthe repo's.gitignoreand a root.ignoredon't change either.What changes
trail initnow writes two kinds of files.Shared, committed once by whoever sets the repo up
.trail/hook.cjs: the hook script, moved from.claude/helpers/trail-hooks.cjs. Inside.trail/, a.claude/*ignore rule can't drop it..claude/settings.json: one SessionStart entry that runs it, and nothing else of trail's. The status line, the other hooks, the allowlist and the footer regex come out of this file.For a teammate without trail, that hook lists the learnings, credits one, then offers to install once, as before. For someone with trail, it hands off to their install.
Personal, kept out of git without touching
.gitignore.claude/settings.local.json: the status line and the allowlist.--no-globalit also holds the other hooks, including the new SessionEnd. It never holds a second SessionStart..git/info/exclude..trail/statusline.cjs: the status line's script, listed in.trail/.gitignore.~/.claude/skills/trail/: the skill. The repo copy in.claude/skills/is gone.~/.claude/settings.json: the other hooks, as before. A repo hook that~/.claudealso covers stands down, and that now includes.trail/hook.cjs.No MCP server for Claude Code
.mcp.jsonand the user-scopetrailserver, and removes trail's entry from both where an older init added it. Other servers in those files stay.graft mcpstand-in for teammates with only graft is no longer added once a repo's.mcp.jsonhas no trail server.Telemetry is sent from the hooks
Messages
· commit .claude/settings.json and .trail/ once — after that, teammates' Claude reads your learnings even before they install trail🌱 Anay Shah and Rushil work in this repo. Commit .trail/ and .claude/settings.json once, and their Claude starts from your learnings, even before they install trail. After that, the existing line about uncommitted learnings applies..trail/and.claude/settings.jsoninstead of.claude/,.mcp.jsonand.trail/..trail/README.md: it explainshook.cjsand the commit-once step. A README still holding the old text is replaced, and an edited one is left alone.Repos set up the old way
The next wiring refresh, at session start after an upgrade or on
trail init, moves a repo to the new layout:.claude/helpers/and the repo skill, plus those folders once they're empty.mcp.jsonserverThe person who upgrades first sees
.claude/settings.jsonchanged and the old files deleted, and the first-reply line asks them to commit it once.The code map moves to
.trail/cache/Where the map lives (
src/context/map-dir.ts).trail/cache/. A repo counts as on trail when it has.trail/learnings/or trail's hooks.graft/.contextDirForandresolveContextDirboth go through this one rule, so the CLI, the hooks, the status line and upkeep agree.--dirandGRAFT_DIRstill win.Kept out of git by
.trail/.gitignorecache/to.trail/.gitignore..gitignoreor a root.ignore. graft's map is ignored exactly as before.An existing map moves instead of being rebuilt
graft/that holds a map is renamed to.trail/cache/the first time anything asks where the map is.graft/that git tracks is left alone: it dates from when the map was committed, and moving it would delete committed files./graft/lines in.gitignoreand old.ignoreblocks are left as they are. They're harmless, and removing them would touch files this change promises to leave alone.Finding it
.trail/is hidden, so a ripgrep from the repo root skips the cards. Given.trail/cacheas the path, it searches them. I checked both.repo map (.trail/cache/INDEX.md))..trail/cache/where they saidgraft/.trail ask,grepandcallersread the map wherever it is.Smaller
.trail/cache/counts as the map's own, not the code's..trail/cache/too.graft/: the subdirectory notice,trail build's help, and "no code map found". Neither doesask's "if the code map is empty".Tests
The init layout
trail-init-layout:--no-globaltrail init,git statusshows only.claude/settings.jsonand.trail/.git/info/excludeis written only when nothing ignores the file.trail/hook.cjsstands down when~/.claudecovers the eventtrail-notes: the graft-to-trail move, idempotence and uninstallgraft-to-trail,hosts-claude-globalandclaude-shim-template, for the new paths and no MCP server.trail/hook.cjs session-startprinted the learnings list and the install offer. Other events printed nothing.The code map move
trail-map-dir:graft/map moves with its cache, and git doesn't see itgraft/stays where it is.trail/.gitignore.trail/learnings/counts as on trail before its hook exists.trail/cache/, and the subdirectory notice test expects the new wording.trail initwith a real build,git statusshowed only.claude/settings.json,.trail/.gitignore,.trail/README.mdand.trail/hook.cjs. There was nograft/and no.ignore, andtrail grep NormalizeBoxanswered from.trail/cache/.graft init, with its map ingraft/, thentrail init: the map moved to.trail/cache/without a rebuild.Both
🤖 Generated with Claude Code