Skip to content

feat(scope): worktrees of one repository share kept answers - #173

Merged
daniloc merged 10 commits into
mainfrom
feat/worktrees-share-structure
Nov 2, 2026
Merged

daniloc merged 10 commits into
mainfrom
feat/worktrees-share-structure

Conversation

@johncwaters

Copy link
Copy Markdown
Contributor

Problem

Part of #170. Kept answers live in each worktree's own .coherence/structure, so a new git worktree's first Structure reading cold-starts tsserver and asks everything again, even when another worktree of the same repo answered it all a minute ago. On my repo that's 99 s and a 1.7 GB tsserver for every new worktree, which hurts when an agent tool makes a worktree per session.

Changes

  • Kept answers already hold no absolute paths (relative path, each file's sha256, a basis of Coherence's version, adapter code and config), so they move between worktrees as is.
  • KeptAnswers.load() reads the worktree's own file first, then the shared one when it has none. Answers are only reused where the hashes still hold in this tree; the rest get asked.
  • save() writes its own file and the shared one. A failed shared write is ignored like a failed own write.
  • New shared-structure.ts: sharedStructureDir(root) (git rev-parse --git-common-dir, cached per root, undefined outside git) and writeAtomically, moved from gaps.ts so both use one.
  • Scope.spec.md: new bullet "a new worktree starts from the repository's kept answers".
  • No lock. The shared file is written atomically and the last writer wins. A lost write only costs some re-asking later.

How did you test this code?

  • New test: a second git worktree of a read repo reads with the server started 0 times, draws the same map as a reading that asks everything, and keeps its own answers after.
  • Refuted with refute (making load() read only the worktree's own file): red on the test's own assertion, then green batched and alone with run --invariant ... --each. The run records are in the PR.
  • The worker-thread test now clears the shared store too, since deleting .coherence/structure alone no longer empties the answers.
  • component-interfaces.test.ts and gaps.test.ts pass (19/19 with the kept-failures PR merged in too). Typecheck, lexicon:check and spec:check pass.
  • On my repo (about 4,280 answers), with fresh worktrees and an empty shared store, running from source on a machine low on memory:
Run Time New tsserver Answers
Cold, first worktree 259.6 s 1,650 MB 4277 asked
Fresh worktree, same commit 5.2 s none 4280 reused
Fresh worktree, a commit no worktree had read 9.3 s 539 MB 4265 reused, 15 asked
3 fresh worktrees at once 20 to 23 s each about 1 GB each 4265 reused, 15 asked each; shared file intact

The 3 at once each asked 15 because the shared file held the other commit's answers (last writer wins).

johncwaters and others added 10 commits October 31, 2026 17:00
Each git worktree kept its language-server answers only under its own
.coherence/structure, so a new worktree's first Structure reading started
a cold TypeScript server and asked every question again, though another
worktree of the same repository had just answered them. On glimmervoid a
new session worktree paid 99 s and a 1.7 GB tsserver for it (#170).

Kept answers already carry no absolute path: they are keyed by relative
path, each file's sha256 and a basis of Coherence's version, adapter code
and config. So a worktree with none of its own now seeds from
<git common dir>/coherence/structure/answers-<language>.json, and every
save writes its own file and that shared one. A seed reuses only the
answers whose hashes still hold in this tree; the rest are asked.
writeAtomically moves to shared-structure.ts so gaps.ts and the kept
answers share one.

Rejected: sharing whole readings by fingerprint | answers alone remove the cold server, and a seeded reading of an unchanged tree asks nothing
Rejected: a per-repository lock | the shared file is written atomically and a lost write only costs a later re-ask
Confidence: high
Scope-risk: narrow
Ticket: coherence-worktree-readings/U3
The worktree seeding had a test but no bullet. "a new worktree starts
from the repository's kept answers" now names it in Scope.spec.md, with
a refutation recorded by refute (load reading only the worktree's own
file turned the test red) and witnessed green batched and alone.

Confidence: high
Scope-risk: narrow
…share only from tracked folders, and ask git as any version answers

- The shared copy is answers-<lang>-<project digest>-<basis digest>.json in
  the git common folder, the four most recent kept per project and language,
  so monorepo projects and branches whose manifests or lockfiles differ no
  longer overwrite each other; a worktree whose own file does not hold falls
  back to the copy its basis names.
- Only a project folder the checked-out commit tracks shares: an untracked
  folder inside another repository writes nothing into its .git.
- The common folder is asked without --path-format (which git before 2.31
  echoes back), resolved against the root, with the git hook location
  variables stripped; mod-install's localSettingsPath uses the same helper.
- writeAtomically uses a random temporary name and removes it on failure.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… the basis digest alone after it

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ing and git-age resolution as invariants, each refuted

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…efused file by its project-relative path

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@daniloc
daniloc merged commit b0d7274 into main Nov 2, 2026
20 checks passed
@daniloc
daniloc deleted the feat/worktrees-share-structure branch November 2, 2026 20:45
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.

2 participants