Skip to content

Make trail work in Claude Desktop and Codex - #733

Open
anirudhkumar-nanonets wants to merge 2 commits into
mainfrom
claude/desktop-and-codex-hosts-cde008
Open

anirudhkumar-nanonets wants to merge 2 commits into
mainfrom
claude/desktop-and-codex-hosts-cde008

Conversation

@anirudhkumar-nanonets

@anirudhkumar-nanonets anirudhkumar-nanonets commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

Issue: Until its hooks were trusted in /hooks, Codex never heard how to save a learning. Asked to save one, it edited a teammate's learning file by hand. In Claude Desktop worktree sessions, editing a file never brought its learnings, and neither app was counted as its own host.
Why: AGENTS.md only covered code search, and trail note wrote to ~/.trail (which Codex's sandbox can't write) before the repo. Desktop's hooks get the main checkout as the project, so a worktree file's path never matched a learning. agent_host had no value for either app, and hook events from Codex said cli.
Fix: AGENTS.md gets a Learnings part. A repo learning saves even when ~/.trail can't be written. Touch learnings take the file's path in its own checkout. A hook puts all its events, and the trail commands it starts, down to the payload's host, now including codex and claude-desktop.

Moves: active users by host, PostHog Postgres (Metabase db 11, posthog_event_full_view), distinct installs over the last 7 days with a repo_seen, query, note_saved or learnings_committed event, cli_name = 'trail', by agent_host. On 2026-10-29: claude-code 1,157 · cli 677 · mcp 262 · claude-desktop and codex 0 (the values don't exist before this PR).

What works where (checked with real sessions on 2026-10-30)

Host Learnings at start Saving one Reusing one agent_host
Claude Desktop Code tab (worktree session) works (from the main checkout's branch) works partly → works claude-code → claude-desktop
Codex, hooks trusted works no → partly (saves when asked; never unasked) works cli → codex
Codex, hooks not trusted (right after trail init) no → partly (AGENTS.md) no → partly (same) works (trail ask) none
Claude Desktop chat (Trail connector, trail-cloud) partly partly (saves to the workspace, not the repo) works not in this repo

Codex: five real sessions

Codex 0.162 (the CLI inside ChatGPT.app, which the app runs as its app-server), default model, workspace-write sandbox, each run in its own home outside /tmp so the sandbox can't write it. "Before" is trail 0.26.0 as installed; "after" is this branch. The repo had one committed learning (money stays in integer cents). Prompt: add applyDiscount with a test; "half-cents always round down".

Run Reached the model about learnings Used the learning Saved the rule unasked Then asked to save
A before, untrusted nothing yes, via trail ask no edited the teammate's learning file by hand
B after, untrusted AGENTS.md Learnings part yes, via trail ask no trail note: turned away for naming no file, then saved inside the sandbox
C after, trusted (--dangerously-bypass-hook-trust) session-start text, prompt learnings, save directive, AGENTS.md yes, credited no not asked
D before, trusted session-start text, prompt learnings, save directive yes, credited no not asked
E after, trusted every queued event from the session says codex (hook_ms ×3, prompt-hook ask, stop-hook check, repo_seen, learnings_committed); in D they said cli

Codex's shell sets CODEX_THREAD_ID, CODEX_SESSION_ID, CODEX_SANDBOX (seen), so detectHost reads those. But a trail command Codex runs itself can't write the event queue under ~/.graft, so those send nothing either way.

Claude Desktop: this session

The transcript and trail's state files show SessionStart, UserPromptSubmit, PostToolUse, PostToolUseFailure, PreToolUse on Bash and Stop all ran; the trail skill and MCP server loaded. PreToolUse on Edit ran but never found a learning: in a worktree session CLAUDE_PROJECT_DIR is the main checkout, so learningsOnTouch saw .claude/worktrees/<name>/src/... (same cause #738 fixed for overlap warnings).

Changes

  • src/hosts/instructions.ts: under trail, the AGENTS.md block (also GEMINI.md, Copilot) ends with ### Learnings: where they are, read them via trail ask, save with trail note, commit them, run trail check. Graft's text is unchanged.
  • src/notes/notes.ts: writeNote falls back to repoPlace when ensureRepoHome throws and the learning goes in the repo. --personal still needs ~/.trail.
  • src/claude/hooks.ts: learningsOnTouch takes the file's path relative to checkoutRoot(dirname(file)); learnings and session state still come from the project dir, so nothing is shown twice. The hook sets TRAIL_AGENT_HOST from the payload's host.
  • src/telemetry/track.ts, contract.ts, TELEMETRY.md: agent_host adds claude-desktop (CLAUDE_CODE_ENTRYPOINT === 'claude-desktop') and codex (Codex's shell variables, or TRAIL_AGENT_HOST from a hook). Every env value is only compared against fixed names; none travels.
  • A learning on what each host does and how to test Codex for real (replaces yesterday's, which said every hook worked in Desktop).

Tests

  • New: host detection per env and from TRAIL_AGENT_HOST, a Codex learnings_committed carrying codex, the trail AGENTS.md text, writeNote with an unwritable home, and a learning reaching an edit in a linked worktree while the project dir is the main checkout (fails without the fix).
  • 829 telemetry, hosts, hooks, trail and upkeep tests with a scratch HOME: 5 fail, all of which also fail on origin/main here (they depend on a real trail on this machine's PATH). tsc --noEmit clean.

trail check

Two flags, both word matches: TELEMETRY.md:40 against "Claude ran with the real HOME…" (Oct 27, about tests loading the real ~/.claude/settings.json), and src/claude/hooks.ts:177 against the replaces:-holds-ids learning (Oct 22): that line has .replace( and the words "file" and "name", and touches no learning field.

Follow-ups

🤖 Generated with Claude Code

@trailhq-graft

trailhq-graft Bot commented Oct 29, 2026 •

Copy link
Copy Markdown

🌱 graft blast radius

4 areas changed → 5 areas can be affected. 20 dependent symbols, depth 2.
Tests: no test reaches Host Detection; 3 areas updated their tests.
Tag: @shhdwi — 9 of 9 areas

flowchart TB
  A0(("Host Registry<br/>6 symbols"))
  A1(("Installation Paths<br/>5 symbols"))
  A2(("Note Persistence<br/>4 symbols"))
  A3(("CLI Entry Point<br/>3 symbols"))
  A4(("Session Learning<br/>2 symbols"))
  classDef reached fill:#D9EDF3,stroke:#3AA7C9,stroke-width:1.5px,color:#0E313C;
  class A0,A1,A2,A3,A4 reached;
Loading
Can be affected Symbols Nearest hop Reached from
Host Registry 6 src/hosts/registry.ts:L1-L186 registry.ts — references, depth 1 Host Instructions
Installation Paths 5 src/init/keep.ts:L121-L133 keepGuardrail — calls, depth 1 Host Instructions, Note Writing
Note Persistence 4 src/notes/memory-notes.ts:L230-L244 saveMemoryCandidates — calls, depth 1 Note Writing
CLI Entry Point 3 src/cli.ts:L1255-L1405 runNoteCommand — calls, depth 1 Host Instructions, Note Writing
Session Learning 2 src/learn/sessions.ts:L265-L273 saveSessionLearnings — calls, depth 1 Note Writing
Who knows this code — 1 person across 9 areas
Area Who knows it
Note Writing · changed @shhdwi — 3 commits, last 4d ago
Host Instructions · changed @shhdwi — 5 commits, last 16d ago
Host Detection · changed @shhdwi — 1 commit, last 7d ago
learningsOnTouch · changed @shhdwi — 14 commits, last 3d ago
Host Registry · affected @shhdwi — 10 commits, last 4d ago
Installation Paths · affected @shhdwi — 2 commits, last 7d ago
Note Persistence · affected @shhdwi — 4 commits, last 3d ago
CLI Entry Point · affected @shhdwi — 51 commits, last 2d ago
…1 further area

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 @ has no GitHub handle in its commit email — tag them by hand, or add a .mailmap entry. A suggestion from history, not a CODEOWNERS rule.

All 20 dependent symbols, grouped by area

Host Registry — 6 symbols in 6 files

  • src/hosts/registry.ts:L1-L186 — registry.ts (references, depth 1)
    11: import { instructionBody, cursorRule, kiroSteering, windsurfRule } from './instructions.js';
  • src/hosts/auto.ts:L1-L155 — auto.ts (imports, depth 2)
    5: * Two lists. The repo gets the main agents' files (Claude Code, Cursor, and
  • src/hosts/cursor-hooks.ts:L1-L117 — cursor-hooks.ts (imports, depth 2)
    13: * the installed `@nanonets/graft` package and calls `hooks.js`' `main(argv[2])`,
  • src/hosts/init.ts:L1-L124 — init.ts (imports, depth 2)
    13: import { installCodexHooks } from './codex-hooks.js';
  • src/hosts/plan.ts:L1-L101 — plan.ts (imports, depth 2)
    15: import { hookTargets } from './codex-hooks.js';
  • src/hosts/retract.ts:L1-L545 — retract.ts (imports, depth 2)
    32: import { hookTargets } from './codex-hooks.js';

Installation Paths — 5 symbols in 2 files

  • src/init/keep.ts:L121-L133 — keepGuardrail (calls, depth 1)
    131: const n = writeNote(place.dir, { ...input, applies: place.repoKey } as WriteNoteInput, 'personal');
  • src/init/keep.ts:L135-L150 — keepPreference (calls, depth 1)
    148: const n = writeNote(place.dir, item.scope === 'repo' ? input : { ...input, applies: place.repoKey }, item.scope === 'repo' ? 'repo' : 'personal');
  • src/init/install-paths.ts:L1-L84 — install-paths.ts (imports, depth 2)
    20: /** The graph build's ignore lines under graft, and under trail the one file that keeps `.trail/cache/` and the status line out of git (#619). */
  • src/init/keep.ts:L184-L252 — keepItems (calls, depth 2)
  • src/init/keep.ts:L172-L177 — keepReview (calls, depth 2)

Note Persistence — 4 symbols in 2 files

  • src/notes/memory-notes.ts:L230-L244 — saveMemoryCandidates (calls, depth 1)
    232: writeNote(
  • src/notes/pr-reviews.ts:L525-L541 — saveReviewFindings (calls, depth 1)
    527: writeNote(
  • src/notes/memory-notes.ts:L252-L279 — finishMemoryRead (calls, depth 2)
  • src/notes/pr-reviews.ts:L549-L576 — finishReviewRead (calls, depth 2)

CLI Entry Point — 3 symbols in 2 files

  • src/cli.ts:L1255-L1405 — runNoteCommand (calls, depth 1)
    1293: // A touch is a file git tracks or one this session edited: never a file
  • src/cli.ts:L1-L2844 — cli.ts (imports, depth 2)
    5: * graft/ and a clone has the graph. A workspace parent (≥2 git children)
  • src/upkeep.ts:L1-L326 — upkeep.ts (imports, depth 2)
    206: /** Under `graft/.cache/`, beside the other derived state: git-ignored, per-clone,

Session Learning — 2 symbols in 1 file

  • src/learn/sessions.ts:L265-L273 — saveSessionLearnings (calls, depth 1)
    267: writeNote(
  • src/learn/sessions.ts:L281-L309 — finishSessionRead (calls, depth 2)
Test signal per changed area — 3 ✓ · 1 ✗

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.

  • ✓ Note Writing — 1 of 1 reached · 2 test files changed here: test/trail-home.test.ts, test/trail-learning-on-touch.test.ts
  • ✓ Host Instructions — 1 of 1 reached · 1 test file changed here: test/hosts-trail.test.ts
  • ✗ Host Detection — 0 of 2 reached · no test file reaches it
    • not reached: claudeHost, detectHost
  • ✓ learningsOnTouch — 1 of 3 reached · 1 test file changed here: test/trail-learning-on-touch.test.ts
    • not reached: telemetryHost, main
23 test suites also reference this code

31 symbols, kept out of the diagram and the table so they cannot crowd out the areas a reviewer has to look at.

  • test/claude-hook-timeouts.test.ts
  • test/claude-hooks-perf.test.ts
  • test/claude-host-standdown.test.ts
  • test/claude-prompt-learnings.test.ts
  • test/claude-session-start-learnings.test.ts
  • test/hosts-antigravity.test.ts
  • test/hosts-auto.test.ts
  • test/hosts-instructions.test.ts
  • test/hosts-plan.test.ts
  • test/hosts-registry.test.ts
  • test/pair-late-connect.test.ts
  • test/trail-cloud.test.ts
  • test/trail-connect-cli.test.ts
  • test/trail-connect.test.ts
  • test/trail-learning-schema.test.ts
  • test/trail-notes.test.ts
  • test/trail-nudge.test.ts
  • test/trail-other-repos.test.ts
  • test/trail-pair.test.ts
  • test/trail-scenario-hooks.test.ts
  • …3 more

⚠️ 3 changed files not in the graph (.trail/learnings/2026-10-29-what-trail-does-in-claude-desktop-anirudhkumar.md, .trail/learnings/2026-10-30-real-codex-runs-what-reaches-codex-anirudhkumar.md, TELEMETRY.md) — no parser claims the extension, or the index predates the file.

graft blast · origin/main...HEAD · depth 2 · 13 changed files

Open the interactive graph → — click an area to see its dependent symbols at file:line.

github-actions Bot added a commit that referenced this pull request Oct 29, 2026
github-actions Bot added a commit that referenced this pull request Oct 30, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant