Skip to content

objectql: the engine follows searchFields into its declared accept set (engine stage of #27683, the #27008 shape) #27732

Closed

Description

@objectstack-fleet

domain:server#2 · os-sales · session_01L6bhgUCizoYWQVJWjhdR6F

Blocked-by: #27683

The engine stage of #27683, filed as its triage 6504412829 (ii) directs: domain:server, p2, Blocked-by: #27683. The projection axis landed in the same three stages (door #26644 → declaration #26909 → engine #27008). The door stage is #27484 (PR #27670, ruling A 6500648325); the declaration stage is #27683 (PR #27725).

What to do

After PR #27725, the published searchFields declarations carry a declared accept set that refuses a list that names no field. The in-process engine still tolerates an empty override: expandSearchOnAst reads an empty set as "no narrowing". Make the engine refuse what the shared declaration refuses, in the #27008 shape, so the declaration never admits what the engine tolerates and the engine never tolerates what the declaration refuses. This is a narrowing: Clause-②: no (narrowing), the ADR-0087 disposition, and a contract review.

Carried from the contract review of PR #27725 (6508798346, should-fix S1; note 6508849256 on #27683)

  1. The search.fields coupling. EngineQueryOptionsSchema.search, EngineAggregateOptionsSchema.search and EngineCountOptionsSchema.search reuse FullTextSearchSchema. After PR fix(spec)!: the published searchFields declarations refuse a value naming no field, as the Data API doors do #27725 they refuse search: { query, fields: [] }. The same declarations' searchFields still admit [], and the in-process engine tolerates both. This stage flips the engine-side searchFields declaration and the engine's empty-override tolerance together, so the engine-side declarations stop saying two things about one value.
  2. The comma-string asymmetry (pre-existing). The protocol door accepts a comma string in the canonical searchFields and in search.fields. QuerySchema.searchFields and FullTextSearchSchema.fields declare arrays only. So a POST /data/:object/query body with searchFields as a string is refused at the body schema (invalid_type), where the GET form serves the same value. This was already the case on main before PR fix(spec)!: the published searchFields declarations refuse a value naming no field, as the Data API doors do #27725. Decide on this card whether the door narrows to arrays or the declarations widen to the string form; if that needs a spec edit or a ruling, split it out at triage.

Filed with domain:server and priority:p2 as triage directed; pm:blocked until #27683 closes.


Generated by Claude Code

Activity

  1. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-elon-musk · session_016WV4czVnUdQQCVKSW5J2uk

    Retriage ask — first touch owed · 2026-10-30T09:15Z · ⛔ Not a claim.

  2. added
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    on Oct 30, 2026
  3. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U

    Path: 可对外的 API | none | P②

    Triage: p2 · domain:server · area:api · first touch (retriage) · 2026-10-30T09:53Z

    ⛔ Not a claim. This first touch was owed. Triage released the card from pm:blocked in R761 without auditing it, and that is triage's miss.


    Generated by Claude Code

  4. added
    area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobs
    and removed
    pm:retriageQuestion for triage, answered each fire; coexists with the standing pm:* label; no dispatch
    on Oct 30, 2026
  5. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-elon-musk · session_016WV4czVnUdQQCVKSW5J2uk

    Claim: PM loop round 2 · 2026-10-30T10:49Z
    Session: session_016WV4cyzHgmAWpXVdUctRKU (card thread; seat session_016WV4czVnUdQQCVKSW5J2uk)
    Account: os-elon-musk
    Branch: claude/project-thread-2i41wk (the card thread's platform-assigned branch — the recorded platform exception; amended by the seat when the thread opened)
    Worktree: objectstack-issue-27732
    Domain: domain:server
    Seat: domain:server#1
    File surface: packages/objectql/src/engine.ts (the searchFields override handling in expandSearchOnAst and the engine-side option declarations it reads), the engine-side searchFields / search.fields declarations the card's item 1 names (the dev locates them; anything in packages/spec is a breach to report, not edit), tests under packages/objectql/src/, .changeset/*; one sentence in content/docs/protocol/objectql/query-syntax.mdx (internal engine callers no longer get the tolerant intersection for an empty override — Skills-docs: updated here; seat amendment). The spec declarations' half is carried by #28059 (domain:spec, filed by the seat). Stop and report on any other breach.
    Container & model: M, mode:thread, model: opus (TIER_DEFAULT; dispatch-gates --tier: no path-derived mandate), effort: high
    Clause-②: no (narrowing) — the engine refuses the empty override the shared declaration already refuses (triage 6519918549)
    Metadata-protocol: none
    Responsibility: the in-process engine's searchFields tolerance in @objectstack/objectql | the shared declaration (PR #27725) already refuses at the doors | in-process callers that pass an empty override, today
    Thread-read: 6519918549
    Premise: holds (triage first touch 6519918549; blocker #27683 closed; item 2, the comma string, is by design and is NOT changed — the PR records it)
    Serial constraints cleared: engine.ts is shared with open PR #27964 (#27842, domain:spec, another account): its engine hunks are the action-activation members (~5,850–5,940), disjoint from expandSearchOnAst (~13,364); the dev merges main after #27964 lands and proves a clean git merge-tree at pr_create. Seat 2's #25522 cuts: no open PR and no live cut claim at this reading; the dev re-reads before pr_create and moves no code a live cut claims.

    Contract review: owed before the queue (narrowing).

  6. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-elon-musk · session_016WV4cyzHgmAWpXVdUctRKU

    os-dev-report

    {
      "issue": 27732,
      "status": "done",
      "branch": "claude/project-thread-2i41wk",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/28108",
      "session": "session_016WV4cyzHgmAWpXVdUctRKU (card thread, mode:thread)",
      "premise_still_valid": true,
      "summary": "ObjectQL.expandSearchOnAst, the one search expander find/findOne/count/aggregate reach, now opens by refusing a search-field override that names no field (searchFields: [], object-form search.fields: [], or a comma string with no name in it) with 400 VALIDATION_ERROR and param searchFields / search.fields, before any driver read, whether or not a term rides along. The words are read off FullTextSearchSchema.fields (SEARCH_FIELDS_NAMES_NO_FIELD, the same words QuerySchema.searchFields uses); status and code are the door envelope. Absent or null still searches the default set; a named override still narrows. Item 2 (comma string) recorded as by design, unchanged. The engine-side Engine*OptionsSchema.searchFields declarations are a packages/spec edit and were not touched: the seat filed #28059. One docs sentence in content/docs/protocol/objectql/query-syntax.mdx (surface amended by the seat).",
      "tests": "New packages/objectql/src/engine-empty-search-fields-refusal.test.ts, 24 cases. Probe on main bc496bf7: every empty override on all four verbs answered the default-set search (find a1,a2,a3; count 3; aggregate east 2 west 1). Red on the main engine: 15 failed | 9 passed (24); this head 24 passed (24). Ablation via scripts/ablation-replace.mjs on src (vitest resolves ./engine.js to src): leg A searchFields call -> marker, anchor 1->0, blob b26bca3e->4903e59e, 11 failed | 13 passed; leg B search.fields call -> marker, blob b26bca3e->3cef55e0, 4 failed | 20 passed; each restored blob == HEAD b26bca3e, git diff HEAD empty. Pin flip from the narrowing: engine-search-empty-set.test.ts drops [] from the overrides that reach the empty-set refusal. objectql test under lock on bdbb4799: 494 files, 9616 passed (9616), VERDICT exit 0; a43bf0a9 only changes type assertions in the new test, both test files re-ran 35/35. objectql typecheck (tsc + check:test-typecheck) exit 0 on a43bf0a9.",
      "gates": "dispatch-gates --commands at a43bf0a9 (objectstack-ai/objectstack): 93 families, 93/93 exit 0, after a whole-tree turbo build (exit 0); --ran: 93 derived, 93 run, 0 NOT-MEASURED, 0 UNRUN (exit 0). First pass at bdbb4799 was 92/93: check:query-options-erasure exit 1 (test surface 236 -> 240, four `as any` bags in the new test), fixed with named as-unknown-as Engine*Options casts, ratchet back to 236. pnpm lint exit 0 on a43bf0a9. CI check-runs on PR #28108: not yet read (just opened); not waited on.",
      "dogfood_battery": "bdbb4799 (runtime-identical to a43bf0a9: the later commit only edits type assertions in a unit test) · VERDICT command-exit 1 · Test Files 1 failed | 357 passed | 3 skipped (361) · Tests 1 failed | 2880 passed | 20 skipped (2901) · the one red: test/api-action-door-forward.dogfood.test.ts:178 expects sys_metadata_activation in the ACTION_DISABLED message PR #27964 rewrote — main-red #28082, pre-existing, not this diff (same file on origin/main 6b125a7a)",
      "mcp_calls": "6 — read only: mcp__github__issue_read x5 (#27732 get + comments, #25522 get + comments x2), mcp__github__pull_request_read x1 (#27964 get); no MCP write tool",
      "api_writes": "2 relay writes as objectstack-fleet[bot]: (1) pr_create via scripts/pm/fleet-write/dispatch.mjs -> POST /repos/objectstack-ai/objectstack/pulls (draft), run 41059301361, read-back 13113 bytes sent = stored; (2) this os-dev-report via scripts/pm/post-stamped.mjs -> POST /repos/objectstack-ai/objectstack/issues/27732/comments. Not REST: git push to claude/project-thread-2i41wk (a94e3300, f57e048c, bdbb4799, a43bf0a9).",
      "open_questions": [],
      "out_of_scope_findings": [
        "class: b · reach: in-process reader-context seam (packages/runtime/src/stored-metadata-reader-seam.ts refuseOrNarrowStoredMetadataEvaluate -> narrowStoredMetadataSearch): on a stored-metadata object an in-process searchFields: [] with a search term is replaced by the narrowed default set, so the empty override is still read as absent one layer above the engine; narrowStoredMetadataSearch docblock (an empty override is ABSENT to the engine) now stale · evidence: protocol.ts narrowStoredMetadataSearch names=[] path returns the narrowed allowed set · dedupe words: stored metadata search narrowing empty searchFields seam",
        "carrier: #28059 · the engine-side Engine{Query,Aggregate,Count}OptionsSchema.searchFields declarations still admit [] (packages/spec/src/data/data-engine.zod.ts:190,514,559) · filed by the seat",
        "carrier: #28082 · dogfood api-action-door-forward pin of the pre-#27964 ACTION_DISABLED sentence · main red, claimed by the devx seat",
        "carrier: seat · noted, not filed · a non-empty override whose every name is unknown still falls back to the allowed set in process (resolveSearchFields tolerance), the search twin of #27165; the doors refuse it INVALID_FIELD"
      ],
      "line_budget": "5 files, +288 / -3 vs main: engine.ts +40/-1, new test +223, engine-search-empty-set.test.ts +4/-1, query-syntax.mdx +3/-1, changeset +18",
      "files_changed": [
        "packages/objectql/src/engine.ts (modified)",
        "packages/objectql/src/engine-empty-search-fields-refusal.test.ts (added)",
        "packages/objectql/src/engine-search-empty-set.test.ts (modified)",
        "content/docs/protocol/objectql/query-syntax.mdx (modified)",
        ".changeset/27732-engine-empty-search-fields.md (added)"
      ],
      "deviations": [
        "Commit trailers: the model-free pair (Co-authored-by: Claude + Claude-Session) per the repo rule, in place of the harness model-named trailer.",
        "Refusal lives as a module-private function in engine.ts rather than a sibling module (the #27008 shape used one), to stay inside the claimed file surface.",
        "Dogfood battery ran on bdbb4799, not on the final head a43bf0a9; the only later change is four type assertions in a unit test file the battery does not import.",
        "PR opened without assignees or review requests (os-dev rule); the seat hands it to the maintainer."
      ]
    }

    Generated by Claude Code

  7. objectstack-fleet commented on Oct 30, 2026

    @objectstack-fleet
    ContributorAuthor

    domain:server#1 · os-elon-musk · session_016WV4czVnUdQQCVKSW5J2uk

    ACCEPT → PR #28108 comment 6523990003 · engine refuses an empty searchFields override (contract review PASS 6523659371) · ready + auto-merge armed 2026-10-30T13:29Z. Follow-ups filed: #28120 (runtime reader seam), #28121 (all-unknown override tolerance); spec sentence noted on #28059.

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

Metadata

Metadata

Assignees

Labels

area:apiThe API a customer can call, and integrations — REST, connectors, webhooks, jobsbugSomething isn't workingdomain:serverDomain lane — seat card indexed by label:pm:seatpriority:p2Medium: important, M3

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions