Skip to content

fix(mcp,contract): scope lease_status to this session; restore the §1 boundary test - #103

Open
V3RON wants to merge 5 commits into
fix/0003-09-error-parityfrom
fix/0003-10-mcp-hygiene
Open

fix(mcp,contract): scope lease_status to this session; restore the §1 boundary test#103
V3RON wants to merge 5 commits into
fix/0003-09-error-parityfrom
fix/0003-10-mcp-hygiene

Conversation

@V3RON

@V3RON V3RON commented Sep 3, 2026

Copy link
Copy Markdown
Contributor

Stacked on #102. Three blocking defects from the adversarial review, plus the dead-code cleanup ADR §11 implies.

lease_status reported a lease the session does not hold

The handler's doc-comment claimed lease.list returns at most one entry because "this session only ever requests mode: "held" leases under one requester id". That is not what lease.list does — the dispatcher filters by owner principal only, with no mode or connection filter — and the handler took leases[0] blindly.

With SIMLOCK_AGENT_ID=agent-7, the CLI and MCP share a principal. An agent running simlock lease --detach in a shell and then calling MCP lease_status got {held: true, mode: "detached"} for a lease its connection never requested and will not release on close. The cache-based implementation this stack deleted got this right.

lease_status still calls lease.list unconditionally — no cache of lease state returns, which is what §11 removed — but the session now tracks only the id of the lease its own lease() call obtained, cleared on release and on the lease-lost push, and filters to mode === "held" and that id. The false doc-comments are corrected.

The boundary test enforcing §1 was blind to multi-line imports

boundary.test.ts kept only lines matching ^\s*import\s and looked for from "…" on that same line. The repo's own formatter breaks any import with more than one named binding across lines — so for exactly the style this project writes, the import { line has no from and the } from "../core/index.js"; line was never examined at all.

The single mechanism enforcing ADR §1's central constraint — "the contract imports nothing from core, daemon, or drivers … this is what keeps private types out of the public package surface" — would have passed a multi-line import { DeviceRecord } from "../core/index.js"; in schemas.ts.

Now it strips comments and scans the whole file, matching the approach no-core-leak.test.ts already used correctly. The fix was demonstrated: a multi-line core import was temporarily added to schemas.ts, the suite failed as it should, and the import was reverted.

Gates that were failing, both clean on main

  • README.md had three JSON examples collapsed onto single lines, failing format:check. Reformatted — and while verifying them against the current schemas, the progress push example turned out to be wrong: the CLI emits it flattened, with no nested progress key. Fixed.
  • fallow reported dead exports left over from the hand-written parsers §11 says are deleted.

The daemon-client package is deleted

Chasing those dead exports showed the whole of src/daemon-client/ was referenced only from comments — src/simlock-client/ supersedes it entirely. Six modules and their tests are removed, so §11's "raw parsers and hand-built payloads deleted" is finally true rather than approximately true. A fallow suppression that went stale when queueDepth moved behind the dispatcher goes with them, and the comments naming the deleted module are repointed.

After this the fallow gate reports zero dead code and one fewer duplication group.

lease.list filters by owner principal only, with no mode or connection
filter, so it could return a held/detached lease this MCP connection
never requested (e.g. the CLI's --detach lease under the same
SIMLOCK_AGENT_ID principal). lease_status took leases[0] blindly,
reporting a foreign lease as held and never releasing it on close.

Track the id of the lease this session's own lease() call obtained
(not a cache of its state -- always re-read via lease.list) and filter
status() to mode === "held" && id === that tracked id. Correct the
false doc-comment claiming leases holds at most one entry.
The stack collapsed three JSON code examples onto single lines, which
oxfmt --check rejects. Re-run oxfmt to restore the multi-line
formatting CI enforces, and correct the "progress" push example: the
CLI emits {push:"progress", ...progress} flattened (no requestId, no
nested "progress" key), not the wrapped shape the example showed.
parseRawLeaseLost and parseRawLeaseProgress (and their RawLeaseLost/
RawLeaseProgress types) are leftovers of the hand-written push parsers
ADR 0003 §11 replaced with the typed contract client -- fallow flagged
both as unused exports, and neither is referenced anywhere in the
repo, including this file's own test.
The test kept only lines matching ^\s*import\s and extracted "from"
from that same line. The project's formatter breaks any import with
more than one named binding across multiple lines, so for such an
import the "import {" line has no "from" and the "} from '...'" line
is never in scope at all -- the one mechanism enforcing ADR 0003 §1's
"the contract imports nothing from core/daemon/drivers" was blind to
the import style this repo actually writes.

Strip comments and scan the whole file text for from "..." specifiers
instead, the way src/simlock-client/no-core-leak.test.ts already does.
Verified by temporarily adding a multi-line `import { ... } from
"../core/index.js"` to schemas.ts: the old logic missed it, the new
logic fails on it.
The typed client in src/simlock-client supersedes it entirely; nothing outside
the package imported it any more, so ADR 0003 §11's "raw parsers and hand-built
payloads deleted" is only true once these six modules go with them.

Also drops a fallow suppression that went stale when queueDepth moved behind the
dispatcher, and repoints the comments that named the deleted module.
@V3RON
V3RON force-pushed the fix/0003-10-mcp-hygiene branch from 04d588f to d6f0815 Compare September 3, 2026 17:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant