Skip to content

worktree-identity — naming fix + branch consolidation plan

Status: BLOCKED on consolidation. Design complete (below). Do NOT implement the naming fix on a feature branch until the repo is consolidated onto master, or you add a 6th divergent branch.

Why sessions/worktrees don't name themselves (root causes)

  1. No title applier. The pipeline is: UserPromptSubmit hook registers the session + injects the SESSION-IDENTITY directive → the model runs worktree-identity update (writes the registry) → nothing pushes the title to the UI. The only thing that renames a session is the CCD MCP tool set_session_title, a session cannot rename itself, and no daemon/peer calls it. Result: the registry is correct but UI titles stay stale. Only 2 of ~30 live sessions ever got a WT-… title (hand-applied once). set_session_title also always prompts the user — so a fully-silent auto-renamer is impossible by design; the best achievable is a peer/scheduled agent that proposes renames from a computed diff.

  2. Ephemeral keying. Session entries key on session_id (HookHandler.Handle), which regenerates per PC restart, so restart orphans every WT-id→ticket mapping and new sessions register as "unnamed". NOTE: in current usage every session's cwd is the non-git main repos root (C:\Users\fives\source\repos), so pure path-keying would collapse them all to one id — path capture only helps sessions actually launched inside a per-worktree dir.

Design (build on consolidated master)

BUG 2 — applier (load-bearing)

  • New verb sync-titles: reads a live-sessions JSON on stdin/--sessions <file> ([{sessionId,title}], from CCD list_sessions), outputs a rename manifest [{sessionId,currentTitle,desiredTitle}] for entries where the entry is named (Description != "unnamed") AND live title != TitleFormatter.Format(entry).
  • The manifest is consumed by an orchestrator/scheduled Claude session that calls set_session_title per row (the only non-headless step: MCP call + user confirm). Wire a scheduled task or fold into an existing coordinator loop so it runs periodically.

BUG 1 — path-aware keying (collision-safe)

  • Add WorktreePath, Branch, LastHeartbeatUtc to RegistryEntry.
  • Program.cs hook resolves cwdgit -C <cwd> rev-parse --show-toplevel + --abbrev-ref HEAD (reuse the hardened git-exec resolver from the WT-354e branch), passes them into HookHandler.Handle.
  • On register when sessionId not found: if worktreePath+branch known AND exactly one existing session entry has that worktreePath+branch AND it is stale (LastHeartbeatUtc older than ~5 min), rebind that entry's Key to the new sessionId and return it (preserves WT-id/ticket/desc). Else register fresh. Update LastHeartbeatUtc on every hook fire. Null worktreePath (non-git cwd) → no reclaim, degrade to today's sessionId keying. This is why it's collision-safe.
  • Tests: single-stale rebind preserves id; two-live no reclaim; null-path no reclaim; heartbeat refresh.

Consolidation prerequisite (do FIRST, council-gate the security merges)

Five states diverge; deployed exe (C:\Tools\worktree-identity\worktree-identity.exe, built 2026-07-11 12:46) is from wt/WT-75a4/p25-worktree-identity (only branch with the worktree verbs).

Branch tip adds
master 9339a33 base + cross-worktree mail
council-fixes 07031ec TOCTOU / unbounded-stdin / git-missing hardening (SECURITY)
wt/WT-354e/wtid-council-deferred 8796c81 git-executable path-hijack hardening (SECURITY)
wt/WT-2505/polyharness-p25 60a7c24 PreToolUse clobber guard (hook-pretool)
wt/WT-75a4/p25-worktree-identity 0fae814 worktree ownership: claim/release/list, hook-pretool, dead-pid lock

Order: master ← council-fixes ← WT-354e (security, council-review both) ← WT-2505 ← WT-75a4 (resolve hook-pretool overlap between WT-2505 and WT-75a4). Rebuild ONE self-contained exe, redeploy to C:\Tools\worktree-identity\ + fleet. THEN implement the design above on master.

Also confirm settings.json PreToolUse hook-pretool is satisfied by the redeployed exe (master alone lacks that verb — the live hook only works because the deployed exe is from WT-75a4).