Skip to content

skills(objectstack-query): teach the now-anchored comparand { $now: true, addDays } - #26407

Merged
objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-r9vctw
Oct 27, 2026
Merged

objectstack-fleet[bot] merged 4 commits into
mainfrom
claude/project-thread-r9vctw

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Warren · project thread
Fixes #26301
Clause-②: no
Metadata-protocol: none
Skills-docs: updated here

维护者速读

  • 改了什么:skills/objectstack-query/rules/filters.md 在 Field References 之后新增一节「Now-Anchored Comparands」:{ $now: true, addDays: OFFSET } 的形状、唯一示例(按每条记录自己的宽限天数判逾期,偏移列存负数天)、哪里接受(六个标量比较、date/datetime 字段)、哪些被拒(无 addDays、小数或字串偏移、多余键、非日期目标、$in/$nin/$between 位置,SQL 下推拒点路径或非数值偏移列),并点明不必用存储公式字段加钩子。SKILL.md 的规则清单与「Cross-field comparisons」各加一句指向它(在余量内,不抬上限)。eval 文件加第 6 题。两行 token 上限按实测抬高,裁决原文写在行旁。
  • 为什么改:该比较数自 18.0 起已在过滤语法里,但技能只分别教了 $field 与日期宏;AI 作者要「按每条记录自己的宽限天数判逾期」时会去建存储公式字段再写钩子。
  • 风险与回滚:只改发布技能包的文字与一个 eval 夹具,不改运行时;回滚即 revert 本 PR(两行上限随之回落)。
  • 先看:① skills/objectstack-query/rules/filters.md 新节(接受/拒绝清单对照 packages/spec/src/data/filter.zod.ts 的 NowAnchorSchema 与 filter.test.ts 的 Analytics datasets: no filter compares a datetime column against now minus an SLA held on a related record #25808 用例);② scripts/check-skills-token-ratchet.mjs 两行上限及注释引的裁决;③ evals/filters-pagination-search.json 第 6 题。
  • 两分钟验证:node scripts/check-skills-token-ratchet.mjs → 末行 81 authored bundle file(s) within their ceilings,其中 rules/filters.md is 2482 tokens (ceiling 2482; headroom 0)、filters-pagination-search.json is 1175 tokens (ceiling 1175; headroom 0)。
  • 需要你做的:Tier H(skills/**),请 approve 或直接 Merge when ready。

What changes

Card #26301, the audit's row Q1 (the comparand { $now: true, addDays: FIELD }, in the filter grammar since the 18.0 floor, taught nowhere in skills/objectstack-query/**):

  1. rules/filters.md — one subsection, "Now-Anchored Comparands", placed directly under Field References and before Relation Filters. Why there and not just before Date Filtering Patterns: the anchor is legal exactly where a { $field } reference is and borrows its addDays grammar, so the author who has just read how a comparand can be another column meets its now-anchored twin on the next screen; the date-token section teaches placeholders that take a literal N only. The section carries, in the spec's words (NowAnchorSchema, filter.zod.ts, and the Analytics datasets: no filter compares a datetime column against now minus an SLA held on a related record #25808 cases in filter.test.ts / engine-now-anchor.test.ts): the shape; the one example (overdue by each record's own grace period, due_at < now + grace_days_offset, the offset column holding NEGATIVE days); that addDays is required and always added (an integer of any sign, -30 keeping the time of day where {30_days_ago} names a calendar day; {now} for the bare instant; or a { $field } naming a number field of the same record, NULL counting as zero days); that the filter cannot negate a positive column, so the signed offset is what gets stored; what is refused with INVALID_FILTER / 400 (no addDays, a fractional or string offset, an extra key — there is no subDays — a target that is not date / datetime, any $in / $nin / $between position; SQL push-down also refuses a dotted or non-number offset field); and that the filter alone answers the question, not a stored formula field plus a hook (the card's "who pays").
  2. SKILL.md — two pointers, inside the file's headroom (priced 4124 → 4168, ceiling 5547): the rules list names "now-anchored comparands" among what filters.md covers, and the Cross-field comparisons entry gains one clause naming the anchor beside { $field }, pointing at the new section. Line 1 (the version stamp PR chore: version packages #25640 owns) is untouched.
  3. Eval 6 in evals/filters-pagination-search.json, in the file's existing shape (the earlier five entries are byte-identical): the overdue-by-grace-days prompt, with the schema stated (a datetime and a number field holding negative days); expected output is the anchor as the whole comparand of $lt, and names the three wrong routes (a stored formula field plus a hook, {N_days_ago}, a { $field: 'due_at', addDays } column-to-column comparison). must_contain pins $now: true, addDays, $field: 'grace_days_offset', $lt; must_not_contain pins subDays and type: 'formula'.
  4. references/_index.md is generator-owned (gen:skill-refs) and already lists data/filter.zod.ts; not touched.

Token-ratchet rows (scripts/check-skills-token-ratchet.mjs), per the maintainer's ruling on the seat's decision card 「skills 修复卡的教学和 eval 都撞 token 天花板,怎么付?」, tap A (2026-10-27 01:21Z), verbatim and untranslated: 「A:先削重复,余额按实测提高」. Step one, cut duplicates first: measured on origin/main a66c9ff4, rules/filters.md carries no passage that restates another in the file (the operator table's case-blind cells and the Case-sensitivity paragraph are a table row and its rule, not a restatement; the Relation Filters limits and the two-step route are stated once each; $field's comparison-position rule appears once here and once in SKILL.md, a different file), and the eval file repeats nothing — so there was nothing to cut and no surviving-home list to give. Step two, raise by the measured remainder: rules/filters.md 2149 → 2482 (+334 landed on 2148, 1 absorbed by headroom, ceiling +333); evals/filters-pagination-search.json 955 → 1175 (+220, zero headroom). Both rows re-pinned at zero headroom; the comments beside them quote the ruling.

Two readings (lines, and tokens as the ratchet counts them, ceil(utf8 bytes / 4); the SKILL.md token figures here are the raw file, the ratchet's priced count excludes the version line):

File Lines before → after Tokens before → after
rules/filters.md 185 → 211 (+26) 2148 → 2482 (+334)
SKILL.md 401 → 403 (+2) 4129 → 4173 (+44)
evals/filters-pagination-search.json (not .md, reported for the raise) 55 → 65 (+10) 955 → 1175 (+220)
Whole package (SKILL.md + references/**/*.md + rules/**/*.md, 5 files) 1063 → 1091 (+28) 9660 → 10038 (+378)

Nothing removed: the card names no stale line in this package.

Sequencing (zero verbs beside these numbers): PR #25640 owns line 1 of every skills/*/SKILL.md, untouched here; the sibling audit PRs #26385, #26388, #26390, #26392 and #26393 edit other packages' rows of the ratchet script, and this branch was cut from a66c9ff4, after #26392 landed; no open PR touches skills/objectstack-query/**.

skip-changeset: the published skills packages carry no package.json; nothing is released from any package.

Tests

  • node scripts/check-skills-token-ratchet.mjs — exit 0: 81 authored bundle file(s) within their ceilings, the two raised rows at headroom 0, SKILL.md at headroom 1379 (no raise).
  • node scripts/check-skills-token-ratchet.mjs --self-test — exit 0, 79 cases pass.
  • pnpm --filter @objectstack/spec run check:skill-docs, check:skill-refs, check:skill-examples — each exit 0 (check:skill-examples after building @objectstack/spec, @objectstack/client and @objectstack/client-react under scripts/pm/os-verify-lock.sh, slot issue-26301, 312 s for the whole closure including @objectstack/formula and @objectstack/lint); the new code block in filters.md is a where: fragment and is deliberately not marked os:check, like every other fragment in the file.
  • Eval 6 is a fixture (the repo has no eval runner): its assertions were checked by script against its expected output — every must_contain string occurs in it, neither must_not_contain string does.
  • Prose cross-checked against the contract, not paraphrased from memory: NowAnchorSchema (packages/spec/src/data/filter.zod.ts), its Analytics datasets: no filter compares a datetime column against now minus an SLA held on a related record #25808 cases in filter.test.ts (the six operators accept it; $in / $nin / $between refuse it; missing, fractional and string offsets and an extra key refuse), engine-now-anchor.test.ts (the resolver pins $now; a numeric target and a text or undeclared offset column refuse) and the $now section of content/docs/protocol/objectql/query-syntax.mdx (NULL offset = zero days; a dotted offset is refused on SQL push-down). No ablation: no tested behaviour changed.

Acceptance notes

  • addDays on a { $field } reference ({ $field: 'due_date', addDays: { $field: 'grace_days' } }, in the grammar since 18.0's floor as well) is likewise untaught in Field References; the audit's row Q1 cites that section as teaching $field alone and names only the anchor. Observation only, outside this card's "Wanted"; not filed (read-only inference, no public-door probe).
  • The dispatch order's "skills/** inside the package's ceilings" cannot hold at the measured headroom (filters.md 1, eval 0); the claim's file surface already names the two ratchet rows under the ruling above.
  • The PR body's first two lines are the platform's attribution block (the os-dev exception for a harness-written attribution comment); Fixes #26301 follows immediately. Skills-docs: updated here is written because it is true, not because it is owed (Metadata-protocol: none).

Verification

Gate list derived with node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack on the final tree (a66c9ff4 base, head 13e75c8c), each run locally with the exit code captured before any pipe; --ran reconciliation below.

Reconciliation (node scripts/pm/dispatch-gates.mjs --ran ran.list --repo objectstack-ai/objectstack, exit 0): 52 derived, 51 run, 1 NOT-MEASURED, 0 UNRUN. The one NOT MEASURED is pnpm check:dual-build-cjs-loads: its prerequisite is a whole-repo build (28 packages without dist/ on this worktree after the targeted closure); this diff touches no package source, and CI's Lint & Repo Gates runs it — a declared narrowing. check:pm-dispatch-gates ran detached on the final head: under six minutes of wall (launched after the 51-gate pass ended at 02:32Z, finished 02:38Z), 1901 cases pass (fast tier; 7 slow sections deferred), exit 0. No gate was red on any head.

Every derived command with its exit code on 13e75c8 (54 lines: the 52 derived, plus `check:skill-refs` and `check:skill-examples` the card asks for)
  • node scripts/check-ci-filter-parity.mjs — exit 0
  • node scripts/check-closing-keyword-parity.mjs — exit 0
  • node scripts/check-closing-keyword-parity.mjs --self-test — exit 0
  • node scripts/check-comment-mask-corpus.mjs — exit 0
  • node scripts/check-declaration-mirrors.mjs — exit 0
  • node scripts/check-declaration-mirrors.mjs --self-test — exit 0
  • node scripts/check-doc-route-spelling.mjs --advisory — exit 0
  • node scripts/check-doc-route-spelling.mjs --self-test — exit 0
  • node scripts/check-dts-emitted.mjs --self-test — exit 0
  • node scripts/check-scripts-symbol-anchors.mjs — exit 0
  • node scripts/check-scripts-symbol-anchors.mjs --self-test — exit 0
  • node scripts/check-self-test-wired.mjs — exit 0
  • node scripts/check-self-test-wired.mjs --self-test — exit 0
  • node scripts/check-self-test-workflow-commands.mjs — exit 0
  • node scripts/check-self-test-workflow-commands.mjs --self-test — exit 0
  • node scripts/check-skills-token-ratchet.mjs — exit 0
  • node scripts/check-skills-token-ratchet.mjs --self-test — exit 0
  • node scripts/check-whole-set-label-write.mjs — exit 0
  • node scripts/check-whole-set-label-write.mjs --self-test — exit 0
  • node scripts/ci/assert-turbo-remote-cache.mjs --self-test — exit 0
  • node scripts/ci/prune-turbo-cache.mjs --self-test — exit 0
  • node scripts/pm/bare-root-worklist.mjs --self-test — exit 0
  • pnpm --filter @objectstack/lint run check:doc-formula-expressions — exit 0
  • pnpm --filter @objectstack/spec run check:skill-docs — exit 0
  • pnpm check:agent-test-spelling — exit 0
  • pnpm check:bash32-floor — exit 0
  • pnpm check:cli-command-ids — exit 0
  • pnpm check:corpus-claim-drift — exit 0
  • pnpm check:cross-package-test-inputs — exit 0
  • pnpm check:doc-authoring — exit 0
  • pnpm check:driver-memory-census — exit 0
  • pnpm check:dts-closure — exit 0
  • pnpm check:dual-build-cjs-loads — NOT MEASURED (exit 3, PREREQUISITE NOT MET)
  • pnpm check:entry-guard — exit 0
  • pnpm check:gitlink-declared — exit 0
  • pnpm check:lean-entry-closure — exit 0
  • pnpm check:merge-driver — exit 0
  • pnpm check:nul-bytes — exit 0
  • pnpm check:parse-guard — exit 0
  • pnpm check:pm-governed-merges — exit 0
  • pnpm check:pm-skill-id-lint — exit 0
  • pnpm check:pnpm-filter-targets — exit 0
  • pnpm check:pnpm-flag-spellings — exit 0
  • pnpm check:ratchet-remedy-authority — exit 0
  • pnpm check:refd-timer-probe — exit 0
  • pnpm check:role-word — exit 0
  • pnpm check:skill-compatibility — exit 0
  • pnpm check:skill-frame-sync — exit 0
  • pnpm check:skill-identifier-liveness — exit 0
  • pnpm check:sourcemap-no-sources-content — exit 0
  • pnpm check:watch-hint-literal — exit 0
  • pnpm check:pm-dispatch-gates — exit 0
  • pnpm --filter @objectstack/spec run check:skill-refs — exit 0
  • pnpm --filter @objectstack/spec run check:skill-examples — exit 0
Token ratchet on 13e75c8
  • skills/objectstack-query/SKILL.md is 4168 tokens (ceiling 5547; headroom 1379). Its release-stamp line (version: "18.1.0", 20 bytes) is written by the version script, not an author, and is not priced.
  • skills/objectstack-query/evals/filters-pagination-search.json is 1175 tokens (ceiling 1175; headroom 0).
  • skills/objectstack-query/rules/filters.md is 2482 tokens (ceiling 2482; headroom 0).
  • 81 authored bundle file(s) within their ceilings; 10 generator-owned file(s) measured, not ratcheted.
  • --self-test: 79 cases pass.
check:skill-examples on 13e75c8

264 prose examples type-check across 3 surface(s) — every marked block parsed, so tsc ran the SEMANTIC pass on all of them


Generated by Claude Code

…rue, addDays }

rules/filters.md gains one subsection, Now-Anchored Comparands, placed under
Field References (the anchor is legal exactly where a { $field } reference
is and borrows its addDays grammar): the shape, the one example (overdue by
each record's own grace days, the offset column holding negative days),
where it is accepted (the six scalar comparisons on a date / datetime field)
and what is refused. SKILL.md points at it from the rules list and the
cross-field comparisons entry. evals/filters-pagination-search.json gains
case 6, the overdue-by-grace-days prompt.

The two touched CEILINGS rows of scripts/check-skills-token-ratchet.mjs rise
by the measured remainder under the maintainer's ruling A (nothing in either
file restates another passage, so nothing was cut first): rules/filters.md
2149 -> 2482, the eval file 955 -> 1175.

Claude-Session: https://claude.ai/code/session_01JQwxbX21W6nMhxhqhmbFEH
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet objectstack-fleet Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Oct 27, 2026
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Oct 27, 2026
@os-zhuang
os-zhuang marked this pull request as ready for review October 27, 2026 02:42
@os-zhuang
os-zhuang enabled auto-merge October 27, 2026 02:42
@os-zhuang
os-zhuang added this pull request to the merge queue Oct 27, 2026
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Reviewed at 2026-10-27T02:53Z — independent contract review of PR #26407 (card #26301, skills/objectstack-query), read adversarially against origin/main (92a0073b), not against the dispatch order's, the seat's or the dev's conclusions.

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 13e75c8c8f30652d37f4f8523943c979457f9c6d
Local-runs: none
Scope: skills/objectstack-query/rules/filters.md, skills/objectstack-query/SKILL.md, skills/objectstack-query/evals/filters-pagination-search.json + scripts/check-skills-token-ratchet.mjs (the two objectstack-query CEILINGS rows), packages/spec/src/data/filter.zod.ts (NowAnchorSchema, AddDaysReferenceSchema, the $now docblock), packages/spec/src/data/filter-comparand-type.ts (isNowAnchor / isWellFormedNowAnchor / malformedNowAnchorRefusal), packages/spec/src/data/filter-lowering.ts, packages/spec/src/data/filter-comparand-shape.ts, packages/core/src/utils/filter-tokens.ts (resolveNowAnchor, MalformedNowAnchorError), packages/drivers/driver-sql/src/sql-driver.ts (nowAnchorOf, nowAnchorPositionError, NUMERIC_SCALAR_TYPES), packages/drivers/driver-sql/src/sql-driver-cross-field-comparison.ts (applyNowAnchorComparison), packages/drivers/driver-mongodb/src/mongodb-filter.ts, packages/formula/src/matches-filter.ts (resolveNowAnchor, resolveDayOffset), packages/objectql/src/engine-now-anchor.test.ts, packages/spec/src/data/filter.test.ts, packages/formula/src/matches-filter-now-anchor.test.ts, .github/workflows/lint.yml, .claude/agents/os-dev.md, .claude/skills/pm-dispatch/SKILL.md:313 (as landed in b33dd25b), issue #25808, the audit table on #26184 (comment 6425678185, row Q1), card #26301 and its four comments (6431849616, 6432006578, 6432051384, 6432431995).

Inputs read over REST (curl, bearer token from the environment, never printed): issues/26301, issues/26301/comments, pulls/26407, pulls/26407/reviews, issues/26407/comments, issues/25808, commits/13e75c8c/check-runs. Files read with git show 13e75c8c:PATH and git show origin/main:PATH after git fetch; the shared checkout was not switched.

① Derived judgments

(a) Every new sentence against the spec on origin/main. The new rules/filters.md section (L57–81 on the head) was checked clause by clause.

  • "the instant the query runs at, shifted by whole days — legal exactly where a { $field } reference is: the whole comparand of $eq/$ne/$gt/$gte/$lt/$lte on a date or datetime field. The engine pins $now to ONE instant per query" — RIGHT: filter.zod.ts $now docblock ("legal exactly where a { $field } reference is — the whole comparand of the six scalar comparisons"; "ONE instant for the whole filter, pinned by the engine's placeholder resolver"; "applies to a date or datetime target only"); filter.test.ts L270 parses all six operators; filter-tokens.ts resolveNowAnchor writes the one pinned ISO instant (engine-now-anchor.test.ts L106–114 shows both the column and the literal arm pinned).
  • The example due_at: { $lt: { $now: true, addDays: { $field: 'grace_days_offset' } } } with the column holding NEGATIVE days — RIGHT: it is the spec's own first @example (review_sla_days_offset, "the column holding the NEGATIVE number of SLA days") and the ruling's letter-A semantics on Analytics datasets: no filter compares a datetime column against now minus an SLA held on a related record #25808 ("now minus the column" is a column that holds a negative number; matches-filter-now-anchor.test.ts L62 "a NEGATIVE offset; a column holds its sign"). The sign is not fixed: addDays is always added, so a stored negative value really moves backwards.
  • "addDays is required and always ADDED: an integer of any sign (-30 is thirty days ago keeping the time of day, where {30_days_ago} names a calendar day; for the bare instant write {now})" — RIGHT: NowAnchorSchema.addDays is non-optional, z.number().int() or AddDaysReferenceSchema; the spec's second @example says exactly this (-30 "with its time of day — unlike {30_days_ago}, which names the calendar DAY"); filter-tokens.ts L420 resolves now as an instant and L434 renders N_days_ago as YYYY-MM-DD. Precision note, not an error: "keeping the time of day" holds against a datetime target; against a date target the instant is read as its UTC calendar day (spec docblock "The target column decides the form") — the section's example target is a datetime, so the sentence is right where it is used.
  • "or a { $field } naming a number field of the same record (NULL counts as zero days)" — RIGHT on same-record and NULL (AddDaysReferenceSchema carries $field only; "A NULL offset column contributes ZERO days", resolveDayOffset returns 0 on null, SQL uses COALESCE). Precision note: the spec says NUMERIC column, and driver-sql accepts the whole numeric class (NUMERIC_SCALAR_TYPES = NUMERIC_VALUE_TYPES number, currency, percent, rating, slider, progress, summary + integer/int/float); teaching "number" is the safe subset, not a falsehood. The same word recurs in "SQL push-down also refuses a dotted or non-number offset field" — strictly "non-numeric"; a currency offset column compiles. Carried as to-do, below.
  • "The filter cannot negate a positive grace_days column: store the signed offset" — RIGHT (spec: "so 'now minus the column' is a column that holds a negative number"; there is no subtraction form).
  • "Refused with INVALID_FILTER / 400: an anchor without addDays, a fractional or string offset" — RIGHT at every door: malformedNowAnchorRefusal (invalidComparandError, code INVALID_FILTER, status 400), MalformedNowAnchorError in the resolver (same code), engine-now-anchor.test.ts L117–129 and L187–192.
  • "any extra key (there is no subDays)" — RIGHT of the spec's declaration: NowAnchorSchema is closedObject(...).strict() and filter.test.ts L263–265 pins "an extra key is refused — there is no subDays". Enforcement gap I read, not a skill error: parseFilterAST runs lowering plus the comparand-type door, whose isWellFormedNowAnchor tests only addDays; the resolver spreads ...node; driver-sql reads the anchor by shape; the memory evaluator pins "an extra key does not disqualify it" (matches-filter-now-anchor.test.ts L167). So on a direct engine.find() where an extra key is tolerated and ignored, while the declaration door (metadata filters, aggregate() whose options are zod-parsed — engine-now-anchor.test.ts L187 "refused by its declaration") refuses it. The sentence is true of the spec as declared; the gap is the engine's, filed below as a card candidate.
  • "a target that is not date / datetime" — RIGHT: spec docblock "on a number, a text or a time, day arithmetic has no meaning and the comparison is refused loudly"; applyNowAnchorComparison L497–507 and having-filter.ts L711 refuse with INVALID_FILTER (engine-now-anchor.test.ts L175). Note: the in-memory evaluator alone (matchesFilter, no column declaration) fails closed rather than loudly; the spec's own words are "refused loudly", which the skill repeats.
  • "and any $in / $nin / $between position" — RIGHT: SetOperatorSchema / RangeOperatorSchema refuse it (filter.test.ts L276–278), driver-sql nowAnchorPositionError refuses a list member (INVALID_FILTER), engine-now-anchor.test.ts L195–200. Same pattern as the file's existing Field References claim for $field.
  • "SQL push-down also refuses a dotted or non-number offset field" — RIGHT on dotted (applyNowAnchorComparison L515–519) and on non-numeric (L528–534, engine-now-anchor.test.ts L179 "a TEXT offset column is refused"); it also refuses an undeclared offset column and the tenant column, which the section does not list (an omission inside "refuses", not a false clause). A nullable offset column is NOT refused — NULL is zero days — and the section says so.
  • "The filter alone answers it — not a stored formula field plus a hook" — the card's "who pays"; RIGHT.
  • Not stated, worth knowing: driver-mongodb refuses the anchor outright (mongodb-filter.ts L299, nowAnchorUnsupportedError), exactly as it refuses { $field }; the file's Field References section is equally silent on Mongo, so the new section inherits that silence rather than adding a falsehood.
  • SKILL.md pointer (L198–200): "{ $now: true, addDays: OFFSET } is the instant the query runs at, shifted by whole days a number field of the record holds" — true for the column arm; it omits the integer-literal arm, which the rules file teaches. A pointer, not a rule; precision to-do.

(b) Cuts. Nothing is deleted anywhere in the diff (skills/** hunks are pure additions; the ratchet hunks replace two rows). The dev's "nothing duplicated to cut" claim was judged by reading the whole head filters.md (211 lines): the new clauses do not restate the Date Macros section (which lists {now} and {30_days_ago} as tokens but never their instant-vs-calendar-day reading) nor the Common Mistakes. One near-restatement inside the new section itself: "legal exactly where a { $field } reference is" plus Field References L53–55 already imply the list-position refusal that "any $in / $nin / $between position" then spells out — about a dozen tokens, explicit on purpose for the refusal list; not a genuine duplicate under ruling A.

(c) Ratchet rows. Measured with the script's own convention, ceil(utf8 bytes / 4), over git show 13e75c8c:PATH (wc -c; the SKILL.md stamp line version: "18.1.0" plus newline, 20 bytes, excluded as the script's ONE exclusion): rules/filters.md 9928 bytes → 2482 = the new row 2482 (before: 8589 → 2148 under the old 2149, so +334 landed, 1 absorbed, ceiling +333 — the row comment's arithmetic is right); evals/filters-pagination-search.json 4699 bytes → 1175 = the new row 1175 (before 3819 → 955, headroom 0, +220); SKILL.md 16691 − 20 = 16671 → 4168 under the untouched 5547 (before 4124). Both raised rows are pinned at zero headroom; no headroom smuggled in. The ruling (tap A, 2026-10-27T01:21Z, 「A:先削重复,余额按实测提高」) is quoted verbatim in both row comments and in the PR body, which is where the script's header requires it ("a PR whose body quotes a maintainer ruling"). No other commit between the merge-base a66c9ff4 and origin/main touches the ratchet script or skills/objectstack-query/**. Gate verdicts on the head: Type Check · source gates (carries check-skills-token-ratchet + --self-test, check:skill-docs, check:skill-refs per lint.yml L5358/L5490/L5621) success at 2026-10-27T02:46Z; Lint · repo & tooling gates success; Lint · ESLint success (no max-len rule found in the ESLint config, and the long row-comment lines pass).

(d) Evals. Case 6 appended; ids 1–6 unique and sequential; the file parses as JSON; every must_contain string ($now: true, addDays, $field: 'grace_days_offset', $lt) occurs in expected_output, neither must_not_contain string (subDays, type: 'formula') does — checked by reading, consistent with the dev's claim. The prompt states the schema (due_at datetime, grace_days_offset number holding negative days), so the expected spelling is determinate. The one eval the card asks for is present; no eval README or SKILL.md prompt count exists to update. Type Check · consumer gates (check:skill-examples, lint.yml L6495) success at 2026-10-27T02:46Z; the new fence is an unmarked where: fragment like the file's other fragments.

(e) Wanted lines met, nothing beyond. The card wants "one subsection in rules/filters.md between Field References and Date Filtering Patterns, with the one example, and where the comparand is accepted and refused": one subsection, placed directly under Field References (L57, inside the named span), one example, acceptance and refusal lists — met. The dev's reason for the placement (the anchor is legal exactly where a { $field } reference is and borrows its addDays grammar) is true of the spec. SKILL.md line 1 (---) and the version stamp are untouched (hunks at L86 and L195 only); the two pointer lines are inside the claim's file surface (skills/objectstack-query/**) though the order said "untouched unless you must" — declared as a deviation, inside headroom, no raise. No file outside the claim's surface.

(f) PR body. Lines 1–2 are the harness attribution comment and the _Requested by_ line; Fixes #26301 on line 3 — exactly the exemption os-dev.md L323 writes ("harness 自写的 PR 正文首行归属注释原样留、只报;Fixes 紧随其后"), and Part-of PR must not also close its card / The card this PR closes must claim this branch are green on the head. Clause-②: no, Metadata-protocol: none line-initial. Skills-docs: updated here is not owed (kind none, no (narrowing) arm) but a written line must be true: under pm-dispatch SKILL.md:313 as landed in b33dd25b, updated here = "this diff teaches it in skills/** ... and the package's evals exercise it" — this diff does both (the section and eval 6), so the line is TRUE. ## 维护者速读 carries the six labels in order; 需要你做的 names approve / Merge when ready for Tier H; 两分钟验证 names the ratchet command and the exact lines it prints (is 2482 tokens (ceiling 2482; headroom 0)), which is the script's report shape. The two readings (per-file and whole-package before/after lines and tokens: 185→211, 401→403, 55→65, package 1063→1091 lines, 9660→10038 tokens) are present; the line counts match wc -l on both trees.

(g) Report and claim. os-dev-report 6432431995 (2026-10-27T02:42Z) carries every key of the os-dev.md L360–382 template plus deviations (6), line_budget, out_of_scope_findings (1); files_changed / rules_retired are not in the template on origin/main and are not present — no gap. Branch claude/project-thread-r9vctw = the claim's Branch: line; mcp_calls: 2 (reads), api_writes: 2. The relay wrote assignee os-warren and label skip-changeset at pr_create.

Check-runs on 13e75c8c read at 2026-10-27T02:51Z: 46 runs, 45 completed, 0 failed, 1 in progress (Test Core (1/8)). Green: both Governed Surface Queue Guard runs, Part-of guard, claim guards, Check Changeset, Check PR Size, Build Core, Lint · ESLint, Lint · repo & tooling gates, Lint · docs & code guards, Type Check · source gates, Type Check · consumer gates, Type Check · workspace, Dogfood Regression Gate, Plan Test Core shards. The remaining shard is not waited for.

② Semver level

skip-changeset is right: skills/objectstack-query/ holds SKILL.md, evals/, references/, rules/ and no package.json (git ls-tree origin/main); scripts/** publishes nothing. Check Changeset is green on the head. Clause-②: no and Metadata-protocol: none are line-initial on the PR body; the skill teaches a comparand the protocol has carried since the 18.0 floor (be531f685e, audit row Q1).

③ Boundary flags

  • Process deviation, not a defect: the dev made 2 GitHub MCP READS (issue_read get and get_comments on skills(objectstack-query): teach the now-anchored comparand { $now: true, addDays: FIELD } #26301) before reading the order's ⛔; no MCP write; declared in the report. Noted, no action.
  • Card candidate (engine, not this PR): NowAnchorSchema is declared closed, but parseFilterAST / the comparand door / the resolver / driver-sql / the memory evaluator all read the anchor by shape and ignore an extra key on a direct find() where, while aggregate() and metadata filters refuse it at the declaration door. ADR-0049 enforce-or-remove shape; class: public door + silent acceptance of an undeclared key (the answer is still the addDays answer). Dedupe words: $now extra key strict find parseFilterAST. Carrier: the seat's 收班简报 or a spec card; not this PR.
  • Precision to-dos for the skill (follow-up, not blocking): (1) "number field" → "numeric field (number, currency, percent …)" where the SQL class is meant; (2) the SKILL.md Cross-field pointer omits the integer-literal arm; (3) driver-mongodb refuses the anchor as it refuses { $field } — one clause in Field References would cover both.
  • Dev's out-of-scope note (addDays on a { $field } reference itself untaught in Field References, in the grammar since An ADR-0021 dataset measure cannot express a deadline that is another column plus an offset held in a third column — the grace-aware on-time rate has no spelling #14104): verified by reading — rules/filters.md L40–55 teaches the bare reference only. A real gap in objectstack-query, outside row Q1's Wanted; 收班简报 note, card at the seat's discretion.
  • Base reconcile: merge-base with origin/main is a66c9ff4 (the dev's stated base); the PR object's base.sha is c51aed4f (main's tip when the PR was opened), an ancestor of today's origin/main tip 92a0073b; none of the eight commits since a66c9ff4 touch the ratchet script or skills/objectstack-query/**. mergeable_state: blocked = the one shard still running, not a conflict.
  • Landing state read: draft=false (readied), one APPROVED review by os-zhuang at 2026-10-27T02:42Z, not merged at 2026-10-27T02:51Z.

Implemented-by: claude/project-thread-r9vctw
Reviewed-by: session_01LqKsGyMNFwwnbF5FTAqyPn

VERDICT: PASS

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

维护者速读

改了什么:skills/objectstack-query/rules/filters.md 在 Field References 之后新增一节「Now-Anchored Comparands」:{ $now: true, addDays: OFFSET } 的形状、唯一示例(按每条记录自己的宽限天数判逾期,偏移列存负数天)、哪里接受(六个标量比较、date/datetime 字段)、哪些被拒(无 addDays、小数或字串偏移、多余键、非日期目标、$in/$nin/$between 位置,SQL 下推拒点路径或非数值偏移列),并点明不必用存储公式字段加钩子。SKILL.md 两句指针(余量内)。eval 第 6 题。两行天花板按你的裁决 A 实测抬高(无重复可削,正文已说明)。
为什么改:该比较数自 18.0 起已在过滤语法里,技能只分别教了 $field 与日期宏;AI 作者要「按每条记录自己的宽限天数判逾期」时会去建存储公式字段再写钩子。
风险与回滚:只改发布技能包文字与一个 eval 夹具,不改运行时;回滚 = revert 本 PR。
先看:① rules/filters.md 新节(接受/拒绝清单逐条对照 NowAnchorSchema 与 #25808 用例核过);② 棘轮两行;③ eval 第 6 题。
两分钟验证:node scripts/check-skills-token-ratchet.mjs 预期末行 81 authored bundle file(s) within their ceilings,rules/filters.md 2482/2482、filters-pagination-search.json 1175/1175,headroom 0。
需要你做的:无——os-zhuang 已 approve + ready(02:42Z);Merge when ready 即落地。
席位意见:同意落地。复核顺带发现一处引擎层不一致(不是本 PR 的):NowAnchorSchema 声明封闭,但 engine.find() 的 where 门、解析器、SQL 驱动与内存求值器都容忍多余键,而 aggregate()/metadata 过滤在 zod 门拒收——席位按 filing gate 立卡交 spec/engine 车道分诊。

席位核对

ACCEPT · domain:skills#2 · session_01LqKsGyMNFwwnbF5FTAqyPn · 2026-10-27T02:55Z — seat review of record for #26301 on head 13e75c8c, against GitHub, not the report's own account. Published surface skills/** ⇒ Tier H: the record of this tier is the independent ## Contract review at comment 6432578572 (Served-tier: CONTRACT_REVIEW_TIER, head 13e75c8c, VERDICT: PASS, Local-runs: none; drafted by a reviewer other than the implementer, the card thread's dev). This seat adds the landing read; ⛔ the seat does not ready, arm or enqueue a Tier H PR.

  1. Shape. Base main, mergeable_state: clean, one commit; body lines 1–2 the harness attribution block, line 3 Fixes #26301 (os-dev.md 323's exemption), Clause-②: no, Metadata-protocol: none line-initial, plus a Skills-docs: updated here line the kind does not owe — written, so judged: TRUE under SKILL.md:313 (teaches it in skills/** and eval 6 exercises it). ## 维护者速读 six labels in order, then What changes / Tests / Acceptance notes / Verification with folds; both line-count readings present and matching wc -l (filters.md 185 → 211, SKILL.md 401 → 403, package 1063 → 1091). Files: rules/filters.md (+26), SKILL.md (+4/−2, not line 1), evals/filters-pagination-search.json (+10), scripts/check-skills-token-ratchet.mjs (+18/−2, two rows) — the claim's surface (02:13Z) incl. the CEILINGS rows under ruling A. Labels documentation, size/s, skip-changeset — right. Branch claude/project-thread-r9vctw, named by the claim. Model: the thread read the ceiling tier from get_session before its first edit.
  2. Record, measured. Every new sentence in filters.md L57–81 true of the spec on origin/main: NowAnchorSchema closed, six scalar operators on date/datetime, one pinned instant, addDays required as an integer of any sign or { $field } to a numeric same-record column, NULL offset = 0 days, list positions refused, SQL dotted/non-numeric offset refused; a stored negative value really moves backwards (Analytics datasets: no filter compares a datetime column against now minus an SLA held on a related record #25808 letter A; matches-filter-now-anchor test L62). Ratchet rows recomputed with ceil(utf8 bytes / 4) over the blobs: filters.md 9928 B → 2482 = row; eval 4699 B → 1175 = row; SKILL.md 4168 under 5547; ruling A quoted in rows and body; no duplicate to cut (judged). No commit between merge-base a66c9ff4 and origin/main touches the script or the package. Eval 6 well-formed, ids 1–6 sequential. The seat re-read the diff and reads the same.
  3. Gates, pinned to head 13e75c8c. 47 check-runs, all completed, none red or pending (reviewer's read 2026-10-27T02:54Z): Type Check · source gates (ratchet + self-test, skill-docs, skill-refs), Type Check · consumer gates (skill-examples), both Governed Surface Queue Guard runs, the Part-of and claim guards, Check Changeset, the Lint lanes.
  4. Accepted deviations. (a) Two GitHub MCP READS by the dev before it read the order's ⛔ — reads, not writes; a process deviation, named. (b) Skills-docs: line written though not owed — true, kept. (c) check:dual-build-cjs-loads NOT MEASURED locally; CI green. (d) Precision nits for a later touch, not blocking: 「number field」 should read 「numeric field (number, currency, percent, …)」 where the SQL class NUMERIC_SCALAR_TYPES is meant; the SKILL.md Cross-field pointer omits the integer-literal arm; driver-mongodb refuses the anchor as it refuses { $field } (Field References is equally silent).
  5. Carrier. Card to file by the seat (filing gate ①, class c, engine not skills): NowAnchorSchema is declared closed, but parseFilterAST's comparand door (isWellFormedNowAnchor checks only addDays), the resolver's spread, driver-sql and the memory evaluator tolerate an extra key on a direct engine.find() where, while aggregate() / metadata filters refuse it at the zod door — an ADR-0049 enforce-or-remove shape; dedupe words $now extra key strict find parseFilterAST. 收班简报: addDays on a { $field } reference itself (An ADR-0021 dataset measure cannot express a deadline that is another column plus an offset held in a third column — the grace-aware on-time rate has no spelling #14104) is untaught in Field References (outside row Q1); the in-memory matchesFilter fails closed on a non-temporal target where the docblock says 「refused loudly」 (SQL and having-filter.ts do refuse) — for the spec seat.
  6. Landing. os-zhuang approved and readied the PR at 2026-10-27T02:42Z (after the contract review was dispatched, before it landed); auto-merge not armed at the seat's read — the maintainer's hand lands it (Merge when ready). ⛔ Nothing for the seat to ready, arm or enqueue. On MERGED the seat closes the card with the landing record.

Generated by Claude Code

…-r9vctw

# Conflicts:
#	scripts/check-skills-token-ratchet.mjs
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT (addendum, pinned to the merge head) · domain:skills#2 · session_01LqKsGyMNFwwnbF5FTAqyPn · 2026-10-27T03:26Z — head d3ab1f9a is the merge of origin/main 96f12585 into claude/project-thread-r9vctw on top of the reviewed head 13e75c8c (parents 13e75c8c + 96f12585; merge commit, no rebase, no force), after the merge queue removed the PR at 2026-10-27T03:18Z on a textual conflict in the ratchet script's neighbouring rows (siblings #26385 / #26388 / #26390 landed meanwhile). The seat read: git diff 13e75c8c d3ab1f9a -- skills/objectstack-query is empty (the three skills files byte-identical to the reviewed head); in scripts/check-skills-token-ratchet.mjs the only lines differing from main are this PR's two rows (rules/filters.md 2149 → 2482, evals/filters-pagination-search.json 955 → 1175) with their ruling-A comments — every sibling row kept as main carries it. So the contract review 6432578572 and the seat's ACCEPT 6432605016 carry over to this head unchanged (the record continues to govern a head moved only by a merge that leaves the reviewed files identical); the dev's merge report is comment 6432941421 on #26301 (ratchet + self-test, skill-docs, skill-refs, nul-bytes, role-word, doc-authoring, merge-driver, ratchet-remedy-authority exit 0 on d3ab1f9a). os-zhuang's APPROVED review stands on 13e75c8c (an authorised approval does not expire with a later push, per the lane's rule); auto-merge reads disarmed by the dequeue and CI on d3ab1f9a is pending at the seat's read (35 check-runs: 9 success, 12 skipped, 14 in progress). On every check green the seat re-arms auto-merge through the relay, as the lane's rule for a kicked PR with a standing approval says; nothing else to ready or enqueue.


Generated by Claude Code

…-r9vctw

# Conflicts:
#	scripts/check-skills-token-ratchet.mjs
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT (second addendum, pinned to the merge head) · domain:skills#2 · session_01LqKsGyMNFwwnbF5FTAqyPn · 2026-10-27T03:50Z — head 7f5666be is the merge of origin/main 75afeffd into claude/project-thread-r9vctw on top of d3ab1f9a (parents d3ab1f9a + 75afeffd; merge commit, no rebase, no force), after the queue removed the PR a second time at 2026-10-27T03:43Z when #26420 landed the row directly above this PR's eval row. The seat read: git diff 13e75c8c 7f5666be -- skills/objectstack-query is empty (the three skills files byte-identical to the reviewed head); the ratchet script differs from origin/main 75afeffd only by this PR's two rows (rules/filters.md 2149 → 2482, evals/filters-pagination-search.json 955 → 1175) with their ruling-A comments. The contract review 6432578572, the seat's ACCEPT 6432605016 and the first addendum carry over unchanged; the dev's second merge report is comment 6433247423 on #26301. os-zhuang's APPROVED review stands; CI on 7f5666be pending at the seat's read (39 check-runs: 14 success, 12 skipped, 13 in progress). Landing order: the sibling #26416 (further along in CI) is re-armed first; this PR is re-armed after #26416 merges and this branch has merged main once more, so the queue does not kick one with the other — the seat does both re-arms through the relay on the standing approval.


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

席位核对

ACCEPT (third addendum, pinned to the merge head) · domain:skills#2 · session_01LqKsGyMNFwwnbF5FTAqyPn · 2026-10-27T04:13Z — head 619a2212 is the merge of origin/main bb60b86f (sibling #26416 landed at 2026-10-27T04:08Z) into claude/project-thread-r9vctw on top of 7f5666be (parents 7f5666be + bb60b86f; merge commit, no rebase, no force; the dev reports a clean merge, no conflict). The seat read: git diff 13e75c8c 619a2212 -- skills/objectstack-query is empty (the three skills files byte-identical to the reviewed head); against origin/main bb60b86f the PR's delta is the same four files, +58/−4, and the ratchet script differs only by this PR's two rows (2149 → 2482, 955 → 1175) with their ruling-A comments. The contract review 6432578572, the seat's ACCEPT 6432605016 and the two earlier addenda carry over unchanged; the dev's third merge report is comment 6433247423 on #26301. os-zhuang's APPROVED review stands; CI on 619a2212 pending at the seat's read (38 check-runs: 10 success, 12 skipped, 16 in progress). No sibling skills PR is in the queue now, so on every check green the seat re-arms auto-merge through the relay on the standing approval; nothing else to ready or enqueue.


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 27, 2026
Merged via the queue into main with commit f9e871a Oct 27, 2026
41 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-r9vctw branch October 27, 2026 04:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skills(objectstack-query): teach the now-anchored comparand { $now: true, addDays: FIELD }

3 participants