Skip to content

init: show each learning the moment Claude writes it - #703

Merged
shhdwi merged 2 commits into
mainfrom
feat/live-learnings
Oct 27, 2026
Merged

shhdwi merged 2 commits into
mainfrom
feat/live-learnings

Conversation

@shhdwi

@shhdwi shhdwi commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

trail init now shows each learning the moment Claude writes it, in the terminal and on Trail's page, instead of all of them at the end.

Why

Init spends about 85% of its time waiting on Claude to write the learnings: about 20 s per read. I replayed a real read to see what helps:

Change Read time
As today 18 s
Lower effort 19 s
Haiku 51 s, and worse learnings
Stream it first learning complete at 5.6 s, then one every ~2 s

So the answer streams. Same prompt, same model, same checks: every learning is verified against its quotes before it is shown, and the list init offers at the end is unchanged.

What changes

  • callClaude takes onFinding. With it, Claude Code runs with --output-format stream-json --include-partial-messages, and each finding is handed over as soon as its JSON object closes (completedFindings). Failures read the same as before.
  • Both reads (PRs and past sessions) check each finding as it lands and pass it to a LiveFeed, which drops the same learning found by the other read.
  • Terminal: · found "<headline>" in pull requests per learning. No "still reading" line after a read already showed one.
  • Trail's page: a Trail that answers the handoff with features: ["live_found"] gets each learning while init reads. The first part of the final list carries replaceFound: true, so a learning dropped later (a duplicate, or not about this code) never reaches the review. An older Trail gets only the final list, as before. Server side: trailhq/trail-cloud#72.

Real runs

trailhq/trail-cloud, at a real terminal, cold:

First learning Done
Before 26.7 s 28.9 s
After 7.1 s 27.5 s

Paired against a local trail-cloud built from the server PR: the live page listed 4 learnings at ~12 s and 10 at ~18 s, then the final view. The database held no live entries after the final list.

trail check

It flags src/cli.ts:1678 and src/cloud/pair.ts:113 against two learnings, on shared words only:

  • "Reading reviews in the detached _trail-pair child after init returns": not what happens. The reads still finish inside init. Live learnings go to the page while init runs, and the final list replaces them before done, which is what keepall.go waits on.
  • "Filtering replaced learnings inside listNotes": this change doesn't touch listNotes.

🤖 Generated with Claude Code

Shrish Dwivedi and others added 2 commits October 27, 2026 16:07
The reads stream Claude Code's answer and check each learning as soon as
it is whole, so the terminal and Trail's page show the first one about
6 s in instead of when both reads finish. Same prompt, model and checks:
the list init offers at the end is unchanged. A Trail that says it takes
live learnings (live_found) gets them while init reads, and the final
list replaces them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@trailhq-graft

trailhq-graft Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

🌱 graft blast radius

5 areas changed → 2 areas can be affected. 2 dependent symbols, depth 2.
Tests: no test reaches PR Review Learning, Init Command; Workspace Pair Handoff, Session Learning have tests the diff did not touch; 1 area updated its tests.
Tag: @anirudhkumar-nanonets — 4 of 7 areas · Rushabh Nagda — Workspace Pair Handoff, Cloud Connection Nudge

flowchart TB
  A0(("Name Call Initialization<br/>1 symbol"))
  A1(("Cloud Connection Nudge<br/>1 symbol"))
  classDef reached fill:#D9EDF3,stroke:#3AA7C9,stroke-width:1.5px,color:#0E313C;
  class A0,A1 reached;
Loading
Can be affected Symbols Nearest hop Reached from
Name Call Initialization 1 src/init/name-call.ts:L97-L123 nameItems — references, depth 1 Claude Integration
Cloud Connection Nudge 1 src/cloud/nudge.ts:L353-L407 waitForConnect — calls, depth 1 Workspace Pair Handoff
Who knows this code — 2 people across 7 areas
Area Who knows it
Workspace Pair Handoff · changed Rushabh Nagda — 2 commits, last 6d ago · @anirudhkumar-nanonets — 1 commit, last 14d ago
PR Review Learning · changed @anirudhkumar-nanonets — 2 commits, last 5d ago
Init Command · changed @anirudhkumar-nanonets — 61 commits, last 5d ago
Cloud Connection Nudge · affected Rushabh Nagda — 1 commit, last 6d ago · @anirudhkumar-nanonets — 1 commit, last 14d ago
Claude Integration · changed only you — nobody else has touched these files
Session Learning · changed only you — nobody else has touched these files
Name Call Initialization · affected only you — nobody else has touched these files

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 2 dependent symbols, grouped by area

Name Call Initialization — 1 symbol in 1 file

  • src/init/name-call.ts:L97-L123 — nameItems (references, depth 1)
    106: const asked = await callClaude<{ id?: unknown; name?: unknown; description?: unknown }>(bin, prompt, NAME_MODEL, opts.timeoutMs ?? NAME_MS, { system: SYSTEM, schema: SCHEMA });

Cloud Connection Nudge — 1 symbol in 1 file

  • src/cloud/nudge.ts:L353-L407 — waitForConnect (calls, depth 1)
    372: const reg = await registerHandoff(
Test signal per changed area — 1 ✓ · 2 ⚠ · 2 ✗

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.

  • ✓ Claude Integration — 7 of 12 reached · 2 test files changed here: test/claude-call-stream.test.ts, test/init-live.test.ts
    • not reached: resultLine, streamed, count, tell, landed
  • ⚠ Workspace Pair Handoff — 1 of 6 reached · 1 test file reaches it, none changed here
    • not reached: constructor, start, learning, finish, registerHandoff
  • ✗ PR Review Learning — 0 of 2 reached · no test file reaches it
    • not reached: findReviewLearnings, beginReviewRead
  • ⚠ Session Learning — 1 of 2 reached · 1 test file reaches it, none changed here
    • not reached: findSessionLearnings
  • ✗ Init Command — 0 of 1 reached · no test file reaches it
    • not reached: runInitCommand
11 test suites also reference this code

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

  • test/init-claude-call.test.ts
  • test/init-keep-cli-review.test.ts
  • test/init-keep-cli.test.ts
  • test/init-name-call.test.ts
  • test/init-name-review.test.ts
  • test/init-run.test.ts
  • test/init-team-rows.test.ts
  • test/init-uncompiled-record.test.ts
  • test/learn-sessions.test.ts
  • test/trail-nudge.test.ts
  • test/trail-pair.test.ts

graft blast · origin/main...HEAD · depth 2 · 11 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 27, 2026
@shhdwi
shhdwi merged commit 48ae88e into main Oct 27, 2026
8 checks passed
github-actions Bot added a commit that referenced this pull request Oct 27, 2026
shhdwi pushed a commit that referenced this pull request Oct 27, 2026
trail check flags hooks.ts:525 and cli.ts:77/2215/2266 against the
"trail skill not loaded on Sonnet", "_trail-pair reads reviews" and
"listNotes filter" learnings. Those are word matches: these lines add
the team sign-in line, an import, and trail login --code. None reads
reviews after init, filters listNotes or changes how the skill loads.
shhdwi added a commit that referenced this pull request Oct 27, 2026
…706)

* Sign a laptop in from an agent: trail login --code, --workspace, and the team file

- `trail login --code <TRL-…>` trades the one-time code Trail's web app
  makes for the signed-in person for this laptop's token: no browser step.
- `trail login` run by an agent prints the sign-in link for its reply and
  leaves a background wait, instead of blocking; `--workspace` picks the
  workspace on the sign-in page. Always listed in --help.
- Once signed in, Claude Code and Cursor get a user-level workspace-only
  `trail` server and Codex its user-level `trail` server, so every agent on
  the laptop has the workspace's tools.
- A signed-in laptop writes `.trail/workspace.json` when it syncs a repo. A
  teammate who pulls it and isn't signed in gets one line (MCP instructions
  and Claude Code's session start) to run `trail login --workspace`. The
  file's address is trusted only when it's the Trail this laptop signs in
  to anyway.
- "Not connected" messages say `trail login`, not the retired connect code.

trail check flags four conflicts; all are keyword matches on "trail":
the Sonnet skill-loading learning, reading reviews in `_trail-pair`, and
filtering in `listNotes`. None of that code changes here.

* chore(release): 0.27.0

* docs(changelog): 0.27.0 notes for #701 and #703 in plain words

trail check flags hooks.ts:525 and cli.ts:77/2215/2266 against the
"trail skill not loaded on Sonnet", "_trail-pair reads reviews" and
"listNotes filter" learnings. Those are word matches: these lines add
the team sign-in line, an import, and trail login --code. None reads
reviews after init, filters listNotes or changes how the skill loads.

* test: the team sign-in line names npx where trail isn't installed (CI)

* learning: tests that name the trail command pass with or without it installed

---------

Co-authored-by: Shrish Dwivedi <shrish@nanonets.com>
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