Shared connected accounts
Admitted fleet panes receive standing access to every connected MCP server whose
account is verified. Provider credentials stay in Clankie's broker. The bridge
exposes clankie_tools and clankie_call from fleet settings and admission;
provider discovery and verified account checks happen on invocation. The native
worker bridge separately supplies message_clankie and, when peer messages are
enabled, list_fleet_seats and message_peer, and the landing review tools
(submit_for_review, review_candidate, review_verdict; see
Landing review). Peer and review invocations require stronger
native identity proof. Manual grants keep their selected direct tools.
ADR 0217 supersedes the
project-tool gate in ADR 0216. Projects still own roles, caps, hiring and tracker binding.
Worker reports use the service's persisted hiring or adopting conversation,
including fleet-qualified remote seats. Many leads run at once, so a hire
belongs to the conversation that hired it. Another lead may still message it:
message_seat (including a question answer) delivers, the result names the
ownerConversationId, and the hire keeps its lead, so its reports and
completion wakes still go there. Only that lead may close it:
close_worker_pane from another conversation is refused with not_owner
naming the owner. A hire whose lead conversation no longer exists, and a
hand-started seat Clankie did not hire, are adopted by the conversation that
messages them. The roster (op: "roster", seat owner) and clankie herdr agent list (owner, hired, and mine when CLANKIE_CONVERSATION_ID is set) show
each worker's lead, or unowned. message_clankie cannot choose
a target or turn agent output into an owner instruction. A removed lead makes
reports fall back to global-default; a retained room still requires its current
actor and route grants. Existing receipt IDs prevent rerouting or replaying an
already accepted report.
When a native seat drives that lead conversation, message_clankie reports
arrive as kind="message", framed as untrusted agent output. Completion harvests
retain kind="watch", and self-wakes retain kind="wake". The message tag
grants no owner authority and changes neither the retained lead route nor receipt
semantics. Room-owned reports still use the existing correlated native reply
and original actor, route and mouth checks.
Load the shipped clankie skill to discover the current catalog, verify the
connected actor, read the issue and decisions, and perform the authorized change.
Tool access does not authorize every outward action. work-items carries the
read-before-write and editing rules.
An outward Discord post or a message into someone else's Clankie thread needs
the owner's go-ahead for its content and destination. If that scope is missing,
present the exact draft and destination and ask before sending; read-only
inspection does not grant posting authority.
Work repositories
Register the work repo as its own project and set owner overrides for push
and release, plus closure if wanted (project settings).
These are working preferences carried in briefs, not a forge permission system.
Work boundaries live in three places:
- Workers and the operator seat use their native harness permission system (Claude Code or Codex); keep the work forge credentials in that harness's approved account context, outside Clankie's connected accounts. Choose those harnesses when native approval prompts are required; a Pi worker supplies no such prompts.
- The forge protects the default branch and requires merge-request approval. Enforce protected-branch writes there, including direct pushes and bypass permissions; a brief saying “never push to main” is not enforcement.
- Clankie's pi runtime uses lane grants and has no native permission prompts. An unseated operator turn, including Linear wakes, Auto rounds and self-wakes, can use its granted shell and connected accounts without a per-action prompt.
Under ADR 0217, any admitted
fleet member can use a verified connected account; a work-project override does
not restrict that account to its project. Harness account context is not an OS
sandbox against another same-user shell (VUH-1687).
Before using a work project, the owner must choose whether to accept pi's lane
grants or route its operator turns through the native seat; do not infer that
choice from push: owner. A per-action pi envelope remains a separate
VUH-1523 decision. Apply the work repo's
preferences to that project, without imposing a global work preset on Discord,
voice, play or other projects.
Windows Claude channels after an update or deploy
The linked Claude bridge follows a republished fleet link after a runtime restart. Its next admitted mailbox poll binds the new runtime's mailbox; readopting the seat does not establish a live channel. Connected tools and native message delivery have separate admission checks, so working Linear calls alone do not prove that the channel or reports work.
A pending Claude Code update can rename the loaded image to
claude.exe.old.<timestamp>.<updater-pid>. Several workers may share that image:
the suffix is the updater's PID, not necessarily the worker's. The Windows probe
accepts only the independently installed launcher's exact predecessor renamed
during the worker's lifetime, with fresh native process, pane ancestry and socket
ownership proof. It does not require a worker restart to recognize that image.
For a released channel, inspect retained receipts and the native proof before reconnecting a busy session. A server-side recognition fix applies to a running bridge that is still retrying when the corrected runtime is deployed. Verify its recovery after the scheduled deploy. If polling has stopped permanently, wait for the worker's turn to end, then coordinate an MCP reconnect with the owner; preserve the session, cwd, account profile and channel launch flag if resume is needed. Never replay an uncertain original as a new message.
Catalog changes in Codex
The fleet bridge lists its catalog when it changes and emits MCP
notifications/tools/list_changed for added, removed or changed definitions.
A seat's mailbox long poll carries its catalog revision, and the service ends a
parked poll when a refresh request, a deploy or the fleet tool and peer settings
change it, so the bridge lists only then (VUH-2062). A bridge without the seat
channel, such as every Codex seat, long-polls
GET /v1/fleet/seats/{paneId}/tool-catalog?wait=25000&revision=R for the same
revision instead; that wait never reads or binds the mailbox (VUH-2390). An
unchanged catalog is still checked every five minutes. A bridge without a fresh
revision (a failing poll, or a service without the wait, which answers 404)
checks every five seconds and tries the wait again after five minutes; bridges
before worker 0.6.19 keep their five-second check without a channel.
While a seat is mid-turn (any Herdr status but idle or done) the service holds a
changed catalog, and the bridge keeps its published one until the turn settles,
checking every five seconds only while such a change waits. An unchanged
catalog is never held.
Temporary discovery failures, including admission refusals, keep the previously
verified schemas. Explicit settings metadata removes disabled tools. Calls still
pass through Clankie's current admission and account checks, and a refusal reports
the service's reason. A notification never replays a tool call.
Codex 0.160.0 logs this notification without updating its executable catalog.
Plain config/mcpServer/reload also reuses an unchanged ready connection.
For locally hired seats with a dedicated app-server and copied worker config,
Clankie's controller atomically persists the proven bridge registration, tool
approval and connection environment revision through config/batchWrite, then reloads.
Hires since VUH-2390 register the bridge as node running this install's
integrations/claude-plugin/worker/bin/fleet-mcp.mjs, with no clankie mcp --fleet
wrapper process; the refresh also accepts the wrapper for seats launched before
and preserves whichever registration the seat was proven with. Codex reconnects Clankie's MCP connection
at the next model step on the same thread. The pane, conversation and message
tool remain available. Refreshes are serialized and failed RPCs are retried at
most three times per catalog change; no tool call or turn is retried.
This workaround does not rewrite the owner's config, apply to manually started
clients, or refresh remote seats. For those clients, first check admission and
fleet.tools, then ask the owner to reconnect. Preserve the exact thread UUID,
cwd, account home and launch flags. A shared daemon can keep an unloaded pane's
thread cached: wait until that exact thread is no longer loaded before resuming
it. Never restart a shared daemon or fork the conversation automatically.
Controller-owned hires need controller recovery rather than a second manual
process attached to their thread. A fresh status-list connection is not proof
that an existing thread can call the tool.
Fleet admission and the kill switch
The existing transport admission is the proof:
- Local panes use
LocalFleetLink: the separate loopback listener proves the socket belongs to the pinned Herdr session and pane. Discovery has no bearer. - Remote relay streams use the service-owned
FleetLinks.fetchadmission. The current stream and registered fleet connection must remain live. - A remote fleet bearer link admits that fleet's tools without proving a pane.
Its audit principal records
pane:unverified; the session key belongs to the fleet. The link does not establish a native project assignment, grant mailbox access or permit peer messages.
On macOS, local admission uses a bounded native libproc census, rather than
lsof socket scans. It observes the unique client endpoint owner, its process
birth and socket identity. Non-owner ancestor lifetimes come from kernel
sysctl process records across users, so Terminal's root login ancestor does
not block a legitimate pane. The owner still requires full same-user process
observation. Two fresh observations
bracket the live pane and linked-session checks. That full proof runs once per
connection: later requests on the same live socket reuse it while the owner and
its lineage up to the pane shell keep their kernel births, Herdr reports the same
shell for the pane, the binding is unchanged and any private seat is still
registered. Any failed recheck runs the full proof again. Shared ownership seen
by a full proof, missing observations, exit, PID reuse and revoked bindings fail
closed. A descriptor handed to another process after its connection was proven is
not seen until that connection is proven again
(details). Service-owned private app-server
registrations retain their separate live checks. See the
native helper for build and verification.
After a service restart, Codex roster status reads live thread metadata and its native waiting flags. It does not reload historical tool output on each poll; explicit question reads and answers still validate their current native items.
The body owns one persistent native helper over private pipes. Every job still
checks fresh kernel facts; the helper itself retains no admission. Herdr foreground and session reads use its socket API directly.
No helper, Herdr CLI or ps process is launched for ordinary mailbox/catalog
polls or restored-private-seat birth checks. The bounded queue starts each 1 s
timeout when the job becomes active. Cancelling one caller removes its queued
job or drains and discards its active reply; other callers keep their proofs.
Queue depth stays at 128; waiting under a burst can exceed 1 s, and queued
callers can abort.
The worker request's deadline follows local admission through Herdr reads,
socket census and project observations. Expired requests remove their queued
helper work; an active helper reply still drains before another job uses the pipe.
Cancellation is a request failure, not evidence that the pane lost membership.
An active timeout, malformed reply or helper exit refuses pending observations,
and the body closes its own helper
at shutdown. Refusals emit fleet.local_proof.refused with a fixed reason;
fleet.local_proof.diagnostic records fixed kernel or transport details.
Neither event contains PIDs, paths, argv or caller headers. The helper reconciles
changing PID lists by inspecting their bounded union.
Unrelated transient PID/descriptor races repeat only the affected process observation,
with its partial owner discarded. These local retries use 1–8 ms jitter and at
most 32 attempts per PID within the unchanged 200 ms scan and 600 ms job caps.
An expired scan, or instability after observing a matching socket, restarts the
entire bounded census to inspect newly born inheritors. Unknown live ownership,
owner/ancestor changes and shared descriptors remain refused; persistent
uncertainty or exceeded bounds can still refuse admission.
When project identity is required, the same helper reads the foreground agent's
executable, exact first launcher arguments, and the shell and agent's microsecond
births. These observations bracket current native-session and foreground-pane
checks. Socket ancestry must agree with the observed process lifetime, including
microseconds. There is no foreground ps/lsof fallback, and project identity
is never bound to a connection. Generic hire receipts recorded with older display timestamps
fail closed until a fresh hire records the complete kernel lifetime.
Local admission can refuse when process or descriptor ownership changes during
the census. The native helper repeats the affected process observation for
descriptor churn; it never skips an uncertain live same-user record. When current
proof is unavailable, HTTP 503 fleet_admission_unavailable marks the request
retryable: nothing was forwarded. Claude and Codex bridges wait briefly and retry
that refused request once. If proof remains unavailable, the reply says to retry
shortly, then ask the lead to inspect admission if it persists. This does not mean
the seat needs a new fleet admission. A definite non-member gets HTTP 403
local_process_membership_required, with guidance to ask the lead for admission;
the bridge does not retry it. Automatic polling and catalog refresh go quiet
on that exact refusal instead of flooding the service. Worker startup hooks
also check Herdr's pane presence: an exact pane_not_found leaves the worker
unclaimed, including a pre-warmed spare that inherited an obsolete pane ID.
Unavailable observations remain unknown and still require server proof.
Only an explicit pre-forward refusal permits that retry. A timeout, lost reply or uncertain receipt remains uncertain: reconcile the original receipt, even if a later request receives an admission refusal. A later refusal does not prove that an earlier call had no effect.
No additional native session, harness executable, canonical cwd or project grant is needed for connected tools after fleet admission. Anything running in an admitted pane, and anyone holding a valid remote fleet bearer, can use the verified connected accounts, including ordinary Linear writes as Clankie. This is the owner's accepted trust boundary; project approval does not narrow it.
fleet.tools defaults to connected. The owner can stop new standing tool
admissions:
clankie fleet status
clankie fleet set --tools off
clankie fleet set --tools connected
The console's /fleet editor exposes the same setting. off lists no standing
fleet tools and refuses new standing admissions, including calls from a stale
catalog. It does not revoke manual grants. Disconnecting a fleet removes its
admission. Calls recheck admission, account binding and settings; the MCP host
also fences account and server configuration.
These checks do not provide atomic revocation. A call already past its last asynchronous check can still reach the provider after tools-off or lost admission; this is not limited to operations already dispatched. Worker requests have a thirty-second total deadline; six concurrent native-first hires and issue reads are covered against an isolated tracker. Those bounds do not make revocation atomic. This is the chosen contract (VUH-1585, ADR 0217): the switch stops new calls rather than promising atomic revocation. The original strict guarantee was not met and was replaced by this decision, not shown to pass. An operation already dispatched to a provider cannot be recalled.
Connected-account reads do not acquire native publication attribution. A write may obtain that optional author/recipient proof within one shared budget of at most two seconds, further limited by the request's remaining time. Missing or expired attribution cannot remove an independently admitted account grant or invent an author. Publication tools that require a proven author still refuse without it. Exact tool invocation and receipt lookup verify only their selected connected account; broad catalog discovery still checks each offered account.
Messages between workers
Workers can message another current seat in their own fleet without asking
Clankie to relay each exchange. The Claude worker plugin and clankie mcp --fleet
share the runSeatChannel bridge and expose:
list_fleet_seats({}): the sender's own fleet, with its identity and exact recipient seat/binding records. A seat on a remote machine sees the other seats on that machine; the service reads the sender's fleet inventory, not only this Mac's.message_peer({seat, text}): setseatto a returnedseatIdand provide the message text. The bridge obtains the sender and recipient bindings; workers do not supply them. A current binding does not authorize a later pane occupant.
The service enforces this authority on local and remote paths. The sender must have a proven native pane process and a matching native session. Transport-only fleet admission, caller-supplied pane IDs and legacy bearer-only links cannot establish that identity. Connected-tool access and project approval do not widen peer-message scope. The recipient must belong to the same fleet, retain the exact current binding and have an available native delivery route. A changed occupant or session requires fresh discovery, not redirecting the old message.
Discovery uses GET /v1/fleet/seats/{paneId}/peers, sending uses
POST /v1/fleet/seats/{paneId}/peer-messages, and reconciliation reads
GET /v1/fleet/seats/{paneId}/peer-messages/{id}. The server derives the sender
from admitted process/session proof and scopes receipt reads to that sender.
On the local listener, that proof already checks the unique socket owner,
process lifetimes, ancestry, native pane/session and private registration. Peer
routes consume it directly and reobserve it at later authority checks, rather
than repeat broad fleet admission around every native check. Discovery brackets
its roster reads with fresh proofs; sending and reconciliation retain their
separate native sender/recipient fences. Project proofs are never reused between requests.
Delivery reuses message_seat and its native harness channel/session API,
receipts and refusal states; it never types terminal keys or revives Swarm.
Messages are framed as agent output from the verified sender, never owner
instructions or new authority. Each send records server audit provenance and an
agent-role entry in Clankie's default transcript; native channel events carry source: peer. This happens without waking
him or creating an owner turn. The receiving
worker and Clankie still act within their existing assignment and permissions.
fleet.peerMessages defaults to on. Only the owner or an authorized operator
changes it through the CLI or the /fleet editor:
clankie fleet status
clankie fleet set --peer-messages off
clankie fleet set --peer-messages on
off hides both peer tools and refuses new sends server-side, including calls
from stale catalogs. It is independent of fleet.tools: either capability can
be disabled while the other remains enabled. Existing receipt reads and native
receipt reconciliation remain allowed with peer messages off. A message already
dispatched cannot be recalled.
After an uncertain send, retain and reconcile the original peer receipt and its
original native delivery receipt. Never POST the same intent again, delete its
receipt state or switch bridges to compensate for missing acknowledgment. An
unknown result is a gap to report, not proof of failure. A successful native
delivery receipt proves the stated handoff, not that the model read or accepted
the message. While unresolved, another message_peer call to the same seat reads
only the original receipt; messages to other seats are sent normally. Once that
receipt is settled, a different follow-up remains unsent; invoke again
deliberately if that new message is still needed. An original that nothing proves
or rules out within 15 minutes settles as superseded with an unconfirmed
outcome: it is never resent, and a different message to that seat goes out as a
new message labelled as following it.
If the original recipient closes or loses its native binding, reconciliation
terminates as recipient_gone with an unconfirmed outcome. Delivery remains
unknown; the original is never resent, and the sender can send a fresh message.
The service retains full bodies for the latest 100 settled messages and every
unresolved message. Older settled bodies are pruned, with compact exact receipts
retained to prevent an old delivery ID from dispatching again.
ADR 0213
records this contract. Peers never cross a fleet boundary: a seat on one machine
cannot message a seat on another, and the send is refused as unknown or ambiguous
in this fleet. Until ADR 0287,
remote seats also saw an empty catalog; that is fixed.
Landing review
When a project names a standing reviewer
(ADR 0287), the worker
bridge adds three tools. The service advertises them with reviews: "on" in its
catalog metadata, so an older service shows none.
submit_for_review({checkout, summary, issue?, evidence?, base?}): the author sends the exact committed change (git diff --binaryfrom the merge base withorigin/maintoHEAD) of the absolutecheckoutit names. The bridge resolves that path to its work-tree root and never reads its own working directory, which is where the session started (VUH-2662). A missing, relative or non-git checkout is refused with nothing stored, and every result echoes thereadcheckout,HEAD, base and patch-id. A seat without the tool (a harness that lacks the review tools, or a bridge started before them) runsclankie review submitin its own pane, proven by the same fleet link. The service hashes it, routes it to a live reviewer seat other than the author and returns the candidate. Passing the sameissueafter CHANGES links the new candidate to the old one.review_candidate({candidateId}): the author and the project's reviewer read the stored patch and the candidate record.review_verdict({candidateId, patchId, verdict, findings?, summary?}): the reviewer recordsapproveorchangesfor the patch it read. CHANGES needs at least one concrete finding. A verdict is refused from the author, from a seat that is not the project's reviewer, and for a different patch-id.
These use the same native seat proof as peer messages, never a caller-supplied
identity, so transport-only admission and bearer links cannot submit or decide.
They do not depend on fleet.peerMessages. Seat routes are
POST /v1/fleet/seats/{paneId}/review-candidates,
GET /v1/fleet/seats/{paneId}/review-candidates/{id} and
POST /v1/fleet/seats/{paneId}/review-verdicts; the owner reads with
clankie review.
Discover and call
clankie_tools({query?: string, names?: string[]}) searches the current permitted
catalog. A query returns at most 20 qualified names with one-line descriptions.
An omitted query returns the first bounded page. names selects at most 10 full
input schemas, with descriptions; unavailable names are omitted. It never emits
an unbounded catalog grouped by server. Its own description names the servers
connected right now (Connected now: linear), and the worker plugin's standing
instructions point at it, so a harness that loads it as a deferred tool still knows
Linear and the other connected accounts are reachable. Discover the schema before dispatch:
{ "query": "linear get issue" }
{ "names": ["linear_get_issue"] }
Call the discovered name through clankie_call({name, arguments}):
{ "name": "linear_get_issue", "arguments": { "id": "VUH-1558", "includeRelations": true } }
A linear_* tracker call that names an item of a cut-over project (its imported
TEAM-n, LOCAL-TEAM-n or UUID, or the project itself) goes to that project's
built-in tracker, whatever the caller's hire, and never to the frozen Linear copy
(VUH-2829). Writes Clankie cannot route there, such as a linear_graphql
mutation or create_worker_comment naming the item, are refused with a pointer.
To read the Linear copy as history, pass linearHistory: true on a read; the
result comes back marked as Linear history.
Otherwise a linear_* tracker call without repo follows the caller's hire
(VUH-2014). A worker hired for a registered project uses that project's saved
convention. On a builtin project (one cut over from Linear), or any other
non-Linear convention, the call goes to the project's own tracker, so the
built-in store records the write as the worker. The tracker scope refuses writes
outside the project, and linear_graphql is refused. On a linear project the
call reaches Linear as before. A project with no tracker binding counts as
linear when its local workspaces' saved conventions are all linear. A project with no saved convention, one that is
gone, or one whose tracker cannot be read here refuses the call. Pass repo to
name another tracker; an explicit repo always wins. A bearer-linked worker
without a verified pane, or a pane that is not a confirmed project hire, keeps the old
routing.
Unverified or unavailable accounts contribute no upstream tools; one failing
server does not remove the others. Fleet discovery still lists the two bridge
tools when no account verifies, but searches return no upstream names and calls
are refused. Linear worker-publishing tools (create_worker_comment and
create_worker_issue) are excluded: they require an exact personaId grant.
Ordinary connected tools use the shared rule matcher, argument checks and
host.call delegation path. Each provider call records fleet and pane provenance.
Codex hire readiness expects the two meta names while fleet tools are on, and
no connected-tool names while off, regardless of project grants. The worker
plugin's message_clankie is a separate expectation; authorized peer tools are a
separate native capability. This check does not create
a grant or prove a native provider call.
Verify the account
/connect linear verifies an API key using stable user/workspace IDs, email and
workspace name. For an existing API key or OAuth connection, run
clankie access linear verify; clankie access linear and /access linear
show the recorded identity. Confirm the intended actor and destination before
writing. No email is a product default.
OAuth verification calls get_user with query: "me" and get_workspace at
Linear's official MCP endpoint using one locked credential snapshot. MCP-audience
tokens never go to GraphQL. Reverification preserves manual account binding when
stable user/workspace identity is unchanged; a changed identity invalidates those
grants. Standing fleet authority uses the current verified account on the next
request. Account changes during a call refuse that call. Verification does not
sign in as a different user.
Manual grants
Discover the real tool names and schemas with Clankie's tool search. A request
selects principalId, workId, server, tools and a lifetime of at most 900
seconds. Tool argument restrictions are exact top-level comparisons:
{
"principalId": "reviewer",
"workId": "issue-identifier",
"server": "linear",
"tools": [
{
"name": "save_comment",
"arguments": { "issueId": "issue-identifier" },
"forbiddenArguments": [
"id",
"parentId",
"projectId",
"initiativeId",
"documentId",
"milestoneId",
"statusUpdateId",
"statusUpdateType"
]
}
],
"ttlSeconds": 900
}
clankie access issue request.json --out worker-grant.json
clankie access list
clankie access revoke GRANT_ID
clankie mcp --grant /private/path/worker-grant.json
Issuance needs operator authentication. --out writes a new private file (0600)
and prints metadata, never the bearer. A failed file delivery attempts to revoke
that grant and reports unconfirmed revocation. Deliver only the private file to
the intended worker through an authorized channel. The bridge exposes granted
tools, with no operator credentials or seat channel. Its endpoint must use HTTPS
or loopback HTTP. Tokens expire and require explicit reissue; task-bound renewal
and coordinator-based delivery are retired by ADR 0213.
Saved task-bound or renewable grants remain on disk and confer no authority.
Revoking a manual grant closes every MCP session using it, aborting pending requests. The host checks that request's abort signal at its final dispatch boundary, after awaited setup, so a completed revoke prevents a call that has not yet dispatched even if its earlier grant read succeeded. Revocation cannot undo an effect already dispatched to the provider.
Projects and retained grant records
Projects retain approved workspaces, explicit native hire assignments, roles,
worker limits and tracker binding. Use clankie project add and the project
settings API for those policies; do not expand workspace approvals to fix a missing
tool catalog. Native project and mailbox proof remains separate from fleet tools.
clankie access project NAME SERVER [--tool NAME]... remains available for inspecting
and maintaining legacy project-grant records. Those records no longer determine
fleet tool access. They produce no bearer; revoking one does not remove standing
fleet tools. The owner disables fleet access with fleet set --tools off or
removes the fleet connection. Retired fleet-grant records remain readable and
explicitly revocable, without copying, migration or automatic revocation.
clankie access fleet continues to refuse issuance.
Native discovery and diagnostics
Register clankie mcp --fleet in Codex with env_vars = ["HERDR_PANE_ID", "HERDR_SOCKET_PATH"], or use the shipped Claude worker plugin. Preserve
source-managed/symlinked harness configuration. The existing PC Node bridge
proxies list/call and needs no new wire protocol for the two-tool catalog.
The bridge retries initial discovery with backoff for up to 20 seconds while a pane settles, including stalled HTTP requests. An unproved initial catalog fails with its real reason; hired Claude and Codex panes wait for the expected wrapper and enabled peer tools before their brief. Verified catalogs remain present through temporary failures. Lists and calls use bounded initialization and body reads, with a thirty-second total request budget. Replacing a service provider connection preserves already dispatched calls until they settle. Discovery never retries an uncertain mutation. Codex can retain its startup catalog despite tool-list-change notifications, so the owner may need to reconnect MCP or restart a pane after cutover. A displayed stale tool never bypasses current service authorization.
Connected calls return typed JSON with a receiptId. A deadline after provider
dispatch returns outcome: "uncertain": the write may have applied. Reconcile
with clankie_call({receiptId}), without the tool name or arguments. That lookup
reads the durable original receipt under the same authenticated worker, granted
tool and connected account; it never dispatches another provider call. Already
admitted calls retain their bounded original completion lifetime so a late
response can settle the receipt. The bridge chooses its receipt ID before
forwarding, preserving reconciliation when the HTTP response is lost.
Connected-call receipts live under ~/.clankie/worker-grants/receipts/.
connected-calls.json commits a small manifest of immutable, content-addressed
buckets in connected-calls.json.d/. A receipt change rewrites its bucket and
the manifest, rather than the whole retained ledger. Legacy array journals
migrate on their next save. Shards are synced before the manifest is committed;
missing or changed committed shards refuse dispatch. All uncertain records and
identity tombstones survive retention; only older settled response bodies and
optional recovery detail are trimmed. Never delete these files to retry a call.
Inbound message claims remain exclusive through timeouts and lost accepted responses. A definitely refused connection before POST delivery releases only the matching claim. An exact authenticated unknown-delivery response releases it only after persisting a terminal ID fence against a delayed original POST. For an exact pending pane delivery without conversation acceptance, lookup can settle absence after service replacement or the host's 20-second request deadline. The service records the attempt owner and deadline, seals its ID and clears only its matching pane fence in one write. Live same-instance requests remain unresolved; expired, cancelled or shutdown requests cannot subsequently accept the message. Accepted history remains authoritative, and corrupt or mismatched evidence stays uncertain. Replacement recovery relies on the service's exclusive process lock. After shutdown the old instance cannot write receipt fences. If an accepted original still needs pane cleanup, it retains uncertainty until the replacement's exact lookup clears that pane, keeping the worker's next message from getting stuck. The current invocation never sends a replacement; a subsequent invocation can send a new message. Authentication failures expose a generic refusal, while authenticated downstream failures retain their actual reason.
Local hired Codex servers outlive a service restart. Their completed launch
registrations persist in local-codex-seats.json under CLANKIE_STATE (default
~/.clankie), independently of the pinned code checkout. After restart, each
request still checks the original server PID/start time, the selected Herdr
socket/session and the current native foreground occupant, so message_clankie
keeps working without transferring an old worker's identity to a replacement.
An unreadable record file never blocks startup: it is moved aside and no seat is
restored. Hires created before these records existed cannot recover; report
through the lead's watch. The outbound Codex adapter's turn state stays in memory.
Fleet membership doctor reports project eligibility and native observations.
eligibility: unsupported or missing project proof does not deny fleet tools.
nativeTools: not-verified means the diagnostic has not inspected that pane's
bridge/catalog or demonstrated a call. Doctor and the roster distinguish bridge
transport presence from freshness: older-than-runtime: the observed bridge
started before the running service, so the seat needs reloading. This is a reload
hint, including after a same-build service restart, not proof of an obsolete
build or successful delivery. Missing start-time facts remain unknown.
The separate workerTools field records served/reported catalogs, missing tools
and stalled calls with their real reason. A new authenticated bridge process
starts a new observation cohort; late reports cannot overwrite it. Cached schemas
and diagnostic observations grant no authority. No durable native binding on
a message receipt means no new message was sent; inspect the native binding.
Operator bridge presence does not prove worker bridge readiness; reconcile an
uncertain original receipt before another attempt. Live PC acceptance is a separate native
check after landing and re-pin: two connected bridge tools plus message_clankie,
and a Linear issue read through clankie_call. Deterministic fixtures do not
establish that acceptance.
VUH-1608's live peer-message acceptance is a separate check with two actual KH2 panes on the PC after landing and re-pin. It must establish discovery, native delivery, authority framing, the owner off switch and honest receipts in those panes. Deterministic server and bridge regressions do not establish that live result.
Boundaries and limits
- Manual grants keep exact top-level
argumentsequality,forbiddenArguments, maximum 900-second expiry, durable revocation and account binding. Omitted arguments remain unrestricted.workIdis provenance, not issue isolation. - Manual sessions belong to one grant. Admitted pane sessions belong to fleet and pane; bearer sessions belong to fleet with no verified pane. Idle sessions expire after 15 minutes; clients reconnect after a service restart.
- Worker publishing stays behind an exact manual
personaIdrestriction. Fleet tool access does not invent a persona or grant worker attribution. - Provider permissions still apply. Ordinary launch isolation is not an OS sandbox; direct access to an operator bearer or the owner's broker provides other authority. The worker sandbox narrows what a hire can read; it isn't a full boundary yet. Keep credentials out of transcripts and use the tracker identity contract and Linear worker publishing for attribution.
- An explicitly prepared installed macOS runtime can confine service and native descendants with an OS profile (ADR 0257). It does not separate worker authority from service credentials inside its private home or from a provided service bearer. Live activation remains owner-held; do not infer confinement from a lower access preference or launch marker.
Retirement issue bookkeeping
Pane retirement and issue settlement have separate receipts. The keeper captures
an exact confirmed native hire while its roster occupant is still proven. A normal
complete-inventory exit can mark that hire gone before bookkeeping; replacement
allocations and recovery release invalidate the captured attribution. Each effect
resolves the project's current saved tracker, preserves the originally observed
issue UUID, and applies the existing project and account fences. A display key
such as LOCAL-VUH-4 cannot become VUH-4, and identical keys in different stores
cannot exchange successors, pause records or write targets.
Unavailable, revoked or ambiguous project bindings refuse issue effects. Saved Linear routes retain their connected resource and scope, including legacy projects with one unambiguous saved Linear convention. Disconnection never chooses the adapter's local compatibility store. Built-in Done still requires the owner's verification; retirement evidence grants no human identity or delivery rollback.
The daily check counts only attributable tracker ownership. Unscoped operator-head
and native-child claims are excluded rather than assigned to the default connector.
A historical pause ledger without scope cannot establish restoration authority.
Source-checkout disposable-store proof is retained under
docs/testing/2026-10-12-local-vuh-8.
Actual installed-pane behavior requires a separate check after deployment.
Retiring and approving machine-specific workspaces
A missing, inaccessible or noncanonical registered local workspace matches no agents. It does not prevent valid unrelated workspaces or projects from resolving. Remove an exact registration explicitly, even after deleting its folder:
clankie project remove-workspace kh2 --workspace /absolute/canonical/retired-worktree
The project, roles, caps, grants, assignments and unrelated settings remain. A workspace referenced by the project tracker cannot be removed until that binding is moved or removed. No directory is deleted by this command.
Remote approvals name the registered fleet machine and target path platform:
clankie project add kh2 --workspace 'C:\code\kh2' --machine pc --platform windows
clankie project remove-workspace kh2 --workspace 'C:\code\kh2' --machine pc --platform windows
The owner approves the exact normalized absolute spelling; the Mac does not canonicalize a Windows path. Approval alone proves no native project membership: matching still requires fresh native process and filesystem proof on that registered machine.
Owner API clients read GET /v1/operator/projects for the current settings and
revision, then call POST /v1/operator/projects/remove-workspace with
projectId, workspaceId and expectedRevision. A stale revision, tracker
reference, changed owner authority or concurrent settings edit refuses removal.
Worker sandbox
A hire runs as the owner's macOS user, so only the kernel can keep it from reading the owner's credentials (VUH-2592, ADR 0296).
Every local hire's environment carries CLANKIE_WORKER_PANE=1 and blank
CLANKIE_OPERATOR_TOKEN and CLANKIE_CAPTAIN_TOKEN. That happens whether or not
the sandbox is on: a bearer that Herdr's server inherited never reaches the
worker. In a worker pane, a blank bearer counts as absent. When no credential can
be read there, clankie says hired workers don't hold the owner's credential,
instead of reporting a Keychain fault.
With clankie fleet set --worker-sandbox on (off by default), each new local hire
starts under /usr/bin/sandbox-exec:
- The pane's shell starts through
apps/clankie/src/worker-shell.sh, which execs the owner's login shell under the profile. Claude and Codex, which Herdr starts in that shell, inherit it. - A prepared command (OpenCode, Pi, Grok, Prime) is wrapped directly.
- The Codex app-server, which runs Codex's tools, gets the same profile when Codex runs with full access. Seatbelt can't nest, so in Codex's own sandbox modes it keeps only Codex's confinement.
The profile allows everything else and denies reading or writing:
- the protected keychain (
clankie operator-credential protectmoves the operator bearer there); - the file broker,
~/.config/clankie/credentials.jsonor$CLANKIE_CREDENTIALS_FILE; - the service checkout's and
~/.clankie/pinned's.env.local.
Their ancestor directories can't be renamed or removed, because that would give a protected file a path the profile doesn't name. Files inside those directories can still be created, edited and deleted. The owner's login keychain stays readable, so a harness keeps its own sign-in. Inside the lowered runtime's envelope (ADR 0257), hires already inherit that envelope and get no second profile.
Limits until the follow-ups land:
- Escapes: a confined worker can still ask the unconfined Herdr server for a new pane, submit a launchd job, drive another app with Apple Events, or write code the owner or service runs later (VUH-2836).
- Worker commands:
evidence push,simulatorandworkstill use the operator bearer until VUH-2837, so protect the bearer only after that lands. - Other secrets: the remaining broker items,
~/.config/clankie,~/.ssh,~/.config/ghand provider keys aren't covered yet (VUH-2838, VUH-2839, VUH-2840).
Owner-authorized work-item gestures
Workers keep using clankie work for the repo’s recorded tracker. Paired devices
with terminalControl can assign work metadata, add or remove a role label,
and append a blocker through the narrow work_item_write operator operation.
This authority stays with the original owner identity through the relay;
worker or execution credentials cannot substitute for it. The CLI equivalents
are clankie work write and clankie work receipt, documented in
the CLI guide.
Retain the returned requestId. After an uncertain result, read the receipt
and tracker; never resend the change with a new ID. Receipt reads and repeated
IDs never dispatch a mutation. The journal checks owner, source item, saved
tracker and connected-account scope before returning an original result.
