Repository navigation
feat(spec): add next_month, next_quarter and next_30_days date-range presets - #30133
Conversation
…presets The three forward presets join DATE_RANGE_PRESETS with their inclusive last-day windows. Core resolves them half-open; the month and quarter ends re-anchor next_*_start on the day it names, so no macro token is added. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzj9os3R42oiDRwN6HZ
Adds next_month, next_quarter and next_30_days labels to all ten locale packs and the filter builder's defaults, teaches the client macro resolver next_month_end and next_quarter_end, and moves the two preset pins. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzj9os3R42oiDRwN6HZ
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzj9os3R42oiDRwN6HZ
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzj9os3R42oiDRwN6HZ
The dashboard-window proof capped its fixture days at today, so the three forward presets selected no fixture rows and every proof under them failed. A window whose last day is after today now writes its own edges; the days written for the other presets are unchanged. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzj9os3R42oiDRwN6HZ
Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzj9os3R42oiDRwN6HZ
📓 Docs Drift CheckThis PR changes 4 package(s): 3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:
What this run could not see
Coarse fallback — 179 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 3222cacf7151968084b89450032a330adf838b53 && git checkout 3222cacf7151968084b89450032a330adf838b53
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin a9fb14bdeeb5e09fec341ead4e0fffd8d8ed00bb 5af3917a89fc44c756aef0dfb386824b19e323f3 && git checkout -B drift-repro a9fb14bdeeb5e09fec341ead4e0fffd8d8ed00bb && git merge --no-ff 5af3917a89fc44c756aef0dfb386824b19e323f3
node scripts/docs-audit/affected-docs.mjs --json a9fb14bdeeb5e09fec341ead4e0fffd8d8ed00bb
|
|
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K Contract-review-claim: session_01WGufzfgvGexKn9vLHsy89K · head f7e8b20 · 2026-11-04T15:25Z Full review of the first head at |
|
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K Contract reviewServed-tier: ① Derived judgments
② Semver levelClause-② yes (widening): three new accepted values on ③ Boundary flags
④ Design judgmentRe-anchoring is the right shape for the ruling: it names the month and quarter after next without a new macro token ( Should-fix 1: analytics-date-range.ts:368-372 re-anchors on local midnight; on a zone that skips 00:00 of a period's first day (America/Asuncion 2023-10-01) Implemented-by: VERDICT: PASS |
A zone that springs forward at 00:00 on a period's first day (America/ Asuncion, 2023-10-01) has no midnight, and the day-start instant read as the day before, so next_month and next_quarter came out empty there. Also drops stale preset counts from the verify docs and lists the forward presets in the plugin-dashboard README. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzj9os3R42oiDRwN6HZ
|
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K Contract-review-claim: session_01WGufzfgvGexKn9vLHsy89K · head 186fb60 · 2026-11-04T15:42Z Delta review of fix round 1 (the four should-fixes of review 6634808446) at |
|
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K Contract review — delta (fix round 1)Served-tier: ① Derived judgments
② Semver levelUnchanged from the full review: Clause-② yes (widening), Metadata-protocol: added. The noon anchor changes only which instant the resolver stands on while naming a day; both rendered bounds are still day-start instants of resolver-named days. No bound of any of the 13 existing presets can move (they never take the re-anchored path), and ③ Boundary flags
④ Design judgmentThe fix is the smallest one that closes the hazard: it moves where the resolver stands, not what it answers, and keeps the "one resolver, no calendar arithmetic in core" line the full review approved. Anchoring at noon rather than the gap-aware Should-fix 1 (body r1, Server bullet): replace "with the reference instant at the start of that day in the same zone" with "with the reference instant moved into that day (its noon) in the same zone" so the bullet does not contradict its own next sentence. Implemented-by: VERDICT: PASS |
|
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K 维护者速读
席位意见:fable 契约复审两轮,都是 PASS。首轮提了 4 个建议项:
UI 侧还缺两个宏,开了后续卡 #30134(ui,p3)。 席位核对 — ACCEPT · domain:spec#3 · head
|
The os verify --dashboard-windows pin counted 13 probes and 13 held; with the three forward presets the run reads 16. Both now follow the vocabulary's length. Co-Authored-By: Claude <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WGufzj9os3R42oiDRwN6HZ
|
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K 席位核对 — ACCEPT carried to head
|
domain:spec#3 · os-support-ai · session_01WGufzfgvGexKn9vLHsy89K
Requested by Yibo Wang · project thread
Fixes #30076
Ruling-ref: 6631151570
Clause-②: yes (widening)
Metadata-protocol: added
Skills-docs: none —
git grep -n "last_90_days\|last_30_days" -- skills .claude/skillshas 0 hits, and noskills/**line lists the preset vocabulary (the dashboards rule only usesthis_quarter/this_yearas examples). Nothing is owed to the paused skills lane.Prior rulings read: date-range-presets,date_range_presets,next_month → 0 hits; none; thread: none; repo: objectstack-ai/objectstack (the card's own ruling 6631151570 is the scope; the tool reads rulings by first-line shape and did not classify it)
ADR-0087: not needed. Nothing is removed or narrowed; the 13 existing windows are byte-identical.
维护者速读
next_month(下月)、next_quarter(下季度)、next_30_days(未来 30 天),服务端analytics-date-range能解析它们,控制台 10 种语言都有标签。packages/spec/src/data/date-range-presets.ts(表和顺序说明)、packages/core/src/utils/analytics-date-range.ts(重锚定),以及下面的测量表。pnpm --filter @objectstack/spec exec vitest run src/data/date-range-presets.test.ts,然后 build spec 后pnpm --filter @objectstack/core exec vitest run src/utils/analytics-date-range.test.ts(含上海、纽约 DST、查塔姆群岛行)。What changes
packages/spec/src/data/date-range-presets.ts):DATE_RANGE_PRESETSaddsnext_month,next_quarter,next_30_days, named afterlast_30_days's spelling.DATE_RANGE_PRESET_MACRO_WINDOWSadds their rows in the inclusive last-day convention (commit 80aef80), and the closed-window rule now covers the forward presets. Every row is pinned by extent indate-range-presets.test.ts(reference day 2026-07-15: August is 31 days, Q4 is 92,next_30_daysis 31 days likelast_30_days).this_month, last_month, next_month,this_quarter, last_quarter, next_quarter, thenlast_7_days, last_30_days, last_90_days, next_30_days. Each forward preset sits in the family it extends, after the backward member, so all month windows are found in one place and the rolling group reads backward then forward. The existing 13 keep their relative order; no forward preset opens a group, because the backward windows are the ones most filters ask for.next_30_days= today plus the 30 days after it (['{today}', '{30_days_from_now}']), the mirror oflast_30_days(['{30_days_ago}', '{today}']). It opens on today because a row due today is still ahead of a forecast.packages/core/src/utils/analytics-date-range.ts):PRESET_WINDOW_TOKENSresolves the three half-open. A bound may now be{ token, from }: resolvefrom, then resolvetokenagain with the reference instant moved into that day (its noon) in the same zone.next_monthends at{ token: 'next_month_start', from: 'next_month_start' }, i.e. the month after next;next_quarterlikewise. The resolver stands at noon of the anchor day, not its first instant: where a zone springs forward at 00:00 on a period's first day (America/Asuncion, 2023-10-01) the day has no midnight, the day-start instant reads as the day before, and the window came out empty (measured:end === start). Seat ruling on this card thread: re-anchor, no new macro token (option A of the seat's two).analytics-date-range-conformance.ts),analytics-date-range-closed-vocabulary.test.tsand lintvalidate-preset-comparands.tsiterateDATE_RANGE_PRESETS, so they take the three without edits. objectqltemporal-comparand-door.tsdoes not iterate the list: it judges a comparand by the field's declared type, so a new preset name needs nothing there (its test passes unchanged). Only prose counts ("the 13 declared names", "each of the thirteen") were made count-free.ui/packages/i18n/src/locales/(ar, de, en, es, fr, ja, ko, pt, ru, zh; zh-Hant has no pack yet, phase 2 of Platform locale table (ruling F of #29349): one list of 11 languages in spec, complete on every surface #29428, so none is created); the three English defaults infilter-builder.tsx's label table (measured: without them the 29115 test renders the raw keysdashboard.filters.range.next_month, 4 cases red);next_month_endandnext_quarter_endin the client resolverui/packages/core/src/utils/date-macros.ts(spec tokens the client did not resolve; without them the console lowers 下月 to['2026-08-01', '{next_month_end}']); and the two pins that counted 13 (dashboard-filters.presetWindows-29116.test.ts,filter-builder-relative-dates-29115.test.tsx).packages/verify(dashboard-window.ts, a reader ofDATE_RANGE_PRESETS): its fixture rows were written only on days up to today, so the three forward windows selected no rows and 5 of 15 cases indashboard-window.test.tswent red. A window whose last day is after today now also writes its own future edges; the days written for the 13 existing presets are unchanged (15/15 green).packages/spec/src/ui/dashboard.test.tscounted 13 presets and 14 default ranges; now 16 and 17. The 29116 test's compareTo case runs on the 13 retired presets only, with a comment naming Dashboard compareTo on a forward date preset (下月, 下季度, 未来 30 天) compares the window with itself #30140.content/docs/ui/dashboards.mdxpreset table; stale "13 presets" / "never after today" prose inpackages/verify/README.mdandcontent/docs/deployment/cli.mdx; the preset list inui/packages/plugin-dashboard/README.md;content/docs/references/**regenerated bygen:docs; thedocs/qa/platform-checklist/areas/api-backend.jsonitemapi-backend.date-range-preset-matrixwidened to 16 variants (revision 3,enumSource.expect: 16).Measurement: preset → spec window → server window → resolves?
Reference: spec on 2026-07-15 (UTC days); server on
now2026-09-09T12:34:56.789Z unless noted. Server windows are[start, end)instants.$between)next_month['{next_month_start}', '{next_month_end}']→ 2026-08-01 … 2026-08-31next_month_start→next_month_startre-anchored onnext_month_startmonth_after_next_starttoken exists;PERIOD_REstops atnext_)next_quarter['{next_quarter_start}', '{next_quarter_end}']→ 2026-10-01 … 2026-12-31next_quarter_start→next_quarter_startre-anchored onnext_quarter_startnext_30_days['{today}', '{30_days_from_now}']→ 2026-07-15 … 2026-08-14today→31_days_from_nowTimezone and DST rows pinned in
analytics-date-range.test.ts(#30076block):next_monthnext_monthnext_monthnext_quarternext_30_daysnext_monthnext_monthnext_quarterThe existing
#17341sweep (every preset, 8 instants × 4 zones) also holds each new end to "the day after the spec's inclusive last day", and the tiling test now includesthis_month → next_monthandthis_quarter → next_quarter.UI reader measurement
ui/packages/core/src/utils/dashboard-filters.tsDATE_RANGE_PRESETSand lowers throughDATE_RANGE_PRESET_MACRO_WINDOWSui/packages/components/src/custom/filter-builder.tsxDATE_RANGE_PRESETS.map(...)for options and label keysui/packages/core/src/utils/date-macros.ts(client resolver)next_month_end/next_quarter_end; added here.next_week_endandnext_year_endare still missing: follow-up #30134ui/packages/types/src/data-protocol.ts:595-610next_month,next_quarter,next_30_days(plus undeclarednext_year,next_7_days,next_90_days); not editedNo
domain:uifollow-up is needed for the preset list itself: both readers derive from the spec.Follow-ups
domain:ui, p3): the console's client resolver leaves{next_week_end}and{next_year_end}unresolved.domain:ui, p2): dashboardcompareToon a forward preset compares the window with itself.compare-to.tsis not touched here.Verification
analytics-date-range.test.ts73 at the round-1 head (two Asuncion rows added). core and lint typecheck passed (after a fullpackages/**build, 80/80 tasks).dashboard-window.test.ts(15); lintvalidate-preset-comparands.test.ts(29); objectqlengine-temporal-comparand-door.test.ts(14); driver-sqlsql-driver-calendar-day-upper-bound.test.ts(18); service-analyticsnative-sql-temporal-comparand-decline.test.ts(8); verify typecheck passed.ui/root):dashboard-filters.presetWindows-29116,filter-builder-relative-dates-29115,dashboard-range-preset-labels-4984,en-zh-key-parity: 4 files, 112 passed.pnpm --filter @objectstack/spec check:generated: all 14 artifacts current.dispatch-gates --commandslisted 108; all 108 run,--ranreconciles 108/108, 0 NOT-MEASURED. 107 exit 0.pnpm check:platform-checklistexits 1 on one finding that is onmainalready:areas/platform-core.jsonanchorspackages/metadata-protocol/src/protocol.ts#assertProtocolCompat, whichorigin/main'sprotocol.tsno longer declares (0 matches at basef68b47ca). This PR's own checklist item passes.check:generatedcrashed once on heap at 4 GB insidecheck:test-typecheckand passed at CI's 6 GB (NODE_OPTIONS=--max-old-space-size=6144).pnpm check:cli-examples-parity, exit 0; the docs gates for the edited prose (check:doc-authoring,check:docs-transcript-drift,check:doc-anchors,check-affected-docs,check-drift-comment,check:published-readme-links,check:issue-citations,check:docs-spec-enumerations) exit 0; the ui 29116 test 51 passed.Human Minimum
packages/specholds no runtime logic.pnpm lintpasses.main, one pull request.packages/spectouched: its generated artifacts are regenerated and committed.content/docs/references/,packages/*/CHANGELOG.mdorcontent/docs/releases/.🤖 Generated with Claude Code
https://claude.ai/code/session_01WGufzj9os3R42oiDRwN6HZ