-
Notifications
You must be signed in to change notification settings - Fork 1
Feature: Repo Identity
Every thatch surface keys memory on a repo identity string (the "project
store"). Identity is resolved once per process at init by detectRepo(),
then threaded as the default store to tools, prompts, nudges, and chat
registration. This feature covers how identity is resolved, and how it
survives the deletion of the directory identity was resolved from -- the
worktree-merged-and-cleaned-up case.
The same resolution also decides where git subprocesses spawn: watchers
and branch listing run git/bash with cwd set to the project
directory, which stops working when that directory is deleted.
- Parse
owner/repofromgit remote get-url origin. - Fall back to the basename of the main checkout, resolved from
git rev-parse --path-format=absolute --git-common-dir. The absolute flag makes the output uniform: a linked worktree reports the MAIN checkout's.git, a root checkout reports its own -- so the basename is the repo, never the worktree/branch directory. - Fall back to the directory basename (existing, non-git directories).
- When the directory no longer exists, steps 1-2 cannot run (spawning
with a deleted cwd rejects), so the
repo_pathscache is consulted; a miss returns"unknown"-- never the basename, which for a worktree is the branch name and would mint a plausible-but-wrong store.
Callers treat "unknown" as unknown identity: the tool layer maps it to
the global store, and the CLI's defaultStore() applies the same mapping.
One row per worktree directory (realpath-canonicalized key): the main
checkout path and the repo slug recorded while the directory was alive.
Writes happen only on successful resolution via steps 1-2, never from the
basename fallback. Access is injected (RepoPathCache interface, adapted
by repoPathCache(db) in src/db.ts) so src/git.ts stays database-free.
Path-form handling matters on macOS, where /var/... and
/private/var/... are the same directory in different forms. Writes
canonicalize with realpathSync (the directory exists at write time);
reads try the raw path first, then the parent-realpath form, because a
deleted directory cannot itself be realpathed.
A cache hit is validated against the CURRENT state of its main checkout before it is trusted:
| Observation | Verdict |
|---|---|
Main checkout missing (statSync throws) |
Keep row, degrade to "unknown" -- a re-clone may be in progress |
| Spawn failure against main checkout | Keep row (transient) |
Main checkout live but git rev-parse exits nonzero |
Evict (definitive: no longer a repo) |
git remote get-url origin parses to a different slug |
Evict (definitive: path recycled or re-cloned from elsewhere) |
| Remote matches (or, with no remote, the slug still equals the main checkout's basename) | Valid -- identity recovered |
Eviction is definitive-only. A wrongly evicted row permanently downgrades
identity to "unknown", which is worse than serving a stale row for one
more call.
resolveSpawnCwd(dir, cache) returns the directory when it exists, else
the validated main checkout from the cache, else null. Worktrees share
refs/heads and config with the main checkout -- only HEAD and the index
are per-worktree -- so git reads are equivalent from either. Consumers:
-
listBranchesfalls back to the main checkout instead of returning[](which made hygiene skip the orphaned-branch check). - The opencode plugin wraps its watcher
commandRunnerwithwithCwdFallback(src/watchers.ts): the spawn cwd is resolved per poll, so a watcher whose worktree dies mid-watch recovers instead of spinning ENOENT errors until the TTL. The fallback is logged once per dead directory. -
watch_command_createaccepts acdoverride, validated at registration by the baseline run (a nonexistent path is rejected).
- Identity resolution returns a correct slug or
"unknown"-- never a plausible wrong name. A wrong-but-plausible name defeats the degrade-to-global safety net. - The cache is only written from live resolutions, and only via the remote or common-dir probes.
- Eviction is definitive-only.
- The cache key is the realpath at write time; reads try raw and parent-realpath forms.
- Database (database.md): the
repo_pathstable. - Watchers (watchers.md): the per-poll cwd fallback wraps
the watcher command runner;
watch_command_createtakescd. - Hygiene (hygiene.md):
listBranchesfalls back to the cached main checkout, so the orphaned-branch check survives worktree deletion.
-
src/git.ts-- resolution chain, cache validation, spawn-cwd fallback -
src/db.ts--repo_pathstable, accessors,repoPathCacheadapter -
src/runtime.ts-- plugin init order (DB before detection), identity from the session directory, watcher runner wrapper -
src/mcp.ts-- MCP server init order -
bin/thatch-- CLIdefaultStore()and hook-line identity resolution
User
- Guide: Behavior Engine
- Guide: Cli
- Guide: Code Review
- Guide: Commands
- Guide: Cross Session Chat
- Guide: Deduplication
- Guide: Default Behaviors
- Guide: Extraction
- Guide: Hygiene
- Guide: Memory
- Guide: Notifications
- Guide: Prediction Engine
- Guide: Overview
- Guide: Setup
- Guide: Skills
- Guide: Watchers
Developer
Dev Feature Guides
- Feature: Behavior Engine
- Feature: Cicd
- Feature: Cli
- Feature: Commands
- Feature: Compaction Recovery
- Feature: Cross Session Chat
- Feature: Database
- Feature: Deduplication
- Feature: Extraction
- Feature: Hygiene
- Feature: Memory Store
- Feature: Multi Host
- Feature: Notifications
- Feature: Nudge Pipeline
- Feature: Opencode Plugin
- Feature: Prediction Engine
- Feature: Qa System
- Feature: Overview
- Feature: Repo Identity
- Feature: Session Lifecycle
- Feature: Setup
- Feature: Sideband
- Feature: Watchers