Files
fleetd/CLAUDE.md
T
Dai Ha 979b2b5632
CI / build (push) Successful in 1m25s
CB-517: add bridge_whoami and make the bridge prompt a portable charter
The communication rules lived only in two opt-in skills, so nothing
always-on told the primary how to orchestrate and nothing guaranteed a
worker loaded its playbook. Move protocol and policy into CLAUDE.md,
which a worker inherits for free (its worktree is a checkout of this
repo), and leave the skills as pure per-job procedure.

bridge_whoami closes the load-bearing gap: every tool already consumed
the caller identity ConnectionIdentity resolves from the connection, but
none reported it, so an agent had to infer its own role from side
channels the daemon does not control. Guessing fails asymmetrically — a
primary acting as a worker is refused by the authz gate and learns at
once, while a worker acting as the primary ends its turn without
bridge_reply and the sender silently receives nothing. The tool reuses
the same Principal the gate is built on, so the two cannot disagree; the
primary gets role only (handing it a sessionId it does not own would
invite the forged reply Authz refuses), and a worker missing from the
registry still gets role + sessionId rather than 'unknown'.

The CLAUDE.md block is written to be copied as-is into any project that
mounts the bridge: repo-local details (Authz paths, the .mcp.json/wiki
exclusions, the skill names) moved below it into a project addendum, and
every role-inference fallback is stated one-way — the mount-name signal
only holds for mcp__bridge__* (the launcher fixes it), not for the
primary's mount, which each project names itself. The wiki carries the
block verbatim as the template, with a sync check.

Because this repo IS the bridge, that block is shipped surface, not
documentation: the addendum adds a mandatory checklist mapping each part
of the code to the part of the prompt it can invalidate.

Also: delegate-by-default policy for the primary — the test is not 'could
I do this faster myself' but 'can I write a brief good enough for a
worker'.

mvn clean install: 356 tests green (353 + 3 for whoami); ide_diagnostics
clean on both changed files.
2026-08-04 16:01:16 +02:00

15 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

  1. Never set, export, or forward ANTHROPIC_BASE_URL (or ANTHROPIC_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.
  2. 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.
  3. 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.
  4. 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.
  5. Never drive the terminal multiplexer directly (no herdr CLI, no socket). The bridge owns policy; the multiplexer owns PTYs. Going around the bridge bypasses every rule above.

Primary (lead) — orchestration

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.

  • Delegate: implementation behind a clear spec, test writing, per-file or wide-area review, log and failure triage, mechanical refactors, doc passes, and any investigation with a stated question. If the unit of work is independent, fan out — one worker per file, area, or dimension — and reduce the replies yourself.
  • Keep: the conversation with the user, decomposition and planning, the final judgment call, merges, and anything that depends on context only you hold.
  • The bar is not "could I do this faster myself?" — usually you could. It is "can I write a brief good enough for a worker to succeed?" If yes, write the brief and send it. A wasted worker turn costs a worker turn; doing it yourself costs your context and your subscription.
  • Parallelize instead of serializing. For independent units, spawn one worktree worker each (bridge_spawn{worktree:true, ticket:…}), dispatch every one with wait:false, then poll the tickets. Waiting for worker A before briefing worker B is the most common way this layer is wasted.
  • Delegating does not delegate responsibility. You still review, verify, and merge.
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}
  • Pass profile: explicitly. Profiles differ in model and cost, not in tier — don't assume the default is what you want.
  • 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; the ticket path is what survives a long task.
  • Every delegation names the worker's playbook. If this project ships role skills, make the first line of content Load the <name> skill. — those skills are opt-in, and that line is what makes them reliable. With no such skill, spell the procedure out in the brief instead.
  • A delegation must be self-contained: scope, the files or PR in question, acceptance criteria, and exactly what to report back. The worker sees your message and the repo — nothing of your context, your plan, or your screen.
  • You are the gate. Workers open PRs; you review and merge. Never delegate the merge.
  • Verify what a worker claims. A worker mounts only the bridge MCP and cannot run your other tooling, and a piped build command (… | tail) hides failures behind a zero exit — re-run the build and the checks yourself before you believe "clean".

Worker — the turn contract

  1. Load the playbook skill the lead named before doing anything else.
  2. Do the assigned scope only. Note anything you spot outside it in one line; don't go hunt it.
  3. 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.
  4. End the turn with exactly one bridge_reply{content}, carrying your complete answer. This is the whole handoff. No bridge_reply ⇒ the sender gets nothing and the exchange stalls.
  5. 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.
  6. 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 is http://127.0.0.1:8765/mcp, and the code behind the rules above is mcp/BridgeMcp (tools), auth/Authz (the role table), mcp/ConnectionIdentity (connection→role), and worker/*Launcher (REPLY_CHARTER).
  • Skills available to delegate: implementer (worktree → commit → push → own PR) and reviewer (scoped review → one structured finding). Name one in every delegation.
  • Never commit .mcp.json (the primary's local copy, flagged --skip-worktree) or wiki/ (a submodule with its own remote).
  • Flows and the error model — rendezvous, bridge_ask, detached delivery, turn-done fallback — are diagrammed in docs/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

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

  1. 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.
  2. ide_diagnostics{file} (or jetbrains 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.
  3. 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.