feat(hooks): auto-claim edited files + cross-session overlap warnings#10
Merged
Conversation
Observed-not-predictive claims: PostToolUse watches Edit / Write /
MultiEdit / NotebookEdit, extracts file_path, and writes a claim into
task_claims for the current session's joined task. Agents don't have to
remember to call task_claim_file for the claim system to protect their
work.
The warning half lives in UserPromptSubmit. A new buildConflictPreface
queries recentClaims(task_id, now - 5m) for claims held by *other*
sessions on the same task, groups them by session, and injects one line
per collaborator ("A: src/viewer.tsx, src/api.ts") plus a nudge to
coordinate via task_post / task_hand_off before editing those files.
Storage: new recentClaims(task_id, since_ts) method — the conflict
preface's query surface. Older-than-window claims are intentionally
excluded; stale claims describe finished work, not live collisions.
Ordering choice worth flagging: claims are written by the session that
did the edit, not reserved by the session that intends to edit.
Predictive claims require agents to remember to call a tool in advance
(they won't). Observed claims require nothing and are always grounded
in actual edits, which makes the resulting warnings trustworthy. Agents
tune out warnings that are wrong a third of the time.
Tests: storage test for recentClaims window + 7 hook-layer tests
(extractTouchedFiles coverage, auto-claim on joined / unjoined / stolen
cases, buildConflictPreface grouping + own-claim exclusion, plus an
end-to-end runHook integration where A edits -> B's next turn sees the
warning naming viewer.tsx and A). All gates green.
4 tasks
NagyVikt
added a commit
that referenced
this pull request
Apr 24, 2026
Add non-negotiable rule #10 and a Worktree discipline section to CLAUDE.md: never edit on the local `main` checkout, always run work in an `agent/*` branch + worktree via `gx branch start`, claim files via `gx locks claim`, finish via PR with `gx branch finish ... --via-pr --wait-for-merge --cleanup`, and coordinate with codex through colony MCP `task_post` / `task_claim_file` / `task_hand_off`. This matches how codex already operates via Guardex and keeps parallel lanes safe from primary-checkout drift. Co-authored-by: NagyVikt <nagy.viktordp@gmail.com>
5 tasks
NagyVikt
added a commit
that referenced
this pull request
May 5, 2026
* wip: bridge daemon fast-path wrapper (no daemon endpoint yet) Adds apps/cli/bin/colony.sh — POSIX shell wrapper that fast-paths `colony bridge lifecycle --json` via curl POST to a (not-yet-existing) daemon endpoint at /api/bridge/lifecycle. Falls back to in-process Node on any failure with stdin replayed from a temp file. Not yet wired: - Worker endpoint /api/bridge/lifecycle (next commit) - package.json bin entry change - Tests - CLAUDE.md rule #10 reconciliation (blocked on user decision) Why WIP commit: rule #10 forbids hooks crossing an HTTP boundary on the write path. The fast-path violates the literal text but preserves the spirit (writes still succeed via fallback when daemon is down). That's an architectural call that needs explicit user sign-off, not my judgement. Committing the wrapper makes the branch durable while that decision is pending. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(cli,worker): daemon fast-path for `colony bridge lifecycle` Every IDE tool event triggers `colony bridge lifecycle ...` from external hook integrations. Cold-starting Node + bundle load on each event pegs ~one core for ~300ms — multiplied across concurrent agents this is a measurable CPU storm visible at the system level. The CLI bin entry is now a POSIX shell wrapper at apps/cli/bin/colony.sh. When invoked as `colony bridge lifecycle --json`, it POSTs the envelope to the long-lived worker daemon at POST /api/bridge/lifecycle (new) and exits — no Node startup. Anything else execs the Node CLI exactly as before. The worker's daemon route reuses the long-lived MemoryStore so the SQLite connection isn't reopened per event. CLAUDE.md rule #10 is reworded to match the new contract: writes still succeed in-process when the daemon is unreachable. The wrapper buffers stdin to a temp file and falls back to Node on curl missing, connection refused, 2s timeout, non-200, unknown flags, or invocation without --json. Tests: - apps/worker/test/server.test.ts: POST /api/bridge/lifecycle routes envelopes through the long-lived store, dedupes duplicate event_ids, surfaces ok:false on malformed input. - apps/cli/test/bin-shim.test.ts: load-bearing rule-10 contract test. With daemon unreachable, the wrapper falls back to Node with stdin and quoted args (including paths with spaces) intact. Also covers COLONY_BRIDGE_FAST=0 disable, --version pass-through, and that bare `bridge lifecycle` (no --json) keeps human-readable output. Opt out at any time with COLONY_BRIDGE_FAST=0. Notes: - pnpm lint OOM'd locally (host has 13GB active swap from unrelated processes); CI lint should run cleanly. Will re-run before merge. - e2e-publish.sh re-run pending — it covers the bin-shim install path end-to-end and is the official guard for changes to the publish surface. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * test(bench): bridge-lifecycle daemon vs in-process Node fallback harness Adds scripts/bench-bridge-fastpath.mjs. Boots a fresh worker against a temp COLONY_HOME on a non-default port, then runs N concurrent shell-shim invocations of `bridge lifecycle --json` once with the daemon reachable (fast path) and once with COLONY_BRIDGE_FAST=0 (forced in-process Node fallback, mirrors today's behavior). Reports mean, median, p95, p99. Local results, 8 concurrent × 4 iterations = 32 events per path: [fast (daemon)] mean=54.2ms p95=74.1ms p99=130.4ms [slow (force-fallback)] mean=260.9ms p95=725.4ms p99=728.7ms speedup (mean): 4.8x saved: 206.7ms/event speedup (p95): 9.8x The p95 collapse from 725ms → 74ms is the Node cold-start tail under burst load — exactly the spawn-storm pattern that motivated this PR (four `colony bridge lifecycle` processes simultaneously at 100% CPU each in the original screenshot). Caveats: includes sh + curl + JSON serialization on the fast path; the "slow" path includes the wrapper buffering stdin then exec'ing Node (~5-10ms extra vs. raw `colony bridge lifecycle`), but that's swamped by the ~250ms Node cold-start it's measuring. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: NagyVikt <nagy.viktordp@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
3 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Turns the soft advisory claims system into something agents actually use — without asking them to call any new tools. PostToolUse observes Edit / Write / MultiEdit / NotebookEdit and writes a claim for the editing session; next turn, UserPromptSubmit surfaces a compact warning naming files that other sessions have recently touched.
Why observed, not predictive
Predictive claims ("I plan to edit X") require agents to call a tool in advance. They won't. Observed claims require nothing from the agent and are always grounded in actual edits — which makes the resulting warnings trustworthy instead of something agents learn to ignore.
What's new
Storage.recentClaims(task_id, since_ts)— the query powering the conflict preface. Stale (outside-window) claims are intentionally excluded; they describe finished work, not live collisions.PostToolUseauto-claim —extractTouchedFiles+autoClaimFromToolUseinpackages/hooks/src/handlers/post-tool-use.ts. Silent-skip on unknown tools and malformed input: PostToolUse runs on every tool call, so any throw would degrade every turn.UserPromptSubmitconflict preface —buildConflictPrefacegroups claims by session ("A: src/viewer.tsx, src/api.ts") so the agent reads one clustered warning instead of three disjoint ones. 5-minute window, own-session claims excluded.recentClaims; 7 new hook tests —extractTouchedFilescoverage (write vs non-write tools, malformed input),autoClaimFromToolUse(joined / unjoined / stolen-claim),buildConflictPrefacegrouping + own-claim exclusion, plus an end-to-endrunHookintegration proving A edits → B's next turn sees the warning namingsrc/viewer.tsxand A.Test plan
Deliberately deferred
Stale handoff reclamation — the next turn's "your handoff expired" nudge — is deferred until we've run the system on a real two-agent task for a day or two. The shape of the right notification depends on how often expiries actually happen; a theoretical design here would probably pick the wrong primitive.
🤖 Generated with Claude Code