Skip to content

fix(spec,core): the rolling last_N_days presets close on {today} at the spec table, the save door's prescription and core's analytics preset arm - #23208

Merged
objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-23170-rolling-preset-upper-bound
Oct 13, 2026
Merged

objectstack-fleet[bot] merged 6 commits into
mainfrom
claude/issue-23170-rolling-preset-upper-bound

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #23170
Clause-②: no (no accept set widens: the three rolling windows gain the inclusive {today} end every calendar preset already has)

What this does

The three rolling presets (last_7_days, last_30_days, last_90_days) were the open arm of DATE_RANGE_PRESET_MACRO_WINDOWS: end: null, prescribed to authors as { $gte: '{N_days_ago}' }. A $gte-only window has no upper bound, so on a column that carries future dates it counted every later day as well. Triage's direction (comment 6139344728), applied here: the three rows become ['{N_days_ago}', '{today}'] and the end: null arm is retired. The refusal's prescription names the window with both its bounds, and core's analytics preset arm selects the same extent, keeping its own end spelling.

One extent at all three faces: from the start of the day N days ago through the end of today, in the reference timezone.

Face Before After
spec table DATE_RANGE_PRESET_MACRO_WINDOWS ['{30_days_ago}', null] ['{30_days_ago}', '{today}']
save-door prescription bareDateRangePresetComparandMessage e.g. { $gte: '{30_days_ago}' } e.g. { $between: ['{30_days_ago}', '{today}'] }
core resolveAnalyticsDateRangePreset end = the reference instant, endExclusive: false end = the start of tomorrow in the reference zone, endExclusive: true (token tomorrow, the same end today already uses)

Files in the claimed surface:

  • packages/spec/src/data/date-range-presets.ts: the three rolling rows, the table type (end: string), the docblock (the open-arm paragraph is replaced by "every entry closes"), and the prescription (the end === null branch is gone, so the message always uses the $between pair).
  • packages/spec/src/data/date-range-presets.test.ts: the rolling rows are pinned to their {today} end. The extent test now covers the rolling rows: their token days and expected windows on the reference day, and day counts of 8, 31 and 91. Also new: the card's measurement as a pin, and a pin that the prescription is the closed $between.
  • packages/core/src/utils/analytics-date-range.ts: the rolling rows of PRESET_WINDOW_TOKENS become ['N_days_ago', 'tomorrow'], the type becomes endBefore: string, the NOW arm is removed, and the docblocks change to match.
  • packages/core/src/utils/analytics-date-range.test.ts: the NOW pins are retired. New pins: every preset is endExclusive; a rolling window ends where today ends in every sweep zone; the card's measurement at this face; a row later today than the reference instant is inside. The #17341 sweep now iterates all thirteen presets, the rolling ones included.
  • packages/lint/src/validate-preset-comparands.test.ts: the verbatim verdict pin now reads its window is { $between: ['{30_days_ago}', '{today}'] }.
  • .changeset/23170-rolling-presets-close-on-today.md: @objectstack/spec patch and @objectstack/core patch.

Claim amendment 6141817295: two out-of-surface pins of the old extent (test expectations only, no source change)

  • packages/services/service-analytics/src/__tests__/analytics-date-range-conformance.test.ts: the preview test now asserts endExclusive true, with the last instant of today inside and the first instant of tomorrow outside. The executor test pins [2026-08-10T00:00:00.000Z, 2026-09-10T00:00:00.000Z, true], and previousPeriod is unchanged at 2026-07-10 to 2026-08-09.
  • packages/verify/src/dashboard-window.test.ts:190: the pin now reads { $gte: '{7_days_ago}', $lte: '{today}' }.

