Skip to content

fix(gateway): close N34 round-2 follow-ups (route-inventory ratchet, discovery reclassified) (#421 follow-up) - #540

Open
LamaSu wants to merge 1 commit into
fix/n34-no-fabricated-routesfrom
fix/n34-followups
Open

LamaSu wants to merge 1 commit into
fix/n34-no-fabricated-routesfrom
fix/n34-followups

Conversation

@LamaSu

@LamaSu LamaSu commented Oct 3, 2026

Copy link
Copy Markdown
Owner

Summary

Follow-ups to #421 (N34, no fabricated routes), from its round-2 cross-family verdict (rm-n34-421-r1a-r2-79e62add, SHIP), which tracked two MEDIUMs. This PR is stacked on #421's branch; retarget it to master after #421 merges.

MEDIUM A: the round-1 ratchet was easy to evade.

  • The gap: it examined only specially named, first declarators at module scope. A new route returning an inline fixture with a 200, such as app.get("/api/marketplace/new", async () => [{ id: "fixture" }]), passed both the ratchet and the refused-route documentation check.
  • The fix: a structural route inventory. Every GET route in an N34 family is probed with demo mode off and must be either refused (501, and documented as refused) or pinned as live in LIVE_GET_ALLOWLIST. A new live GET anywhere in those files fails the test until someone deliberately pins it.
  • Reproduced first: with the reviewer's inline-fixture route injected, the old tests passed and the new test fails. The file was restored and checked with cmp.

MEDIUM B: the agent context pack misclassified the discovery routes.

  • The gap: POST /api/discover/scan and /onboard were listed as demo-only. In fact they return real discovered devices whenever this gateway can run mDNS, and fall back to 501, or to marked examples in demo mode, only when it cannot.
  • The fix: the pack lists them under a new "Conditionally Available Routes" section, with a note that says exactly that.
  • Reproduced first: the pinning test failed at 79e62add.

Tests

  • Changed: context-pack-availability.test.ts and n34-routes-fabrication-ratchet.test.ts, 18 tests.
  • Mutation check: reverting context-pack.ts fails 4 tests.
  • The full gateway suite at this head: 222 files, 3759 passed, 6 skipped. tsc --noEmit is clean.

Agent: pcc-readmodels (c255d7dc).

🤖 Generated with Claude Code

https://claude.ai/code/session_01Sbd5dpwvmRsJqdff9zvNW6

…discovery reclassified)

Two MEDIUM follow-ups from the SHIP verdict on PR #421
(rm-n34-421-r1a-r2-79e62add.astra.verdict.md, lines 9 and 11).

MEDIUM A: the fabrication ratchet is easy to evade. Its source scan only
counts module-scope fixture-named declarators, and the context-pack
fallback only records routes that already refuse (501). A new GET route
serving an inline fixture literal with 200 is invisible to both.
Reproduced: adding
  app.get("/api/marketplace/new", async () => [{ id: "fixture" }])
to marketplace.ts left all 14 existing N34 tests green. Fixed with a
structural, behavior-based check in context-pack-availability.test.ts: for
each of the ten N34 route families, probe every registered GET with demo
off and pin the exact set that answers live (not 501 not_available), the
same shrink-only/name-pinned shape as the fabrication ratchet's own
ALLOWLIST, applied to routes instead of fixture bindings. A new live GET
in one of these families — real data or fixture alike — now fails until a
reviewer explicitly allowlists it. Confirmed the new test catches the
reproduction and passes once the probe route is removed; reworded the
ratchet's header comment (it claimed the other checks covered inline
literals, which they do not).

Scope the structural check does not cover (documented in both the new
test's comment and the ratchet's reworded header): a new POST/PUT/PATCH/
DELETE route serving a fixture with 200, a live GET added to an N34 route
file outside the ten probed here, and an existing route's handler
rewritten in place (same path, a real read swapped for a literal).

MEDIUM B: the agent context pack blanket-listed
POST /api/discover/scan and POST /api/discover/onboard under
demoOnlyEndpoints ("501 unless demo mode"). In fact discover.ts returns
real discovered devices whenever this gateway's mDNS works, regardless of
demo mode, and only falls back to refuse/example behavior when discovery
is unavailable (discover.ts:268-283, :305-318) — a gateway-network
condition, not a demo-mode one. Reproduced: a pinning test asserting the
pack classifies these two routes as conditionally live (not demo-only)
failed against 79e62ad on all three assertions (listed under
demoOnlyEndpoints; no conditionallyLiveEndpoints field; markdown placed
them inside the demo-only block). Fixed by moving both routes to a new
CONDITIONALLY_LIVE_ENDPOINTS list with accurate wording matching the MCP
tool descriptions (pcc_discover_scan/onboard already stated the
condition correctly), exposed as pack.conditionallyLiveEndpoints in the
JSON pack and a new "## Conditionally Available Routes" markdown section
placed after the Data Types block so it doesn't fall inside either of the
existing demo-only slice checks. Updated the reviewer-r1b/r1a F-E test
(refused subseteq documented) to also accept conditionallyLiveEndpoints,
since this test sandbox has no working mDNS and so observes the same 501
a true demo-only route would — the pack's conditional note, not a
blanket refusal claim, is what an agent actually needs here.

Verification: new/changed tests green (18/18 in the two touched test
files); full gateway suite and tsc clean at the commit (see
n34-followups-gw-full.log); mutation check on both fixes (revert on a
file copy, confirm the relevant new test fails, restore, cmp clean).

Reproduction log: n34-followups-repro-79e62add.log. Review brief:
n34-followups-brief.md.

subagent: implementer-hotel
agent: pcc-readmodels (c255d7dc)

This branch has not been deployed

No deployments
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