Skip to content

Skills optimization flight — skills/objectstack-query: RESTRUCTURE (≈ −4,088 tok, −33%) — seven rules stated twice across SKILL.md and rules/, 27% of tokens on an aggregation surface with zero app-author call sites, context (74 real usages) taught nowhere, and the canonical expand example expands nothing #14302

Closed

Description

@os-litant

Member card of the skills catalog optimization program #14292 (maintainer mandate 2026-09-02, verbatim: 「审核所有的 skills,进行全面的优化。」). Filed by the skills lane seat (session session_01LraLgQVGq8egUwfYZpbYt1). Read-only audit at objectstack origin/main a59f78d. The full findings table is the audit record: the dev posts it verbatim as the first comment on this card at claim time (seat scratchpad audit/objectstack-query/findings.md).

Audit summary

6 files, 12,270 tokens (SKILL.md 5,443 · aggregation.md 2,226 · filters.md 2,144 · pagination.md 1,381 · evals 558 · generated 518); total headroom 255. Real usage: 6 patterns. Option-key frequency in real query literals: where 169 · context 34 · fields 17 · orderBy 15 · expand 15 · search 12 · aggregations 9; in examples/apps .find 29, .count 0, .aggregate 0, context/...ctx 40. The package spends 27% (3,327 tok) on the aggregation cluster and zero on context. Verdict RESTRUCTURE: SKILL.md and rules/* restate seven rules near-verbatim; ~1,180 tokens are other packages' anchors; the package never states its own calling convention, so its most-copied example shape ({ object: 'account', … }) is not passable to any engine method (ENGINE_FIND_OPTION_KEYS rejects unlisted keys).

Top findings

id span proposal Δ
QRY-B-01 SKILL.md:214-231≡filters.md:100-115 + 6 more pairs MERGE-INTO rules/* — SKILL.md keeps one-line pointers −1,100
QRY-D-01 SKILL.md:274-279,327-337,440-445,550-558; aggregation.md:16-27,138-144,156-166; pagination.md:12-18,218-222 REWRITE-AS-CONSTRUCT — one 6-row "removed key → replacement" table; delete the history −700
QRY-D-05 evals/README.md:1-38 "Planned Evals" never built (DEFERRED, #14296 item 2) −558
QRY-C-01 SKILL.md:496-539 MERGE-INTO objectstack-data (same heading, prescription, example; :535-537 names data as the anchor) −470
QRY-C-02 filters.md:221-258 OVERRIDDEN — KEEP: query owns date-macros.zod.ts; ui deletes its copy and points here 0
QRY-C-04 filters.md:260-280 MERGE-INTO objectstack-automation :157-164 (delete + pointer) −330
QRY-C-03 SKILL.md:617-632 DELETE "CRM Analytics Query Blueprint" — ui's dataset/widget surface −260
QRY-B-02 SKILL.md:43-51 DELETE "When to Use This Skill" −180
QRY-E-03 SKILL.md:60-69,256-266,341-352,407-425 vs :371-377,390-396,604-612 ADD calling-convention table — engine find/aggregate (object first arg, legal keys) · protocol findData({object, query}) · nested expand · view filter rule +120
QRY-B-03 SKILL.md:32-40 DELETE "Skill Boundaries" −110
QRY-C-05 SKILL.md:624, aggregation.md:199-207 DELETE both compareTo copies; ui is the anchor −110
QRY-D-03 SKILL.md:20-28 DELETE blurb −100
QRY-D-02 aggregation.md:88-96 DELETE the alias/push-down bug-history parenthesis −95
QRY-F-01 absent ADD context / system reads — 3 lines + { where, limit: 1, context: SYS } +90
QRY-F-03 SKILL.md:136-146, filters.md:18-21 ADD $icontains/$like/$ilike + the case-sensitivity rule (filter.zod.ts:548-549) +70

Incidental falsehoods (fix in this flight)

  1. SKILL.md:407-425 — the canonical expand example expands nothing: it projects fields: ['title','status'] while expanding assignee and project, dropping both FK columns; packages/objectql/src/engine.ts:8345-8347 skips a projected-away FK. Contract at packages/spec/src/data/query.zod.ts:509, pinned at query-expression-conformance.test.ts:1575-1586. The Rules list at :427-434 omits the requirement — add it. HIGH.
  2. Sibling: objectstack-ui :1706-1708 "unknown placeholders silently pass through" is FALSE (throws UnknownFilterTokenError) — the ui flight deletes it; this package's filters.md:282-286 is the correct anchor and STAYS.

Flight scope

IMPLEMENT (shrink-only): QRY-B-01 (rules/* are the anchor; SKILL.md keeps one-line pointers — SKILL.md shrinks, rules do not grow), D-01, C-01, C-03, C-04, C-05, B-02, B-03, D-02, D-03, the three funded additions (E-03 paid by B-02+B-03; F-01 paid by C-01; F-03 paid by B-01 in SKILL.md and by deletions in filters.md), falsehood 1, E-02/E-04 corrected examples, QRY-A-01 description edit (regenerate skills/README.md).

ANCHOR RULINGS: this package is the anchor for the filter DSL, date macros / filter tokens (filters.md:221-258 and :282-286 STAY; the ui flight points here — if the ui flight's PR body lists a true row missing here, it becomes a follow-up on this card), pagination, aggregation grammar. Search mirror → anchor objectstack-data (C-01: delete + pointer). Flow-filter rule → anchor objectstack-automation (C-04). compareTo and dataset analytics → anchor objectstack-ui (C-03, C-05).

DEFER (pending #14296): QRY-D-05 / H-02 / H-03 (evals, item 2); QRY-B-05 (generator-owned _index.md headline — the fix is upstream in packages/spec/src/data/query.zod.ts: file the spec-side twin as an out-of-scope card).

Flight constraints (binding)

  • ONE draft PR, first line Fixes #<this card>; governed ⇒ stays draft; review requests are the seat's step.
  • Token ratchet: no ratcheted file may grow; additions paid by deletions in the SAME file; ⛔ re-wrap is not payment; ⛔ no ceiling raise; ⛔ no new files; ⛔ do not touch the ratchet script.
  • ⛔ Never edit another package's files: MERGE-INTO another package = DELETE here + one-line pointer; a missing/weaker anchor goes in the PR body under "follow-up for ".
  • Generated files untouched; frontmatter edits ⇒ regenerate skills/README.md (pnpm --filter @objectstack/spec gen:skill-docs).
  • Live surface with zero measured usage ⇒ one row pointing at its schema, never delete the last mention; retired/tombstoned surface ⇒ delete.
  • A false claim matching a spec .describe() string ⇒ file the spec-side twin as an out-of-scope card.
  • Gates: node scripts/check-skills-token-ratchet.mjs, pnpm --filter @objectstack/spec check:skill-examples, pnpm check:skill-compatibility, pnpm check:skill-identifier-liveness, plus node scripts/pm/dispatch-gates.mjs --commands <changed paths>; record the head sha.
  • PR body: per-item 落点 | before | after list keyed by finding id; per-file token delta; needs:contract-review on both carriers (falsehood 1 and the $icontains rows are contract-semantics claims).

Refs: #14292 · #14296 · #13658.

Activity

  1. added
    documentationImprovements or additions to documentation
    priority:p1High: required for production / M2
    pm:queueReady for the PM dispatch loop
    domain:skillsDomain lane — seat card indexed by label:pm:seat
    pm:dispatchedDispatched to a dev agent by /pm-dispatch
    and removed
    pm:queueReady for the PM dispatch loop
    on Sep 2, 2026
  2. self-assigned this
    on Sep 2, 2026
  3. os-litant commented on Sep 2, 2026

    @os-litant
    ContributorAuthor

    Claim: PM loop round 2
    Session: session_01LraLgQVGq8egUwfYZpbYt1
    Branch: claude/issue-14302-skills-query-optimization
    Worktree: objectstack-issue-14302
    Domain: domain:skills
    File surface: skills/objectstack-query/** — SKILL.md, rules/aggregation.md, rules/filters.md, rules/pagination.md, every file shrink-only; evals/** and generated references/_index.md untouched; no file is deleted ⇒ ⛔ no edit to scripts/check-skills-token-ratchet.mjs; skills/README.md + content/docs/ai/skills-reference.mdx regenerated only because the drift gate demands it after the QRY-A-01 description edit (one generator writes both — measured by the two sibling flights); ⛔ no other package, ⛔ no .claude/** (stop on breach; explain in the report)
    Container & model: M (4 authored files, 12,270 tokens, judgment-heavy merges across SKILL.md ↔ rules/*), mode:subagent, model: opus — no path-derived mandate for skills/** (floor sonnet · default opus · ceiling fable); the content limb applies, compensated by the seat's in-seat review at CONTRACT_REVIEW_TIER (served model read claude-fable-5-1 at 06:08Z)
    Clause-②: yes — corrects published contract-semantics claims (falsehood 1: the canonical expand example projects away the FK columns it expands; the engine calling convention and ENGINE_FIND_OPTION_KEYS rejection; $icontains/$like/$ilike case-sensitivity; context system reads) ⇒ needs:contract-review on both carriers at PR creation; in-seat review before any human review request
    Serial constraints cleared: this package is the ANCHOR for the filter DSL, date macros / filter tokens, pagination and aggregation grammar — rules/filters.md:221-258 and :282-286 STAY; the delivered ui flight (PR #14338, ACCEPTed) already points here and its PR body lists five TRUE rows absent from rules/filters.md — those are funded additions of THIS flight (paid inside rules/filters.md); the api flight (#14304, not yet dispatched) will point at this package for request-side query syntax — keep the filter-DSL sections addressable; the automation anchor for QRY-C-04 (objectstack-automation/SKILL.md:157-164) and the data anchor for QRY-C-01 are stable paths (neither flight dispatched yet); no open PR touches skills/objectstack-query/** (open PR list read 06:2xZ); in flight alongside #14297 (data, landing) and #14299 (platform, patch round) on disjoint directories

    Decision re-read: no maintainer ruling on this card; binding inputs = the mandate, the value gate, the ratchet discipline, and #14296 (item 2 deferred: eval stub untouched). Landing form: first line Part of #14302 (QRY-D-05/H-02/H-03 deferred to #14296 keep the card open).


    Generated by Claude Code

  4. os-litant commented on Sep 2, 2026

    @os-litant
    ContributorAuthor

    Dev claim (os-dev subagent, dispatched by the PM claim above)

    Session: session_01LraLgQVGq8egUwfYZpbYt1 (same id as the PM claim — the dev subagent runs inside the dispatching session; the distinguishing marker is this comment plus the branch below)
    Branch: claude/issue-14302-skills-query-optimization (empty branch pushed at claim time as the write-route probe — * [new branch], accepted)
    Worktree: /home/user/objectstack-issue-14302, BASE 96b627d135826253981cfa01d74b2832ccdea194
    Assignee: untouched (PM-owned).

    PM mechanism assumption 1 — VERIFIED at my actual base: git diff --stat a59f78d 96b627d1 -- skills/objectstack-query is empty, so every span in the audit record is valid line-for-line at BASE. Ratchet baseline re-measured at BASE and matches the audit's price tag exactly (SKILL.md 5443/5552 · aggregation.md 2226/2357 · filters.md 2144/2149 · pagination.md 1381/1382 · evals 558/567 · references/_index.md 518 generated).

    File surface I will touch: skills/objectstack-query/SKILL.md, rules/aggregation.md, rules/filters.md, rules/pagination.md, plus skills/README.md and content/docs/ai/skills-reference.mdx only as generator output after the QRY-A-01 frontmatter edit. evals/** untouched (QRY-D-05/H-02/H-03 deferred to #14296); generated references/_index.md untouched; no new files, no deletions, no ratchet-script edit, no other package.

    Landing form: ONE draft PR against main, first line Part of #14302 (not a closing keyword — the deferred eval items keep this card open), needs:contract-review on both the PR and this card at PR creation.

    The full audit record follows as the next comment.

    Generated by Claude Code


    Generated by Claude Code

  5. os-litant commented on Sep 2, 2026

    @os-litant
    ContributorAuthor

    audit-record — skills/objectstack-query (read-only audit at a59f78d)

    Transliteration key. The GitHub body sanitizer is measured to delete angle-bracket / HTML-comment-shaped fragments from stored bodies (even inside backticks), so this record spells two of them without brackets. Substitute back when reading:

    • HTMLCOMMENT-OS-CHECK = the opt-in marker for check-skill-examples: the literal string os:check wrapped in an HTML comment, placed on the line directly above a fence (packages/spec/scripts/check-skill-examples.ts:18-22). Appears twice, in QRY-E-01.
    • UNIT = the placeholder segment of the parameterised date-macro grammar, i.e. {N_UNIT_(ago|from_now)}. Appears once, in QRY-C-02.

    Posted verbatim otherwise. Source: seat scratchpad audit/objectstack-query/findings.md.


    objectstack-query — published-skill audit (read-only)

    Pinned ref: objectstack origin/main = a59f78d. The checkout sits at a39b02a;
    git diff a59f78d HEAD -- skills/objectstack-query/ is empty, so every span below is
    valid at the pinned ref.

    Package price tag (node scripts/check-skills-token-ratchet.mjs)

    File Tokens Ceiling Headroom
    SKILL.md 5,443 5,552 109
    rules/aggregation.md 2,226 2,357 131
    rules/filters.md 2,144 2,149 5
    rules/pagination.md 1,381 1,382 1
    evals/README.md 558 567 9
    references/_index.md 518 — (generated) —
    Total 12,270 255

    Real-usage corpus (what was grepped)

    examples/** (app-crm, app-showcase, app-todo, embed-objectql), packages/apps/**
    (account, setup, studio), packages/objectql/src/*.test.ts (real query shapes),
    packages/services/service-analytics/**, packages/qa/dogfood/**.

    Option-key frequency in real engine calls (packages/objectql/src/*.test.ts):
    where 169 · context 34 · fields 17 · orderBy 15 · expand 15 · search 12 ·
    aggregations 9 · top 6 · limit 5 · groupBy 4 · searchFields 1.
    In examples/** + packages/apps/**: where 26, context:/...ctx 40,
    .find 29, .findOne 1, .count 0, .aggregate 0.

    Search form: bare string search: '…' 74 usages vs object form
    search: { query: … } 5 (all five in conformance tests exercising the tolerated shape).

    Distinct real authoring patterns found: 6 — (1) engine.find(obj, {where, …}) in
    hooks/jobs/endpoints; (2) list-view / nav-item filter rules; (3) expand for lookup
    display; (4) bare-string search + searchFields; (5) defineDataset measures/dimensions
    for analytics; (6) offset paging on list surfaces.


    Findings

    id dim file:span evidence (measured) proposal Δtok gate ①②③ conf human-floor
    QRY-A-01 A SKILL.md:3-9 (desc) + SKILL.md:85-231 The description promises "designing a list view's filter spec". The real list-view filter spec is filter: [{ field, operator, value }] over a 20-operator strictObject enum (packages/spec/src/ui/view.zod.ts:243-254: equals/not_equals/contains/icontains/greater_than/in/is_null/before/between… — "the SINGLE authoring vocabulary … rejects genuinely unknown operators at parse time", view.zod.ts:220-223). Real usages: examples/app-showcase/src/ui/views/task.view.ts:70,80,114. The package teaches only the $ dialect and never names the view vocabulary; objectstack-ui/SKILL.md:1736 carries the array shape. An agent routed here for that task emits a shape the view schema refuses. REWRITE-AS-CONSTRUCT — drop the list-view clause from description, add a 3-row "which filter dialect" table (ObjectQL $ / view rule / dataset measure filter) +70 −180 (paid by QRY-B-02, same file) = −110 ① yes — decides which dialect is emitted ② no, the routing must be stated ③ table HIGH yes
    QRY-B-01 B SKILL.md:214-231 ≡ rules/filters.md:100-115; SKILL.md:380-399 ≡ rules/aggregation.md:118-136; SKILL.md:361-378 ≡ rules/aggregation.md:98-116; SKILL.md:316-338 ≡ rules/aggregation.md:5-27; SKILL.md:274-295 ≡ rules/pagination.md:12-72; SKILL.md:303-310 ≡ rules/pagination.md:41-49; SKILL.md:548-566 ≡ rules/aggregation.md:156-208 diff of the field-reference pair differs only in heading level and sample names (actual_revenue/estimated_revenue vs actual_cost/budget); the filtered-aggregation pair shares the verbatim code comment // An unknown operator inside filter refuses INVALID_FILTER/400 — it never silently answers the unfiltered number. (SKILL.md:397-398 = aggregation.md:134-135). Seven rules stated twice with cosmetically different examples; the whole package is paid in every session, so the second copy buys nothing and forces the reader to pick an authority. MERGE-INTO rules/* — SKILL.md keeps the pointer table at SKILL.md:75-82 only −1,100 ① yes — removes an ambiguity about which copy governs ② yes, deletion alone ③ pointer table already exists HIGH yes
    QRY-B-02 B SKILL.md:43-51 "## When to Use This Skill" — six bullets that restate the frontmatter description ("constructing a filter expression", "sort or paginate", "aggregation queries", "expand related records", "full-text search", "offset vs keyset"). The description is what routes the load; by the time the body is in context the routing decision is already made. DELETE −180 ① no — it changes no decision, the load already happened ② yes ③ n/a HIGH yes
    QRY-B-03 B SKILL.md:32-40 "## Skill Boundaries" table (4 rows) restates the description's "Do not use for …" clause and skills/README.md:94-108 "Cross-skill routing". Two of its rows (objectstack-platform, objectstack-ui) are not even mentioned in the description's exclusions, so it is not a summary of anything the package decides. DELETE (routing lives in description + catalog) −110 ① no ② yes ③ n/a MED yes
    QRY-B-04 B SKILL.md:20-52 308 tokens of preamble (blurb 55 + legend 73 + boundaries 110 + when-to-use 180 ≈ overlapping spans) sit before the first rule at SKILL.md:54. The single most decision-changing fact in the package — that object is the first argument to every engine method and is not a query key (QRY-E-03) — appears nowhere. Order buries the decisive content under preamble that changes nothing. RESTRUCTURE — open with the calling-convention construct; preamble deleted by B-02/B-03 pays for it 0 (net of B-02/B-03) ① yes ② partly ③ construct HIGH yes
    QRY-B-05 B references/_index.md:14 Generated index describes node_modules/@objectstack/spec/src/data/query.zod.ts — the file carrying the entire QueryAST — as "Sort Node". Cause is upstream: packages/spec/src/data/query.zod.ts:10-16 opens with a Sort Node JSDoc and its own comment says "build-docs.ts takes the FIRST JSDoc block in the file as this page's description". The package's only pointer to the QueryAST schema is mislabelled. Fix upstream (reorder the JSDoc in query.zod.ts), then regenerate — not a hand-edit of the generated file 0 ① yes — the agent is told to Read the source; a mislabelled pointer costs a wrong read ② no ③ n/a HIGH yes (generator-owned artifact)
    QRY-C-01 C SKILL.md:496-539 vs skills/objectstack-data/SKILL.md:110-165 Both carry the same heading ("⛔ Searching by a related record's title — mirror the value, always"), the same sentence "This is the one prescription — emit it every time.", the same task/project_name mirror example, the same "❌ … a formula field is virtual, no driver materializes a column" paragraph, and the same error-text block. SKILL.md:535-537 itself points at data as the anchor: "Modelling side of this (the field, the hooks, the lint wording): objectstack-data → Search Fields (searchableFields)." MERGE-INTO objectstack-data — query keeps 2 lines: "search never traverses; mirror the title into a stored field → objectstack-data → Search Fields" −470 ① no — the second copy changes no decision the first does not ② yes ③ the surviving pointer already exists HIGH yes
    QRY-C-02 C rules/filters.md:221-258 vs skills/objectstack-ui/SKILL.md:1659-1757 ui carries the richer reference: a 36-token fixed table by category, the parameterised {N_UNIT_(ago|from_now)} grammar, the *_end-is-a-calendar-DAY trap (ui:1672-1674 — restated again at rules/filters.md:288-290), the {current_user_id}/{current_org_id} table, and a 3-row near-miss table ({current_user}, {user_id}, {organization_id}) that query's version covers with two of the three. Query's unique contribution is one sentence — that tokens also resolve on the engine read/write paths, so they are safe in a hand-issued query. MERGE-INTO objectstack-ui (anchor: it already owns the full table); query keeps the one engine-path sentence + a pointer −380 ① no for the table, yes for the engine-path sentence (kept) ② yes ③ ui's table is the construct HIGH yes
    QRY-C-03 C SKILL.md:617-632 "## CRM Analytics Query Blueprint" — all five rows restate the dataset/widget surface objectstack-ui owns: KPI widgets + compareTo (ui:1509-1530, including the same "bare string form was removed in 17"), dateGranularity bucketing (ui:837-841), matrix rows/columns (ui:815-841), multi-level grouping, "prefer declarative operators". Its own closing line (SKILL.md:630-631) concedes the routing: "model analytics in report/dashboard metadata first". DELETE — replace with the one routing line already present −260 ① no — every row is a UI-metadata decision made in the ui package ② yes ③ n/a HIGH yes
    QRY-C-04 C rules/filters.md:260-280 vs skills/objectstack-automation/SKILL.md:157-164 automation already states it: "a filter token that resolved to nothing so the condition was dropped … a dropped filter condition widens the query, so routing it would let a delete_record empty the object while the run reported success. Fix the metadata — objectstack validate names the offending template." query restates the same rule over 21 lines, adding only the static-checkability split (validate fails inside a filter, warns outside). MERGE-INTO objectstack-automation (anchor: it is a flow-node config rule, not a query-DSL rule) −330 ① no — the decision is made while authoring the flow node ② yes ③ n/a MED yes
    QRY-C-05 C SKILL.md:624 + rules/aggregation.md:199-207 compareTo: { kind: 'previousPeriod' | 'previousYear', dimension? } documented twice inside this package and a third time, in full, at objectstack-ui/SKILL.md:1509-1530 (which the query text explicitly defers to: "see objectstack-ui → Period-over-period — compareTo"). DELETE both query copies, keep the pointer −110 ① no ② yes ③ n/a HIGH yes
    QRY-D-01 D SKILL.md:274-279, 327-337, 440-445, 550-558; rules/aggregation.md:16-27, 138-144, 156-166; rules/pagination.md:12-18, 218-222 ≈1,173 tokens of tombstone callouts, of which roughly half is why it was removed narration: "No engine or driver ever read it — a query carrying cursor silently returned page 1 forever"; "it only ever declared a capability that did not run"; "whether they worked depended on which driver sat behind the object"; "because exactly one of the six backends that read an aggregation ever honoured it"; "even there the builder drops the field argument, so lag(revenue) renders as LAG()". The customer agent cannot emit these keys: retiredKey() types them never (objectstack-upgrade/SKILL.md:407 "a tombstone whose input type is never. Assigning anything to it fails to [compile]"), the parse rejection carries the prescription verbatim, and the prescriptions ship greppable in the install (objectstack-upgrade/SKILL.md:251-252). objectstack-upgrade owns the retirement procedure and counts 113 tombstones / 96 prescriptions (upgrade:567-568) — this package narrates 6 of them. REWRITE-AS-CONSTRUCT — one 6-row "removed key → live replacement" table in SKILL.md, delete the per-key history everywhere −700 ① the replacement changes a decision; the history does not ② yes for the history ③ one table HIGH yes
    QRY-D-02 D rules/aggregation.md:88-96 Bug-history narration: "including the column keys (earlier SQL drivers ignored alias, so an aliased group came back under the field name when the query was pushed down and under the alias when it fell back — decided by a capability bit and the timezone, neither of which the caller can see)." The live rule is the preceding clause ("Read the result under alias ?? field"); the parenthesis describes behaviour that no longer exists. DELETE the parenthesis −95 ① no ② yes ③ n/a HIGH yes
    QRY-D-03 D SKILL.md:20-28 Opening blurb (20-23) restates the description in prose; the "Schema vs. runtime" legend (25-28) explains a three-way callout convention (⛔/⚠️/✅) that is self-evident at each callout, and its ⚠️ arm is used exactly once in the whole package (SKILL.md:541-544, the experimental search knobs). DELETE the blurb; fold the legend's one live sentence into that single ⚠️ callout −100 ① no ② yes ③ n/a MED yes
    QRY-D-04 D SKILL.md:87-88 "ObjectStack uses a declarative, database-agnostic filter DSL inspired by Prisma, Strapi, and MongoDB." — positioning prose; changes no emitted token. DELETE −25 ① no ② yes ③ n/a HIGH no
    QRY-D-05 D evals/README.md:1-38 558 tokens (4.5% of the package) headed "## Planned Evals" — nine natural-language intentions, no fixtures, no expected outputs, no grader rubric rows. skills/README.md:60-61 ships evals/ to consumers ("inert (but harmless) in consumer installs") — so every customer session pays 558 tokens for evals that were never built. Contrast: objectstack-ui ships a real fixture (evals/analytics-inline-vs-dataset.json), objectstack-automation ships evals/approvals/test-revise-loop.md. Either build the fixtures or DELETE the file −558 ① no — it changes no authoring decision ② yes ③ n/a HIGH yes
    QRY-E-01 E whole package packages/spec/scripts/check-skill-examples.ts:18-22: a self-contained block opts into type-checking with a HTMLCOMMENT-OS-CHECK HTML comment on the line directly above its fence. Measured: SKILL.md 22 ts fences, rules/aggregation.md 11, rules/filters.md 14, rules/pagination.md 11 — 58 fences, 0 markers. Every other schema-domain package carries markers (ui 11, api 6, data 5, ai 4, i18n 4, formula 2, platform 2, automation 1, upgrade 1); objectstack-query and the schema-less pm-dispatch are the only two at zero. Nothing in this package is protected against spec drift. ADD HTMLCOMMENT-OS-CHECK to the canonical find / aggregate / expand examples once they are given a real call shape (QRY-E-03) — paid by QRY-B-01 in the same file +40 ① yes — an unchecked example is what rots into a false claim ② no ③ the marker is the construct HIGH no
    QRY-E-02 E SKILL.md:407-425 The package's canonical expand example expands nothing. It projects fields: ['title', 'status'] while expanding assignee and project — neither foreign-key column is retained. packages/objectql/src/engine.ts:8345-8347 reads const val = record[fieldName]; if (val == null) continue;, so a projected-away FK skips the whole relation. Pinned as contract in two places: packages/spec/src/data/query.zod.ts:509 — "The projection must RETAIN the foreign-key column: fields: ['title'] with expand: 'project_id' resolves nothing, because the relation is carried by that key"; and packages/objectql/src/query-expression-conformance.test.ts:1575-1586 — "Projecting it away (fields: ['title'] alone) leaves expansion nothing to resolve … Measured while writing this test, and worth pinning: it is the one sharp edge in the remedy this card now prescribes." The Rules list at SKILL.md:427-434 does not state the requirement. REWRITE-AS-CONSTRUCT — add the FK to the example's projection and add the rule as a 5th bullet; paid by QRY-C-03 in the same file +35 ① yes — the current example teaches a query that returns no relations ② no ③ corrected canonical example HIGH yes
    QRY-E-03 E SKILL.md:60-69, 256-266, 280-294, 341-352, 407-425, 584-593 vs SKILL.md:371-377, 390-396, 604-612 The package uses two incompatible calling conventions and never names either. Most examples are standalone literals with object: inside ({ object: 'account', limit: 20, offset: 40 }); the aggregation examples use the real engine signature (engine.aggregate('deal', { … }), object as first argument). No engine method accepts object as an option: packages/objectql/src/engine.ts:8914 find(object: string, query?: EngineQueryOptions, …), and EngineQueryOptionsSchema (packages/spec/src/data/data-engine.zod.ts:96-160) declares no object key. The mismatch is fatal, not cosmetic: ENGINE_FIND_OPTION_KEYS (engine.ts:390-394) is context, where, fields, orderBy, limit, offset, search, searchFields, expand and an unlisted key throws — pinned at packages/objectql/src/engine-unknown-option.test.ts:124-135: "%s rejects an unknown option, naming the legal set" / find('task') does not recognise option 'bogus'. Even the protocol door keeps object outside the query (query-expression-conformance.test.ts:1583: findData({ object: 'showcase_task', query: { … } })). A reader copying the package's most common example shape gets a runtime rejection. ADD a 4-row calling-convention construct (engine find/aggregate · protocol findData · nested expand value · view filter rule) at the head of SKILL.md — paid by QRY-B-02 + QRY-B-03 in the same file +120 ① yes — highest-frequency decision in the package ② no ③ table HIGH yes
    QRY-E-04 E SKILL.md:341-352 and rules/aggregation.md:43-54 Both group-by examples carry fields: and (SKILL.md) orderBy:. ENGINE_AGGREGATE_OPTION_KEYS (engine.ts:404-406) is exactly context, where, groupBy, aggregations, having, timezone — fields and orderBy are not in it, so passing these literals to engine.aggregate() throws. rules/aggregation.md:57-59 even calls fields "a readability convention, not a requirement" without saying it is rejected on the engine path. REWRITE-AS-CONSTRUCT — show the aggregate examples in the engine.aggregate(obj, {…}) shape with only legal keys ≈ −30 ① yes ② no ③ corrected example HIGH yes
    QRY-F-01 F absent from the package context is the second-most-used query option in the repo and has zero coverage. 34 occurrences in packages/objectql/src/*.test.ts query literals; 40 in examples/** + packages/apps/** (e.g. examples/app-showcase/src/security/bind-position-sets.ts:65,92, examples/app-showcase/src/automation/jobs/sweep-project-health.ts:170,184, examples/app-showcase/src/security/seed-approval-demo.ts:132). It is a declared find/aggregate option (engine.ts:391,404; data-engine.zod.ts:71 context: ExecutionContextSchema.partial().optional()) and the RLS/system-read escape hatch. Grepped for a home: objectstack-data covers only runAs exemption for readonly (data:282-289), objectstack-automation covers flow runAs:'system' (automation:166-193), objectstack-platform has no hit. No package teaches the query option. ADD a 3-line note + one example ({ where, limit: 1, context: SYS }) — paid by QRY-C-01 in the same file +90 ① yes — a hook/job query without it hits RLS and silently returns fewer rows ② no ③ canonical example HIGH yes
    QRY-F-02 F SKILL.md:464-495, evals/README.md:32-34 The package teaches only the object form search: { query, fields }. Measured: bare string search: '…' = 74 usages, object form = 5 (all in conformance tests probing the tolerated shape). packages/spec/src/data/data-engine.zod.ts:120-127: "The bare string IS the canonical Tier-1 contract (ADR-0061 D1 …) — it is what every surface sends, what the engine's $search expansion actually serves, and what the dogfood HTTP proof pins. The structured FullTextSearchSchema form remains for the declared Tier-2 knobs." The dogfood proof is packages/qa/dogfood/test/showcase-search.dogfood.test.ts:33,41: query({ search: 'retail', searchFields: ['industry'] }). The package never shows search + sibling searchFields, though it quotes error text about $searchFields at SKILL.md:500,504. REWRITE-AS-CONSTRUCT — make the bare-string + searchFields pair the canonical example, demote the object form to one line ≈ 0 ① yes — the taught form is the 5-usage one ② no ③ canonical example HIGH yes
    QRY-F-03 F SKILL.md:136-146, rules/filters.md:18-21 Both string-operator tables list four operators ($contains/$notContains/$startsWith/$endsWith) and omit the three that exist: $icontains (packages/spec/src/data/filter.zod.ts:669), $like (:689), $ilike (:710). Worse, neither table states the case rule that filter.zod.ts:548-549 makes explicit — "$contains / $notContains / $startsWith / $endsWith compare CASE-SENSITIVELY. $icontains is the case-INSENSITIVE twin" — while the package's canonical examples are exactly the user-facing cases where case-sensitivity is wrong ({ email: { $contains: '@company.com' } } SKILL.md:144, { name: { $contains: 'john' } } filters.md:18). $icontains appears in this package only inside the full-text-search prose (SKILL.md:468). ADD 3 rows + a one-line case note to both tables — paid by QRY-B-01 (SKILL.md) and QRY-C-02/C-04 (filters.md) +70 ① yes — picks a different operator ② no ③ table rows HIGH yes
    QRY-F-04 F SKILL.md:314-400, rules/aggregation.md (whole) Speculative surface for app authors. .aggregate( has 0 call sites in examples/** and packages/apps/**; .count( has 0. The 32 non-test .aggregate( call sites repo-wide are the platform's own analytics service (packages/services/service-analytics/src/plugin.ts:295, strategies/objectql-strategy.ts:114). What app authors actually write is defineDataset({ dimensions, measures }) — examples/app-showcase/src/ui/datasets/revenue-pulse.dataset.ts:14-57, whose measure spelling is { name, label, aggregate: 'count', field, filter, derived, format }, not the { function, field, alias, filter } this package teaches — plus the React-block spelling aggregate={{ field, function, groupBy }} (examples/app-showcase/src/ui/pages/renewals-pipeline.page.ts:166). Three spellings; the package teaches the one app authors do not call and never routes between them. Caveat: zero usage in examples/** is evidence, not proof — a third-party app calling engine.aggregate() in a custom endpoint is plausible; the aggregation content is therefore over-weighted, not valueless. REWRITE-AS-CONSTRUCT — keep the engine-aggregate core (functions, groupBy, having, per-aggregation filter, dateGranularity); replace the analytics framing with a 3-row "which aggregation spelling" routing table; the recovered budget pays QRY-F-01/F-03 ≈ −400 (with C-03, C-05, D-02) ① yes — routing table changes which spelling is emitted ② partly ③ table MED yes
    QRY-F-05 F absent from the package The list-view filter spec named in the description has zero examples in the package. See QRY-A-01 for the schema and real usages. Covered by QRY-A-01 — — HIGH yes
    QRY-G-01 G whole package Retired-spelling residue check: clean. Grepped silently|ignored|dropped|legacy|deprecated|M9|v5\.0|OLD|NEW|project(as workspace): 11 hits, all of them inside removal callouts describing pre-removal behaviour (SKILL.md:27,275,367,398,553; aggregation.md:93,104,135,160; filters.md:282; pagination.md:14) or the correct live claim "rejected loudly, never ignored". No legacy, no deprecated, no M9, no v5.0, no OLD/NEW, no project-as-workspace. Nothing to fix under G; the hits are D-01/D-02 density, not residue. none (measured negative) 0 — HIGH no
    QRY-G-02 G SKILL.md:11,274,330,333,440,550,624; aggregation.md:22,143,158; pagination.md:12,163,218; evals/README.md:15,21,30 Four spellings for one fact: @objectstack/spec` 17 (5×), "removed in 17" (5×), "protocol 17" (4×), "spec 17.x" (1×). skills/README.md:81-91 "Conventions enforced across skills" fixes vocabulary but not this one. Normalize to one spelling (@objectstack/spec 17) ≈ −15 ① no ② n/a ③ n/a HIGH no
    QRY-G-03 G SKILL.md:479,491,500,504, 541 Three spellings of the search-field narrowing knob used without introducing any: fields (inside the search object, :479/:491), searchFields (inside a quoted error, :500), $searchFields (inside the same quoted error, :504). The engine reads ast.searchFields ?? raw?.fields (engine.ts:8779-8780); the protocol normalizes $searchFields → searchFields (engine.ts:8772-8776). A reader cannot tell which to emit. REWRITE-AS-CONSTRUCT — one line naming all three and which layer takes which; folds into QRY-F-02's rewrite ≈ 0 ① yes ② no ③ one line MED yes
    QRY-H-01 H evals/README.md:32-34 Rubric row 9 would fail a correct answer: "Search subset — 'Keyword search over articles.' Expect search: { query, fields } only". A model emitting the canonical Tier-1 contract engine.find('article', { search: 'machine learning' }) — the form with 74 real usages and the one the ADR-0061 dogfood proof pins (QRY-F-02) — is graded wrong. REWRITE-AS-CONSTRUCT — accept the bare string as the expected answer, keep the Tier-2-knob failure condition ≈ 0 ① yes — a grader teaching the wrong form propagates it ② no ③ corrected row HIGH yes
    QRY-H-02 H evals/README.md:7 Heading is "## Planned Evals" — nine intentions, no fixture, no expected output, no pass/fail rubric rows. Nothing here can score a skill. See QRY-D-05 for the cost. Build or DELETE (see D-05) ① no ② yes ③ n/a HIGH yes
    QRY-H-03 H evals/README.md:9-38 The nine evals test operator choice, nesting, pagination, aggregation, having, bucketing, expand, search — none tests the two highest-frequency real decisions the package gets wrong: the calling convention (QRY-E-03) and context (QRY-F-01). Six of the nine hinge on "did the answer avoid a removed key" — keys tsc already types never, so they test what the compiler tests. ADD 2 rows (calling convention, context), DELETE 3 removed-key-only rows ≈ −50 ① yes ② partly ③ rubric rows MED yes
    QRY-I-01 I whole package 12,270 tokens against 6 distinct real authoring patterns. The aggregation cluster — SKILL.md:314-400 (808) + rules/aggregation.md (2,226) + SKILL.md:617-632 (293) = 3,327 tokens, 27% of the package — serves a surface with 0 app-author call sites (QRY-F-04), while context (74 combined real usages) gets 0 tokens and the list-view filter spec named in the description gets 0 tokens. rules/filters.md spends 1,039 of its 2,144 tokens (:203-290, 48%) on date macros and flow-filter tokens that objectstack-ui and objectstack-automation already own. Headroom is 255 tokens total, so every addition must be paid in-file. See the ranked proposals; net ≈ −4,088 (−33%) with the three additions funded −4,088 ① yes ② yes ③ yes HIGH yes

    Incidental falsehoods (by-product, not the job)

    1. SKILL.md:407-425 — the canonical expand example expands nothing. Full evidence in
      QRY-E-02: fields: ['title','status'] + expand: { assignee, project } drops both FK
      columns, and engine.ts:8345-8347 skips a relation whose FK is absent. Contract stated at
      packages/spec/src/data/query.zod.ts:509 and pinned at
      packages/objectql/src/query-expression-conformance.test.ts:1575-1586. HIGH confidence.

    2. Sibling package — skills/objectstack-ui/SKILL.md:1706-1708 contradicts itself and the
      implementation.
      It says: "DON'T invent tokens. Unknown placeholders silently pass
      through as literal strings
      — the resulting SQL compares text against
      '{my_made_up_token}' and matches zero rows." Thirty-four lines earlier the same file says
      the opposite and is correct (ui:1672-1673: "An unrecognised placeholder is a build error
      (validate-filter-tokens) and a runtime throw, never a silent literal"). The implementation
      agrees with the earlier claim: packages/core/src/utils/filter-tokens.ts:44 ("# An unknown
      token throws") and :378 (throw new UnknownFilterTokenError(cls.token, cls.suggestion)),
      with packages/core/src/utils/filter-tokens.test.ts:208,225 ("throws on an unknown
      placeholder and names the near miss" / "throws on a near-miss period spelling").
      objectstack-query/rules/filters.md:282-286 states it correctly. HIGH confidence — the
      defect is in objectstack-ui, not in the audited package, and it is the one place where the
      duplication in QRY-C-02 currently protects the reader.

    Generated by Claude Code


    Generated by Claude Code

  6. added
    needs:contract-reviewMarker only — PR awaits its at-tier Contract review PASS; no check reads it, the record decides
    on Sep 2, 2026
  7. os-litant commented on Sep 2, 2026

    @os-litant
    ContributorAuthor

    os-dev-report

    {
      "issue": 14302,
      "status": "done",
      "branch": "claude/issue-14302-skills-query-optimization",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/14443",
      "premise_still_valid": true,
      "summary": "Implemented the flight's IMPLEMENT list on skills/objectstack-query: the seven twice-stated rules merged into rules/* (SKILL.md keeps one-line pointers, the rules files did not grow), the four removal-history spans collapsed into one 6-row 'removed key -> live replacement' table, and the search mirror / flow-filter rule / compareTo / CRM analytics blueprint pointed at their anchors in objectstack-data, -automation and -ui. Four funded additions landed paid in-file: the calling convention (object is the FIRST argument; the closed ENGINE_FIND/AGGREGATE/COUNT_OPTION_KEYS sets), `context` as the RLS/system-read escape hatch, the three missing string operators with the case rule quoted from filter.zod.ts, and the corrected canonical expand example (falsehood 1 - the old one projected away both FK columns, so it resolved no relations). All five TRUE rows the delivered ui flight listed as missing were verified at source and ported into rules/filters.md, paid by the QRY-C-04 deletion. Net -2,306 authored tokens (11,194 -> 8,888); every edited file shrank, no ceiling raised, no file added or deleted, evals/** and the generated references/_index.md untouched. The gap to the ~-3,530 target is itemised in the PR body, not smoothed: -380 is the C-02 override the target figure still carried, -400 is QRY-F-04 (MED, not in the IMPLEMENT list), ~+250 is the post-audit ui rows, ~+194 is pointer lines the raw-span estimates did not price. PR stays DRAFT (governed surface); needs:contract-review and skip-changeset applied and read back twice, the second read delayed.",
      "tests": "Head sha 3a2b76a0 (merge of origin/main 6aea1f55 into the branch, so the derivation is not from a stale tree). Union re-run AFTER the final commit; every exit code captured before any pipe (cmd >file 2>&1; EXIT=$?). `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` re-derived after regeneration over the 5-path change set: 36 commands, byte-identical to the pre-merge derivation. Ran all 36: 35 exit 0, 1 exit 3 = NOT MEASURED by that gate's own text (`check-test-completeness` prints 'PREREQUISITE NOT MET - this gate grades a saved turbo run test log, and no log was named ... record this gate as NOT MEASURED. It is not a red.'); recorded as NOT MEASURED, not as green. Verdict lines quoted from the gates: `check-skills-token-ratchet: 38 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted.` / `Skill docs in sync` / `263 prose examples type-check across 3 surface(s) - every marked block parsed, so tsc ran the SEMANTIC pass on all of them` / `check-skill-identifier-liveness OK - Leg 1: 499 citation(s) over 48 published file(s) checked against 92908 implementation word tokens (3 ledgered exemption(s)); Leg 2: 9 registered exhaustive section(s), 0 ledgered gap(s).` / `check-skill-compatibility-version: 11 SKILL.md file(s) reconciled against 78 workspace packages`. check:skill-examples ran against a fresh build of the @objectstack/spec and @objectstack/client-react closures (`pnpm --workspace-concurrency=2 --filter '@objectstack/spec...' --filter '@objectstack/client-react...' build`, exit 0), so it refused nothing rather than false-greening on a stale dist. Heavy steps under scripts/pm/os-verify-lock.sh, OS_VERIFY_LOCK_SLOT=issue-14302 (build held 465s, waited 0s). No ablation was run: this is a prose-only diff with no guard to mutate. Baseline ratchet measured at BASE before the first edit and matched the audit's price tag exactly; identifier-liveness run BEFORE the first deletion and after the last.",
      "mcp_calls": "7",
      "open_questions": [],
      "out_of_scope_findings": [
        "filed as #14441: QRY-B-05 - packages/spec/src/data/query.zod.ts opens with SortNode's JSDoc, so the generated skills/objectstack-query/references/_index.md:14 describes the whole QueryAST file as 'Sort Node'; fix is upstream then regenerate, generated artifact untouched here (finding label, unassigned, no queue label, deduped against 4 closed near-misses)"
      ]
    }

    Notes the PM will want beside the JSON (full detail is in the PR body, not restated here):

    • PM mechanism assumption 1 — CONFIRMED. git diff --stat a59f78d 96b627d1 -- skills/objectstack-query empty at my actual base; every audit line number was exact.
    • PM mechanism assumption 2 — HALF FALSIFIED. The QRY-A-01 description edit regenerates content/docs/ai/skills-reference.mdx only. skills/README.md is byte-unchanged — it renders just the description's FIRST sentence, which A-01 did not touch. check:skill-docs green on both.
    • PM mechanism assumption 3 — FALSIFIED (harmlessly). No skills/objectstack-query heading is registered exhaustive; the Leg 2 BINDINGS table holds 9 rows, all in objectstack-api / -data / -platform / -ui. No deletion here could red Leg 2.
    • Ruling conflict, resolved in favour of the dispatch: the card body says the PR's first line is Fixes #<this card>; the dispatch and the PM claim comment say Part of #14302 because QRY-D-05 / H-02 / H-03 are deferred to [Decision] Skills optimization program — batch 1 (4 items): new-file ceilings for splits · planned-eval stubs · react-blocks double rendering · published pm-dispatch scope #14296. Part of was used, and no closing keyword appears anywhere in the PR body or in any commit message (scanned).
    • QRY-C-05 disposition: compareTo was established at source as a LIVE surface (packages/spec/src/ui/dashboard.zod.ts:438 declares it a strictObject; what was removed is the bare-string form and compareTo.offset), so the live branch of the ruling applied — delete both copies, keep the pointer. Target verified present on main and on the ui branch.
    • follow-up for objectstack-automation (in the PR body): the anchor at objectstack-automation/SKILL.md:157-164 does not carry the config.filter precedence rule, nor the objectstack validate fails-inside-a-filter / warns-outside split. Both were deleted here under C-04.
    • Channel: repo-scoped REST is 403 for this session, so all single-card reads went through the public-repo embedded JSON payload (zero quota) and only writes plus one dedupe search used MCP — 7 calls total. PR bodies do NOT SSR that payload, so the mandatory PR-body read-back used pull_request_read; it came back intact end-to-end.

    Generated by Claude Code


    Generated by Claude Code

  8. os-litant commented on Sep 2, 2026

    @os-litant
    ContributorAuthor

    os-dev-report — correction to one field of the report above

    The mcp_calls field in the report comment reads "7". That was miscounted at write time: 8 MCP calls had already been made when it was written, and the run's true total is 11. Every other field in that comment stands; nothing about the delivery changes. Correcting rather than re-posting, so the PM's scanner still sees exactly one report.

    The 11, in order: (1) dev claim comment · (2) audit-record comment · (3) search_issues dedupe for the out-of-scope card · (4) issue_write create #14441 · (5) create_pull_request #14443 · (6) pull_request_read get — the mandatory PR-body read-back, needed because PR pages do NOT server-render the embedded JSON payload that issue pages do · (7) issue_write update, PR labels · (8) issue_write update, card label · (9) the report comment itself · (10) issue_read get_comments — the report read-back, needed because the comment had rolled onto a second timeline page the payload channel does not carry · (11) this correction.

    Every single-card read that COULD go through the zero-quota public-repo payload channel did (card body, all comments, both label read-backs, both delayed label confirmations); the 11 above are writes plus the three reads that channel provably cannot serve.

    Also worth recording from that read-back: the sanitizer ate one angle-bracket fragment from the report comment — Fixes #(this card) was stored as `Fixes #`. Cosmetic, and it cannot trip the closing-keyword parser since no number survives beside it. The PR body and the audit-record comment were both read back byte-intact.

    Generated by Claude Code


    Generated by Claude Code

  9. os-litant commented on Sep 2, 2026

    @os-litant
    ContributorAuthor

    Contract review round 1 (clause ②, in-seat; served model read claude-fable-5-1 = CONTRACT_REVIEW_TIER) on PR #14443 head 3a2b76a0 — FAIL on two spans, patch round dispatched. Everything else passed; the PASS ledger is recorded here so round 2 only re-reads the patched spans.

    FAIL 1 — the calling-convention table, find/findOne row (SKILL.md). packages/objectql/src/engine.ts:390-402 defines ENGINE_FIND_OPTION_KEYS as the nine query keys PLUS ...ENGINE_DRIVER_PASSTHROUGH_KEYS (:362-364: transaction, tenantId, tenantIds, timezone, bypassTenantAudit, preserveAudit). "Closed set — an unlisted key is refused by name" over a nine-key list tells a reader transaction is refused; it is accepted. count and aggregate rows are exact.
    FAIL 2 — the $like/$ilike refusal sentence (rules/filters.md). "driver-mongodb, objectql having and service-analytics refuse them (INVALID_FILTER)" is not backed: measured refusals are driver-memory's shared $like gate (packages/drivers/driver-memory/src/filter-refusal.ts:34, #7536) and driver-mongodb throwing Unsupported filter operator (pinned at mongodb-operator-key-clobber.test.ts:159-160); no having / service-analytics refusal and no INVALID_FILTER binding were found.

    PASS — derived judgments verified at source: ENGINE_COUNT_OPTION_KEYS = context, where, ENGINE_AGGREGATE_OPTION_KEYS = context, where, groupBy, aggregations, having, timezone (engine.ts:403-406); unknown option refused by name (:552); filter→where, top→limit folds (:8916); trailing options.context wins over query.context, writes take the trailing argument only (:1865-1877); { isSystem: true } spelling (examples/app-showcase/src/security/bind-position-sets.ts:50); the six removed keys are retiredKey() tombstones (query.zod.ts:271, :549, :552, :564, :567; array_agg/string_agg at :87-91, :159-160); count_distinct is the sixth function (data-engine.zod.ts:401); the case-sensitivity rule is quoted verbatim from filter.zod.ts:548-549 incl. the ASCII folding domain; FK-projection requirement for expand (query.zod.ts:509; engine.ts:8358-8359, :8372); bare-string search is the canonical Tier-1 contract (data-engine.zod.ts:120-127); the six Tier-2 knobs are [EXPERIMENTAL — not enforced] (query.zod.ts:442-447); dotted searchFields → 400 INVALID_FIELD (engine.ts:992, :1081); DATE_MACRO_WRAPPED_RE = /^\$?\{([a-zA-Z0-9_]+)\}$/ (date-macros.zod.ts:203), CONTEXT_TOKEN_WRAPPED_RE = DATE_MACRO_WRAPPED_RE (context-tokens.zod.ts:109), CONTEXT_TOKEN_SUGGESTIONS user_id/organization_id (:198, :203), isDateMacroToken (:166) / isContextToken (:95), FILTER_TOKEN_UNKNOWN (validate-filter-tokens.ts:65), braces-inside-a-string left untouched (filter-tokens.ts:50), server-side resolution on read and write paths (engine.ts:8723, :8741) and the analytics normalizer (service-analytics/src/strategies/filter-normalizer.ts), client-side resolvers in @object-ui/core utils/filter-tokens.ts; VIEW_FILTER_OPERATORS is 20 operators incl. equals, icontains, is_null, before, between; compareTo is live strictObject (dashboard.zod.ts:438) with the bare-string form and .offset retired (:272, :290) — the live-surface branch (delete + pointer) was the right ruling.
    ② Semver: no release; skip-changeset consistent. ③ Boundary flags: open_questions empty; QRY-B-05 filed as #14441; QRY-E-01 / F-04 not done and listed — accepted (not in the IMPLEMENT list); the automation follow-up (two rules the anchor lacks) recorded for #14308.

    Label stays on both carriers until round 2 passes.


    Generated by Claude Code

  10. os-litant commented on Sep 2, 2026

    @os-litant
    ContributorAuthor

    Contract review round 1 applied — PR #14443, new head 9663526 (was 3a2b76a). Still draft; needs:contract-review and skip-changeset unchanged on both carriers.

    Defect 1 — real, fixed. The calling-convention table listed nine keys for engine find/findOne and called the set closed. packages/objectql/src/engine.ts:396-400 spreads ENGINE_DRIVER_PASSTHROUGH_KEYS (:368-370 — transaction, tenantId, tenantIds, timezone, bypassTenantAudit, preserveAudit) into it, so as written the table said an explicit tenantId is refused by name. The row now names all fifteen, and the prose carries the source's own reason (:360-366): on find/findOne/update/delete the option bag IS the base of the driver options, while count/aggregate never forward the bag and so deliberately reject those keys. The count and aggregate rows were already exact and are untouched.

    Defect 2 — verified at source, so the face list is kept and sharpened rather than dropped. Review offered "verify each face with file:line and the exact message, or reduce the sentence"; I took the first route and all three refusals hold. Each is an allowlist miss, which is why grepping the drivers for a literal $like finds nothing:

    • driver-mongodb — mongodb-filter.ts:1071-1074 throws Unsupported filter operator "$like" …; unsupportedFilterError at :366-371 sets INVALID_FILTER / 400. Pinned at mongodb-operator-key-clobber.test.ts:158-161.
    • objectql having — having-filter.ts:136-141 CONDITION_OPERATORS is sixteen operators without $like/$ilike; anything else reaches unknownOperator (:174-205) → invalidFilterError, INVALID_FILTER / 400.
    • service-analytics — strategies/filter-normalizer.ts:418-435 MONGO_TO_CUBE_OP omits them; the miss branch at :1032-1046 throws invalidFilterError (:403-406, INVALID_FILTER / 400).

    Two of the review's readings did not survive measurement, and correcting them made the prose better rather than weaker: driver-memory ANSWERS these operators (memory-driver.ts:58 declares its vocabulary as [...FILTER_OPERATORS, '$like', '$ilike']; arms at :1522 and memory-matcher.ts:458), and filter-refusal.ts's hasDanglingLikeEscape gate (:36, :720-726) refuses a malformed pattern — a lone unpaired trailing backslash — not the operator. The skill now states which faces answer, which refuse, and the escape rule binding all of them. The spec .describe() at packages/spec/src/data/filter.zod.ts:697 is accurate, so no spec-side twin card is owed from this round.

    Gates at 9663526, all five card-named ones, exit captured before any pipe, against a freshly rebuilt @objectstack/spec + @objectstack/client-react closure (build exit 0) so check:skill-examples could not false-green on a stale dist:

    exit=0  node scripts/check-skills-token-ratchet.mjs
    exit=0  pnpm --filter @objectstack/spec run check:skill-examples
    exit=0  pnpm check:skill-identifier-liveness
    exit=0  pnpm --filter @objectstack/spec run check:skill-docs
    exit=0  pnpm check:skill-compatibility
    

    Tokens. No ceiling changed. Every edited file is still smaller than it started: SKILL.md 5,443 → 3,915 · aggregation.md 2,226 → 1,846 · filters.md 2,144 → 2,136 (the round-2 additions paid in-file by two Common-Mistakes blocks that restated the sections above them) · pagination.md 1,381 → 1,158. Edited-file total 11,194 → 9,055, net −2,139. The PR body's token table is refreshed and its "authored total" row relabelled — round 1 summed only the four edited files while listing the two untouched ones in the same table.

    Generated by Claude Code


    Generated by Claude Code

  11. os-litant commented on Sep 2, 2026

    @os-litant
    ContributorAuthor

    Round 3 — CI red on #14443 fixed. New head 49de20f (was 9663526). Still draft; all four labels unchanged.

    The red. pnpm check:role-word reds on improvement as well as regression. Round 2's deletion of two Common-Mistakes blocks in rules/filters.md removed one baselined occurrence, and the gate refused with its own remedy: role-word count improved 9 → 8 — ratchet DOWN: run node scripts/check-role-word.mjs --update and commit the baseline. Reproduced locally first (exit 1, same message), then --update moved exactly one row — skills/objectstack-query/rules/filters.md: 9 → 8 — and no other, asserted on git diff before committing. It is its own commit, 8208cf30. No ceiling changed.

    Why it reached CI. Round 2 re-ran only the five card-named gates instead of re-deriving the union, and this family is reached by the prose paths, not by the card's list. My error; round 3 runs the whole re-derived list.

    One thing worth flagging, which only the merge exposed. The data flight (#14427) landed on main between rounds and ratcheted a different row of the same scripts/role-word-baseline.json (objectstack-data/SKILL.md 4 → 2). Deriving and gating on my un-merged branch would have read a stale copy of the very file I had just changed — the dispatch-gates STALE TREE warning named it explicitly. So I merged current main (a98b61b) before measuring anything; the merge is textually clean (different lines) and both rows are correct on the merged tree: data 2, query 8. I also re-verified all three cross-package pointers survived that flight — objectstack-data/SKILL.md:100 (C-01), objectstack-ui/SKILL.md:1410 (C-05), and the automation anchor (C-04).

    Union at 49de20f — re-derived over the now 6-path change set: 42 commands, six more than rounds 1-2, pulled in by the baseline file itself (check:bash32-floor, check:cli-command-ids, check:entry-guard, check:parse-guard, check:pnpm-filter-targets, check:watch-hint-literal — the same six the api flight measured). All 42 run, exits captured before any pipe: 41 green, 1 NOT MEASURED (check-test-completeness exits 3, "PREREQUISITE NOT MET … record this gate as NOT MEASURED. ⛔ It is not a red."). Nothing red.

    The gate that failed, now green, and the six new families:

    ✓ check-role-word — Ledger: 43 baselined file(s) still carrying it (126 occurrence(s))
    ✓ check-bash32-floor: 26 tracked shell file(s) name no bash 4+ construct …
    ✓ check-cli-command-ids: 315 command-id literal(s) across 112 file(s) all resolve
    ✓ check:entry-guard: 198 scripts/ file(s) — every entry guard goes through invoked-as.mjs
    ✓ check:parse-guard (exit 0)
    ✓ check:pnpm-filter-targets: 142/181 `--filter` occurrence(s) across 32 file(s) resolve
    ✓ check-watch-hint-literal: 43 declaration(s) across 4 rostered name(s)
    

    The five card-named gates at the same head, against a freshly rebuilt @objectstack/spec + @objectstack/client-react closure (build exit 0):

    ✓ check-skills-token-ratchet: 37 authored bundle file(s) within their ceilings
    ✅ 265 prose examples type-check across 3 surface(s) — every marked block parsed
    check-skill-identifier-liveness OK — Leg 1: 491 citation(s) over 47 published file(s); Leg 2: 8 registered exhaustive section(s), 0 ledgered gap(s)
    ✅ Skill docs in sync
    ✓ check-skill-compatibility-version: 11 SKILL.md file(s) reconciled against 78 workspace packages
    

    (The ratchet's file count and Leg 2's binding count each dropped by one against round 2 because the data flight deleted skills/objectstack-data/rules/hooks.md. Nothing in this PR moved either number.)

    Tokens unchanged by round 3 — no skill file was touched. SKILL.md 5,443 → 3,915 · aggregation.md 2,226 → 1,846 · filters.md 2,144 → 2,136 · pagination.md 1,381 → 1,158; edited-file total 11,194 → 9,055, net −2,139. The PR body now carries a "Files outside the declared surface" section naming scripts/role-word-baseline.json with the gate-prescribed reason, and every head sha is refreshed.

    Generated by Claude Code


    Generated by Claude Code

  12. os-litant commented on Sep 2, 2026

    @os-litant
    ContributorAuthor

    ACCEPT — PR #14443 @ 49de20f7 (round 3), verified against GitHub, not the report.
    Session: session_01LraLgQVGq8egUwfYZpbYt1 · seat review in-seat at CONTRACT_REVIEW_TIER (served model read claude-fable-5-1).

    Landing form. Draft; first line Part of #14302 (QRY-D-05 / H-02 / H-03 deferred to #14296 item 2 keep this card open); no closing keyword in body or commits (the Part-of PR must not also close its card guard is green). Six files against merge base a98b61b3: the four authored skill files, the generated content/docs/ai/skills-reference.mdx, and scripts/role-word-baseline.json moving exactly one row (skills/objectstack-query/rules/filters.md 9 → 8, commit 8208cf30, gate-prescribed after round 2 deleted a baselined occurrence). Ratchet: SKILL.md 5,443 → 3,915 · aggregation.md 2,226 → 1,846 · filters.md 2,144 → 2,136 · pagination.md 1,381 → 1,158 — every edited file shrank, no ceiling raised, script untouched. origin/main merged in at 49de20f7; the branch commits are the four flight commits, the round-1 patch, the baseline row and two merges.

    CI at 49de20f7: green — Lint & Repo Gates (the job that was red on 96635262) success; Type Check ×5, Test Core 1–6, Build Docs, Governed Surface Queue Guard, single-writer / same-issue / Part-of guards, Spec property liveness, doc links, PR size all success.

    Contract review — round 2 PASS (recorded here; round 1 was FAIL at comment 5505932564).

    • ENGINE_FIND_OPTION_KEYS = the nine query keys plus ENGINE_DRIVER_PASSTHROUGH_KEYS (transaction, tenantId, tenantIds, timezone, bypassTenantAudit, preserveAudit) — packages/objectql/src/engine.ts:362-370, spread at :396-400; count = context, where; aggregate = context, where, groupBy, aggregations, having, timezone, both without passthrough by the source's own comment. The table now says exactly that.
    • $like / $ilike faces: refused as allowlist misses at driver-mongodb/src/mongodb-filter.ts:1071-1074, objectql/src/having-filter.ts:136-141 (CONDITION_OPERATORS), service-analytics/src/strategies/filter-normalizer.ts:418-435 (MONGO_TO_CUBE_OP), each with an INVALID_FILTER / 400 envelope; driver-memory ANSWERS them (memory-driver.ts:58, :1522, memory-matcher.ts:458); hasDanglingLikeEscape gates a malformed pattern, not the operator. The reviewer's two contrary readings in round 1 were wrong and the dev's correction stands. filter.zod.ts:697 is accurate — no spec twin owed.
    • Round 3 changed no skill file; the contract verdict carries over unchanged.

    Labels. needs:contract-review stripped from #14302 and #14443 by this comment's author under the 2026-08-31 ruling (PASS ⇒ same seat strips, both carriers); card → documentation, priority:p1, pm:dispatched, domain:skills; PR → documentation, size/l, skip-changeset (verified against scripts/check-empty-changeset.mjs's own enumeration).

    Next. Reviewers os-zhuang + hotlong requested with draft: true; the merge is the maintainer's. At merge this card flips pm:dispatched → pm:blocked with Blocked-by: #14296. Seat ledger: the two flow-filter rules the automation anchor lacked have since landed in PR #14467 (### Filter tokens (config.filter)), so that follow-up is closed; #14441 (SortNode JSDoc) stays the spec lane's.


    Generated by Claude Code

  13. removed
    needs:contract-reviewMarker only — PR awaits its at-tier Contract review PASS; no check reads it, the record decides
    on Sep 2, 2026
  14. os-litant commented on Sep 2, 2026

    @os-litant
    ContributorAuthor

    Round 1 MERGED — PR #14443 landed on main at 12:00Z. The card stays open for its second round, claimed in the same stroke.

    Claim: PM loop round 4 — second round on this card
    Session: session_01LraLgQVGq8egUwfYZpbYt1
    Branch: claude/issue-14302-query-eval-stub-removal (fresh from origin/main; round 1's branch is merged and is not reused)
    Worktree: objectstack-issue-14302-r2
    Domain: domain:skills
    File surface: DELETE skills/objectstack-query/evals/README.md (558 tokens — the "planned evals" stub QRY-D-05 / H-02 / H-03 deferred); drop its ceiling row from scripts/check-skills-token-ratchet.mjs (the one edit to that script a flight may make); ⛔ nothing else
    Container & model: S (two-file mechanical deletion under a ruling; no contract claim), mode:subagent, model: sonnet — the floor; the seat reviews the diff by hand
    Clause-②: no ⇒ ⛔ no needs:contract-review
    Ruling executed: #14296 item 2 = A (comment 5507419465, 09:27Z) — planned-eval stubs deleted, ceilings step down; quoted in the PR body
    Serial constraints cleared: no open PR touches skills/objectstack-query/**; the ratchet script is touched only to drop one row (#14564 api and the i18n flight each drop their own — different lines, merges clean); light flight, no heavy slot

    Landing form: the ACCEPT ledger above names the eval items as the only deferral, so this PR closes the card — first line Fixes #14302. Governed ⇒ draft; skip-changeset. State stays pm:dispatched.


    Generated by Claude Code

  15. added a commit that references this issue on Sep 2, 2026
    3e1a218
  16. os-litant commented on Sep 2, 2026

    @os-litant
    ContributorAuthor

    Dev claim (os-dev subagent, round 2, dispatched by the PM claim above)

    Session: session_01LraLgQVGq8egUwfYZpbYt1 (same id as the PM claim — the dev subagent runs inside the dispatching session; the distinguishing marker is this comment plus the branch below)
    Branch: claude/issue-14302-query-eval-stub-removal (empty branch pushed at claim time as the write-route probe — [new branch], accepted)
    Worktree: /home/user/objectstack-issue-14302-r2, BASE 7286dd58e806ed321cbdfc23a1c455db8f80b1ad
    Assignee: untouched (PM-owned).

    Verified before touching anything: skills/objectstack-query/evals/ held only README.md (no other file or subdirectory); the README lists nine prose "Planned Evals" scenarios, not a path listing, so there was no separate fixture path to check for existence.

    File surface: git rm skills/objectstack-query/evals/README.md; drop its one ceiling row from scripts/check-skills-token-ratchet.mjs — the only edit to that script. Nothing else.

    Landing form: ONE draft PR against main, first line Fixes #14302 (the seat's ACCEPT comment on #14443 names the eval items as the only deferral on this card), skip-changeset label, no needs:contract-review.

    Full report follows as the next comment.

    Generated by Claude Code


    Generated by Claude Code

  17. os-litant commented on Sep 2, 2026

    @os-litant
    ContributorAuthor

    os-dev-report

    {
      "issue": 14302,
      "status": "done",
      "branch": "claude/issue-14302-query-eval-stub-removal",
      "pr": "https://github.com/objectstack-ai/objectstack/pull/14585",
      "premise_still_valid": true,
      "summary": "Round 2 of #14302, executing #14296 item 2 (comment 5507419465, ruled A) for skills/objectstack-query: deleted the 558-token evals/README.md planned-eval stub (nine prose scenarios never built into eval cases) and dropped its one ceiling row from scripts/check-skills-token-ratchet.mjs -- the only edit to that script. Confirmed evals/ held nothing else before deleting. No prose edit, no new file, no ceiling raised, no other package touched.",
      "tests": "Reverse verification: node scripts/check-skills-token-ratchet.mjs was RED before dropping the row ('cannot read skills/objectstack-query/evals/README.md -- red, not a skip (#4690)', exit 1) and GREEN after ('36 authored bundle file(s) within their ceilings; 11 generator-owned file(s) measured, not ratcheted', exit 0); --self-test: 64/64 cases pass. check:skill-docs (pnpm --filter @objectstack/spec check:skill-docs) -> 'Skill docs in sync', no regen needed. Gate union re-derived at head 3e1a218e via node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands -> 24 commands, all run under scripts/pm/os-verify-lock.sh (slot issue-14302-r2-gates; held 20m28s across two acquisitions, first hit its queue budget and resumed the same slot). 22/24 exit 0 (including check:pm-dispatch-gates, 1240 self-test cases pass, and check:skill-identifier-liveness / check:skill-compatibility, both green and unaffected by the deletion); 2 NOT MEASURED at their own documented exit 3 (check-test-completeness: no saved turbo-test log; check:doc-formula-expressions: @objectstack/formula not built -- this flight touches neither). Every exit code captured before any pipe (redirected to a log file, $? read directly). node scripts/check-nul-bytes.mjs clean (7953 tracked files).",
      "mcp_calls": "11",
      "open_questions": [],
      "out_of_scope_findings": []
    }

    Generated by Claude Code


    Generated by Claude Code

  18. os-litant commented on Sep 2, 2026

    @os-litant
    ContributorAuthor

    os-dev-report — correction to one field of the report above

    The mcp_calls field in the report comment reads "11". That was the count at write time; the mandatory post-write read-backs (this run's PR-body read-back and the report-comment byte-intact read-back, both confirmed clean) added 3 more calls, bringing the run's true total to 14. Every other field in that comment stands; nothing about the delivery changes. Correcting rather than re-posting, so the PM's scanner still sees exactly one report.

    Read-back results, for the record: the report comment above (5510267674) and the dev-claim comment (5510262047) both came back byte-intact — no angle-bracket-shaped fragments in either, nothing for the sanitizer to eat. The PR body (#14585) was read back immediately after the skip-changeset label write and matches what was sent verbatim, footer included (session-URL form, create-time).

    _Generated by Claude Code


    Generated by Claude Code

  19. os-litant commented on Sep 2, 2026

    @os-litant
    ContributorAuthor

    ACCEPT — round 2 (eval-stub deletion), PR #14585 @ 3e1a218e

    Session: session_01LraLgQVGq8egUwfYZpbYt1 (skills lane seat)

    Verified against GitHub, not the report:

    Governed surface (skills/**, scripts/check-skills-token-ratchet.mjs): the PR stays draft; review requested from os-zhuang and hotlong; landing is the maintainer's.


    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

Labels

documentationImprovements or additions to documentationdomain:skillsDomain lane — seat card indexed by label:pm:seatpriority:p1High: required for production / M2

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions