QA run · ai.mcp-stdio-fail-closed (1/1) · fb82f9b1 · 2026-10-16 · 1 PASS / 0 PARTIAL / 0 FAIL / 0 BLOCKED / 0 NOT-RUN #23995
Description
Run record for the re-run #23950 asks for, after its six stale clauses were re-pointed. Runner: an os-dev subagent dispatched by the domain:devx seat 2 PM (session session_016PVjjuVPte5XEJvaQx1E4t). Protocol: .claude/skills/checklist-test/SKILL.md + docs/qa/platform-checklist/RUNNER.md. Executed sequentially (no subagent tool in this runner).
Environment fingerprint
- framework:
fb82f9b180bcc6af04ce8d36153fd52d2eb6845c, the tip of branchclaude/issue-23950-checklist-stale-clausesat run time. Its product tree isorigin/main022c6e799; the branch addsdocs/qa/platform-checklist/areas/*.jsononly. Built withpnpm install --frozen-lockfile && pnpm build(75/75 tasks green). .objectui-sha:daeb77fc84dd824e74c95b87cdede85afdb248e7(this item drives no console).- host:
os start -p 47311 -d file:(run dir)/ai/data.dbfromexamples/app-showcase, withOS_PORT=47311. The personas were provisioned first onos dev --ui --seed-adminover the same port and DB, and that server was stopped before the stdio legs. - personas: the seeded admin; a contributor-bound member and a client_liaison-bound member, each minted by invite + sign-up and placed through
POST /api/v1/data/sys_user_position; oneosk_key each throughPOST /api/v1/keys. The contributor owns one invoice of its own (POST /api/v1/data/showcase_invoice). - stdio client: line-delimited JSON-RPC that writes nothing until the
principal-boundline is in the log (see the note under the verdicts). - timestamps: 2026-10-16, 16:21Z to 16:26Z.
Scope
Selector ai.mcp-stdio-fail-closed resolves to 1 runnable item: ai.mcp-stdio-fail-closed at revision 5, P0, surface cli. Revision 5 is the revision #23950's branch carries. The item text was read at the branch head c4bf2ba4e. After the run, revision 5's clause 1 and clause 6 dropped their quotation of the revocation message's wording. That edit unpins wording only; the code, status and no-row evidence below is what both clauses score.
Tier 1 pin (RUNNER rule 6): pnpm --filter @objectstack/mcp exec vitest run --maxWorkers=2 src/__tests__/plugin.test.ts: 1 file, 17 passed. The pin covers the keyless and unknown-key refusals and the stdio-off boot. It does not cover the per-call revocation or the lifecycle arm of the up-front admission, so those were driven by hand.
Per-clause verdicts
| item | # | clause (short) | verdict | evidence |
|---|---|---|---|---|
| ai.mcp-stdio-fail-closed | 0 | enabled + keyless refuses to serve | pass | exit 1; [MCP] The stdio transport is enabled (OS_MCP_STDIO_ENABLED / autoStart) but OS_MCP_STDIO_API_KEY is not set. … Refusing to start an unscoped stdio server (ADR-0101).; health probe after exit: connection refused |
| ai.mcp-stdio-fail-closed | 1 | unknown and lifecycle-refused keys rejected up front | pass | osk_unknown: exit 1, [MCP] OS_MCP_STDIO_API_KEY did not resolve to a valid identity (unknown / revoked / expired / owner-less). Refusing to start stdio (ADR-0101).; the key revoked in clause 6, restarted: exit 1, [MCP] OS_MCP_STDIO_API_KEY is refused (API_KEY_REVOKED): … Refusing to start stdio (ADR-0101).; neither process serves |
| ai.mcp-stdio-fail-closed | 2 | valid key binds a real principal, logged | pass | [MCP] stdio transport principal-bound to OS_MCP_STDIO_API_KEY identity (the contributor's user id) (RLS/FLS/tenant applied); the id equals the contributor's sys_user id |
| ai.mcp-stdio-fail-closed | 3 | MCP rows/fields equal the member's REST rows/fields; admin sees all | pass | RLS (contributor, showcase_invoice): MCP query_records and REST list both return exactly the contributor's own invoice, with identical field sets; get_record of INV-1003 (another owner) is an error on MCP and a 404 RECORD_NOT_FOUND on REST; the admin gets 13 rows including INV-1003 on both surfaces. FLS (client_liaison, showcase_project): MCP and REST both return 5 rows with identical ids and field sets, and budget / spent / budget_remaining are absent; the admin's rows carry budget |
| ai.mcp-stdio-fail-closed | 4 | member aggregate equals the member's REST aggregate, not the admin's | pass | count by status: MCP member [{status: draft, n: 1}] = REST member [{status: draft, n: 1}]; REST admin draft 3 / paid 5 / sent 5; MCP admin agrees with REST admin |
| ai.mcp-stdio-fail-closed | 5 | aggregate over an FLS-masked field fails closed | pass | as client_liaison: sum(budget), count grouped by budget and count_distinct(spent) each answer isError with PERMISSION_DENIED and [Security] Field read denied: not permitted to aggregate [budget] on 'showcase_project' (spent for the third); no number returned; REST sum(budget) 403 with the same message; control count by status: 4 groups |
| ai.mcp-stdio-fail-closed | 6 | revocation honored on the next call, fail-closed, API_KEY_REVOKED | pass | same live session: pre-revoke query_records returns 1 row; admin PATCH /api/v1/data/sys_api_key/KEY_ID {"revoked": true} 200; next query_records answers isError with content {"error": {"code": "API_KEY_REVOKED", "status": 403, "message": …}} and no records key; get_record likewise; REST with the same key 403 API_KEY_REVOKED |
| ai.mcp-stdio-fail-closed | 7 | stdio off: no key demanded | pass | neither variable set: Server is ready, and [MCP] Transport not auto-started (HTTP is served per-request at /api/v1/mcp regardless). … |
Derived item verdict
ai.mcp-stdio-fail-closed r5: pass (8 of 8 clauses).
Fixture notes, not defects of this item:
showcase_invoicehas nonumberfield; its invoice number isname, and a create carryingnumberis refused 400 INVALID_FIELD.- An MCP client that writes
initializethe moment it spawnsos startgets a boot that never loaded the config.os startspawnsservewith stdio inherited and no positionalconfigargument, and the CLI's argument parser reads a pending stdin line as that argument. The boot then saysNo objectstack.config.ts found — booting from artifact (default host)…, the showcase boot exits 1, and the client'sinitializeis never answered. Withobjectstack.config.tspassed explicitly toserveand the same early write, the config loads. This run's client waited for theprincipal-boundline before writing, so no verdict above is affected. The defect is handed to the dispatching seat for its own card.
Timing: the build was shared across this re-run's five records (pnpm build 8m19s under the verify lock). This item took about 5 minutes: 1 minute of provisioning boot and 4 minutes of legs.
Checklist-accuracy findings, if any, close out on the wave anchor #23878; nothing is extracted from this record by its runner (the early-write defect above goes to the dispatching seat).
Activity
objectstack-fleet commentedon Oct 17, 2026
Close-out (
domain:devxseat 2): every clause PASS; nothing to extract. The re-pointed item landed with #24000 (e18b23fac5), Fixes #23950.Generated by Claude Code