Repository navigation
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
Description
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)
SKILL.md:407-425— the canonicalexpandexample expands nothing: it projectsfields: ['title','status']while expandingassigneeandproject, dropping both FK columns;packages/objectql/src/engine.ts:8345-8347skips a projected-away FK. Contract atpackages/spec/src/data/query.zod.ts:509, pinned atquery-expression-conformance.test.ts:1575-1586. The Rules list at:427-434omits the requirement — add it. HIGH.- Sibling: objectstack-ui
:1706-1708"unknown placeholders silently pass through" is FALSE (throwsUnknownFilterTokenError) — the ui flight deletes it; this package'sfilters.md:282-286is 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, plusnode 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-reviewon both carriers (falsehood 1 and the$icontainsrows are contract-semantics claims).
Activity
os-litant commentedon Sep 2, 2026
Claim: PM loop round 2
Session:
session_01LraLgQVGq8egUwfYZpbYt1Branch:
claude/issue-14302-skills-query-optimizationWorktree:
objectstack-issue-14302Domain:
domain:skillsFile surface:
skills/objectstack-query/**—SKILL.md,rules/aggregation.md,rules/filters.md,rules/pagination.md, every file shrink-only;evals/**and generatedreferences/_index.mduntouched; no file is deleted ⇒ ⛔ no edit toscripts/check-skills-token-ratchet.mjs;skills/README.md+content/docs/ai/skills-reference.mdxregenerated 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 forskills/**(floor sonnet · default opus · ceiling fable); the content limb applies, compensated by the seat's in-seat review atCONTRACT_REVIEW_TIER(served model readclaude-fable-5-1at 06:08Z)Clause-②: yes — corrects published contract-semantics claims (falsehood 1: the canonical
expandexample projects away the FK columns it expands; the engine calling convention andENGINE_FIND_OPTION_KEYSrejection;$icontains/$like/$ilikecase-sensitivity;contextsystem reads) ⇒needs:contract-reviewon both carriers at PR creation; in-seat review before any human review requestSerial constraints cleared: this package is the ANCHOR for the filter DSL, date macros / filter tokens, pagination and aggregation grammar —
rules/filters.md:221-258and:282-286STAY; the delivered ui flight (PR #14338, ACCEPTed) already points here and its PR body lists five TRUE rows absent fromrules/filters.md— those are funded additions of THIS flight (paid insiderules/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 touchesskills/objectstack-query/**(open PR list read 06:2xZ); in flight alongside #14297 (data, landing) and #14299 (platform, patch round) on disjoint directoriesDecision 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
os-litant commentedon Sep 2, 2026
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, BASE96b627d135826253981cfa01d74b2832ccdea194Assignee: untouched (PM-owned).
PM mechanism assumption 1 — VERIFIED at my actual base:
git diff --stat a59f78d 96b627d1 -- skills/objectstack-queryis 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.md518 generated).File surface I will touch:
skills/objectstack-query/SKILL.md,rules/aggregation.md,rules/filters.md,rules/pagination.md, plusskills/README.mdandcontent/docs/ai/skills-reference.mdxonly as generator output after the QRY-A-01 frontmatter edit.evals/**untouched (QRY-D-05/H-02/H-03 deferred to #14296); generatedreferences/_index.mduntouched; no new files, no deletions, no ratchet-script edit, no other package.Landing form: ONE draft PR against
main, first linePart of #14302(not a closing keyword — the deferred eval items keep this card open),needs:contract-reviewon 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
os-litant commentedon Sep 2, 2026
audit-record — skills/objectstack-query (read-only audit at a59f78d)
objectstack-query — published-skill audit (read-only)
Pinned ref: objectstack
origin/main=a59f78d. The checkout sits ata39b02a;git diff a59f78d HEAD -- skills/objectstack-query/is empty, so every span below isvalid at the pinned ref.
Package price tag (
node scripts/check-skills-token-ratchet.mjs)SKILL.mdrules/aggregation.mdrules/filters.mdrules/pagination.mdevals/README.mdreferences/_index.mdReal-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):where169 ·context34 ·fields17 ·orderBy15 ·expand15 ·search12 ·aggregations9 ·top6 ·limit5 ·groupBy4 ·searchFields1.In
examples/**+packages/apps/**:where26,context:/...ctx40,.find29,.findOne1,.count0,.aggregate0.Search form: bare string
search: '…'74 usages vs object formsearch: { query: … }5 (all five in conformance tests exercising the tolerated shape).Distinct real authoring patterns found: 6 — (1)
engine.find(obj, {where, …})inhooks/jobs/endpoints; (2) list-view / nav-item
filterrules; (3)expandfor lookupdisplay; (4) bare-string
search+searchFields; (5)defineDatasetmeasures/dimensionsfor analytics; (6) offset paging on list surfaces.
Findings
SKILL.md:3-9(desc) +SKILL.md:85-231descriptionpromises "designing a list view's filter spec". The real list-view filter spec isfilter: [{ 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:1736carries the array shape. An agent routed here for that task emits a shape the view schema refuses.description, add a 3-row "which filter dialect" table (ObjectQL$/ view rule / dataset measure filter)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-208diffof the field-reference pair differs only in heading level and sample names (actual_revenue/estimated_revenuevsactual_cost/budget); the filtered-aggregation pair shares the verbatim code comment// An unknown operator insidefilterrefuses 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.rules/*— SKILL.md keeps the pointer table atSKILL.md:75-82onlySKILL.md:43-51description("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.SKILL.md:32-40description's "Do not use for …" clause andskills/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.description+ catalog)SKILL.md:20-52SKILL.md:54. The single most decision-changing fact in the package — thatobjectis 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.references/_index.md:14node_modules/@objectstack/spec/src/data/query.zod.ts— the file carrying the entireQueryAST— as "Sort Node". Cause is upstream:packages/spec/src/data/query.zod.ts:10-16opens with aSort NodeJSDoc and its own comment says "build-docs.tstakes the FIRST JSDoc block in the file as this page's description". The package's only pointer to the QueryAST schema is mislabelled.query.zod.ts), then regenerate — not a hand-edit of the generated fileReadthe source; a mislabelled pointer costs a wrong read ② no ③ n/aSKILL.md:496-539vsskills/objectstack-data/SKILL.md:110-165task/project_namemirror example, the same "❌ … aformulafield is virtual, no driver materializes a column" paragraph, and the same error-text block.SKILL.md:535-537itself points at data as the anchor: "Modelling side of this (the field, the hooks, the lint wording): objectstack-data → Search Fields (searchableFields)."objectstack-data— query keeps 2 lines: "searchnever traverses; mirror the title into a stored field → objectstack-data → Search Fields"rules/filters.md:221-258vsskills/objectstack-ui/SKILL.md:1659-1757{N_UNIT_(ago|from_now)}grammar, the*_end-is-a-calendar-DAY trap (ui:1672-1674 — restated again atrules/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.objectstack-ui(anchor: it already owns the full table); query keeps the one engine-path sentence + a pointerSKILL.md:617-632objectstack-uiowns: KPI widgets +compareTo(ui:1509-1530, including the same "bare string form was removed in 17"),dateGranularitybucketing (ui:837-841), matrixrows/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".rules/filters.md:260-280vsskills/objectstack-automation/SKILL.md:157-164delete_recordempty the object while the run reported success. Fix the metadata —objectstack validatenames the offending template." query restates the same rule over 21 lines, adding only the static-checkability split (validate fails inside a filter, warns outside).objectstack-automation(anchor: it is a flow-node config rule, not a query-DSL rule)SKILL.md:624+rules/aggregation.md:199-207compareTo: { kind: 'previousPeriod' | 'previousYear', dimension? }documented twice inside this package and a third time, in full, atobjectstack-ui/SKILL.md:1509-1530(which the query text explicitly defers to: "see objectstack-ui → Period-over-period —compareTo").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-222cursorsilently 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 thefieldargument, solag(revenue)renders asLAG()". The customer agent cannot emit these keys:retiredKey()types themnever(objectstack-upgrade/SKILL.md:407"a tombstone whose input type isnever. 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-upgradeowns the retirement procedure and counts 113 tombstones / 96 prescriptions (upgrade:567-568) — this package narrates 6 of them.SKILL.md, delete the per-key history everywhererules/aggregation.md:88-96alias, 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 thetimezone, neither of which the caller can see)." The live rule is the preceding clause ("Read the result underalias ?? field"); the parenthesis describes behaviour that no longer exists.SKILL.md:20-28descriptionin prose; the "Schema vs. runtime" legend (25-28) explains a three-way callout convention (⛔/SKILL.md:541-544, the experimental search knobs).SKILL.md:87-88evals/README.md:1-38skills/README.md:60-61shipsevals/to consumers ("inert (but harmless) in consumer installs") — so every customer session pays 558 tokens for evals that were never built. Contrast:objectstack-uiships a real fixture (evals/analytics-inline-vs-dataset.json),objectstack-automationshipsevals/approvals/test-revise-loop.md.packages/spec/scripts/check-skill-examples.ts:18-22: a self-contained block opts into type-checking with aHTMLCOMMENT-OS-CHECKHTML comment on the line directly above its fence. Measured:SKILL.md22 ts fences,rules/aggregation.md11,rules/filters.md14,rules/pagination.md11 — 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-queryand the schema-lesspm-dispatchare the only two at zero. Nothing in this package is protected against spec drift.HTMLCOMMENT-OS-CHECKto the canonicalfind/aggregate/expandexamples once they are given a real call shape (QRY-E-03) — paid by QRY-B-01 in the same fileSKILL.md:407-425fields: ['title', 'status']while expandingassigneeandproject— neither foreign-key column is retained.packages/objectql/src/engine.ts:8345-8347readsconst 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']withexpand: 'project_id'resolves nothing, because the relation is carried by that key"; andpackages/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 atSKILL.md:427-434does not state the requirement.SKILL.md:60-69,256-266,280-294,341-352,407-425,584-593vsSKILL.md:371-377,390-396,604-612object: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 acceptsobjectas an option:packages/objectql/src/engine.ts:8914find(object: string, query?: EngineQueryOptions, …), andEngineQueryOptionsSchema(packages/spec/src/data/data-engine.zod.ts:96-160) declares noobjectkey. The mismatch is fatal, not cosmetic:ENGINE_FIND_OPTION_KEYS(engine.ts:390-394) iscontext, where, fields, orderBy, limit, offset, search, searchFields, expandand an unlisted key throws — pinned atpackages/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 keepsobjectoutside 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.find/aggregate· protocolfindData· nestedexpandvalue · view filter rule) at the head of SKILL.md — paid by QRY-B-02 + QRY-B-03 in the same fileSKILL.md:341-352andrules/aggregation.md:43-54fields:and (SKILL.md)orderBy:.ENGINE_AGGREGATE_OPTION_KEYS(engine.ts:404-406) is exactlycontext, where, groupBy, aggregations, having, timezone—fieldsandorderByare not in it, so passing these literals toengine.aggregate()throws.rules/aggregation.md:57-59even callsfields"a readability convention, not a requirement" without saying it is rejected on the engine path.engine.aggregate(obj, {…})shape with only legal keyscontextis the second-most-used query option in the repo and has zero coverage. 34 occurrences inpackages/objectql/src/*.test.tsquery literals; 40 inexamples/**+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:71context: ExecutionContextSchema.partial().optional()) and the RLS/system-read escape hatch. Grepped for a home:objectstack-datacovers onlyrunAsexemption forreadonly(data:282-289),objectstack-automationcovers flowrunAs:'system'(automation:166-193),objectstack-platformhas no hit. No package teaches the query option.{ where, limit: 1, context: SYS }) — paid by QRY-C-01 in the same fileSKILL.md:464-495,evals/README.md:32-34search: { query, fields }. Measured: bare stringsearch: '…'= 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$searchexpansion actually serves, and what the dogfood HTTP proof pins. The structuredFullTextSearchSchemaform remains for the declared Tier-2 knobs." The dogfood proof ispackages/qa/dogfood/test/showcase-search.dogfood.test.ts:33,41:query({ search: 'retail', searchFields: ['industry'] }). The package never showssearch+ siblingsearchFields, though it quotes error text about$searchFieldsatSKILL.md:500,504.searchFieldspair the canonical example, demote the object form to one lineSKILL.md:136-146,rules/filters.md:18-21$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 thatfilter.zod.ts:548-549makes explicit — "$contains/$notContains/$startsWith/$endsWithcompare CASE-SENSITIVELY.$icontainsis 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).$icontainsappears in this package only inside the full-text-search prose (SKILL.md:468).SKILL.md:314-400,rules/aggregation.md(whole).aggregate(has 0 call sites inexamples/**andpackages/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 isdefineDataset({ 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 spellingaggregate={{ 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 inexamples/**is evidence, not proof — a third-party app callingengine.aggregate()in a custom endpoint is plausible; the aggregation content is therefore over-weighted, not valueless.aggregatecore (functions,groupBy,having, per-aggregationfilter,dateGranularity); replace the analytics framing with a 3-row "which aggregation spelling" routing table; the recovered budget pays QRY-F-01/F-03descriptionhas zero examples in the package. See QRY-A-01 for the schema and real usages.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". Nolegacy, nodeprecated, noM9, nov5.0, noOLD/NEW, noproject-as-workspace. Nothing to fix under G; the hits are D-01/D-02 density, not residue.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@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.@objectstack/spec17)SKILL.md:479,491,500,504,541fields(inside thesearchobject, :479/:491),searchFields(inside a quoted error, :500),$searchFields(inside the same quoted error, :504). The engine readsast.searchFields ?? raw?.fields(engine.ts:8779-8780); the protocol normalizes$searchFields→searchFields(engine.ts:8772-8776). A reader cannot tell which to emit.evals/README.md:32-34search: { query, fields }only". A model emitting the canonical Tier-1 contractengine.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.evals/README.md:7evals/README.md:9-38context(QRY-F-01). Six of the nine hinge on "did the answer avoid a removed key" — keystscalready typesnever, so they test what the compiler tests.context), DELETE 3 removed-key-only rowsSKILL.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), whilecontext(74 combined real usages) gets 0 tokens and the list-view filter spec named in the description gets 0 tokens.rules/filters.mdspends 1,039 of its 2,144 tokens (:203-290, 48%) on date macros and flow-filter tokens thatobjectstack-uiandobjectstack-automationalready own. Headroom is 255 tokens total, so every addition must be paid in-file.Incidental falsehoods (by-product, not the job)
SKILL.md:407-425— the canonicalexpandexample expands nothing. Full evidence inQRY-E-02:
fields: ['title','status']+expand: { assignee, project }drops both FKcolumns, and
engine.ts:8345-8347skips a relation whose FK is absent. Contract stated atpackages/spec/src/data/query.zod.ts:509and pinned atpackages/objectql/src/query-expression-conformance.test.ts:1575-1586. HIGH confidence.Sibling package —
skills/objectstack-ui/SKILL.md:1706-1708contradicts itself and theimplementation. 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 saysthe 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 implementationagrees with the earlier claim:
packages/core/src/utils/filter-tokens.ts:44("# An unknowntoken throws") and
:378(throw new UnknownFilterTokenError(cls.token, cls.suggestion)),with
packages/core/src/utils/filter-tokens.test.ts:208,225("throws on an unknownplaceholder and names the near miss" / "throws on a near-miss period spelling").
objectstack-query/rules/filters.md:282-286states it correctly. HIGH confidence — thedefect is in
objectstack-ui, not in the audited package, and it is the one place where theduplication in QRY-C-02 currently protects the reader.
Generated by Claude Code
Generated by Claude Code
os-litant commentedon Sep 2, 2026
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):
git diff --stat a59f78d 96b627d1 -- skills/objectstack-queryempty at my actual base; every audit line number was exact.content/docs/ai/skills-reference.mdxonly.skills/README.mdis byte-unchanged — it renders just the description's FIRST sentence, which A-01 did not touch.check:skill-docsgreen on both.skills/objectstack-queryheading is registered exhaustive; the Leg 2BINDINGStable holds 9 rows, all in objectstack-api / -data / -platform / -ui. No deletion here could red Leg 2.Fixes #<this card>; the dispatch and the PM claim comment sayPart of #14302because 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 ofwas used, and no closing keyword appears anywhere in the PR body or in any commit message (scanned).compareTowas established at source as a LIVE surface (packages/spec/src/ui/dashboard.zod.ts:438declares it astrictObject; what was removed is the bare-string form andcompareTo.offset), so the live branch of the ruling applied — delete both copies, keep the pointer. Target verified present onmainand on the ui branch.objectstack-automation/SKILL.md:157-164does not carry theconfig.filterprecedence rule, nor theobjectstack validatefails-inside-a-filter / warns-outside split. Both were deleted here under C-04.pull_request_read; it came back intact end-to-end.Generated by Claude Code
Generated by Claude Code
os-litant commentedon Sep 2, 2026
os-dev-report — correction to one field of the report above
The
mcp_callsfield 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_issuesdedupe for the out-of-scope card · (4)issue_writecreate #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_writeupdate, PR labels · (8)issue_writeupdate, 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
os-litant commentedon Sep 2, 2026
Contract review round 1 (clause ②, in-seat; served model read
claude-fable-5-1= CONTRACT_REVIEW_TIER) on PR #14443 head3a2b76a0— 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/findOnerow (SKILL.md).packages/objectql/src/engine.ts:390-402definesENGINE_FIND_OPTION_KEYSas 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 readertransactionis refused; it is accepted.countandaggregaterows are exact.FAIL 2 — the
$like/$ilikerefusal sentence (rules/filters.md). "driver-mongodb, objectqlhavingand service-analytics refuse them (INVALID_FILTER)" is not backed: measured refusals are driver-memory's shared$likegate (packages/drivers/driver-memory/src/filter-refusal.ts:34, #7536) and driver-mongodb throwingUnsupported filter operator(pinned atmongodb-operator-key-clobber.test.ts:159-160); nohaving/ service-analytics refusal and noINVALID_FILTERbinding 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→limitfolds (:8916); trailingoptions.contextwins overquery.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 areretiredKey()tombstones (query.zod.ts:271, :549, :552, :564, :567;array_agg/string_aggat:87-91, :159-160);count_distinctis the sixth function (data-engine.zod.ts:401); the case-sensitivity rule is quoted verbatim fromfilter.zod.ts:548-549incl. the ASCII folding domain; FK-projection requirement forexpand(query.zod.ts:509;engine.ts:8358-8359, :8372); bare-stringsearchis 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); dottedsearchFields→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_SUGGESTIONSuser_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/coreutils/filter-tokens.ts;VIEW_FILTER_OPERATORSis 20 operators incl.equals,icontains,is_null,before,between;compareTois livestrictObject(dashboard.zod.ts:438) with the bare-string form and.offsetretired (:272, :290) — the live-surface branch (delete + pointer) was the right ruling.② Semver: no release;
skip-changesetconsistent. ③ Boundary flags:open_questionsempty; 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
os-litant commentedon Sep 2, 2026
Contract review round 1 applied — PR #14443, new head 9663526 (was 3a2b76a). Still draft;
needs:contract-reviewandskip-changesetunchanged on both carriers.Defect 1 — real, fixed. The calling-convention table listed nine keys for engine
find/findOneand called the set closed.packages/objectql/src/engine.ts:396-400spreadsENGINE_DRIVER_PASSTHROUGH_KEYS(:368-370—transaction,tenantId,tenantIds,timezone,bypassTenantAudit,preserveAudit) into it, so as written the table said an explicittenantIdis refused by name. The row now names all fifteen, and the prose carries the source's own reason (:360-366): onfind/findOne/update/deletethe option bag IS the base of the driver options, whilecount/aggregatenever forward the bag and so deliberately reject those keys. Thecountandaggregaterows 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
$likefinds nothing:driver-mongodb—mongodb-filter.ts:1071-1074throwsUnsupported filter operator "$like" …;unsupportedFilterErrorat:366-371setsINVALID_FILTER/ 400. Pinned atmongodb-operator-key-clobber.test.ts:158-161.having—having-filter.ts:136-141CONDITION_OPERATORSis sixteen operators without$like/$ilike; anything else reachesunknownOperator(:174-205) →invalidFilterError,INVALID_FILTER/ 400.strategies/filter-normalizer.ts:418-435MONGO_TO_CUBE_OPomits them; the miss branch at:1032-1046throwsinvalidFilterError(: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-memoryANSWERS these operators (memory-driver.ts:58declares its vocabulary as[...FILTER_OPERATORS, '$like', '$ilike']; arms at:1522andmemory-matcher.ts:458), andfilter-refusal.ts'shasDanglingLikeEscapegate (: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()atpackages/spec/src/data/filter.zod.ts:697is 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-reactclosure (build exit 0) socheck:skill-examplescould not false-green on a stale dist: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
os-litant commentedon Sep 2, 2026
Round 3 — CI red on #14443 fixed. New head 49de20f (was 9663526). Still draft; all four labels unchanged.
The red.
pnpm check:role-wordreds on improvement as well as regression. Round 2's deletion of two Common-Mistakes blocks inrules/filters.mdremoved 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--updatemoved exactly one row —skills/objectstack-query/rules/filters.md: 9 → 8 — and no other, asserted ongit diffbefore 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
mainbetween rounds and ratcheted a different row of the samescripts/role-word-baseline.json(objectstack-data/SKILL.md4 → 2). Deriving and gating on my un-merged branch would have read a stale copy of the very file I had just changed — thedispatch-gatesSTALE TREE warning named it explicitly. So I merged currentmain(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-completenessexits 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:
The five card-named gates at the same head, against a freshly rebuilt
@objectstack/spec+@objectstack/client-reactclosure (build exit 0):(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.jsonwith the gate-prescribed reason, and every head sha is refreshed.Generated by Claude Code
Generated by Claude Code
os-litant commentedon Sep 2, 2026
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 readclaude-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 (thePart-of PR must not also close its cardguard is green). Six files against merge basea98b61b3: the four authored skill files, the generatedcontent/docs/ai/skills-reference.mdx, andscripts/role-word-baseline.jsonmoving exactly one row (skills/objectstack-query/rules/filters.md9 → 8, commit8208cf30, 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/mainmerged in at49de20f7; 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 on96635262) 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 plusENGINE_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/$ilikefaces: refused as allowlist misses atdriver-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 anINVALID_FILTER/ 400 envelope;driver-memoryANSWERS them (memory-driver.ts:58,:1522,memory-matcher.ts:458);hasDanglingLikeEscapegates 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:697is accurate — no spec twin owed.Labels.
needs:contract-reviewstripped 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 againstscripts/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 flipspm:dispatched→pm:blockedwithBlocked-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
os-litant commentedon Sep 2, 2026
Round 1 MERGED — PR #14443 landed on
mainat 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_01LraLgQVGq8egUwfYZpbYt1Branch:
claude/issue-14302-query-eval-stub-removal(fresh fromorigin/main; round 1's branch is merged and is not reused)Worktree:
objectstack-issue-14302-r2Domain:
domain:skillsFile 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 fromscripts/check-skills-token-ratchet.mjs(the one edit to that script a flight may make); ⛔ nothing elseContainer & model: S (two-file mechanical deletion under a ruling; no contract claim),
mode:subagent,model: sonnet— the floor; the seat reviews the diff by handClause-②: no ⇒ ⛔ no
needs:contract-reviewRuling 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 slotLanding 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 stayspm:dispatched.Generated by Claude Code
skills(query): delete the planned-eval stub and drop its ceiling row
os-litant commentedon Sep 2, 2026
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, BASE7286dd58e806ed321cbdfc23a1c455db8f80b1adAssignee: untouched (PM-owned).
Verified before touching anything:
skills/objectstack-query/evals/held onlyREADME.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 fromscripts/check-skills-token-ratchet.mjs— the only edit to that script. Nothing else.Landing form: ONE draft PR against
main, first lineFixes #14302(the seat's ACCEPT comment on #14443 names the eval items as the only deferral on this card),skip-changesetlabel, noneeds:contract-review.Full report follows as the next comment.
Generated by Claude Code
Generated by Claude Code
os-litant commentedon Sep 2, 2026
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
os-litant commentedon Sep 2, 2026
os-dev-report — correction to one field of the report above
The
mcp_callsfield 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-changesetlabel write and matches what was sent verbatim, footer included (session-URL form, create-time)._Generated by Claude Code
Generated by Claude Code
os-litant commentedon Sep 2, 2026
ACCEPT — round 2 (eval-stub deletion), PR #14585 @
3e1a218eSession:
session_01LraLgQVGq8egUwfYZpbYt1(skills lane seat)Verified against GitHub, not the report:
main,mergeable_state: clean, 1 commit, 2 files / 39 deletions, labelsdocumentation+skip-changeset. The diff was read in-seat:skills/objectstack-query/evals/README.mddeleted (the nine never-built "Planned Evals" scenarios), and exactly one ceiling row dropped fromscripts/check-skills-token-ratchet.mjs. No prose edit, no ceiling raised, no other package touched — the ratchet's deleted-file rule (drop the row only) is followed to the letter.3e1a218e: Lint & Repo Gates success (13:48Z), Type Check ×4 success, Test Core 1–6 success, Dogfood Regression Gate success, "No other open PR may claim the same issue" success. Nothing red, nothing pending.Fixes #14302is the correct landing form — the card closes on merge and the seat stripspm:dispatchedthen.needs:contract-review.--self-test64/64) matches the gate's documented behaviour.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