1e6daa5c73
CI / build (push) Successful in 1m13s
CB-505 claimed authorization is "enforced on both entry paths". It is — but only REST was ever tested. Coverage showed BridgeMcp.deny(), principal(), callerTerminal(), worktreeRequest() and every tool-registration lambda at ZERO executed lines: no test had ever constructed a BridgeMcp, because the existing BridgeMcpTest calls only the static handler methods. So the MCP half of the security control had ten REST tests' worth of nothing behind it. An unexercised security control is a claim, not a control. Made testable by separating policy from plumbing rather than by reaching for a mocking library the project does not use: - denyFor(Principal, Action, target) is the decision — testable directly. - deny(exchange, ...) shrinks to pulling the caller out of the SDK exchange. - principalFrom(role, terminal, pid) extracts identity reconstruction from McpSyncServerExchange, an SDK type with no fake available. Moved the `authz == null` enforcement switch OUT of the exchange-facing wrapper and INTO denyFor. Found by a failing test: as written, any future tool calling denyFor directly would have silently skipped the gate. The switch now lives with the decision it governs. New BridgeMcpAuthzTest constructs a real BridgeMcp — which is why coverage moved so far, since that also runs the constructor and all the tool wiring — and pins the table on this path: primary orchestrates, worker cannot; worker replies only as itself; the primary cannot forge a worker reply; anonymous gets nothing; and 401-shaped vs 403-shaped refusals are counted apart. Verified as real controls, not decoration: with the gate forced open, 5 of the 9 fail. 335 tests (was 326).