CLAUDE.md already carries a mandatory before-done checklist ("the prompt is part
of the product"). It covered the instruction surface but not the operator-facing
one, and the result was measurable: CB-506 through CB-525 shipped without a
single wiki mention, while the Roadmap went on claiming Stage 5 was finished.
Discipline is what already failed, so this rides the existing gate rather than
adding a new habit to remember: one more row, firing when a change touches
anything an operator can use, configure, or observe. The row names where the
other two kinds of change go too (contracts to Implementation, coverage to the
Roadmap), so "nothing to document" is a decision the table makes rather than a
default you fall into.
Addendum-only — the canonical block is untouched and still byte-identical to the
wiki template (verified).
16 KiB
claude-bridge — project instructions
Bridge communication (enforced — read this first)
Canonical block. Everything down to §Layering is the portable bridge charter, copied verbatim into every project that mounts the bridge MCP. Keep it byte-identical with the template in the wiki (Use Cases → The portable CLAUDE.md block); improvements go to the template first, then out to each project. Anything specific to this repo lives under §Project addendum below, never inline above it.
If no bridge_* MCP tools are mounted in this session, this section does not apply — skip it.
bridged is the sole communication gateway between agents here. The orchestrating session (the
primary) and every delegated peer (a worker) mount the same MCP server and talk only
through its bridge_* tools. No session addresses a peer, a broker, or the network directly.
Which role am I? — settle this before acting
Both roles read this file. A worker runs in a git worktree of this same repo, so it inherits
this CLAUDE.md verbatim, and every rule below is role-conditional.
Call bridge_whoami. It returns {"role":"primary"} or {"role":"worker","sessionId":…, "profile":…,"worktree":…,"branch":…}, resolved by the daemon from your connection — unforgeable,
and the same resolution its authorization gate uses. Don't infer what you can ask.
Only if that call is unavailable, fall back to these — each is one-way, so keep reading until one
fires: the reply charter in your system prompt ("You are an off-subscription worker in the
claude-bridge fleet") ⇒ worker; bridge tools prefixed mcp__bridge__* ⇒ worker (the
launcher fixes that mount name; a primary's mount is named by whoever wrote its .mcp.json, so it
varies); ANTHROPIC_BASE_URL set ⇒ worker (Claude-model workers run on a clean env, so its
absence proves nothing). Still unsure ⇒ act as a worker. The two mistakes are not symmetric: a
primary acting as a worker is refused by the authorization gate — loud and self-correcting — while a
worker acting as the primary ends its turn with no bridge_reply, and the sender silently receives
nothing. Fail toward the recoverable error.
Invariants — both roles, no exceptions
- Never set, export, or forward
ANTHROPIC_BASE_URL(orANTHROPIC_AUTH_TOKEN). The primary stays on subscription; only the bridge puts a worker off it, at spawn. Mounting the bridge must never move a session across that boundary. - The bridge is the only channel. Text you print in your terminal reaches nobody — the other
side cannot see your screen. An answer that isn't in a
bridge_*call is silently discarded. - Identity comes from the connection, never an argument. Workers never pass a target; you cannot act as another session. Spawn/stop/send/drain are primary-only; reply/ask are worker-only-and-only-as-itself. A call outside your role is refused, not queued.
- Delivery is status-gated: one message per turn. Don't busy-poll a peer's terminal and don't
re-send because a call looks slow — the bridge delivers when the peer is
idle/blocked. - Never drive the terminal multiplexer directly (no
herdrCLI, no socket). The bridge owns policy; the multiplexer owns PTYs. Going around the bridge bypasses every rule above.
Primary (lead) — run this on every task, in order
Delegate by default — that is the job. With the bridge mounted you are an orchestrator on a
metered subscription, and workers are cheap, parallel, and disposable. The default answer to "who
does this?" is a worker, not you. Reach for bridge_send before you reach for Edit. The steps
below are the procedure — run them in order, every task, not only the big ones.
- Know your role —
bridge_whoami, once per session, before anything else. - Split. Write the unit list. Every unit carries: scope · the files or PR in question · acceptance criteria · exactly what to report back. A unit with no acceptance criteria is not ready to delegate — refine it or keep it.
- Gate each unit on one question: "can I write a brief good enough for a worker to succeed?" — not "could I do this faster myself?" (usually you could; doing it yourself costs your context and your subscription, while a wasted worker turn costs a worker turn). Yes ⇒ delegate. The keep-list is closed: the conversation with the user, decomposition and planning, the final judgment call, verification, merges, and anything that depends on context only you hold. Nothing else is yours by default.
- Spawn every delegated unit first —
bridge_spawn{profile, worktree:true, ticket}, one per unit, before sending any. Passprofileexplicitly: profiles differ in model and cost, not in tier, so the default is rarely what you want. - Then send them all —
bridge_send{sessionId, content, wait:false}. Line 1 of every brief isLoad the <name> skill.naming the worker's playbook; those skills are opt-in and that line is what makes them reliable. Where the project ships no such skill, spell the procedure out in the brief instead. The brief is self-contained — the worker sees your message and the repo, nothing of your context, your plan, or your screen. - Collect —
bridge_poll{ticket}→bridge_ack{ticket, msgId}. Answer a worker'sbridge_askwithbridge_send{turnId, content}— notsessionId. A worker gone quiet is diagnosed withbridge_status, never by reading its terminal. - Verify yourself. Re-run the build and the checks. A worker mounts only the bridge MCP and
cannot run your other tooling, and a piped command (
… | tail) hides failures behind a zero exit — never promote a worker's "clean" to a fact. - Review — fan out. Spawn reviewers against the diff, one per dimension or per file, with
wait:false. Never the implementer of the scope it reviews, and brief them from the diff — not from the implementer's rationale, which carries its own blind spot. Dispatch each PR's reviewers as it lands; don't wait for the last implementer. Under ~50 changed lines, skip the fan-out and read it yourself. - Adjudicate, merge, tear down — yours alone. Read the diff yourself: fully if it is small,
targeted at the reported findings and the risky paths if it is large. Reviewer findings direct
your attention; they never substitute for it. Then merge, then
bridge_stop{paneId}.
Steps 3 and 4 are separate on purpose — spawning and sending in one loop is how parallel work
silently becomes serial, and it is the most common way this layer is wasted. For the same reason,
prefer wait:false + bridge_poll for anything non-trivial: a blocking bridge_send is capped by
your own MCP client call timeout (~60s), well below the task's real runtime.
Delegating does not delegate responsibility. Workers open PRs; you are the gate. Never delegate the merge — and merging on a reviewer's word is delegating it by proxy.
| Intent | Tool |
|---|---|
| Confirm your own role | bridge_whoami |
| See backends available | bridge_profiles |
| Start a worker | bridge_spawn{profile?, cwd?, worktree?, ticket?} → sessionId + paneId |
| See the fleet | bridge_list · one worker's state: bridge_status{sessionId} |
| Delegate (blocking) | bridge_send{sessionId, content} |
| Delegate (long task) | bridge_send{sessionId, content, wait:false} → ticket → bridge_poll{ticket} |
Answer a worker's bridge_ask |
bridge_send{turnId, content} — not sessionId |
| Collect a held reply | bridge_poll{target} · then bridge_ack{target, msgId} |
| Tear down | bridge_stop{paneId} |
Worker — the turn contract
- Load the playbook skill the lead named before doing anything else.
- Do the assigned scope only. Note anything you spot outside it in one line; don't go hunt it.
bridge_ask{question}when a decision is genuinely the lead's (ambiguous requirement, two defensible fixes, "bug or intended?"). It blocks and you resume the same turn with the answer. Don't ask what you could decide yourself.- End the turn with exactly one
bridge_reply{content}, carrying your complete answer. This is the whole handoff. Nobridge_reply⇒ the sender gets nothing and the exchange stalls. - Report honestly. State only what you actually ran and its real output, including failures. You mount only the bridge MCP — the primary's other servers (IDE, forge, docs) are not yours, so never claim the result of a check you had no way to run.
- Never merge. Stage files explicitly — never
git add -A— and leave alone anything the project marks as not-yours-to-commit.
Where each rule lives (don't duplicate — extend the right layer)
| Layer | Scope | Reaches |
|---|---|---|
| the launcher's reply charter | the one rule that must survive with no repo: end every turn with bridge_reply |
every worker, at launch, every peer kind |
| this section | protocol + orchestration policy | primary and every Claude worker — tracked in git, so worktrees inherit it |
| role playbook skills | per-job procedure (commit/PR recipe, finding format) | a worker told to load one |
| the bridge's own docs | design detail, flows, error model | on demand |
A rule belongs in exactly one layer — the outermost one that must obey it. Peers that don't read
CLAUDE.md (non-Claude adapters) get the charter only, so any rule they must obey belongs in the
charter, not here.
Project addendum — claude-bridge (not part of the canonical block)
- This repo is the bridge. The daemon is
bridged, its MCP mount ishttp://127.0.0.1:8765/mcp, and the code behind the rules above ismcp/BridgeMcp(tools),auth/Authz(the role table),mcp/ConnectionIdentity(connection→role), andworker/*Launcher(REPLY_CHARTER). - Skills available to delegate:
implementer(worktree → commit → push → own PR) andreviewer(scoped review → one structured finding). Name one in every delegation. - Never commit
.mcp.json(the primary's local copy, flagged--skip-worktree) orwiki/(a submodule with its own remote). - Flows and the error model — rendezvous,
bridge_ask, detached delivery, turn-done fallback — are diagrammed indocs/MCP-Contract.md§6, kept out of this file because it loads into every session's context.
The prompt is part of the product — update it with the code (mandatory)
This repo is the bridge, so the canonical block above is not documentation about someone else's system: it is the instruction surface this codebase ships. Every change here must end by asking whether the block still tells the truth. A code change that silently invalidates it is an incomplete change — the agents reading it have no other source.
Before you call any work done, check the row that matches what you touched:
| You changed… | Re-read and update… |
|---|---|
a bridge_* tool — added, removed, renamed, or its params/semantics |
the primary's intent→tool table; any rule that names that tool |
Authz / the role table |
invariant 3, and the primary-only vs worker-only claims |
ConnectionIdentity / how a caller is resolved |
the bridge_whoami paragraph and the fallback ladder |
REPLY_CHARTER, or a launcher's mount/flags |
the fallback ladder (mcp__bridge__*), and the layering table's top row |
| the injector / status gating | invariant 4 |
| worktree provisioning or the parity overlay | the "both roles read this file" premise — it rests on the worker's worktree being a checkout of this repo |
.claude/skills/** |
the addendum's skill list, and the "name the playbook" rule |
| a new peer kind (non-Claude adapter) | what that peer can read — anything it must obey belongs in its charter, not in the block |
anything an operator can use, configure, or observe — an MCP tool, a bridged.yaml knob, an endpoint, a visible behaviour |
Features — one entry: what it does · the knob that turns it on · why it exists · the gotcha |
That last row is not bookkeeping. Chapters 1–10 answer how is this built and why this way;
none of them has a home for what can it do and how do I turn it on, so for twenty tickets a
shipped capability landed nowhere and the Roadmap went on claiming the stage was finished. The
why line is the one that matters — without it a decision gets re-litigated from scratch a month
later. Internal contract changes go to wiki/9-Implementation.md instead; test and coverage work
is a Roadmap line. A change that touches none of the three earns no entry, and that is a normal
outcome rather than an omission.
Then propagate: the block in this file and the template in the wiki
(Use Cases → The portable CLAUDE.md
block) must stay byte-identical, and other projects carrying the block need the same edit. Verify
rather than trust:
python3 - <<'PY'
import pathlib
c = pathlib.Path("CLAUDE.md").read_text()
w = pathlib.Path("wiki/7-Use-Cases.md").read_text()
S, E = "## Bridge communication (enforced", "## Project addendum — claude-bridge"
block = c[c.index(S):c.index(E)].rstrip() + "\n"
i = w.index("```markdown\n") + len("```markdown\n")
print("in sync:", w[i:w.index("\n```\n", i) + 1] == block)
PY
IDE MCP tools & validation workflow (enforced)
Primary only. Workers have no IDE MCP mount — if you are a worker, skip this section and report the build/test output you actually ran (see §Bridge communication → Worker).
Two IDE MCP servers are connected: intellij-index (semantic code intelligence) and
jetbrains (file problems, reformat, debugger). IntelliJ has multiple projects open; our
module is bridged. Always pass these to IDE MCP tools:
project_path=/Users/dai.ha/LTMS/claude-bridge/bridged- IDE paths are relative to
bridged/(e.g.src/main/java/dev/ltms/bridged/...)
After editing any file — mandatory
ide_sync_files{paths}— the built-in Edit/Write tools write to disk; the IDE index is stale until synced, or IDE nav/refactor/diagnostics give wrong results.ide_diagnostics{file}(orjetbrains get_file_problems) — clear all errors and warnings. IDE inspections catch what a build won't (unused params/fields, redundant modifiers, resource leaks, "always same arg", …). These diagnostics are per-file.mvn clean install(Bash) — required for overall project health (clean build + full test run). A per-file-clean file can still break the build or another module. This is the whole-project gate before declaring work done or committing.
Whenever dependencies change (or a pom.xml edit), validate CVEs with
jetbrains get_file_problems{filePath: "bridged/pom.xml"} — its Mend.io check reflects the
dependencies on disk. (Note: ide_diagnostics / intellij-index does NOT re-resolve dependencies
after a pom edit without a full Maven reimport, so it reports stale CVE results — don't trust it
for this.) Treat a CVE warning like any other: bump to a patched version and confirm
mvn clean install still passes. If the latest available version is still flagged (EOL line,
"insufficient information", or config-file-only advisories), document it as accepted in the pom
rather than chasing a fix that doesn't exist.
Use IDE MCP tools for navigation, refactoring, and diagnostics only
- Navigate (prefer over Grep/Read for symbols):
ide_find_definition,ide_find_class,ide_find_file,ide_find_references,ide_find_implementations,ide_find_super_methods,ide_call_hierarchy,ide_type_hierarchy,ide_search_text(regex:jetbrains search_in_files_by_regex). - Refactor (prefer over multi-file Edit / rm / mv):
ide_refactor_rename(position-based, updates all refs/overrides/tests),ide_refactor_safe_delete,ide_move_file,jetbrains reformat_file. - Diagnose:
ide_diagnostics/jetbrains get_file_problems.
Do NOT route build/test/one-offs through the IDE
Keep mvn (compile/test/package/clean install) and all one-off shell commands on Bash.
Do not use jetbrains build_project, execute_run_configuration, or execute_terminal_command
to replace them.
Java 25 notes
Prefer the unnamed lambda parameter _ for required-but-unused params; a non-public
static void main(String[]) is valid (JEP 512) and boots via java -jar.