LearnNewsExamplesServices
Frontmatter
id16343
titleFail the unit suite when an MCP handler signature silently degrades
stateClosed
labels
enhancementaitesting
assigneesneo-kimi-iris
createdAtAug 2, 2026, 1:41 PM
updatedAtAug 2, 2026, 3:29 PM
githubUrlhttps://github.com/neomjs/neo/issues/16343
authorneo-kimi-iris
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[x] 16334 Census: which MCP handler signatures silently degrade, read from the handlers not the contracts
blocking[]
closedAtAug 2, 2026, 3:29 PM

Fail the unit suite when an MCP handler signature silently degrades

Closed Backlog/active-chunk-11 enhancementaitesting
neo-kimi-iris
neo-kimi-iris commented on Aug 2, 2026, 1:41 PM

Context

Filed as the AC5 follow-up of #16334. The handler-signature census (ai/scripts/diagnostics/mcpHandlerSignatureCensus.mjs) answered "is this mechanically checkable?" with yes, by construction: the classification IS mechanical, and the instrument already carries a --fail-on-defects lint mode. What does not exist yet is the gate that runs it on every PR.

Receipts from the census lane (both reproducible with --root <worktree>):

  • Pre-fix tree (dfdb0fc4dc, parent of the #16330 merge): defect set = 2 — get_ingestion_progress (class 2, destructure-under-positional) and get_pull_request_diff (class 3, truncation: 1 param for 4 contract args); starting set = 9 (independently reproduces the contributor's contract-side count from #16250).
  • Current dev: defect set = 0, starting set = 7 (9 − the 2 now-annotated), suspects = 0, unresolved = 0, class 1 = 159.
  • --fail-on-defects exits 1 on the pre-fix tree, 0 on current dev.

The Problem

The bug class #16250 proved is re-fileable today: a contributor adds an operation to any of the six MCP servers whose handler destructures an object (or declares fewer params than the contract) without x-pass-as-object, and every existing check stays green — the contract validates, the tool lists, calls that pass only the first argument "work" (defaults win, extras dropped). The two production instances were found by hand, weeks apart. The census makes the class mechanically detectable; without a gate, detection stays a manual ritual that must be remembered.

The Architectural Reality

  • Dispatcher: ai/mcp/ToolService.mjs:107-112 — positional spread of argNames unless x-pass-as-object: true.
  • Instrument: ai/scripts/diagnostics/mcpHandlerSignatureCensus.mjscensus(root) returns classified rows for all 159 operations; --fail-on-defects is the CLI tripwire. Pure static analysis (yaml + acorn), no server boot, no network — unit-suite-priced.
  • The named home (from #16334): test/playwright/unit/ai/mcp/validation/OpenApiValidatorCompliance.spec.mjs — the existing contract-vs-implementation compliance spec. A sibling spec file in the same directory is equally in-pattern (GuideToolParity.spec.mjs precedent).

The Fix

Add a unit spec under test/playwright/unit/ai/mcp/validation/ that executes census(repoRoot) and asserts the gate conditions (ACs below). The standard unit config (playwright.config.unit.mjs) already runs in CI, so the gate inherits enforcement with no new workflow surface.

Gold standard rejected: a dedicated CI workflow step invoking the script's CLI. Heavier (new workflow file, new failure surface outside the suite agents already run locally), and it splits "did the contracts validate" (suite) from "do the handlers match the dispatch" (workflow) across two places a contributor must check. The suite is the existing home for contract-vs-implementation checks; keep the family together.

Acceptance Criteria

  • A spec under test/playwright/unit/ai/mcp/validation/ runs census() over all six servers and fails when the defect set (classes 2, 2M, 3) is non-empty, printing the defect rows as the failure message.
  • The same spec fails when unresolved > 0 — a census that cannot see a binding is not a green census.
  • Negative receipt: the spec (or a sibling fixture) proves the gate trips on the two known-positive shapes — a destructure-under-positional fixture (class 2) and a truncation fixture (class 3) both fail the assertion. The instrument's own fixture spec (mcpHandlerSignatureCensus.spec.mjs) already pins both shapes; this AC is about the GATE firing, not the classifier.
  • Runs green on current dev (defect set is empty) and inside UNIT_TEST_MODE=true npx playwright test -c test/playwright/playwright.config.unit.mjs.

Out of Scope

  • Repairing any operation a future red gate names — each repair is a live tool-surface behavior change with its own ticket and review (per #16334's Out of Scope).
  • Changing the dispatcher, the contracts, or any handler.
  • Extending the census taxonomy (suspects stay human-judged; the gate fires on classes 2 / 2M / 3 and unresolved only).

Related

  • #16334 — the census (delivers the instrument; this ticket is its AC5 follow-up)
  • #16250 / PR #16330 — the two proven defect instances and their fix
  • #16231 — established that a missing annotation can break a call

Live latest-open sweep at 2026-08-02T11:46Z (20 newest open issues) + A2A in-flight claim scan (30 newest): no equivalent found.

Origin Session ID: session_5c970912-b750-4835-ad51-fbb3d2bc4ebe

Retrieval Hint: query_raw_memories("handler signature census lint gate fail-on-defects six servers")

tobiu referenced in commit be2c331 - "test(ai): the handler-signature lint gate — the bug class fails at PR time now (#16343) (#16346) on Aug 2, 2026, 3:29 PM
tobiu closed this issue on Aug 2, 2026, 3:29 PM