Claim amendment 6142517145: text this change made false, corrected in this round (no behaviour change except item g's dead branch)

  • (a) packages/lint/src/rule-explanations.ts, the os explain filter-preset-comparand paragraph.
    • BEFORE: "which the verdict names — a rolling preset is { $gte: START }, a calendar one { $between: [START, END] } — or an ISO date."
    • AFTER: "which the verdict names — { $between: [START, END] }, its first day through its last, and a rolling preset's last day is {today} — or an ISO date."
  • (b) packages/core/src/utils/analytics-date-range-conformance.ts, the finding sentences.
    • The ROLLING docblock. BEFORE: "The three presets whose upper bound is NOW rather than a calendar boundary." AFTER: "The three rolling presets: their window runs through today and stops before tomorrow begins."
    • The @param options note. BEFORE: "the rolling presets (whose bound is NOW)". AFTER: "every preset, each anchored on that instant's calendar day".
    • The finding text. BEFORE: "rolling window ends at NOW and REACHES its bound". AFTER: "rolling window stops BEFORE tomorrow begins, as today does".
  • (c) Docblocks and comments in driver-memory and service-analytics source.
    • packages/drivers/driver-memory/src/memory-analytics.ts. BEFORE: "The three rolling last_N_days presets end at NOW and stay INCLUSIVE." AFTER: "…stop where 'today' stops, at tomorrow's midnight, with endExclusive: true as well."
    • packages/services/service-analytics/src/dataset-executor.ts. BEFORE: "the ten calendar presets report the first instant AFTER the window … The three rolling presets end at NOW, a moment they REACH, so their bound is already the last day." AFTER: "every preset reports the first instant AFTER the window (this_month ends at the next month's start, and a rolling last_N_days window at tomorrow's start) …".
    • Found by the sweep, same claim and same class (see Sweep below):
      • packages/services/service-analytics/src/strategies/native-sql-strategy.ts. BEFORE: "the three rolling ones end at NOW and reach it ($lte)". AFTER: "every preset stops BEFORE its end instant ($lt), the three rolling ones before tomorrow begins, as today does".
      • packages/services/service-analytics/src/strategies/objectql-strategy.ts. BEFORE: "while the three rolling ones end at NOW, a moment they reach". AFTER: "every preset stops BEFORE its end instant … the three rolling ones before tomorrow begins, as today does".
  • (d) packages/verify.
    • dashboard-window.ts:22-23. BEFORE: "its first and last day, inclusive, the last open for the three rolling last_N_days presets". AFTER: "its first and last day, inclusive; the three rolling last_N_days presets end on {today}".
    • dashboard-window.ts:554. BEFORE: "first and last day inclusive, the rolling end open". AFTER: "first and last day inclusive (every preset names its last day)".
    • Found by the sweep: packages/verify/README.md:161, which ships in the package's files. BEFORE: "e.g. { $gte: '{7_days_ago}' }". AFTER: "e.g. { $gte: '{7_days_ago}', $lte: '{today}' }".
  • (e) content/docs/deployment/cli.mdx:2035-2036. BEFORE: "({ $gte: '{7_days_ago}' } for last_7_days, …)". AFTER: "({ $gte: '{7_days_ago}', $lte: '{today}' } for last_7_days, …)".
  • (f) Two driver-memory test comments.
    • memory-analytics-date-range-timezone.test.ts. BEFORE: "that leg ends at NOW … (endExclusive is false for the rolling family …)". AFTER: "that leg stops before tomorrow begins, as today does … (endExclusive is true for every preset; …)".
    • memory-analytics-date-range-conformance.test.ts:35. BEFORE: "Frozen so the three rolling presets, whose bound is NOW, are comparable." AFTER: "Frozen so the presets, every one anchored on NOW's calendar day, are comparable."
  • (g) packages/lint/src/validate-preset-comparands.ts:301, the dead branch.
    • BEFORE: end === null ? '{ $gte: …}' : '{ $between: […] }'. AFTER: '{ $between: […] }'.
    • Proved unreachable on the merged head before removal, three ways:
      • Type level: in lint's program, null extends (typeof DATE_RANGE_PRESET_MACRO_WINDOWS)[DateRangePreset][1] resolves to unreachable, and tsc exits 0. The control, asserting reachable, fails with TS2322 (exit 2).
      • Runtime: 13 presets in the built table, 0 null ends, 13 string ends.
      • Producers: presetComparandVerdict takes its end only from the table, its one call site is finding(), and no site in the tree assigns into the table.
  • Pending release note .changeset/pre/15953-verify-dashboard-window-family.md:15. This is an in-place correction of the quoted window only, and the 63-byte frontmatter is byte-identical. BEFORE: "as written ({ $gte: '{7_days_ago}' }, …)". AFTER: "as written ({ $gte: '{7_days_ago}', $lte: '{today}' }, …)".

Sweep. git grep was run over the tree (CHANGELOGs and content/docs/releases/** excluded) for sentences that state the rolling end as NOW, open or $gte-only. Every hit is either edited (above) or true as written:

  • The .changeset/23170-* lines describe the before-state.
  • { $gte: '{30_days_ago}' } in filter examples and tests is an authored macro filter, not a preset's window: date-macros.zod.ts, skills/objectstack-query/rules/filters.md, the lint, objectql and spec test fixtures, and the measured positive control in validate-preset-comparands.ts:91.
  • "the measurement that found the open arm" in this PR's own tests is history.
  • open arm in the component-type, flow-template and messaging files is an unrelated sense of the phrase.
  • analytics-date-range.ts:64 ("An end at NOW would drop …") is a counterfactual that is true.

Why check-empty-changeset is red, by design

node scripts/check-empty-changeset.mjs --base origin/main refuses this PR by name under the foreign changeset rule (ruling D on #17712), because the PR changes .changeset/pre/15953-verify-dashboard-window-family.md, a note it did not add. The rule names two classes with opposite remedies, and this PR is the DELIBERATE CORRECTION class: "your change may have made this PENDING release note false, and you rewrote it in the same stroke. Remedy: do NOT restore it -- say so on the PR and get it confirmed; restoring it from the base would put the false sentence back." The note is pending: the 18.0.0-next.0 release consumed it into .changeset/pre/, and it is aggregated into the stable 18.0.0 notes at pre exit. Its sentence quoted the rolling window as { $gte: '{7_days_ago}' }, which this PR makes false, so it is corrected in place and the red is left standing. The confirmation is the PR comment that names the gate.

Verification (merged head da95e4e8e)

  • Merge: origin/main (75cada0ea) was merged with a merge commit by scripts/pm/os-regen-merge.sh (no conflicts, no os-regen debt). Then pnpm install --frozen-lockfile, and a whole-workspace build (turbo run build --filter=!@objectstack/docs, 74/74 tasks).
  • pnpm --filter @objectstack/spec check:generated: all 14 artifacts are up to date.
  • Gates:
    • dispatch-gates --commands on the merged head gives 113 commands, plus the 29 commands of CI's type-check source-gates lane that are outside the derived list (pnpm check:future-spec-major included), 142 in all.
    • Each was run with its exit code captured: 141 exit 0, and check-empty-changeset exits 1 (the deliberate correction above).
    • check:dual-build-cjs-loads is measured this time (exit 0 after the full build).
    • --ran: 113 derived, 113 run, 0 NOT-MEASURED, 0 UNRUN.
  • Tests at the merged head:
    • lint 137 files / 6663 tests passed (the preset-comparand and os explain tests included);
    • core 95 / 2401;
    • driver-memory 70 / 1718;
    • service-analytics 183 / 4497 passed, 262 skipped;
    • verify dashboard-window.test.ts 10/10 (boots the stack);
    • the cli unit-tier cli.mdx readers (docs-cli-enumeration-parity, create-example-retired-docs-parity, init), 3 files / 87 tests;
    • spec date-range-presets and filter-preset-comparand, 24/24.
    • Earlier, at 7ca2abfba, before the merge: spec's full suite, 661 files / 20110 tests.
  • Typecheck: lint, core, driver-memory, service-analytics and verify typecheck scripts, each exit 0 (lint and verify include check:test-typecheck).
  • Ablation, of core's extent at 7ca2abfba, via scripts/ablation-replace.mjs: the rolling end was reverted to the reference instant. 5 red, 44 green: stops where today stops, keeps the rest of today, and the #17341 sweep for the three rolling presets. The today−5 / today+5 pin stayed green, which is the expected direction because core's face already counted 1. Restored: blob == HEAD, and git diff HEAD is empty.
  • Measurements:
    • H1: the entry .d.ts narrows to end: string, and the api-surface snapshot does not move.
    • H3: the card's measurement is pinned at the spec face and the core face; the HTTP face with a today+5 row is NOT MEASURED, because verify's fixtures are never after today.
    • objectui at its local HEAD: 0 readers of the table. Its PRESET_RANGES already closes on {today}.

Patch round (seat note 6144366518)

This answers contract review 6144351693 (FAIL on da95e4e8e, one shipped sentence). Head: 8927fbc43. No merge of origin/main was needed: git merge-tree against 06dbc4e59 is clean, and this round touches no generated artifact.

  • The ADR-0087 semantic entry, packages/spec/src/migrations/entries/semantic/18.filter-preset-ordering-comparand-refused.ts, replacement only, in the entry's own double-quote spelling.
    • BEFORE: { $gte: "{30_days_ago}" } for last_30_days. AFTER: { $between: ["{30_days_ago}", "{today}"] } for last_30_days.
    • Nothing else in the entry changed. No literal pin of that text exists in the tree, and nothing committed is generated from it: spec-changes.json and the protocol-upgrade pages are gitignored, and check:spec-changes / check:upgrade-guide are green.
  • The changeset .changeset/23170-rolling-presets-close-on-today.md.
    • It gains '@objectstack/lint': patch (the changed os explain filter-preset-comparand paragraph) and '@objectstack/verify': patch (the changed README in its files).
    • It gains one sentence naming the entry correction.
  • Double-quote sweep, over packages/ and content/docs/ for "{7_days_ago}", "{30_days_ago}" and "{90_days_ago}":
    • Edited: the entry above, the only hit that stated a preset's window as $gte-only.
    • True as written, measurement records (an authored macro comparand against a bare preset name, 51 rows seeded / 38 in-window, no future-dated row): packages/core/src/utils/temporal-comparand.ts:23, packages/lint/src/validate-preset-comparands.ts:29, packages/objectql/src/temporal-comparand-door.ts:20, packages/spec/src/data/filter.zod.ts:1718 and packages/spec/src/data/filter-preset-comparand.test.ts:11.
    • True as written, placeholder examples with no $gte: packages/objectql/src/temporal-comparand-door.ts:478,481 and packages/objectql/src/engine-aggregate-having-temporal-door.test.ts:297.
    • True as written, already closed: packages/spec/src/data/analytics.zod.ts:833 (["{7_days_ago}", "{today}"]).
    • True as written, release-owned CHANGELOG history: packages/lint/CHANGELOG.md:8950, packages/objectql/CHANGELOG.md:3850 and packages/spec/CHANGELOG.md:36696.
    • content/docs/: 0 hits.
  • Re-measured on 8927fbc43:
    • pnpm --filter @objectstack/spec check:generated, after a real spec build: all 14 up to date.
    • dispatch-gates --commands derives 115 (it adds check:spec-changes and check:upgrade-guide, both exit 0). check-changeset-no-major --base origin/main exits 0, check-adr-0087-registration --base origin/main exits 0, and check-empty-changeset --base origin/main exits 1, the designed red (PR comment for this head).
    • Of the 115, 114 exit 0 and 1 is the designed red. Three gates that read built output first refused with PREREQUISITE NOT MET in this fresh worktree (check:skill-examples, check:dual-build-cjs-loads, check:lean-entry-closure). After a whole-workspace build they were re-run, each exiting 0, together with check:sourcemap-no-sources-content, check:dts-closure and check:published-files, also each exiting 0. --ran: 115 derived, 115 run, 0 NOT-MEASURED, 0 UNRUN.
    • Spec migration and spec-changes tests: 11 files / 283 tests.
    • lint rule-explanations plus validate-preset-comparands: 35/35.
    • pnpm --filter @objectstack/spec typecheck exits 0.

Acceptance notes

  • ResolvedAnalyticsDateRange.endExclusive is now true for every value the preset resolver returns. The field stays, because each face's array arm still produces endExclusive: false for a caller's explicit window.
  • The rolling windows span N+1 calendar days (last_7_days covers 8), which is the extent the console's lowering already selects. That was triage's ruling, and it is pinned as such.
  • packages/verify/src/dashboard-window.ts still carries $lte-absent arms (selects, nestedIn, and the to ? spread at the condition build). The closed table no longer feeds them. They are left as is because the amendment covers comments only in that file.
  • The CLI integration-tier test test/verify-dashboard-windows.test.ts is declared to CI.
  • origin/main moved 12 commits after this merge, and 7 gate-input files changed there. None of them is a family that reads a path this diff touches. The queue rebuilds on current main.

Generated by Claude Code

…he spec table, the save door's prescription and core's analytics preset arm

The three rolling windows were the table's open arm ($gte-only), so the
prescribed window counted every future-dated row; core's preset arm ended at
NOW. Both now select [N_days_ago, end of today], the extent the console's
lowering already uses; core keeps its own stops-before spelling (tomorrow).

Claude-Session: https://claude.ai/code/session_012uFegdfR6K6uqvLzt7XsdZ
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 13, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 6 package(s): @objectstack/core, @objectstack/driver-memory, @objectstack/lint, @objectstack/service-analytics, @objectstack/spec, @objectstack/verify, touching 10 documentable anchor(s). ⚠️ 3 changed file(s) yielded no anchor (packages/services/service-analytics/src/dataset-executor.ts, packages/verify/README.md, packages/verify/src/dashboard-window.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

⛔ 1 release-owned page(s) name something this change touched. These are read-only:

  • content/docs/releases/v17/17-6.mdx (via MemoryAnalyticsService (symbol, a top-level class))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 3 changed file(s) yielded no anchor (packages/services/service-analytics/src/dataset-executor.ts, packages/verify/README.md, packages/verify/src/dashboard-window.ts) — pages documenting those are invisible to this run
  • 2 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 54 of 206 client-bound route-ledger rows — the other 152 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 152: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 97 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

Coarse fallback — 146 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 06dbc4e59d1a37011114441317b6f918386cd690 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from d94062790db8a4c37826a764690b39ae4be49908 — the merge of head 8927fbc43cbdb646f0578cc8f0967a3672b569dd into base 06dbc4e59d1a37011114441317b6f918386cd690, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin d94062790db8a4c37826a764690b39ae4be49908 && git checkout d94062790db8a4c37826a764690b39ae4be49908
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 06dbc4e59d1a37011114441317b6f918386cd690 8927fbc43cbdb646f0578cc8f0967a3672b569dd && git checkout -B drift-repro 06dbc4e59d1a37011114441317b6f918386cd690 && git merge --no-ff 8927fbc43cbdb646f0578cc8f0967a3672b569dd

node scripts/docs-audit/affected-docs.mjs --json 06dbc4e59d1a37011114441317b6f918386cd690

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 06dbc4e59d1a37011114441317b6f918386cd690 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

…nclusive {today} end, in core's stops-before spelling

Claude-Session: https://claude.ai/code/session_012uFegdfR6K6uqvLzt7XsdZ
Co-authored-by: Claude <noreply@anthropic.com>
…lling preset end as NOW, open or $gte-only now read the closed {today} window; the unreachable end === null verdict branch is removed

Claude-Session: https://claude.ai/code/session_012uFegdfR6K6uqvLzt7XsdZ
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

check-empty-changeset is red by design on this PR: a DELIBERATE CORRECTION of a pending release note

The gate's verdict lines, verbatim:

This PR changes a changeset it did not add:

   .changeset/pre/15953-verify-dashboard-window-family.md
     present on the merge base and CHANGED by this PR -- this is somebody else's release note

  DELIBERATE CORRECTION -- your change may have made this PENDING release note false, and you rewrote it in the same stroke. Remedy: do NOT restore it -- say so on the PR and get it confirmed; restoring it from the base would put the false sentence back.

Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: da95e4e8e8f1ce7d21fe425fdc4eceabe3341dab
Local-runs: none

Inputs read. Card #23170: body and all 7 comments (6139344728 triage, 6139488818 claim, 6141817295 and 6142517145 amendments, 6142485762 and 6143940540 os-dev-report, 6143978957 seat ACCEPT — read, not used as evidence). PR #23208: body, both PR comments (6141838681 docs-drift, 6143910790 gate-and-reason), the 21-file list, and the net diff against main at the head (243 added / 170 deleted; git diff from merge base 75cada0ea to the head lists the same 21 files). The head's 52 check-runs. Every head read was addressed by the 40-hex sha (git show da95e4e8e8f1ce7d21fe425fdc4eceabe3341dab:PATH, git grep PATTERN da95e4e8e8f1ce7d21fe425fdc4eceabe3341dab -- PATHS); FETCH_HEAD was never read for content (consulted once, by rev-parse, to confirm my own fetch had landed on that sha). origin/main was fetched twice (01a3421f4, then 197fbe2c0 for the final sweep — it moved between them) and read as origin/main:PATH. The installed @changesets/* were read from the sibling checkout's pnpm store (/home/user/objectstack-issue-23147/node_modules/.pnpm/: cli 3.0.3, read 1.0.1, assemble-release-plan 7.0.0, apply-release-plan 8.1.1, matching the root package.json pin ^3.0.3) because /home/user/objectstack carries no node_modules.

Check-runs on the head (read 2026-10-13, every run completed, none in progress). Check Changeset: failure, three times (one per PR event, 09:33 / 10:50 / 10:55) — the designed red. Job 115260066110's steps: 11 Require a changeset success; 12 Reject an empty-frontmatter changeset failure, its annotation naming .changeset/pre/15953-verify-dashboard-window-family.md with the two-class remedy text verbatim; 13 Require an ADR-0087 disposition success; 15 Guard against accidental major bumps success (so the Clause-② line parsed). Every other run that ran: success — Test Core and its six shards, Build Core, Build Docs, every Lint · and Type Check · lane, Lint & Repo Gates, TypeScript Type Check, Spec property liveness, Governed Surface Queue Guard, the four claim guards, Check Documentation Links, Flag docs affected by code changes, Dogfood Regression Gate. Skipped by path or event: Temporal Conformance, the Dogfood Regression Gate matrix, Console Pin Gate, Dogfood Verify CLI (ci.yml:2214 skips it on pull_request and merge_group by design; packages/cli/test/verify-dashboard-windows.test.ts pins no window literal, so nothing there goes red on main's full run), Packed-tarball smoke. The PR's mergeable_state: unstable is that one non-required red.

① Derived judgments

(a) The table and its type — right. readonly [start: string, end: string] holds for all 13 rows: each is two wrapped macro tokens, and date-range-presets.test.ts pins every token's membership in the macro vocabulary. Head readers of DATE_RANGE_PRESET_MACRO_WINDOWS outside tests: packages/spec/src/data/date-range-presets.ts:166 (the refusal), packages/lint/src/validate-preset-comparands.ts:300 (the verdict), packages/verify/src/dashboard-window.ts:628 (the family's condition), packages/core/src/utils/analytics-date-range.ts (docblock only; its own PRESET_WINDOW_TOKENS states ends in the stops-before spelling), and the generated api-surface/data.json:158 / export-origins/data.json:155, which record the name only — so the narrowing moves no generated artifact, consistent with the 21-file list. No reader on the head branches on end === null any more (filter-lowering.ts:345's end === null is a $between comparand element, unrelated). verify's to ? … at :629, end ? … at :547 and $lte === undefined at :556 / :561 are dead arms the closed table never feeds. objectui at its local HEAD: 0 readers of the table; its packages/core/src/utils/dashboard-filters.ts:176-178 already lowers the three to {N_days_ago} through {today}. The changeset's sentence "A reader that compared end with null still compiles" is TRUE under the root tsconfig.json (strict: true, inherited by spec): TypeScript permits === against null on any operand type (the comparability relation special-cases a nullable comparand, so no TS2367); the branch is dead, not ill-typed.

(b) One extent at every face — right. Spec: $between: ['{N_days_ago}', '{today}'], whose bare-day maximum covers the whole day (the calendar-day rule the table's docblock and filter.zod.ts:361 state). Verify: hands { $gte: '{N_days_ago}', $lte: '{today}' } (dashboard-window.ts:628-629) and judges by inclusive day. Core: ['N_days_ago', 'tomorrow'] with endExclusive: true (analytics-date-range.ts:145-147, :185-192), both bounds the reference zone's midnight. Memory driver: parseDateRangeString calls the shared resolver and compiles $lt: end for an exclusive end (memory-analytics.ts:1198-1247, :2253-2255). Native SQL: shared resolver at :1296, $lt at :1323. ObjectQL strategy: shared resolver at :2069, $lt at :2076-2078. Dataset executor: lowerDatasetCompareDateRange (:645-652) then inclusiveCalendarDayWindow reads the exclusive end, so the last day is today. Preview: lowerPreviewDateRange (:618-625), shared resolver. Every face: from the first instant of the day N days ago through the last instant of today, in the reference timezone — the console's extent. No face on the head selects another extent. Pins: spec (the rows, day counts 8 / 31 / 91, the today−5 / today+5 measurement, the $between prescription); core (all 13 endExclusive; the rolling end equals today's end in every sweep zone; today±5; a later-today row inside; the #17341 sweep now over all 13); lint (verdict text verbatim); service-analytics (preview: last instant of today inside, tomorrow's first instant out; executor: [2026-08-10T00:00:00.000Z, 2026-09-10T00:00:00.000Z, true], previousPeriod 31 days); driver-memory (the conformance kit holds every face to the shared resolver); verify (dashboard-window.test.ts:190 pins the handed condition; the family proves counts under every preset on first / last day and a day either side, never after today). Not pinned anywhere: a future-dated row at the HTTP door (POST /analytics/dataset/query, today+5) — the dev declares it NOT MEASURED; the door lowers through the pinned strategies and the pinned bare-day rule, so the declaration is accepted; a pin there is a follow-up, not a blocker.

(c) Rows selected — judged in ②. Neither an accept-set narrowing nor a widening; a bug-class correction of query semantics toward the one ruled extent.

(d) Shipped sentences — one FALSE sentence remains on the head. Verified TRUE in context on the head: every sentence of .changeset/23170-rolling-presets-close-on-today.md (the before-state, the card's measurement, the three rows, the type and its compile claim, the $between prescription and its old $gte form, the tomorrow / endExclusive: true arm, the array arm keeping $lte at every face, and the "add $lte: '{today}'" advice — a bare-day $lte covers the whole day); packages/lint/src/rule-explanations.ts:2299-2301 ($between, first day through last, rolling last day {today}); packages/core/src/utils/analytics-date-range-conformance.ts:160, :188-190, :221; content/docs/deployment/cli.mdx:2035-2036 and packages/verify/README.md:161 (both match what dashboard-window.ts:628-629 hands the door); native-sql-strategy.ts:1319-1321; objectql-strategy.ts:2073-2077; dataset-executor.ts:668-673 (its later "[#18245]" paragraph still says the ten calendar presets open and close at the zone's midnight — true, merely no longer the whole set); memory-analytics.ts:2240-2242; dashboard-window.ts:22-23, :554; the two driver-memory test comments; .changeset/pre/15953:15. Sweep of packages/ and content/docs/ on the head for "end(s) at NOW", "bound is NOW", "REACHES", "$gte-only", "open arm", "no upper bound", "rolling end open", "end: null", "endBefore: null", "endExclusive: false", and every {7|30|90_days_ago} spelling in single AND double quotes: every hit is an authored macro-filter example (date-macros.zod.ts:16, content/docs/references/data/date-macros.mdx:20, skills/objectstack-query/rules/filters.md:133, the lint positive control at validate-preset-comparands.ts:91, test fixtures), an unrelated sense of "open arm" / "no upper bound", the array arm (endExclusive: false at memory-analytics.ts:1198, dataset-executor.ts:651, preview-evaluator.ts:632), release-owned CHANGELOG history (packages/verify/CHANGELOG.md:38, packages/cli/CHANGELOG.md:78, the next.0 rendering of the old sentence), or the counterfactual at analytics-date-range.ts:64 — EXCEPT one: packages/spec/src/migrations/entries/semantic/18.filter-preset-ordering-comparand-refused.ts:31-33, the ADR-0087 semantic entry's replacement: "the date-macro window the preset already means — { $gte: "{30_days_ago}" } for last_30_days, { $between: ["{week_start}", "{week_end}"] } for this_week, and so on (the rejection names the exact window per preset; DATE_RANGE_PRESET_MACRO_WINDOWS in @objectstack/spec/data is the table)". On the head the rejection names { $between: ['{30_days_ago}', '{today}'] } for last_30_days, so the entry now prescribes, as the preset's own window and as what the rejection names, the exact $gte-only form this card retires. It ships: spec-changes.ts:259 projects replacement into spec-changes.json, packages/cli/src/commands/migrate/meta.ts:790, :800 prints "surface → replacement" to an upgrading consumer, and the entry is in packages/spec/src/**, the path leg under review. The dev's sweep used the single-quote spelling and missed the double-quote one. This entry has been corrected in place twice before, each with a spec changeset sentence (packages/spec/CHANGELOG.md:21195 9df3934, :21210 6696056), so an in-place edit is the established remedy. Sweep of origin/main past the merge base (75cada0ea to 197fbe2c0: 13 commits, 120 files, nine new changesets): the lines main added carry no sentence of this class — no DATE_RANGE_PRESET_MACRO_WINDOWS, rolling-end, N_days_ago, end: null, endExclusive: false or $gte-only spelling landed after the merge base, and the only path main and this PR both touch is rule-explanations.ts, at hunks 598-670 against this PR's 2296.

(e) The end === null branch removal in lint — truly unreachable on the merged head. presetComparandVerdict reads end only from the table (validate-preset-comparands.ts:300), the table is Readonly with end: string, and git grep finds no site assigning into it. Taking the proof on the merged head rather than on origin/main was right: on origin/main the arm is still live.

(f) DELIBERATE CORRECTION — CONFIRMED. On the head @objectstack/verify's family hands the dataset door { $gte: '{7_days_ago}', $lte: '{today}' } for last_7_days (dashboard-window.ts:628-629, pinned at dashboard-window.test.ts:190), which is exactly the corrected quoted window at .changeset/pre/15953-verify-dashboard-window-family.md:15; that line is the file's only change, and the frontmatter ('@objectstack/verify': minor, '@objectstack/cli': minor; 63 bytes with both fences) is byte-identical to the merge base. Mechanism, read from the installed packages: @changesets/read 1.0.1 dist/index.mjs:29 enumerates .changeset/pre/*.md as ids prefixed pre/; @changesets/assemble-release-plan 7.0.0 dist/index.mjs:328 drops pre/ ids only while preState.mode !== "exit", so at changeset pre exit + version every consumed pre/ note is aggregated into the stable 18.0.0 release notes; @changesets/apply-release-plan 8.1.1 dist/index.mjs:301-313 is what renamed it into pre/ at the 18.0.0-next.0 cut. release.yml:503, :763 counts pre/*.md as pending stock, consistently. Check Changeset runs on pull_request only (pr-automation.yml:41-42; merge_group appears in 15 other workflows, not this one) and is not a required context (pr-automation.yml:710-712), so the designed red blocks no queue build. PR comment 6143910790 is accurate sentence by sentence (gate and exit code, the file's presence on the merge base, what changed, the 63-byte frontmatter, the pending-note mechanism, the confirmation route). The correction is CONFIRMED as right in substance; because this record is FAIL on other grounds, it is not the same-head PASS the queue needs — the record on the next head restates this confirmation.

② Semver level

Changeset: @objectstack/spec: patch, @objectstack/core: patch, body carrying Clause-②: no (no accept set widens …); the launch-window guard (step 15) and the ADR-0087 disposition gate (step 13) both ran green on the head.

  • No accept set moves. The save door refuses the same inputs as before (only its prescription text changes); the lint rule flags the same inputs; core's resolver accepts the same 13 names and refuses the same strings. The type narrowing end: string | null → end: string on a Readonly const tightens a reader's guarantee, is compile-compatible for every reader (including one comparing to null), and is not an accept set.
  • What the rows do. At the analytics preset arm the three rolling windows now include later-today rows they dropped (end moves from the reference instant to tomorrow's midnight); relative to the old prescription an authored window now excludes future-dated rows. That is a correction of query semantics toward one ruled extent on a bug card — not a Clause-② yes (no new key on a published payload, no validator widened) and not a (narrowing) breaking change (no accepted input refused, no export removed, nothing authorable retired). patch + patch is right; minor would be owed only for an added surface, major only for a refused input or a removed export — neither happens. No ADR-0087 marker is owed: nothing authorable is removed and no breaking level is declared; the null arm was a const's value shape, not an authorable key. In pre mode every package of the fixed group bumps to 18.0.0-next.1 regardless; the level governs only the CHANGELOG section.
  • Owed attribution, missing: @objectstack/lint ships a changed os explain filter-preset-comparand paragraph and @objectstack/verify ships a changed README (in its files); AGENTS.md rule 3 ("Add a changeset for anything that publishes") and precedent 9fbabd76a (an explain-text fix carried @objectstack/lint: patch) put a patch line for each in the changeset. Version-neutral under fixed; fix in the same round.
  • Clause-②: no — right.

③ Boundary flags

Report 6142485762: (1) stale PR body — rewritten in round 2; the head body carries both amendment sections and the designed-red section: answered. (2) model-named trailer amended before push — the four branch commits carry only Claude-Session and Co-authored-by: Claude: verified. (3) spec / core / lint repo vitest projects not run locally, declared to CI — Test Core and six shards green on the head: answered. (4) service-analytics / verify suites narrowed to one file in round 1 — round 2 ran service-analytics in full; verify ran its one file; CI green: answered. (5) a derived-gate shell loop stopped by a safety check, the two gates run directly — procedural, no effect. (6) origin/main not merged in round 1 — merged in round 2 (2a2a6cf0c); main has since moved 13 commits (tip 197fbe2c0 at my last fetch), overlapping this diff only in packages/lint/src/rule-explanations.ts at hunks 598-670 against this PR's 2296, so the queue's rebuild merges clean. Open question (text made false, a–g) — answered A by the seat and executed; each item verified true on the head in ①(d).

Report 6143940540: (1) three sweep edits beyond the six (native-sql-strategy.ts, objectql-strategy.ts, verify/README.md) — each corrected a sentence the change made false, comment or README only; right, and the seat adopted them. (2) dead-code proof on the merged head, not origin/main — right (①(e)). (3) lock queue-timeouts — procedural. (4) dashboard-window.ts's $lte-absent arms left (selects, nestedIn, the to ? spread, fixtureDays's end ?) — dead on the head and harmless; deleting them is a comments-plus-code follow-up outside this card. Out-of-scope notes: endExclusive always true on the preset arm — true; the field stays load-bearing because the array arm emits false at every face (memory-analytics.ts:1198, dataset-executor.ts:651, preview-evaluator.ts:632). N+1 calendar days (last_7_days covers 8) — the ruled extent, the console's, pinned (spec day counts 8 / 31 / 91; executor previousPeriod 31 days). HTTP face with a future-dated row NOT MEASURED — accepted as declared (①(b)); a verify-family pin that writes one fixture after today would close it later.

Escalated by this record (not a dev flag): the ADR-0087 semantic entry in ①(d), outside every amended surface, so the seat amends the claim surface for it.

Why FAIL. One shipped sentence in the contract path leg still teaches the open window: packages/spec/src/migrations/entries/semantic/18.filter-preset-ordering-comparand-refused.ts:31-32 (replacement) says { $gte: "{30_days_ago}" } is the window last_30_days means and what the rejection names; the head's rejection names { $between: ['{30_days_ago}', '{today}'] }. The seat's own rule for this round (6142517145: published text this round makes false is fixed in this round) applies to it as it did to items a–g. Remedy, one patch round: (1) in that entry's replacement, change { $gte: "{30_days_ago}" } for last_30_days to { $between: ["{30_days_ago}", "{today}"] } for last_30_days (the entry's own double-quote spelling; nothing else in the entry; entry prose is scanned as source per the entries README, so keep it a quoted example); (2) in .changeset/23170-rolling-presets-close-on-today.md, add '@objectstack/lint': patch and '@objectstack/verify': patch and one sentence naming the entry correction; (3) seat amends the claim surface for the entry and re-renders this review at tier on the new head, where the DELIBERATE CORRECTION's confirmation is restated. Everything else judged above is right as it stands.

Implemented-by: claude/issue-23170-rolling-preset-upper-bound
Reviewed-by: session_012uFegdfR6K6uqvLzt7XsdZ

VERDICT: FAIL

…acement names the closed last_30_days window; the changeset carries lint and verify

Claude-Session: https://claude.ai/code/session_012uFegdfR6K6uqvLzt7XsdZ
Co-authored-by: Claude <noreply@anthropic.com>
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

check-empty-changeset is red by design on head 8927fbc43 (supersedes 6143910790): a DELIBERATE CORRECTION of a pending release note

  • Gate: node scripts/check-empty-changeset.mjs --base origin/main, the foreign changeset rule (ruling D on finding: random changeset filenames collide silently across parallel agents — a round overwrote a sibling PR's minor changeset and every gate stayed green #17712). Measured at head 8927fbc43cbdb646f0578cc8f0967a3672b569dd: exit 1.
  • Note it names: .changeset/pre/15953-verify-dashboard-window-family.md. It is on the merge base 75cada0ea, and this PR did not add it. The file is unchanged since da95e4e8e, so the 63-byte frontmatter is still byte-identical to the merge base.
  • What changed under it: this PR closes the rolling last_N_days windows of DATE_RANGE_PRESET_MACRO_WINDOWS on {today}. @objectstack/verify's dashboard-window family therefore hands the dataset door { $gte: '{7_days_ago}', $lte: '{today}' } for last_7_days, pinned at packages/verify/src/dashboard-window.test.ts. The note's sentence quoted { $gte: '{7_days_ago}' }, which this PR makes false. Only that quoted window was corrected, in place.
  • Why it is not restored: the note is pending. The 18.0.0-next.0 release consumed it into .changeset/pre/, and it is aggregated into the stable 18.0.0 notes at pre exit. Restoring it from the base would publish the false sentence.
  • Confirmation: contract review 6144351693 confirmed the correction in substance (its finding (f)). Seat note 6144366518 asks the next same-head record to restate that confirmation.
  • The other changeset gates on this head: check-changeset-no-major --base origin/main exits 0, and check-adr-0087-registration --base origin/main exits 0 ("this PR adds no declared-breaking changeset (2 non-breaking changeset(s) seen)").

The gate's verdict lines on this head, verbatim:

This PR changes a changeset it did not add:

   .changeset/pre/15953-verify-dashboard-window-family.md
     present on the merge base and CHANGED by this PR -- this is somebody else's release note

  DELIBERATE CORRECTION -- your change may have made this PENDING release note false, and you rewrote it in the same stroke. Remedy: do NOT restore it -- say so on the PR and get it confirmed; restoring it from the base would put the false sentence back.

Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 8927fbc43cbdb646f0578cc8f0967a3672b569dd
Local-runs: none

Inputs read. Card #23170: body and all 9 comments (6139344728 triage, 6139488818 claim, 6141817295 / 6142517145 / 6143978957 / 6144366518 seat amendments and notes, 6142485762 / 6143940540 / 6145883266 os-dev-report). PR #23208: body, all 4 PR comments (6141838681 docs-drift, 6143910790 and 6145826297 designed-red notes, 6144351693 the round-1 contract record), the 22-file list, the net diff against main at the head (247 added / 171 deleted; merge base 75cada0ea), and the round delta da95e4e8e...8927fbc43 (1 commit, 2 files). The head's 48 check-runs, plus the failing job's step list and annotations (115303399756). origin/main fetched once for this record, at 6d7f4df3c, and read as origin/main:PATH; the PR branch fetched and every head read addressed by the 40-hex sha (git show 8927fbc43cbdb646f0578cc8f0967a3672b569dd:PATH, git grep PATTERN 8927fbc43cbdb646f0578cc8f0967a3672b569dd -- PATHS); FETCH_HEAD never read. One git read beyond those: a driverless merge-tree of the head against 6d7f4df3c in a throwaway bare clone under this record's scratch directory (the entries README's own recipe; no merge.os-regen.driver configured; deleted afterwards). It built, ran and re-ran nothing of the project, and nothing in /home/user/objectstack was checked out, stashed or written.

Check-runs on the head (every one of the 48 completed, none in progress). Failure: Check Changeset only, twice (11:21Z on synchronize, 12:44Z on edited), the designed red: in job 115303399756 step 11 Require a changeset success, step 12 Reject an empty-frontmatter changeset added by this PR failure with the annotation naming .changeset/pre/15953-verify-dashboard-window-family.md and the two-class remedy text, step 13 Require an ADR-0087 disposition success, step 15 Guard against accidental major bumps success. Success, 39 runs: the five required contexts Lint & Repo Gates, TypeScript Type Check, Test Core (and its six shards), Build Core, Governed Surface Queue Guard; every Lint · and Type Check · lane; Build Docs, Spec property liveness, Check Documentation Links, Flag docs affected by code changes, Dogfood Regression Gate, filter, the four claim guards, and Auto Label / Check PR Size on the first event. Skipped by path or event, 7: Console Pin Gate, the Dogfood Regression Gate matrix, Dogfood Verify CLI, Temporal Conformance (live PG + MySQL), Packed-tarball smoke (opt-in), and Auto Label / Check PR Size on the second event. No run other than Check Changeset is red. The PR's mergeable_state: unstable is that one non-required red; the PR is still draft and unarmed, as the owning seat's landing to-do (6143978957) expects before this record.

① Derived judgments

(1) The round-1 FAIL ground is closed. packages/spec/src/migrations/entries/semantic/18.filter-preset-ordering-comparand-refused.ts:31 now reads { $between: ["{30_days_ago}", "{today}"] } for last_30_days; git diff da95e4e8e 8927fbc43 -- that file is that one line and nothing else (the round's whole diffstat is 2 files, 4 insertions, 1 deletion). It is consistent with what the save door names on this head: DATE_RANGE_PRESET_MACRO_WINDOWS.last_30_days is ['{30_days_ago}', '{today}'] (date-range-presets.ts:134) and bareDateRangePresetComparandMessage renders { $between: ['{30_days_ago}', '{today}'] } (:165-166); the lint verdict quotes the same pair (validate-preset-comparands.ts:300-302, pinned verbatim at :486-487 of its test). The entry's this_week example and the (the rejection names the exact window per preset; …) parenthesis are now true of every preset. The entry still keeps the entries' own rules: no other file was touched for it (registry.ts, spec-changes.json and the upgrade-guide pages are gitignored projections; the 22-file list carries no generated artifact), the filename is unchanged, surface still carries no backtick, the replacement carries no tracker number, and the quoted-example convention holds — the tokens stay double-quoted inside the single-quoted TS string, the $between: ["…", "…"] shape already occurs in this entry (its $between count moves 3 to 4) and in nine sibling entries, and "{today}" double-quoted occurs nowhere else under packages/spec/src/migrations/entries. Entry prose is scanned as source, and the mechanical proof that no live ratchet matched the new sentence is the head's green Test Core (all six shards) and every Lint · lane; check:spec-changes and check:upgrade-guide, which project this entry in memory, run inside the required TypeScript Type Check job, green. No literal pin of the replacement text exists on the head: git grep for date-macro window the preset already means hits the entry alone, and packages/cli/test/migrate-meta-engine-guidance.test.ts:187 lists the entry id in a roster without its text. It ships as before: spec-changes.ts:259 projects replacement, packages/cli/src/commands/migrate/meta.ts:790, :800 print surface → replacement.

(2) The changeset. .changeset/23170-rolling-presets-close-on-today.md frontmatter is now '@objectstack/spec': patch, '@objectstack/core': patch, '@objectstack/lint': patch, '@objectstack/verify': patch, closed by its fence (4 lines, well-formed). The new bullet is TRUE on the head: the entry is carried by spec-changes.json (spec-changes.ts:259) and by os migrate meta (meta.ts:790, :800), its replacement names { $between: ["{30_days_ago}", "{today}"] } for last_30_days (:31), and the before-text { $gte: "{30_days_ago}" } is what the round diff removes. Every other sentence re-read against the head: the before-state is what origin/main still carries (date-range-presets.ts:130-132 three null ends, :162-163 the $gte-only branch; analytics-date-range.ts:148-150 null, :193 end: now.toISOString(), endExclusive: false); the today−5 / today+5 measurement is the card's; the three rows (:133-135) and the narrowed type readonly [start: string, end: string] (:117-118) are on the head, and the "still compiles" sentence is the round-1 judgment on a file this round did not touch; the prescription bullet matches :165-166 and the lint verdict that quotes it; the core bullet matches PRESET_WINDOW_TOKENS ['N_days_ago', 'tomorrow'] (:145-147), endExclusive: true for every preset (:188, one return path), the "day the window stops before" spelling (:110-115), and the array arm's inclusive reading at every face (round 1 ①(b), files unchanged); the closing advice is right because a bare-day $lte covers its whole day. Clause-②: no is still right: this round changes one shipped sentence and a changeset, no accept set. Levels: patch for lint (a corrected os explain filter-preset-comparand paragraph) and verify (a corrected README that ships in its files) is the AGENTS.md rule-3 level for a fix to published text, and precedent 9fbabd76a; all four packages sit in one fixed group of 71 under pre mode next, so the lines are version-neutral and govern only each CHANGELOG section. On CI: check-changeset-no-major (step 15) success, check-adr-0087-registration (step 13) success, the Clause-② line parsed; no ADR-0087 marker is owed (nothing authorable removed, no breaking level declared).

(3) The double-quote sweep, re-run by my own git grep on the head over packages/ and content/docs/ for "{7_days_ago}", "{30_days_ago}", "{90_days_ago}": 13 hits, matching the dev's list line for line. The entry (:31, edited, the only hit that stated a preset's window). Five measurement records, true as written — $gte "{30_days_ago}" … count=38 … positive control at packages/core/src/utils/temporal-comparand.ts:23, packages/lint/src/validate-preset-comparands.ts:29, packages/objectql/src/temporal-comparand-door.ts:20, packages/spec/src/data/filter.zod.ts:1718, packages/spec/src/data/filter-preset-comparand.test.ts:11 (an authored macro comparand measured against a bare preset name on a 51-row seed with no future-dated row; none states the preset's window or its end). Three placeholder examples with no $gte (temporal-comparand-door.ts:478, :481; engine-aggregate-having-temporal-door.test.ts:297). One already closed (analytics.zod.ts:833, ["{7_days_ago}", "{today}"]). Three release-owned CHANGELOG lines (packages/lint/CHANGELOG.md:8950, packages/objectql/CHANGELOG.md:3850, packages/spec/CHANGELOG.md:36696). content/docs/: 0. The single-quote twins beside a bare $gte with no upper bound are all authored macro-filter examples or fixtures, none a statement of a preset's window: the placeholder-grammar example { signal_at: { $gte: '{30_days_ago}' } } at date-macros.zod.ts:16 and its generated page content/docs/references/data/date-macros.mdx:20; the lint positive control validate-preset-comparands.ts:91; the "spelling that works" fixtures in lint, objectql, spec and the verify widget filter (validate-preset-comparands.test.ts:103, :199; filter-preset-comparand.test.ts:80, :140; field.test.ts:1212; engine-judge-filter.test.ts:221, :238; engine-temporal-comparand-door.test.ts:58, :214, :245, :267; report-joined-block-dataset.test.ts:231; verify/src/dashboard-window.test.ts:112); and the toContain('{30_days_ago}') pins (lint :54, :268; spec date-range-presets.test.ts:70, filter-preset-comparand.test.ts:50, :135, dashboard.test.ts:500), which hold on the head because the $between message carries the token. No sentence this change made false remains in either spelling.

(4) Carry-over. The round delta da95e4e8e...8927fbc43 is one commit touching exactly .changeset/23170-rolling-presets-close-on-today.md and the migration entry; the other 20 files of the PR carry the same blob at both shas. So the round-1 judgments 6144351693 ①(a) (the table, its narrowed end: string type, its readers and the moved-nothing generated artifacts), ①(b) (one extent at every face — spec, verify, core, the memory driver, both service-analytics strategies, the dataset executor and preview — and their pins), ①(c) (rows selected, judged in ②), ①(e) (the end === null lint branch truly unreachable on the merged head) and ② (patch + patch, Clause-②: no, no ADR-0087 marker) hold unchanged on this head, re-anchored where re-read above (date-range-presets.ts:117-118, :133-135, :165-166; analytics-date-range.ts:145-147, :188; validate-preset-comparands.ts:300-302; dashboard-window.ts:22-23, :554, :628-629). This record therefore stands alone as the same-head record for 8927fbc43.

(5) DELIBERATE CORRECTION on .changeset/pre/15953-verify-dashboard-window-family.md — CONFIRMED. The file is unchanged since da95e4e8e: blob 770b9acb9 at both da95e4e8e and 8927fbc43, against 144d744b5 at the merge base 75cada0ea (and still 144d744b5 on origin/main 6d7f4df3c). Its diff from the merge base is one line, :15: the quoted window becomes { $gte: '{7_days_ago}', $lte: '{today}' }. That is exactly what packages/verify/src/dashboard-window.ts:628-629 hands the dataset door — { $gte: from, $lte: to } from the table's ['{7_days_ago}', '{today}'] — pinned at dashboard-window.test.ts:190. The frontmatter ('@objectstack/verify': minor, '@objectstack/cli': minor, 63 bytes with both fences) is byte-identical to the merge base (same sha1 of the first four lines). The note is pending stock under .changeset/pre/ (consumed at the 18.0.0-next.0 cut, aggregated into the stable notes at pre exit — round 1's reading of the installed changesets packages, restated here), so restoring it from the base would publish the false window. Check Changeset runs on pull_request only: .github/workflows/pr-automation.yml:41-43 (types: [opened, synchronize, reopened, labeled, unlabeled, edited]), zero merge_group mentions in the file, its own text at :728-729 says the job is not one of the required contexts, and the workflow's blob is identical at the merge base, origin/main and the head — so the designed red blocks no queue build. PR comment 6145826297 is accurate sentence by sentence: the gate and exit 1 (step 12), the file on the merge base and not added by the PR (status modified), unchanged since da95e4e8e with the 63-byte frontmatter identical, what changed under it and where it is pinned, why it is not restored, the confirmation route, and the other two changeset gates exit 0 (steps 13 and 15 success on CI). In words: the DELIBERATE CORRECTION is CONFIRMED, on this head, as right in substance and in mechanism.

(6) origin/main past the merge base. 6d7f4df3c is 22 commits and 187 files past 75cada0ea (the dev's 06dbc4e59 and the round-1 record's 197fbe2c0 are both inside that range). Sweep of every line main added for this card's class (DATE_RANGE_PRESET_MACRO_WINDOWS, N_days_ago, end: null, endBefore, endExclusive, "ends at NOW", "bound is NOW", $gte-only, "open arm", "no upper bound", the three rolling names, the two resolver names): one hit, dateRange: { field: 'opened_at', defaultRange: 'last_7_days' } at packages/verify/src/dashboard-window.test.ts:390 (slice 6's negative-control fixture) — a dashboard date-filter position, the legal position, not a window statement. The new changeset .changeset/15953-verify-dashboard-window-now-removed.md retires DashboardWindowProofOptions and states no window shape. Four files overlap the PR: rule-explanations.ts (main at 601-670 vs the PR at 2299), verify/README.md (503, 559 vs 161), dashboard-window.test.ts (14-19 and +377-449 vs 190), dashboard-window.ts (68, 488-539, 595-632, 690, 845-872 vs 22-23, 554) — disjoint hunks every time. The driverless merge-tree of the head against 6d7f4df3c is clean (tree c20d99a60, four auto-merges, no conflict); the merged tree keeps the verify pin at :193 with $lte: '{today}', the dashboard-window.ts comment edits at :23 and :504, the table read at :577-578 (fixtureDays at :492 still reads $lte), the README at :161, and the lint paragraph. No semantic conflict: main's commits add no reader of end as null and no new rolling-end sentence. The dev's "merge-tree clean against 06dbc4e59" is consistent and now holds against the tip.

(7) CI on the head. As read above: every check green except Check Changeset, by design; no other run red; the five required contexts all success.

② Semver level

Carried unchanged from round 1 on a round that touched no accept set: @objectstack/spec: patch, @objectstack/core: patch for a correction of query semantics toward the one ruled extent on a bug card — no new key on a published payload, no validator widened, no accepted input refused, no export removed, nothing authorable retired — plus @objectstack/lint: patch and @objectstack/verify: patch for corrected published text (the os explain paragraph; the README in files), the owed attribution round 1 named as missing. Clause-②: no — right. No ADR-0087 marker owed. Version-neutral under the 71-package fixed group in pre mode; the level governs the CHANGELOG section only. check-changeset-no-major and check-adr-0087-registration green on the head.

③ Boundary flags

Report 6145883266, deviations: (1) the changeset sentence names the entry correction only, and the lint and verify lines carry no body sentence of their own — acceptable: bullet 2 already names the lint verdict that quotes the prescription, and the README restates the window the body's first bullet states; the CHANGELOG section each package receives describes the change truly. (2) Three dist-reading gates first refused with exit 3 in a fresh worktree and were re-run after a whole-workspace build — procedural; the first-pass exits are rightly not counted. (3) A lock queue-timeout re-taken — procedural. (4) The task worktree recreated through git -C /home/user/objectstack (fetch and worktree add/remove only) and removed after the writes — git worktree list on the shared checkout shows no 23170 worktree now; procedural, and consistent with the report. open_questions: none. out_of_scope_findings, each carried in the PR's Acceptance notes with no other carrier: endExclusive always true on the preset arm (true, :188; the field stays load-bearing for the array arm); the rolling windows span N+1 calendar days (the ruled extent, pinned at day counts 8 / 31 / 91); verify's $lte-absent arms dead under the closed table (true on the head at dashboard-window.ts:547, :556, :561, :629, harmless, kept by slice 6 on the merged tree, a comments-plus-code follow-up outside this card). Prime Directive 10: none is a reproducible defect, a contract violation or an authoring trap, so an acceptance note is the right carrier. The round-1 escalation (the ADR-0087 semantic entry) is closed by this head; nothing is escalated by this record.

Implemented-by: claude/issue-23170-rolling-preset-upper-bound
Reviewed-by: session_012uFegdfR6K6uqvLzt7XsdZ

VERDICT: PASS

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 protocol:data size/m tests tooling

Projects

None yet

2 participants