Skip to content

spec/rest: the query door's aggregation entry still strips an undeclared key, while MCP's aggregate_records now refuses it by name (#28550) #28609

Closed

Description

@objectstack-fleet

domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo

Filing gate: ① out-of-scope finding from the #28550 dev (os-dev-report 6555058015), answered by the CONTRACT_REVIEW_TIER review on PR #28600 (comment 6555135229, ③) as a legitimate follow-up and not a rider. Filed by domain:server seat 2. ⛔ Not a claim. No grade: triage sets the grade.

Reach: any caller of POST /data/:object/query that sends an aggregation entry with a key the spec does not declare.
Reader who acts: domain:server (packages/spec AggregationNodeSchema, the REST query door).

What happens

PR #28600 (#28550) makes the MCP tool aggregate_records take each aggregation entry as the spec's AggregationNodeSchema members, closed: an undeclared entry key (for example bogusKey, or where meant as filter) is refused by name. The REST query door parses the same entry with AggregationNodeSchema, which is a plain object, so it still strips an undeclared entry key and answers. After #28600 the two doors judge one entry shape differently: MCP refuses, REST strips. REST computes nothing wrong today (a stripped unknown key changes nothing it computes), so this is a consistency gap, not a wrong answer.

Wanted

One strict contract at the producer: the aggregation entry refuses an undeclared key by name on every door that accepts it, in the same wording the unknown-key campaign uses (strictUnknownKeyError), with the retired distinct keeping its tombstone sentence. This is a spec narrowing, so the PR declares Clause-②: no (narrowing) and Metadata-protocol: narrowed, and a contract review at CONTRACT_REVIEW_TIER is owed. Test: REST and MCP give one refusal for an undeclared entry key, and entries with only declared members answer as today on both.

Dedupe: searched AggregationNodeSchema strict; no open card.

Links back: #28550, PR #28600


Generated by Claude Code

Activity

  1. added
    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobs
    domain:serverDomain lane — seat card indexed by label:pm:seat
    on Nov 1, 2026
  2. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo

    Triage: p3 · domain:server · area:api · pm:queue · 2026-11-01T12:13Z

    ⛔ Not a claim.

    Ruling-ref: the maintainer, 2026-11-01T12:11Z: 「定级你可以自己决定」 (server seats grade lane cards; the split is seat 1's, this seat took this card), with 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」.

    • Read fresh. After PR fix(mcp)!: an aggregate_records aggregation entry carries its filter to the engine and refuses an undeclared key by name #28600 the MCP tool refuses an undeclared aggregation-entry key by name and the REST query door strips it. No computed answer is wrong today.
    • Grade p3: a consistency gap between two doors on one entry shape, not a wrong answer and not a security exposure.
    • Route: domain:server (spec AggregationNodeSchema and the REST query door). It is a spec narrowing: the PR declares Clause-②: no (narrowing) and Metadata-protocol honestly, and a CONTRACT_REVIEW_TIER contract review is owed before the queue.
    • Dispatchable now; this seat dispatches it.

    Generated by Claude Code

  3. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo

    Claim: PM loop round 4 · card thread · 2026-11-01T12:20Z
    Session: session_0186vfjHHMsRXC9EUT9hTFMo (the seat); the card thread is session_01FEkQ5m158G1nvmxNYr7Jwq
    Account: os-project-manager (the seat's linked user; the card's assignee)
    Branch: claude/project-thread-i0hitl (the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)
    Worktree: the card thread's own container checkout
    Domain: domain:server
    Seat: domain:server#2
    Ruling-ref: the maintainer, 2026-11-01T03:46Z: 「server 车道现在就是你负责,任务有一大堆啊,都可以派,包括 p3」, with 2026-10-26T10:29Z 「p1 p2 处理完了,就可以处理 p3」. With 2026-11-01T12:11Z 「定级你可以自己决定」; graded p3 by this seat (6557790954).
    File surface: packages/spec/src/data/query.zod.ts (AggregationNodeSchema), the REST query door, the MCP data tools, generated spec surfaces and ledgers; changesets
    Container & model: M, mode:thread, model: opus (--tier names no path mandate: PM's call, default), effort: high
    Clause-②: no (narrowing) expected — an undeclared aggregation-entry key the REST door stripped is refused by name
    Metadata-protocol: none (expected; the dev measures and declares)
    Responsibility: the dev makes the producer schema strict, folds any duplicate MCP check into it, with red-first, controls and ablation; the seat runs the CONTRACT_REVIEW_TIER review before the queue
    Thread-read: none
    Premise: read from PR #28600's review (6555135229); the dev measures the REST strip on origin/main first
    Serial constraints cleared: PR #28600 merged (20dcc075); seat 3's analytics cards #28555-#28558 may touch nearby code, read before editing
    Resume-from: none — fresh from origin/main

    State: → pm:dispatched, assigned to os-project-manager, recorded by this claim.


    Generated by Claude Code

  4. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-project-manager · session_01FEkQ5m158G1nvmxNYr7Jwq
    os-dev-report
    {
    "issue": 28609,
    "status": "done",
    "branch": "claude/project-thread-i0hitl",
    "pr": "#28706",
    "session": "session_01FEkQ5m158G1nvmxNYr7Jwq",
    "premise_still_valid": true,
    "summary": "AggregationNodeSchema (packages/spec/src/data/query.zod.ts) is now a strictObject: an undeclared entry key is refused by name with the unknown-key campaign's message, hints where/filters/conditions -> filter and as -> alias, and the retired distinct keeps its tombstone sentence. REST POST /data/:object/query and the runtime dispatcher (both via FindDataRequestSchema) now answer 400 VALIDATION_FAILED with fields[] at query.aggregations.N, before any read. MCP aggregate_records folds its own closed copy of the entry into the spec node (z.array(AggregationNodeSchema)), so its refusal is the same sentence word for word and distinct answers its tombstone on both doors; tools/list now lists distinct as not:{} with its [REMOVED] text. Declared lines measured: Clause-② no (narrowing), Metadata-protocol none (authorable surface unmoved, QueryAST is a request surface; the order expected narrowed, the claim none), so @objectstack/spec + @objectstack/mcp patch with BREAKING banner and ADR-0087 not-required (no-migration-prescription).",
    "tests": "Red first on base 0b6a52e: rest src/aggregation-entry-undeclared-key-door.test.ts 3 failed / 2 passed (bogusKey, entry where, entry as answered 200; distinct tombstone and declared-only control green); head 5/5. Ablation: node turned back into z.object(shape) via scripts/ablation-replace.mjs (anchor 1/1), spec rebuilt, ablation-dist-preflight marker in 2 dist files; reds REST door 3 + REST gate table 1, runtime two-door rows 2, MCP entry file 3, spec block 4 (13), controls green; restore blob == HEAD, git diff HEAD empty, rebuilt, --absent clean over 588 files. Package suites: spec 869 files / 24570 passed; rest 364 files / 6915 passed; runtime 504 files / 8189 passed (after building @objectstack/runtime...); mcp 48 files / 612 passed. Evidence: pm-orders/server/evidence/28609/.",
    "gates": "Local, final head 09f089d: node scripts/pm/dispatch-gates.mjs --ran: 85 derived families accounted for, all with exit codes, 0 NOT-MEASURED, every exit 0 (record pm-orders/server/evidence/28609/gates-ran-09f089de.txt). Second runs: check:doc-authoring first exit 1 on an issue id in the node's customer-facing history sentence (fixed in 3f30282), check-plugin-teardown-shape --self-test exit 3 until its fixture commit was fetched. check:type-check-debt and check:dual-build-cjs-loads exit 0. CI families: in_progress at report time, the seat reads the PR check-runs.",
    "dogfood_battery": "09f089de · os-verify-lock: VERDICT command-exit 0 (held 33m08s incl. the dogfood^... build) · Test Files 369 passed / 3 skipped (372) · Tests 2981 passed / 28 skipped (3009) · no failures",
    "mcp_calls": "0 — no MCP GitHub calls; reads via gh api REST",
    "api_writes": "1 relay stroke (fleet-write fw-20261101T142732Z-e16b88: pr_create -> #28706, no assignees, no labels) + 1 os-dev-report comment on #28609 via post-stamped.mjs",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: none · noted in the PR body Acceptance notes, not filed: open PR #28688 also edits docs/audits/2026-07-unknown-key-strictness-ledger.md (different rows; text merge expected clean, counts shards differ: this PR data.md, #28688 integration.md, #28682/#28704 api.md); the landing seat re-runs gen:strictness-ledger after whichever lands first"
    ]
    }


    Generated by Claude Code

  5. objectstack-fleet commented on Nov 1, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#2 · os-project-manager · session_0186vfjHHMsRXC9EUT9hTFMo

    Landing record · #28609 · PR #28706

    domain:server seat 2 · session_0186vfjHHMsRXC9EUT9hTFMo · 2026-11-01T15:14Z

    Reading Probe Result
    Merged PR #28706 state merged, merge commit 36fa4ffa
    On main git merge-base --is-ancestor 36fa4ffa origin/main yes
    Queue refs git ls-remote origin refs/heads/gh-readonly-queue/* matching 28706 0
    Content git grep -n "AggregationNodeSchema = " origin/main -- packages/spec/src/data/query.zod.ts lazySchema(() => strictObject( at :284

    Release: claim 6557859202 (session_01FEkQ5m158G1nvmxNYr7Jwq, branch claude/project-thread-i0hitl) · landed.

    • State: pm:dispatched removed; assignee cleared; the card closed on merge.
    • Resume-from: none.

    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsdomain:serverDomain lane — seat card indexed by label:pm:seatfindingpriority:p3

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions