docs(skills): objectstack-query — engine aggregate row admits search / searchFields - #20776
Conversation
…/ searchFields The Calling Convention row for engine `aggregate` stopped at `timezone`, while `ENGINE_AGGREGATE_OPTION_KEYS` admits `search` and `searchFields` and `aggregate()` expands them through the same ADR-0061 expander `find` uses. Together with the closed-set sentence the row told an agent that a searched aggregate is refused. The row now lists both keys and says they filter the input rows before grouping, AND-ed with `where`. Claude-Session: https://claude.ai/code/session_01KTZmMfzVzjNvyaLyQ8mHvg Co-authored-by: Claude <noreply@anthropic.com>
… aggregate accepts The passthrough paragraph said all six driver passthrough keys are illegal on `count` and `aggregate`. `timezone` is one of the six and is a legal option on `aggregate` in its own right — `ENGINE_AGGREGATE_OPTION_KEYS` lists it and `aggregate()` reads it for date bucketing — which the row above already showed. The paragraph now carves that one key out; `count` still refuses it with the other five. Claude-Session: https://claude.ai/code/session_01KTZmMfzVzjNvyaLyQ8mHvg Co-authored-by: Claude <noreply@anthropic.com>
|
Generated by Claude Code |
Contract reviewServed-tier: Inputs read: card #20500 (body + all 5 comments: triage 5877514141, claim 5903909164, dev report 5904112375, claim-surface amendment 5904148643, dev report 5904236951), PR #20776 (body, file list, diff endpoint against Check-runs on
① Derived judgmentsThe diff changes no accept set and no public API: it edits a published skill's text only. The public surface that moves is the text an agent reads, so each changed statement is judged against
② Semver level
③ Boundary flagsDev report 5904112375 (round 1):
Claim-surface amendment 5904148643 (the seat): the four in-place conditions hold as stated — same defect class (both statements are about the aggregate legal option set), mechanical pinned form (a one-key carve-out), same gate family (docs-only, same 24 derived commands). Condition ③ (no other claim on the file) is the seat's own serial-constraints reading; the mechanical counterpart on this head, Dev report 5904236951 (round 2):
Observation for the objectql lane, not a flag on this PR: the doc comment on Governance: Implemented-by: VERDICT: FAIL One line to fix, inside the claim surface: |
… aggregate keys `rules/aggregation.md` said `fields` is not one of the "six keys" `engine.aggregate()` accepts and pointed at the calling-convention row, which now lists eight. A count drifts every time the set moves; the sentence now says "not one of the keys" and leaves the enumeration to the row it cites. Net zero lines, no re-wrap. Claude-Session: https://claude.ai/code/session_01KTZmMfzVzjNvyaLyQ8mHvg Co-authored-by: Claude <noreply@anthropic.com>
Contract reviewServed-tier: Inputs read: card #20500 (body and all 8 comments: triage 5877514141, claim 5903909164, dev reports 5904112375 / 5904236951 / 5904713155, claim-surface amendments 5904148643 / 5904535717, round-3 state 5904782579), PR #20776 (body, file list, the diff endpoint against Check-runs on
① Derived judgmentsThe diff changes no accept set and no public API: no code, no schema, no generated artifact, no changeset. The public surface that moves is the text of a published skill (
Untouched sentences that depend on the changed ones, re-judged: Self-contradiction sweep, all six files under Commit hygiene: the three commits carry the model-free trailer pair ( ② Semver level
③ Boundary flagsDev report 5904112375 (round 1). Deviation "label-write refused" ( Amendment 5904148643 (seat). The four in-place conditions held (previous record); the mechanical counterpart of condition ③, Dev report 5904236951 (round 2). Deviation " Amendment 5904535717 (seat). Dev report 5904713155 (round 3, status Round-3 state 5904782579 (seat) matches what was read: Is the PR complete for the card at this head? Plainly:
PR body — what would mislead a merger, on Governance. Implemented-by: VERDICT: PASS Every statement the diff makes is true of |
维护者速读(终稿)— PR #20776 · query 技能:aggregate 的合法选项键补全(#20500)skills 席 1 · 改了什么:只改
为什么改:PR #20487 之后,运行时的 风险与代价(含回滚):纯文档,不动代码,不发布任何包。token 上限都没动。回滚:revert 本 PR。 席位意见:ACCEPT,建议批准。
你要做的:在本 PR 上给 APPROVED,并确认是否由席位挂上 Generated by Claude Code |
Fixes #20500
Clause-②: no
What changed
Two files, three places, all inside the claim surface (claim 5903909164 with amendments 5904148643 and 5905295272). In
skills/objectstack-query/SKILL.md, the Calling Convention section: (1) The engineaggregaterow (:26): the legal option keys now listsearchandsearchFieldsaftertimezone, with one clause saying the two search keys filter the input rows before grouping, AND-ed withwhere, exactly as onfind. (2) The passthrough paragraph (:38-44): it said all six driver passthrough keys are deliberately ILLEGAL oncountandaggregate;timezoneis one of the six and is a legal option onaggregatein its own right (inENGINE_AGGREGATE_OPTION_KEYS, read byaggregate()for date bucketing), which the row above already showed. The paragraph now carves that one key out:aggregatereads it itself, so it is legal there and the row lists it;countrefuses it with the rest. (3) Inskills/objectstack-query/rules/aggregation.md:49(round 3,cc924448): "fieldsis not one of the six keysengine.aggregate()accepts" no longer states a count — "not one of the keysengine.aggregate()accepts" — so it cannot contradict the row it cites, which now lists eight. Net +2 lines across the three commits (aggregation.mdnet 0). Nothing else moved: the closed-set sentence atSKILL.md:31and thefields/orderBysentence (now:254) stay, both still true.Landing sites: the row the dispatch expected (
:26); the:38-44paragraph the seat added after the first report's out-of-scope finding; andrules/aggregation.md:49, which the at-tier contract review ofa87b7380(5904504985, FAIL) found — a sentence that names a count, invisible to the first census's key-name probes. Thecontent/docs/**half of the census (two docs sentences that enumerate the same set) is carded as #20792, ⛔ not in this PR.In-place fix (patch round,
a87b7380)In-place fix under the bounded exemption (claim amendment 5904148643), all four conditions holding: ① the same defect class as the card — the skill misstated the legal
aggregateoption set (the:26row understated it; the:38-42paragraph overstated the illegal set by one key); ② mechanical, in an already-pinned form — the carve-out namestimezoneas the one passthrough keyaggregateaccepts and reads; ③ no other claim holds the file (no open PR touchesskills/objectstack-query/**, per the claim's serial-constraints reading); ④ the same gate family — the list derived ata87b7380is the same 24 commands as atf997cf8e, no new verification surface. Measured onorigin/mainc9c182edbefore writing:ENGINE_COUNT_OPTION_KEYS(packages/objectql/src/engine.ts:556) iscontext,where, andcount()rejects against it (:16168), sotimezoneis refused oncount;ENGINE_AGGREGATE_OPTION_KEYS(:562-565) containstimezoneand none oftransaction,tenantId,tenantIds,bypassTenantAudit,preserveAudit(0 hits each);aggregate()spans:16267-16686and readsconst tz = query.timezoneat:16598. The replacement sentence reuses the file's own vocabulary ("date bucketing",:89and:257; "the row above").Why
Since PR #20487,
ENGINE_AGGREGATE_OPTION_KEYS(packages/objectql/src/engine.ts:562atc9c182ed) iscontext,where,groupBy,aggregations,having,timezone,search,searchFields. The row stopped attimezone, and together with the closed-set sentence it told an agent that a searched aggregate is refused. An agent reading that would group a searched page on the client, which is the wrong-number workaround #20358 retired: group counts over a page window instead of over all searched rows, with no error.Behaviour verified at the code, not at the comment
aggregate()(engine.ts:16267-16277):rejectUnknownEngineOptions(object, 'aggregate', query, ENGINE_AGGREGATE_OPTION_KEYS)admits both keys, thenexpandSearchOnAggregateOptions(:11104-11114) builds a carrier AST fromwhere+search+searchFieldsand runs the one ADR-0061 expanderfinduses,expandSearchOnAst(:11062-11086): each term becomes an$orof$icontainsover the resolved searchable fields, the result is AND-ed with the caller'swhere({ $and: [where, searchFilter] }), and the two search keys are deleted from the bag. All of that runs before the aggregate AST is built and before any grouping, so the grouped answer is the grouping of the searched rows, which is what the new clause says.Census (list-shaped probes, as the dispatch asked)
At
c9c182ed, overskills/andcontent/docs/:havingandtimezone: 1 hit,skills/objectstack-query/SKILL.md:26(the row corrected here);groupBy,aggregationsandhavingtogether: 2 hits, the same row andcontent/docs/kernel/runtime-services/data-service.mdx:91, which lists theQueryASTclauses (it already namessearch; it is not an enumeration of the engine option set, so no edit);ENGINE_AGGREGATE_OPTION_KEYS: 2 hits,SKILL.md:31and:254(:252before this PR), both prose sentences, both still true;aggregaterow spelling: 1 hit,:26.skills/objectstack-query/rules/aggregation.md:104already sayswherefilters the input rows before grouping, the vocabulary the new clause reuses.Round-3 census (count-shaped probes, after the review). Over all six files of
skills/objectstack-query/**: number words,onlynearaccept/key/option,keysnearaccepts/legal/closed set, and list-shaped rows, with the literalengine.aggregate()as control — the one stale statement wasrules/aggregation.md:49("six keys"), fixed here; every other number word counts a set of that size. Overcontent/docs/**: two sentences enumerate the aggregate set falsely —content/docs/protocol/objectql/query-syntax.mdx:1337andcontent/docs/data-modeling/queries.mdx:672— carded as #20792 with the evidence and a proposed patch; they are outside this PR. The full probe list and hit counts are in the dev report 5904713155.The two
skills/**readingsTokens in the ratchet's own convention,
ceil(utf8 bytes / 4).c9c182ed)cc924448)skills/objectstack-query/SKILL.md, linesskills/objectstack-query/rules/aggregation.md, linesskills/**(every file), linesskills/**(every file), tokensSKILL.md, linesPM line budget for this card: net +2 lines at most; +2 used (0 by the row commit
f997cf8e, +2 by the paragraph commita87b7380, 0 bycc924448), not exceeded. No existing line was re-wrapped:SKILL.md:38-41are byte-identical tomain,:42was extended in place and two lines follow it;rules/aggregation.md:49is replaced in place.Changeset
skip-changeset, by measurement: no released package'sfiles[]names askillspath (everypackages/**/package.jsonscanned; positive control:create-objectstacklistsdist,README.md,CHANGELOG.md). Scaffolded projects pull the catalog from the repository pathobjectstack-ai/objectstack/skillsthrough theskillsCLI (packages/create-objectstack/src/skills-install.ts:62), never from an npm tarball.Gates
Head
cc924448(round 3): the same 24 derived families pluscheck:skill-refsran green oncc924448(dev report 5904713155); their--ranreconciliation is carried by the report's 47-family superset over the round's working tree, not by a 24-only run. CI oncc924448: 31 check-runs — 23success, 7 path-filteredskipped, 1failure(Check Changeset, the missingskip-changesetlabel; see Changeset);Lint & Repo Gates,TypeScript Type CheckandType Check · source gates(the token ratchet and the skill-doc gates)success. The table below is the round-2 record ona87b7380.Derived from the actual change with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commandsat heada87b7380(24 commands, the same list as atf997cf8eand as the dispatch), all run on heada87b7380, exit codes captured before any pipe, reconciled with--ran.--commandsprinted it)a87b7380node scripts/check-ci-filter-parity.mjsnode scripts/check-closing-keyword-parity.mjsnode scripts/check-closing-keyword-parity.mjs --self-testnode scripts/check-comment-mask-corpus.mjsnode scripts/check-doc-route-spelling.mjs --advisorynode scripts/check-doc-route-spelling.mjs --self-testnode scripts/check-skills-token-ratchet.mjsnode scripts/check-skills-token-ratchet.mjs --self-testpnpm --filter @objectstack/lint run check:doc-formula-expressionspnpm --filter @objectstack/spec run check:skill-docspnpm check:agent-test-spellingpnpm check:corpus-claim-driftpnpm check:cross-package-test-inputspnpm check:doc-authoringpnpm check:driver-memory-censuspnpm check:gitlink-declaredpnpm check:nul-bytespnpm check:pm-governed-mergespnpm check:refd-timer-probepnpm check:role-wordpnpm check:skill-compatibilitypnpm check:skill-frame-syncpnpm check:skill-identifier-livenesspnpm check:watch-hint-literalpnpm --filter @objectstack/spec run check:skill-refs(extra, not derived; AGENTS.md names it for aSKILL.mdedit)--ranreconciliation ata87b7380: 24 derived, 24 run, 0 NOT-MEASURED, 0 UNRUN (dispatch-gates --ran, exit 0). Ratchet family:check-skills-token-ratchetprintsskills/objectstack-query/SKILL.md is 3990 tokens (ceiling 5552; headroom 1562).check:doc-formula-expressionsneeds@objectstack/formulaand@objectstack/lintbuilt; they were built underos-verify-lock.shbefore the gate ran (turbo cache hit, VERDICT command-exit 0), so this round it passed on its first run. At the earlier headf997cf8ethe same 24 were green too (that round's firstcheck:doc-formula-expressionsrun exited 3 for the missing build, NOT MEASURED, and passed after the build).check:skill-docsreads frontmatter only (packages/spec/scripts/build-skill-docs.ts), so no generated artifact moves for a body edit;skills/README.mdandcontent/docs/ai/skills-reference.mdxstay byte-identical. No package build or test is owed: the diff touches no package (turbo ls --affectedis blind here by construction — the skill lives outside the package graph).A repo-wide
pnpm lint(eslint . --no-inline-config) is CI-owned; it was not run locally and is not claimed (NOT MEASURED locally, CI reports it). Population reading from eslint's own configuration:pnpm exec eslint --print-config skills/objectstack-query/SKILL.mdprintsundefined(no config block matches the file), and everyfiles:glob ineslint.config.mjsis a{ts,tsx,mts,cts,js,jsx,mjs,cjs}pattern, so the one edited file is outside eslint's population and this diff cannot move any lint verdict on any file.Acceptance notes
SKILL.md:38-42said all six driver passthrough keys are illegal onaggregate, whiletimezoneis legal and read there — is fixed in this PR by the patch rounda87b7380, under the bounded in-place exemption (see In-place fix above).content/docs/**half —query-syntax.mdx:1337andqueries.mdx:672enumerate the aggregate option set as "onlywhere/groupBy/aggregations" — is carded as [finding] two content/docs sentences enumerate the engine aggregate option set as "only where / groupBy / aggregations" — false bycontext,having,search,searchFields(the docs half of #20500) #20792 (filed for triage;domain:devxby the lane table), carrying the evidence and the round-3 dev's proposed patch.Fixes #20500closes the card over theskills/**half, which is the card's own defect; the docs half lives on [finding] two content/docs sentences enumerate the engine aggregate option set as "only where / groupBy / aggregations" — false bycontext,having,search,searchFields(the docs half of #20500) #20792.ENGINE_DRIVER_PASSTHROUGH_KEYS(packages/objectql/src/engine.ts:507-513) still says the six keys are "deliberately NOT legal" oncount/aggregate— stale bytimezoneonaggregate; a code comment, not a shipped surface (carrier: the next edit of that block).维护者速读(草稿)
skills/objectstack-query技能里「Calling Convention」表的 engineaggregate一行,合法选项键补上search、searchFields,并加一句:这两个键在分组之前过滤输入行,与where取交集,行为与find一致。另将同段落里「六个直通键在 count / aggregate 上一律非法」的表述修正为 timezone 例外(aggregate 自己读取它做日期分桶,count 仍拒绝);rules/aggregation.md里「six keys」这一数字去掉(表里已是八个键)。技能包净 +2 行。content/docs里同样写错的两句另立 [finding] two content/docs sentences enumerate the engine aggregate option set as "only where / groupBy / aggregations" — false bycontext,having,search,searchFields(the docs half of #20500) #20792,不在本 PR。search#20487 落地后,运行时的aggregate已经接受并执行search/searchFields,技能却还写着不接受。AI 读了这份技能会绕路:先查一页再在客户端分组,得到的分组计数只覆盖一页而不是全部命中行,且没有任何报错。写给 AI 的技能说错一句等于产品缺陷。skills/**不在任何 npm 包的files[]里,脚手架项目从仓库路径拉取)。token 棘轮上限未动(SKILL.md 3915 → 3990 / 5552;aggregation.md 1846 → 1845 / 2357)。回滚即 revert 本 PR。Generated by Claude Code