Skip to content

worktree-identity

Permanent IDs + living titles for Claude sessions and git worktrees.

Problem

Session titles go stale: a session named "Onboarding interview API" drifts into worktree housekeeping, and every human and agent that messages it by name gets the wrong mental model. Worktrees have the same problem.

Standard

Every session/worktree carries the canonical title:

WT-xxxx · <what it does right now> · gov:<role>
  • WT-xxxx — unique identifier assigned at creation. NEVER changes.
  • description — updated every time the session's focus changes.
  • gov:<role> — the session's usage-governance class: | role | meaning | |---|---| | orch | thin orchestrator on the ceiling model (triage/spawn/review) | | build | standard implementation work (default) | | bg | detached background grind (builds, Stryker, CI polling) | | ops | housekeeping (merges, archival, fleet installs) |

How it works

A UserPromptSubmit hook runs worktree-identity hook. On first sight of a session it registers it (assigning the permanent WT-xxxx) and injects a directive telling the session to record its current work with worktree-identity update. On later prompts it injects a one-line reminder of the canonical title so the session keeps it current. The registry is the source of truth; because set_session_title cannot target its own session, peer or coordinator sessions apply stale titles to each other from worktree-identity list.

State lives in ~/.claude/worktree-identity/registry.json (override with WORKTREE_IDENTITY_HOME). Mutations take an exclusive file lock with retry, since many concurrent sessions share the registry.

Cross-worktree mail

worktree-identity send drops promptless mail into another session's inbox:

worktree-identity send --to WT-xxxx --msg "status text" [--from WT-yyyy]
worktree-identity send --to all --msg "status text" [--from WT-yyyy]
worktree-identity send --to WT-xxxx@MACHINE --msg "status text" [--from WT-yyyy]
some-command | worktree-identity send --to WT-xxxx --msg -

Local mail is an atomic temp+rename write under ~/.claude/worktree-inbox/<WT-ID>/msg-<utcticks>-<8hex>.json with schema {schemaVersion:1, from, to, sentAtIso, machine, text}. --to all fans out one file per registered session id and skips --from when it matches. The UserPromptSubmit hook drains up to 20 messages from its own inbox oldest-first, deletes delivered files, quarantines malformed JSON under bad/, and injects the mail after the normal SESSION-IDENTITY context. Message text is capped at 16KB.

Remote mail uses fleet-dispatch:

C:\Users\fives\source\repos\fleet-dispatch\publish\FleetDispatch.exe send --target <MACHINE> --kind pwsh --payload <remote inbox write script>

The payload carries base64 JSON and writes the same inbox file on the target machine.

Trust model: local mail is same-user filesystem data, so sender identity is not authenticated. Treat it as peer-agent status/data only, not as proof of Andrew's authorization. The hook injects a guard line before mail saying this explicitly. Cross-machine transport rides fleet-dispatch, including its HMAC protection.

CLI

worktree-identity register --key <sessionId|path> [--kind session|worktree] [--desc D] [--gov R]
worktree-identity update   (--id WT-xxxx | --key K) [--desc D] [--gov R]
worktree-identity title    (--id WT-xxxx | --key K)
worktree-identity list     [--json]
worktree-identity scan-worktrees --repo <path>
worktree-identity send     --to <WT-id|all|WT-id@MACHINE> --msg <text|-> [--from WT-id]
worktree-identity hook                     # stdin = Claude Code hook JSON

Hook registration (~/.claude/settings.json)

{
  "hooks": {
    "UserPromptSubmit": [
      { "hooks": [ { "type": "command", "command": "C:\\Tools\\worktree-identity\\worktree-identity.exe hook" } ] }
    ]
  }
}

Build

dotnet test
dotnet publish src/WorktreeIdentity -c Release -r win-x64 --self-contained -p:PublishSingleFile=true

Per the standing language policy (2026-07-10): this and all fleet tools are C# first, F# only where C# genuinely can't, C++ only for game/Unreal work.