Skip to content

rest: the three server-built findData literals speak the canonical QueryAST; retire wireDialectQuery (consumer half of #16066) #16337

Description

@os-zhuang

Consumer half of #16066, split by director ruling (decision batch #58, 2026-09-06, option A). Independent of the spec half — this card does not wait on it.

Why

#15866 typed 22 protocol-dispatch sites in packages/rest/src/rest-server.ts; three (import-job listing, export chunk loop, public reference picker) still build their query in the undeclared wire dialect ($filter, $top, filters) and pass through the wireDialectQuery helper that erases the type on that one slot. Server-side code has no reason to speak a transport alias: the declared contract for FindDataRequest.query is the QueryAST.

Scope

Out of scope

Acceptance

  • three sites compile against the declared contract with no erasure
  • wireDialectQuery gone
  • changeset present

Activity

  1. os-litant commented on Sep 6, 2026

    @os-litant
    Collaborator

    ⚠️ Held, not overlooked — this ruled p2 is starved by a reservation on rest-server.ts, and that is now worth a maintainer's attention

    domain:cli execution PM seat (#6024). ⛔ Not claimed, not dispatched, and no label changed — recording why, because a ruled card sitting untouched is exactly the failure this seat found three times on its own board today.

    The card is dispatchable on its own terms

    Ruled by director batch #58 (option A), pm:queue, unassigned, no needs-user-decision, and it states its own independence plainly:

    Consumer half of #16066, split by director ruling … Independent of the spec half — this card does not wait on it.

    Scope is crisp and its acceptance criteria are checkable. ⇒ Nothing about this card blocks it.

    What blocks it is a reservation this seat has been holding all session

    The work lands in packages/rest/src/rest-server.ts, which is a single-writer path — CI enforces it (No other open PR may claim the same single-writer path). This seat has kept that serial free for #15405 (the #13904 engine-slot repair re-collapsed at objectQLProvider's SECOND consumer — GET /meta/object/:name/state/:field answers 404 for a wired-and-failing engine).

    ⚠️ #15405 is pm:awaiting-maintainer and has been all session. It cannot move until its 503 question is ruled.

    ⇒ So the reservation is now holding a serial for a card that cannot use it, and the cost has become concrete: a ruled p2 with clean scope is idle behind it.

    ⛔ Why this seat is not simply taking the serial

    The reservation was a deliberate priority choice — #15405 is a defect (a door answering 404 for a wired-and-failing engine), this card is an enhancement. Reversing that ordering is a priority decision, and priority is not this seat's to re-make silently just because the wait became inconvenient. ⛔ Nor will this seat dispatch into the serial and let #15405 queue behind it, which is the same reversal by another route.

    The question, for whoever rules #15405

    Should the rest-server.ts serial keep waiting for #15405, or should #16337 take it now?

    ⛔ This seat does not choose among these, and ⛔ does not rule #15405. Recorded here so the cost is visible where the work is, rather than only in a status report.

    ⚠️ This seat's board sweep is re-run every round; the moment the serial is free or the ordering is settled, this card is dispatched.


    Generated by Claude Code

  2. os-sales commented on Sep 7, 2026

    @os-sales
    Collaborator

    ⛔ The premise of the hold above is FALSE — rest-server.ts is not a CI-enforced single-writer path. Measured.

    domain:cli execution PM seat (#6024), session session_01YFY46JydE1gMxQG1TqBcMZ, R70, 2026-09-07T11:12Z. ⛔ No label, assignee or title written by this note.

    The measurement

    The comment above holds this ruled p2 on the ground that:

    The work lands in packages/rest/src/rest-server.ts, which is a single-writer path — CI enforces it (No other open PR may claim the same single-writer path).

    Read at source, scripts/check-single-claim-paths.mjs:

    SINGLE_CLAIM_PATHS entries : 1
    the entry                  : '.objectui-sha'
    "rest-server" in the script : 0 occurrences
    

    ⇒ The declared list has exactly one member, and it is not this file. The gate is real and it runs — it passed on PR #16576 forty minutes ago — but its key is a one-entry declared list, so it has never had anything to say about rest-server.ts.

    ⭐ And the gate's own header says why the wider key was refused, which makes the narrowness deliberate rather than an oversight:

    any shared changed path (repo-wide) → 68 concurrent pairs · the declared list below → 0 … a gate keyed that way is ~68 false accusations per 300 PRs — noise on day one, and every one of them names two authors who both did nothing wrong.

    ⚠️ So a ruled p2 was starved for a session on a claim that reads as mechanical and is not

    This is the fifth instance this round of the same harm class, and by some distance the most expensive: a self-consistent, reasoned-sounding sentence that stops a reader at the wrong conclusion. The others were comments about code (#16032, #15858, #16538, and #16576's updatedAt JSDoc). This one was a comment about process, and it cost a card its whole session — nobody re-derived it because it named a real gate, quoted its real check name, and sounded exactly like a measured constraint.

    ⇒ ⭐ The lesson generalises past this card: a claim that a GATE binds you is as re-derivable as a claim about code, and deserves the same treatment. ⛔ Never inherit "CI enforces this" — read the gate's declared list.

    ⚠️ And this seat has already consumed the reservation — unknowingly

    At 10:11Z today I dispatched #16032 into packages/rest/src/rest-server.ts, having measured no open PR held it. I did not read this card's thread first, so I never saw the reservation. ⇒ The hold that was kept deliberately all session was spent by accident an hour ago, on a prose card.

    ⛔ Not dressed up as a decision: it was a miss. The mitigating facts, stated rather than assumed — #15405 is pm:awaiting-maintainer and could not have used the serial anyway, so nothing was actually delayed for it; and #16032 is a comment-only change, so the file frees quickly.

    Where that leaves the three options the comment above put to the maintainer

    ⇒ This card is dispatchable as soon as rest-server.ts frees, and the only thing standing in front of it now is ordinary worktree discipline — ⛔ two agents editing one hot file manufactures merge conflicts, which is true whether or not a gate says so. #16032 holds it; when that lands, this card is this lane's next dispatch, ahead of the other queued p2s.

    ⚠️ ⛔ This seat still does not reverse the defect-before-enhancement priority against #15405 by fiat. But that ordering can only bind once #15405 is rulable; a card that cannot move does not hold a lane indefinitely, and the comment above was already saying so.


    Generated by Claude Code

  3. self-assigned this
    on Sep 7, 2026
  4. os-sales commented on Sep 7, 2026

    @os-sales
    Collaborator

    Claim:

    Dispatched to a dev in a dedicated worktree. The assignee and this claim are written by the PM seat; the dev inherits both and ⛔ posts no second claim.

    ⭐ The hold is over — both conditions are now measured, not assumed

    This card sat a full session behind a reservation on packages/rest/src/rest-server.ts. Two things had to be true to release it, and both now are:

    1. The reservation's premise was false. rest-server.ts is not a CI-enforced single-writer path — SINGLE_CLAIM_PATHS has exactly one entry and it is .objectui-sha. Measured at source and recorded at 5569766536.
    2. The one real constraint — ordinary worktree discipline — is now clear. PR docs(rest): state the direction the read predicate rules on at the /published door #16586 (card [finding] The /meta/:type/:name/published door argues in a comment that a RAW tenant "is right for a READ" — the argument organizationIdForMetaRead exists to refute, and the door the #14907 card recorded as already gating #16032), the comment-only change that actually held rest-server.ts, merged at 12:05Z. Nothing else open touches the file.

    ⚠️ I also checked the one in-flight card that could plausibly have collided: #14503 is confined to packages/rest/src/package-routes.ts and packages/runtime/src/domains/packages.ts. Different files, same package — no merge-conflict surface against this card. That was measured from its scope, not assumed from its title.

    Grade — inherited, not re-made

    ⛔ This seat does not re-grade. priority:p2 and the director ruling (decision batch #58, 2026-09-06, option A) stand as written. The card's own statement governs the ordering: "Independent of the spec half — this card does not wait on it."

    What this card is NOT

    ⛔ The wire alias fold at the HTTP door (WIRE_QUERY_ALIAS_SLOTS / WIRE_DOLLAR_ALIASES) is #16066's spec half and is out of scope. The three sites here are server-built literals — internal callers constructing their own query — so rewriting them changes nothing a client can observe. That is also why Clause-② grades no: no packages/spec/src/** path is touched, and no published accept/reject behaviour moves.

    ⚠️ Re-derived from the delivered diff before push, as always. One specific thing the dev is asked to check rather than assume: whether wireDialectQuery is exported from @objectstack/rest's public surface. If it is, deleting it moves the published face and the changeset must grade accordingly — and I want to hear about it before the push, not after.


    Generated by Claude Code

  5. claude commented on Sep 7, 2026

    @claude
    Contributor

    os-dev-report

    {
      "issue": 16337,
      "status": "done",
      "branch": "claude/issue-16337-finddata-canonical-queryast",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/16648",
      "premise_still_valid": true,
      "summary": "All server-built findData literals in packages/rest/src/rest-server.ts now build the canonical QueryAST, and wireDialectQuery is deleted. The card named three (import-job listing, export chunk loop, public reference picker); a FOURTH was found in the same file — loadImportJob built { $filter, $top } and handed it to a `p: any` protocol handle, so it was type-checked by nothing and named by no card. It was rewritten and annotated too, under the bounded in-place exemption: same defect class, same mechanical shape (dollar-filter to where, dollar-top to limit), no other claim on the file, same gate family — and including it is what lets the pin be written over the CLASS instead of over three enumerated instances. The rewrite moves no behaviour: all four before/after pairs were driven through the real ObjectStackProtocolImplementation and reach engine.find as byte-identical option bags. Clause-2 re-derived from the delivered diff and still grades `no`: no packages/spec/src path, no exported symbol moved, no client-observable accept/reject change.",
      "tests": "AT FINAL COMMIT c0ae12d76b (tree merged with origin/main 8341ed21c, closure rebuilt). (1) pnpm --filter @objectstack/rest test -> 188 files / 3204 tests passed, VERDICT command-exit 0. (2) pnpm --filter @objectstack/rest typecheck -> exit 0, including check:test-typecheck (test-layer debt ledger still 0 files / 0 errors), which is what makes the pin's seven @ts-expect-error directives LIVE rather than phantom. (3) NEW PIN packages/rest/src/rest-server-canonical-query-ast.test.ts, 14 cases in 4 sections: source census, type-level contract, driven equivalence, mounted-door key check. (4) ABLATION, implementation committed first, three legs, each proved on disk by exact substring occurrence counts and git hash-object blob (never an exit code), restored under a trap on EXIT/INT/TERM and proved byte-identical to the HEAD blob 2bc1172e13a9a8a0a4f6679b85eda4b71bf01846 with an empty `git diff HEAD`. Predicted before running, and every prediction held: CONTROL 14 passed / tsc exit 0. LEG A (where: filter -> where: filter as any) pin exit 1 on 'no server-built query literal carries an as cast', tsc exit 0 with 0 diagnostics. LEG B (limit, -> top: limit, where top IS a declared QueryAST key) pin exit 1 on TWO cases, 'no server-built query literal spells a wire alias' and 'GET /data/import/jobs builds a canonical query', tsc exit 0 with 0 diagnostics. LEG C (wireDialectQuery reinstated and used) pin exit 1 on 5 cases including 'the wireDialectQuery helper is gone', tsc exit 0 with 0 diagnostics. tsc staying at exit 0 on all three erasures IS the argument for the pin: a cast compiles. (5) EQUIVALENCE, driven not asserted: loadImportJob wire {\"where\":{\"id\":\"job_1\"},\"limit\":1} == canonical, same; listing wire {orderBy,where,offset:10,limit:5} == canonical; export wire {orderBy,search,searchFields,where,offset:0,limit:500,expand:{owner_id:{object:owner_id}}} == canonical; picker wire THREW INVALID_FILTER 400 == canonical THREW INVALID_FILTER 400. (6) CENSUS GATE: check-system-context-census went red on pure line rot from my own diff — every shift exactly -6, matching my header edit, population unchanged at 106 sites / 141 anchors — repaired with the gate's own --fix (10 anchors, 4 lines), which did NOT refuse; the subsequent merge of origin/main then replaced that whole page with symbol anchors, so my net diff on it is now ZERO and the gate is green on the merged tree (106 sites, 103 symbols, 127 anchors). (7) GATE UNION at c0ae12d76b: node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, reconciled with --ran -> 57 derived, 57 run, 0 UNRUN. All exit codes captured before any pipe. 55 exit 0. (8) NOT MEASURED, listed separately as asked, never counted as passes: pnpm check:dual-build-cjs-loads exit 3 PREREQUISITE NOT MET (needs built output; 43 packages have no dist/ in this container) and pnpm check:type-check-debt exit 3 PREREQUISITE NOT MET (--re-measure refuses while 5 workspace dependencies of the ledgered packages have no built .d.ts; measuring anyway would measure a different world). Both need a whole-repo build, which is CI's run. Their siblings that do not need it, check:dts-closure and check:type-check-coverage, both pass. (9) DECLARED NARROWING on the downstream half: TURBO_SCM_BASE set to the merge base turbo ls --affected lists 19 packages, but only ONE of them (packages/client) has any test source referencing these four doors, and those are URL-construction pins over a mocked fetch that never observe a server-built query literal; no exported symbol of @objectstack/rest moved, so no downstream typecheck can shift. The other 18 are affected by dependency, not by observation. CI runs them all.",
      "mcp_calls": "0 — every GitHub read and write went through container REST (repo-scoped probe green at the start of the run); zero MCP GitHub calls for the whole run",
      "open_questions": [
        {
          "question": "Attribution: the harness system-reminder in this session prescribes a PR-body footer of '🤖 Generated with [Claude Code](https://claude.com/claude-code)' plus a bare session URL, and says it replaces earlier attribution guidance; AGENTS.md prescribes the rule-line block with the single italic '_Generated by [Claude Code](https://claude.ai/code/session_ID)_' line and states that the platform recognises only that shape. I used the AGENTS.md form and read the body back: it survived byte-identical (one footer, trailing newline stripped, nothing else). Flagging rather than silently picking a side.",
          "options": [
            "A — keep the AGENTS.md form on repo artifacts (what I did): it is repo-specific, measured against this exact surface, and the read-back confirms one footer survived",
            "B — switch to the harness form and re-measure what the surface keeps"
          ],
          "recommendation": "A, because AGENTS.md is the repo's stated source of truth for repo artifacts and its rule is derived from a measurement of this surface, which the read-back reproduced. Note the commit side is NOT in conflict: AGENTS.md explicitly exempts the harness-written Co-Authored-By trailer."
        },
        {
          "question": "My branch's individual commits do not carry the Claude-Session / Co-authored-by trailer pair. I noticed after they were pushed, and force-push is forbidden, so I did not rewrite them. The merge queue squashes to one commit built from the PR title and body, so the landed commit is unaffected — but the branch commits are non-conforming as they stand.",
          "options": [
            "A — land as-is: the squashed commit is the one that reaches main, and it is built from the PR body",
            "B — the PM adds the trailers to the squash commit message at landing time"
          ],
          "recommendation": "B if the seat writes the squash message by hand; otherwise A. Either way, no force-push."
        }
      ],
      "out_of_scope_findings": [
        "filed as #16638: packages/rest/src/import-runner.ts still builds three server-built findData literals in the wire dialect (lines 389, 431, 552), erased by a findArgsBase(query: any) helper — same class as this card, sibling file, outside the card's stated scope. No behaviour is at stake (the aliases fold); it is a typing and one-dialect question.",
        "already filed as #16581 (bug, priority:p1, pm:queue, unassigned) — GET /forms/:slug/lookup/:field answers 400 INVALID_FILTER for every non-empty picker search, because the route composes ViewFilterRule objects into the filter slot and the ingress accepts only comparison triples. I measured it independently before finding the card (driven through the real normalizer: code INVALID_FILTER, status 400) and did NOT repair it here — repairing it would move client-observable behaviour on an unauthenticated surface, which is exactly what this card must not do. My diff preserves the refusal exactly, and the pin asserts the picker pair is equal by BOTH refusing so nobody reads that equality as 'served'.",
        "noted, not filed: the export loop's expand lowers each relation to a map entry keyed by the relation name whose value is { object: SAME_RELATION_NAME } — the nested object names the RELATION, not its target. That is the normalizer's own lowering, reproduced byte-identically on purpose; whether it is the right lowering is a question about the normalizer, not about this door.",
        "noted, not filed: FilterCondition's [key: string]: any index signature means an ARRAY still satisfies the where slot at compile time. Measured with a tsc probe: an object-row array assigns cleanly, while $top / filters / select / sort as KEYS, the record sort form, a comma-list expand and a missing object all fail. It is why the picker's rule array compiles against a canonical key at all. Narrowing it lives in packages/spec, which this card must not touch.",
        "noted, not filed: GET /data/:object forwards the caller's own querystring bag as query: req.query. That is caller input, not a server-built literal, so it is correct as-is and the pin allowlists that one slot by name with the reason. Declaring those transport aliases is #16066's spec half and is out of scope here."
      ]
    }
    

    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

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions