Repository navigation
mcp stdio: relaying calls reach a child one at a time so each ask reaches only its caller; a lease reads whole frames; Frames reads each byte once (plane-mcp findings 5, 13, 14) - #645
Conversation
…pls the unserved Plane traits (finding 12; spec Law 11, never-constructed). Source scan: the honest plant for a pure deletion; fails while NestedPlane, SAMPLING_OP, impl Plane/SessionPlane for McpPlane exist
…wledging them, and a live ask lapses and is round-capped (findings 16, 17) Plants the failing tests for plane-mcp findings 16 and 17 (DESIGN 1.6.0 lines 379-390). era() answers logging/setLevel and resources/(un)subscribe with -32601 and initialize advertises neither resources.subscribe nor logging; a live ask armed at t lapses at t + ASK_TIMEOUT_NS and a request past MAX_LIVE_ASK_ROUNDS is not livened. arm, lapsed and may_liven are present as the unfixed behaviour (no deadline, no cap) so the plant compiles and fails by assertion.
… its live asks (findings 16, 17)
16: resources/subscribe, resources/unsubscribe and logging/setLevel were acknowledged with {} and
dropped while initialize advertised resources.subscribe. Nothing on the plane announces a resource
change (every Listen::step is fed no updates) and a line answer carries no log records to filter, so
delivery is not possible on this carrier: the verbs now answer -32601, initialize no longer
advertises subscribe or logging, and the unused with_level, subscriptions_full, MAX_RESOURCE_SUBS and
the retained-uri Era variants are removed. A watch is subscriptions/listen's; subscribe stays for the
old revisions on their sessions (DESIGN 1.6.0 lines 379-390).
17: a live ask is armed with ASK_TIMEOUT_NS when issued (first ask and each next ask of a round); the
tick drops a lapsed entry and emits the documented fallback (the input_required result, line.rs
fallback) on its session; the retry unit carries round+1 and a request past MAX_LIVE_ASK_ROUNDS is
handed the input_required result as it is instead of being livened again.
… struck cross-plane sampling route (finding 12) The crate serves only plane_door::door; nothing constructed McpPlane as a Plane. Removes src/tool_plane.rs (impl Plane / SessionPlane for McpPlane, the sampling row to DestinationFacts::NestedPlane, the roots row, the second meter that disagreed with the served door's) and tool_meta::SAMPLING_OP. Law 11 (spec 420-421, 2123-2129): no call is routed to another plane on the content's say-so; spec 1930: a capability that is never constructed does not ship. Server and the McpPlane field were read only by that impl and go with it; McpPlane stays as the PlaneMeta/Plugin carrier the door reads (tool_door.rs PLANE/CLAIMS). The served door's meter is untouched. Tests driving the dead impl retire under the coordinator's finding-12 order (F25, spec 1181); surviving assertions move to plane-free tables (conformance, declarations_agree).
…indings 2, 3)
Plants three tests in busbar-plane-mcp src/tests/call.rs, driving the pure settle the door
serves every tools/call answer through (call::settle_call):
- a_result_reaches_its_caller_as_the_upstream_sent_it: a result carrying `Vec<String>` and
`<b>x</b>` in content text, structuredContent and _meta must reach the caller unchanged, its
bytes the upstream's own. Fails today: the built-in markup strip serves `Vec` and `x`
(finding 2, call.rs:1382).
- structured_output_breaking_the_published_schema_is_relayed_unchanged: a structuredContent
that fails the tool's published outputSchema must be relayed unchanged, dispatched, audited
applied. Fails today: busbar answers its own isError "The structured result was NOT served"
(finding 3, call.rs:1355-1378). Replaces structured_output_breaking_the_published_schema_is_a_tool_failure,
which asserted the replacement.
- the_task_path_names_no_rewrite_of_the_result: a source plant over door_tasks.rs (the task
continuation is reached only through a live door). Fails today: door_tasks.rs:755 runs the
strip over every completed task result.
Spec (docs/design/BUSBAR-1.6.0.md): Law 11, lines 2123-2133 ("A plane reads content ONLY to
translate dialects and to meter usage", 2126; "Code that answers, executes or branches on what a
request or response asks for is a defect: it is deleted", 2131-2132); product hard rules
3369-3373 ("every client header and body field passes through unchanged, except what busbar
governs").
…caller text says relayed Three plants for the MCP-FIX sampling group (kernel ingress audit H5; plane-mcp findings 19, 20): - crates/busbar/tests/plane_host_universal_purity.rs no_crate_carries_a_resolved_completion_seam: a source scan (comments stripped, non-test files) of every crates/*/src for the kernel's resolved-completion seam (CompletionArrival, CompletionIngress, install_completion_ingress, set_test_completion_ingress, completion_ingress) and the LLM plane's synthesize_completion. Grep-style plant: the fix is a pure deletion. - crates/busbar-plane-mcp/tests/purity.rs the_plane_satisfies_no_upstream_ask: a source scan of the plane's src for the satisfier vocabulary (InputRequiredLoop::may_satisfy). Grep-style plant: the fix is a pure deletion. - crates/busbar-plane-mcp/src/tests/call.rs an_ask_refusal_tells_the_caller_the_ask_is_relayed_never_satisfied: runtime assertion over the caller-visible refusal text (an ungranted sampling ask; a complete result still carrying requestState), which today says busbar "satisfies" asks and that an ask "terminates at busbar and is never forwarded to you". a_granted_sampling_ask_is_relayed_to_the_caller_unchanged: a PIN, green on predev: the HTTP upstream path already relays a granted sampling ask byte for byte. Spec: docs/design/BUSBAR-1.6.0.md Law 11 lines 2123-2133 (2127-2132: busbar routes no call to another plane on the content's say-so, answers nothing on the caller's behalf; code that answers what a response asks for is deleted); the relay-only ruling 418-422.
…ip and the outputSchema replacement are deleted (findings 2, 3)
Finding 2 [HIGH] (markup stripper on tool results):
- call.rs completed(): the result no longer passes through sanitize::normalise_json. The
caller's answer carries the upstream's own `result` bytes (serde_json RawValue over the far
end's answer) under the caller's id, instead of a re-serialised parsed Value; the one
addition is the dialect's `resultType: complete` on a result object that carries none. A
`resultType` the upstream sent is no longer overwritten.
- door_tasks.rs continuation_answered(): a completed task stores the upstream's result as it
came (End::Completed(value)).
- sanitize.rs: normalise_json deleted (no caller left). normalise / normalise_opt stay: they
serve busbar's own section text (catalogue descriptions, prompts/get, resources/read) and
busbar's words on a failed leg.
- busbar-plane-mcp Cargo.toml: serde_json names `raw_value` itself.
Finding 3 [HIGH] (structuredContent schema failure replaced by busbar's error):
- call.rs completed(): the outputschema::check branch and its "The structured result was NOT
served" answer are deleted; the upstream's result is relayed unchanged, dispatched, audited
applied.
- outputschema.rs and src/tests/outputschema_tests.rs deleted: the check has no caller ("a
capability that is never constructed does not ship", THE DESIGN 1930).
Tests retired: src/tests/outputschema_tests.rs (16 tests of the deleted check);
sanitize_tests.rs every_wrapper_normalises_through_the_same_function loses its normalise_json
half (renamed the_optional_wrapper_normalises_through_the_same_function, normalise_opt half
kept). call.rs a_result_is_normalised_stamped_and_dispatched is renamed
a_result_is_stamped_and_dispatched and now pins the relayed bytes and the empty-result stamp.
Prose: call.rs module doc, sanitize.rs module doc, tools_config.rs output_schema doc,
tests/sanitize_complexity.rs doc, docs/mcp.md (output_schema row, catalogue and
markup-normalisation paragraphs) no longer claim results are stripped or validated.
Spec (docs/design/BUSBAR-1.6.0.md): Law 11, 2123-2133 (2126 "A plane reads content ONLY to
translate dialects and to meter usage"; 2131-2132 "Code that answers, executes or branches on
what a request or response asks for is a defect: it is deleted, never added"); product hard
rules 3369-3373 ("every client header and body field passes through unchanged, except what
busbar governs"); 1930.
… clock refuses sealed state, own and relayed (finding 11) Plants for the two MEDIUM findings; both fail by assertion on the unfixed tree: - ask: the state minted for the final round, re-sent with no inputResponses, proceeds to the tool (spec 379-390, the ask gate). - door: a host whose clock.now fails (served and failing, or unserved) accepts a lapsed own state and a lapsed relayed state because now=0, and mints a state stamped at the epoch (spec 1576, 3295-3370 fail closed).
…aled state (findings 10, 11) 10: decide() redeemed the final round's state before the answered check, so the state minted for the last round, re-sent with no inputResponses, proceeded to the tool. The answered check now runs before the final redeem; the refusal spends nothing (spec 379-390, the ask gate; ask.rs module promise). 11: DoorSeal::now returns None when clock.now fails, and the reads that fed a sealed state's window (decide_ask, relay_ask, the task relay's mint and open) no longer substitute 0: decide_ask treats a failed clock as no sealer, relay_ask refuses as no sealer, the task relay mints and opens nothing. Fail closed (spec 1576, 3295-3370).
…m's ask is relayed to the caller
Kernel ingress audit H5 (Law 11) and plane-mcp findings 19 (InputRequiredLoop::may_satisfy) and 20
(prose says asks terminate at busbar or are satisfied by it; the code relays them).
1.5.5 did not do this: `git grep -n -i 'synthesize_completion\|completion_ingress\|createMessage'
v1.5.5 -- crates/` is empty (exit 1); 1.5.5 had no MCP plane. So this is a deletion, not a parity
break.
busbar-kernel (crates/busbar-kernel/src/ingress/arrival.rs): the resolved-completion seam is
deleted — CompletionArrival, CompletionIngress, INSTALLED_COMPLETION_INGRESS,
install_completion_ingress, TEST_COMPLETION_INGRESS_HOOK, set_test_completion_ingress,
completion_ingress. It had no in-tree reader; it existed so a plane could run a chat completion on
the LLM plane on an upstream's say-so.
busbar-llm: synthesize_completion (native_ingress.rs) is deleted with its production install
(linked::install_protocol_seams, which now installs only the stream translator) and its test-kit
seed (testkit/mod.rs). The ArrivalCtx downcast it shared with the test kit's shell moves into
testkit/shell.rs, its one remaining caller. Prose naming the re-entry is corrected (lib.rs,
native_ingress.rs, unit/audit.rs).
busbar-plane-mcp:
- client/jsonrpc.rs: the satisfier is deleted — InputRequiredLoop (new/may_satisfy/rounds) and its
AskRefusal (Ungranted/LoopExhausted), constructed by nothing but their own tests. The module
header said an upstream's ask "terminates at busbar" and is "NEVER proxied outward"; it now says
what the code does: a granted ask is relayed to the caller with inputRequests as sent, an
ungranted one is refused, relayed rounds are capped. ServerRequestGrants::allows loses a false
dead_code allow (the stdio relay reads it).
- call.rs: the caller-visible texts. The ungranted refusal said the server "asked busbar to
satisfy" an ask; it now says the ask was not relayed to the caller. The terminal check's refusal
said "An upstream's ask terminates at busbar and is never forwarded to you"; it now says an ask is
relayed only as an input-required result and this answer (a complete result still carrying an
ask's field) is not served. The audit reason word `ask_not_proxied` is unchanged.
- tools_config.rs, tool_ops.rs (OP_SAMPLING doc), tool_records.rs (the "synthesize a completion"
and "sampling spend" rows), client/peer.rs: satisfier prose reworded to "relayed to the caller".
- The HTTP upstream path (call::judge_ask -> Settled::Relay) and the stdio relay
(tool_program.rs) already relay a granted sampling/elicitation/roots ask unchanged and satisfy
none; nothing on a served path changes. The stdio cross-caller correlation is untouched.
- `max_input_required_rounds` stays: it is a predev config key (in the config schema snapshot and
docs/mcp.md), and it already caps RELAYED rounds (judge_ask, tool_door.rs); only its doc changes
("never relay one").
- Tests retired with the satisfier (client/tests/ask_tests.rs): an_ungranted_ask_is_refused_and_
the_message_names_the_grant, a_revoked_grant_bites_on_the_very_next_retry,
each_grant_is_independent, a_hostile_upstream_cannot_amplify_cost_by_asking_forever,
the_bound_is_per_dispatch, a_cap_of_zero_denies_every_round. They drove only the deleted loop;
the live gate's grant and round-cap refusals are held by tests/call.rs
(an_ungranted_ask_is_refused_and_never_forwarded, an_ask_mixing_an_ungranted_kind_is_refused,
the_round_cap_bites_before_the_grant_is_read) and client/tests/peer_tests.rs.
busbar (tests only): plane_node.rs comments and the purity self-test fixture no longer name the
deleted synthesizer.
Spec (docs/design/BUSBAR-1.6.0.md): Law 11, lines 2123-2133 (2127-2132: busbar routes no call to
another plane on the content's say-so, answers nothing on the caller's behalf; code that answers
what a response asks for is deleted, never added); the relay-only ruling, lines 418-422 ("routes
no call to another plane"); B.3 item 10, line 5139 (an elicitation request is plane traffic to the
caller); the mcp bullet, lines 379-390 (baseline is predev behaviour).
…finding 4) Two served-door tests in the root rig (crates/busbar/src/root/tests/door_steps.rs): - a_host_the_deployment_refuses_is_refused_in_the_arguments: the rig's deployment blocks `blocked.example` (security.blocked_metadata_hosts); a call whose arguments name it must be refused 403 / -32000 / tool_argument_refused before it is sent. The plane's own predicate admits it, so this fails on the unfixed tree: the plane never asks the host. - every_internal_address_the_guard_refuses_is_refused_in_the_arguments: the nine addresses the connector's guard refuses as internal (198.18.0.1, 192.0.0.8, 0.1.2.3, [::ffff:198.18.0.1], [::1%25lo], [fe80::1%25eth0], 224.0.0.1, 255.255.255.255, [ff02::1]) are refused in arguments by that same judgement. The rig's kernel services now carry the deployment's one destination judge (the connector's guard), as root::serve::kernel_services composes it in production, so the test host's dest.judge is the kernel's real judgement, not a stub. Spec (docs/design/BUSBAR-1.6.0.md): 5140 (B.3 item 11: argument-embedded URL judging is dest.judge), 1579 (the dest.judge row), 614-621 (one guard, every scattered check deleted).
…anged (finding 2) Plants an_upstream_error_message_reaches_its_caller_unchanged in busbar-plane-mcp src/tests/call.rs: an upstream error whose message carries `Vec<String>` and `<b>x</b>` must reach the caller byte-identical inside busbar's failure words. Fails today: call.rs upstream_failure_result runs the markup strip over the whole text, serving `Vec` and `x`. The envelope shape (isError result) is unchanged by this plant. Spec (docs/design/BUSBAR-1.6.0.md): Law 11, 2126 and 2131-2132; product hard rules 3369-3373.
…d (finding 2)
call.rs upstream_failure_result no longer runs sanitize::normalise over its text. The text is
busbar's fixed wording ("The MCP server `<server>` did not complete this tool call: ...")
around the reason, and on an upstream error the reason carries the upstream's own JSON-RPC
error message, which the strip rewrote (`Vec<String>` -> `Vec`). The envelope (an isError
result) is unchanged. sanitize.rs and tests/sanitize_complexity.rs docs no longer name the
failure text as a strip site.
Spec (docs/design/BUSBAR-1.6.0.md): Law 11, 2126 and 2131-2132; product hard rules 3369-3373.
The structured-output check it reported is deleted (34df85d), so nothing emits the code. Retired the way the catalogue retires a code (busbar-kernel diagnostics 8013): the number and slug are kept, `retired: true`, the title marked RETIRED, the summary says what was removed and why, the action is "Nothing emits this code." The plane's declares.json carries the same title/summary/action (src/tests/diagnostics_tests.rs the_declares_file_states_the_catalog holds it to the constant). docs/diagnostics-mcp.{md,json} edited by hand in the kernel renderer's exact form (render_markdown_for appends " *(retired)*" to the heading; render_json_for writes "retired": true): no committed-page test exists for the mcp page. Spec (docs/design/BUSBAR-1.6.0.md): Law 11, 2131-2132; 1930.
…used as ask_unavailable naming the clock (finding 11) The relayed state's seal is one function, seal_relayed, shared by relay_ask and the task relay (behaviour unchanged: a clock failure still answers as NoSealer). The plants call it with each site's window and assert the refusal reason ask_unavailable and the CLOCK_UNAVAILABLE words; both fail by assertion (spec 3295-3370, fail closed).
…y text (findings 10, 11) A clock.now failure answered as NoSealer, whose text says the deployment has no auth.signing_key. busbar's own ask and a relayed state presented to decide_ask now refuse with ClockUnavailable (reason word ask_clock_unavailable); a relayed ask and a task relay's park refuse with Unavailable (ask_unavailable) carrying CLOCK_UNAVAILABLE. Still fail closed, never the epoch (spec 3295-3370).
… rules are deleted (finding 4) plane-mcp finding 4 [HIGH]: the argument SSRF guard decided in the plane (argguard::judge_host over busbar_contract::net::host_is_cloud_metadata / is_alternate_ipv4_encoding / host_is_private_or_loopback under the registration's allow_private) and never asked the host. - argguard.rs: judge_host now asks the host's ONE destination judge for every host the walk finds (an IPv6 literal bracketed, as dest.judge reads host[:port]) and renders its DEST_* verdict; a host that gives no verdict refuses the value (fail closed). SsrfPolicy and the local host rules are deleted; the walk, the scheme allowlist and the host reader stay. - call.rs: the guard leaves admit_trusted (and its allow_private parameter) for judge_arguments, run on the admitted call, so the door's ask seal and the judge never hold the unit's handle counter at once. The refusal bytes are unchanged: 403, JSON-RPC -32000, data.reason tool_argument_refused, the same call-log line. - tool_door.rs: dest_verdict asks dest.judge (Services::dest_judge_as) under the deployment's default egress class, DEST_REFUSE_PRIVATE unless the registration granted allow_private, name only (never DEST_RESOLVE, so it never pends), each on a fresh handle of the unit's ticket. - door_tasks.rs: the task path's re-judgement of the arguments asks the same judge. - tests: argguard_tests judge through a stand-in host over the contract's address predicates; call tests prove the host's verdict alone decides, each host is asked once for the called tool, and a host with no verdict refuses. Spec (docs/design/BUSBAR-1.6.0.md): 5140 (Appendix C B.3 item 11: argument-embedded URL judging is dest.judge, called where it is called today), 5111 (the B.2 row: dest.judge may pend: no), 1579 (the dest.judge row: judged against the egress rules, allow-list, class, metadata hosts), 614-621 (every outbound check in ONE place; every scattered check deleted in its favour), 4945 (class 0 of dest.judge is the deployment's security section).
…d-frame lease skips the frame's rest; Frames reads a message once Three plants, each failing on the unfixed tree by assertion (all compile against it): - finding 5 [HIGH, SECURITY] (src/tests/tool_program.rs each_callers_ask_reaches_only_that_caller): two callers A and B on one stdio child, the child asks each for a sampling. The stand-in door keeps the child's line of relayed calls (`open`, arrival order). First-reader-wins (`peer.claim`) hands A's ask to B's exchange, which reads it first. Asserted: A's ask reaches only A, B's only B, neither is refused; an ask raised with no call in the line is refused once (`ask_unattributed`). Law 11 (spec 2125-2132): an ask is relayed down the session it belongs to, as-is. - finding 13 [MEDIUM] (busbar-core-connector program_tests a_lease_opened_mid_frame_reads_from_the_next_frame): a lease opened while the program is part way through one frame reads that frame's rest as its first bytes. The test framer gains a `piece` knob so one frame arrives in several pieces, as the stdio line framer cuts a line its sink cannot hold. - finding 14 [MEDIUM] (tests/alloc_gate.rs a_childs_message_read_in_small_pieces_is_parsed_once): a 2000-item message pushed in 64-byte pieces; Frames::push re-parses everything held per piece, so the allocation count is quadratic. Asserted: under 20 per item. The no-blocking rule (spec 1276-1283): bounded work on the worker.
… tests stay deleted (finding 12); predev's refusal_words class table lived only in that unserved impl, so the xtask refusal-collapse scan drops its mcp row
…gotiator, through the door tests/sessions.rs drives the MCP door over its own ABI under stand-in host services and a stand-in connector (two upstreams: one that requires a session, one stateless) and asserts THE DESIGN section 2 mcp bullet (docs/design/BUSBAR-1.6.0.md lines 379-390), finding 1 of the plane-mcp audit: - a 2025-06-18 initialize answers its revision and an Mcp-Session-Id of 128 bits; a request in that session is served and lowered (no stateless-only members); - a session presented by another owner is 404 on POST and DELETE; the owner's DELETE ends it; - a GET naming no revision opens the 2024-11-05 stream with its endpoint event, a message POSTed to the address is 202 and answered on the stream; a GET naming a revision, a plain GET and a DELETE with no session are 405; - an upstream that refuses the stateless revision and requires initialize is reached by negotiation (and re-initialised when it forgets the session); a stateless upstream sees the stateless request exactly as the dialect builder writes it, with no handshake; - subscribe stays for the old revisions (line 380): a 2025-06-18 session's resources/subscribe hears the announcing upstream's notifications/resources/updated on its GET stream. Fails on the unfixed tree by assertion: every non-POST claim is refused 405 and initialize is refused -32602. src/tests/session_tests.rs plants finding 18: a push whose trim frees more than its own cost underflows `cost - freed` (tool_sessions.rs push), a panic under overflow checks.
…oth directions; the session table's byte account never underflows
Finding 1 (plane-mcp, HIGH). THE DESIGN section 2, the mcp bullet (docs/design/BUSBAR-1.6.0.md
lines 379-390), wired on the served path through a new door child, src/door_sessions.rs:
- Inbound on the endpoint's three claims: a POST carrying the stateless marker, or naming neither
a session nor initialize, is the stateless path untouched (adapt::classify_post); initialize
opens a session in the revision negotiation picks (revision::negotiate; no revision config), its
id 128 bits from the host's random.fill, bound to its owner {principal, credential} = the
kernel's caller reference (derived from the key id) or "<ungoverned>"; any other owner, unknown,
ended or expired id is 404 on every path (POST, GET, DELETE, the 2024-11-05 message address);
a dispatched session method is raised (adapt::raise, with the header and parameter mirror) into
the one dispatch and lowered after it (adapt::lower_result); an MCP-Protocol-Version that
disagrees with the session is 400; DELETE ends the owner's session.
- GET: with a session, that session's event stream (resumed after Last-Event-ID from the table);
with none and no MCP-Protocol-Version, the 2024-11-05 stream whose first event names the message
address (adapt::endpoint_event), its answers delivered on the stream and its POSTs answered 202
(revision::sessionless_get); a GET naming a revision, a GET accepting no event stream and a
DELETE naming no session are 405 (lines 387-390). The streams are held as K6 sessions on the
door's existing drive/tick machinery (door_listen).
- Sessions are this process's; per-owner session and byte quotas are tool_sessions::Bounds; the
ungoverned chain's single owner isolates nothing, and the instance's first 2024-11-05 stream
says so once as the declared diagnostic mcp-legacy-stream-ungoverned (lines 385-387).
- Subscribe stays for the old revisions (line 380, lane-lead ruling): a session's
resources/subscribe, resources/unsubscribe and logging/setLevel are answered from its own state
under a per-owner subscription cap; an upstream's notifications/resources/updated (on the event
stream it answers a relayed call with, or from a stdio child) reaches every watching session's
GET stream after the subscriber's entitlement is re-asked; its notifications/message reaches the
caller's session past that session's level floor. initialize declares resources.subscribe and
logging on every session revision.
- Outbound (client::negotiate on the served path): the walk's first hop is the stateless request
byte for byte as before (the predev fence), or, to an upstream busbar holds a session with, that
request lowered into it; a refusal moves the client ladder over the door's own connector
(initialize, notifications/initialized, the lowered request), and a session the upstream forgot
(404) is re-initialised once. Verify-on-call's tools/list negotiates the same way. An upstream
that answered the stateless revision is remembered (tool_sessions::UpstreamTable) and never
renegotiated; 401/403/407/429 are not revision signals. The ladder's last rung (the 2024-11-05
event stream as a client) is not carried: an upstream that refuses both the stateless request and
initialize fails the call with an upstream-failure answer naming the revision.
- The line carrier's initialize negotiates too (line.rs): a session revision a client asks for is
answered in it and the carrier session's following lines are raised and lowered.
- The prose that claimed ONE revision is rewritten (codec.rs PROTOCOL_VERSION, checks.rs
SUPPORTED_PROTOCOL_VERSIONS, which stays the stateless dispatch's data.supported so the stateless
path's bytes do not move, door.rs 405 words, docs/mcp.md).
Finding 18 (LOW): tool_sessions push releases freed - cost when a trim frees more than the pushed
event cost, instead of underflowing cost - freed.
An unconfigured node claims no /mcp route, so its GET /mcp stays the 1.5.5 404.
…initialize negotiation (Era::Opened, the raised line) re-applied on L's carrier, which refuses logging/setLevel and resources/(un)subscribe with -32601 and declares neither in any revision it speaks; the subscription uri bound moves to door_sessions.rs, where the session revisions keep subscribe
…a lease reads whole frames; Frames reads each byte once Finding 5 [HIGH, SECURITY]: a request over stdio names no call it serves (no related-request id on the wire; the progress token is never echoed; the child numbers its own ids), so the door makes attribution unambiguous by SERIALISING (lane-lead ruling): calls to a member whose grants let its asks be relayed reach its child one at a time, in arrival order (door_program `Relaying` line, `in_line`); a call behind waits (Step::Wait, woken when the call ahead leaves, or at its own `timeout:` deadline, then answered as an upstream failure, never sent: `take_turn`). A call whose asks went to its caller holds its place until its retry takes it up or the ask's state TTL lapses (`ProgramRelay::hold`). The ask is the call's first in line (`first_in_line`, `Peer::owner`, decided once by the first exchange to read it); an ask raised with no call in line is refused once (`-32001` `ask_unattributed`). Law 11, spec 2125-2132. Finding 13: busbar-core-connector program.rs: a lease opened mid-frame skips that frame's rest. Finding 14: tool_program.rs Frames scans only new bytes and parses a value once, when whole (spec 1276, 1283).
…te.rs keeps the counting allocator and finding 14's stdio plant; its decode pin drove the deleted unserved Plane impl (finding 12) and goes with it
…ix-passthrough: call.rs's settle doc says both an ask and a result are relayed
…d ahead) into lane-mcpfix-deadplane: purity keeps both source scans (no satisfier of an upstream's ask; no cross-plane route or unserved plane impl)
|
Merged up with #616 (ask) and #615 (sampling), which sit ahead of this PR in the predev merge queue and touch the same files (call.rs, purity.rs, door_tasks.rs), so this PR stays clean when they land. Resolutions: call.rs's settle doc says both an ask and a result are relayed; purity.rs keeps both source scans; the relayed task leg opens the retry's Proof of the combined tree: Latchkey cli-af296ba4-4bea-4bc7-8d9b-22c41bf0b838 (every mcpfix group plus ask and sampling; identical on all touched paths to the bounds head): RED on 6d297d1 failed 3 of 3 by assertion; GREEN-EXIT=0 for fmt --check, clippy -p busbar-kernel -p busbar-llm -p busbar-plane-mcp --all-targets -D warnings, cargo test -p busbar-kernel / busbar-llm / busbar-plane-mcp, cargo build -p busbar --bin busbar, busbar plane_host_universal_purity and plane_node. |
Pull request was converted to draft
promote into
|
|
Closed: superseded by #703 (the combined MCP-FIX stack carries this piece). Branch kept. |
MCP-FIX group D (stdio): plane-mcp audit findings 5 [HIGH, security], 13 and 14.
This is
lane-mcpfix-stdiowith its fix commit'swip:prefix dropped (no force-push: pushed aslane-mcpfix-stdio-v2; the RED is 11b3ca7, pushed aslane-mcpfix-stdio-v2-red;origin/lane-mcpfix-stdio-red9754c46 is the superseded refuse-when-two-open design).Finding 5 (security): a request over stdio names no call it serves (no related-request id on the wire; the progress token is never echoed; the child numbers its own ids), so a child's ask could reach the wrong caller (first reader wins). Ruling applied: calls to one stdio child whose asks may be relayed are SERIALISED per child, in arrival order (door_program
Relayingline,in_line;take_turnin tool_door.rs). A call behind waits (Step::Wait, woken when the call ahead leaves, or at its owntimeout:deadline, then answered as an upstream failure, never sent). A call whose asks went to its caller holds its place until its retry takes it up or the ask's state TTL lapses (ProgramRelay::hold). The ask belongs to the first call in line (first_in_line,Peer::owner, decided once by the first exchange to read it). An ask raised while no call is open is refused (-32001ask_unattributed).Finding 13: busbar-core-connector program.rs: a lease opened mid-frame skips that frame's rest and reads from the next frame.
Finding 14: tool_program.rs
Framesscans only new bytes and parses a message once, when whole (linear, not quadratic, in a message read in small pieces).Open points (the wire's limits, stated)
timeout:.Spec (docs/design/BUSBAR-1.6.0.md): Law 11, 2125-2132 (an ask is relayed down the session it belongs to, as-is); the no-blocking rule, 1276-1283 (bounded work on the worker).
RED plant: 11b3ca7:
tool_program::tests::each_callers_ask_reaches_only_that_caller(finding 5), tests/alloc_gate.rsa_childs_message_read_in_small_pieces_is_parsed_once(finding 14), busbar-core-connectorprogram::tests::a_lease_opened_mid_frame_reads_from_the_next_frame(finding 13). Each compiles against the unfixed tree and fails by assertion.Merges (landing order): origin/predev, lane-mcpfix-deadplane (#638: tests/alloc_gate.rs keeps the counting allocator and the finding-14 plant; its decode pin drove the deleted Plane impl), lane-mcpfix-sessions-v2 (#640; both touch door_program.rs, tool_program.rs, tool_door.rs).
Proof: Latchkey cli-56d97d32-ba80-4936-87c9-ff577ec079ab on e066934: RED-EXIT=1 (PLANT-RED finding5, finding14, finding13); GREEN-EXIT=0: fmt --check, clippy -p busbar-plane-mcp -p busbar-core-connector --all-targets -D warnings, cargo test -p busbar-plane-mcp, cargo test -p busbar-core-connector (lib, no_thread_per_connection, no_unix_carrier, off_task_reactor;
universal_needsneeds the hop's dlopen cdylibs and is left to CI), cargo build -p busbar --bin busbar.Includes #634, #637, #638, #639 and #640 by merge.