Skip to content

fix(driver-memory): a preset dateRange on a date field counts the reference zone's calendar days, from UTC westward too - #27341

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-pgd72f
Oct 28, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/project-thread-pgd72f

Conversation

@objectstack-fleet

Copy link
Copy Markdown
Contributor

Requested by Bill · project thread

Fixes #27272

Clause-②: yes (widening)
Metadata-protocol: none

维护者速读

  • 改了什么:内存驱动的分析查询里,预设日期范围(如“本季度”)落在声明为 date 的字段上时,改为按参考时区的日历日计数;datetime 字段仍按时刻比较。这条窗口规则从 service-analytics 挪到 @objectstack/core,两边共用同一份。
  • 为什么改:从 UTC 往西(洛杉矶,连 UTC 本身也是),“本季度”会漏掉 7 月 1 日、多算 10 月 1 日,报表数字静默出错;上海只是碰巧算对。
  • 风险与回滚:只影响“预设范围 × 已声明为 date 的字段”,在内存驱动上;显式起止日期、datetime 字段、未声明类型的字段行为不变,service-analytics 行为不变(只是改为调用 core)。回滚即还原本 PR。
  • 先看:packages/core/src/utils/analytics-date-range.ts 末尾新增的两个导出;packages/drivers/driver-memory/src/memory-analytics.ts 的 parseDateRangeString;packages/services/service-analytics/src/dataset-executor.ts 删掉的旧副本。
  • 两分钟验证:pnpm --filter @objectstack/driver-memory exec vitest run src/memory-analytics-date-range-date-field.test.ts,预期 7 条全过,其中洛杉矶、UTC、上海的 this_quarter 都只列 7/1 和 9/30。
  • 需要你做的:无,席位落地。

What changes

The rule has one home. ADR-0053's window rule (a date column compares the calendar days the period covers in the reference zone; a datetime column compares instants) was spelled once, in service-analytics/src/dataset-executor.ts (dateRangeWindowBounds over the private inclusiveCalendarDayWindow, PR #27271). @objectstack/driver-memory depends only on core, spec and types, so it cannot import that. Both functions move, unchanged in behaviour, into @objectstack/core's utils/analytics-date-range.ts, beside resolveAnalyticsDateRangeString, whose header already says the lowering lives there because these two faces cannot import each other. dataset-executor.ts imports both from core and re-exports dateRangeWindowBounds, so the four service-analytics importers keep their import line. Nothing in packages/spec moves.

The memory face uses it. MemoryAnalyticsService asks the driver whether the time dimension's field is declared date. If it is, a preset resolves through dateRangeWindowBounds(window, timezone, true) to the inclusive day pair, and the existing bound construction reads that pair exactly as it reads a caller's bare-day [a, b] ($gte first day, $lt the next day after the last). Every other field keeps the instant window it had.

One driver accessor. The declared kind lives in InMemoryDriver's private temporalFields (filled by syncSchema), so the driver gains isDeclaredDateField(object, field), the same pattern as filterComparandStorageForm. An object never declared answers false: the driver does not guess types from values.

Surface widening. The order's file surface was memory-analytics.ts, its test, the rule's home and dataset-executor.ts. It grew by packages/drivers/driver-memory/src/memory-driver.ts (the accessor only) and packages/core/src/utils/analytics-date-range.ts (+ its test), on the seat's call (option A; option B, inferring the kind by probing a comparand's stored shape, was refused as a type guess from a value). Clause-②: yes (widening): @objectstack/core gains the exports dateRangeWindowBounds and inclusiveCalendarDayWindow, and InMemoryDriver gains the public method isDeclaredDateField. Nothing is removed or narrowed. One changeset, patch for core, driver-memory and service-analytics.

Tests

  • New packages/drivers/driver-memory/src/memory-analytics-date-range-date-field.test.ts (7): the card's rows (06-30, 07-01, 09-30, 10-01), clock frozen at 2026-09-09, declared via syncSchema. this_quarter in America/Los_Angeles, UTC and Asia/Shanghai selects 07-01 and 09-30; Los Angeles this_month selects 09-30 and not 10-01; an explicit ['2026-07-01', '2026-09-30'] is unchanged. A declared datetime field keeps the zone-midnight instant pair (both edges pinned one second either side). An undeclared field keeps the instant pair.
  • packages/core/src/utils/analytics-date-range.test.ts (+5): the rule itself, three zones, the non-date arm and the explicit arm.
Red before the fix (origin/main ebfadb0)
× America/Los_Angeles: this_quarter selects Jul 1 through Sep 30
× UTC: this_quarter selects Jul 1 through Sep 30
× Los Angeles: this_month on Sep 9 selects Sep 30 and not Oct 1
AssertionError: expected [ '2026-09-30', '2026-10-01' ] to deeply equal [ '2026-07-01', '2026-09-30' ]
Tests  3 failed | 4 passed (7)

Shanghai and the datetime and undeclared pins were green before and after. UTC is red too, not only zones west of it: the bare day 2026-07-01 sorts before 2026-07-01T00:00:00.000Z as text.

Ablation (fix committed first, then the face's date branch disabled through scripts/ablation-replace.mjs)
ablation-replace: ok mutation landed: anchor 1 -> 0, blob a0d8cdeee81b -> c3b278652e1c
× America/Los_Angeles: this_quarter selects Jul 1 through Sep 30
× UTC: this_quarter selects Jul 1 through Sep 30
× Los Angeles: this_month on Sep 9 selects Sep 30 and not Oct 1
Tests  3 failed | 4 passed (7)
ablation-replace:   blob after restore  a0d8cdeee81be65ef52afc75d4390a4c11069e62

A first attempt used a marker that threw a ReferenceError (red for the wrong reason) and is discarded; the run above is the one that counts. git diff HEAD was empty after the restore.

Package suites (HEAD 4ddf1d4)
@objectstack/core               test + typecheck: Test Files 124 passed, Tests 2808 passed
@objectstack/driver-memory      test + typecheck: Test Files 74 passed, Tests 1767 passed
@objectstack/service-analytics  test + typecheck: Test Files 195 passed, Tests 4705 passed | 282 skipped
eslint --no-inline-config on the six touched source files: exit 0

Acceptance notes

  • Deviation, for triage to rule on if it disagrees: an undeclared field keeps the instant window. The card measured the undeclared case wrong as well. This PR follows dateRangeWindowBounds' own contract from analytics: a preset dateRange on a date-typed field counts the wrong calendar days east of UTC (Shanghai this_quarter counts Jun 30, drops Sep 30) #27172 / PR fix(service-analytics): a preset dateRange on a date field counts the reference zone's calendar days, east of UTC too #27271, "a column whose type the caller cannot name answers false and keeps the instants", and this driver's rule that it does not guess types from values (memory-temporal.ts). So a bare day stored in a field the driver was never told is date is still compared to the preset's instants as text. Making it count calendar days would need the face to read the type from the stored value, which is the option the seat refused.
  • The face's $or over both stored forms is unchanged; on a declared date field the Date arm reads the day pair as UTC midnights, as it does for a caller's bare-day window.
  • No behaviour change in service-analytics: its four call sites resolve the same function, now from core.
  • The branch carries two early WIP commits (7cd672d6, 46faf53d) committed without the session trailer pair; the final commit 4ddf1d4a carries it. The branch name is not claude/issue-*, so it is not force-pushed and history is not rewritten.

Verification

Local subset, measured at 4ddf1d4a: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 64 families from the 7 changed paths; each ran from the worktree with its exit code captured before any pipe.

dispatch-gates --ran: 64 derived, 63 run, 1 NOT-MEASURED, 0 UNRUN
node scripts/check-adr-0087-registration.mjs --base origin/main :: exit 0
node scripts/check-adr-0087-registration.mjs --self-test :: exit 0
node scripts/check-changeset-no-major.mjs --base origin/main :: 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-adoption.mjs :: exit 0
node scripts/check-comment-mask-adoption.mjs --self-test :: exit 0
node scripts/check-comment-mask-corpus.mjs :: exit 0
node scripts/check-dev-prereqs.mjs --self-test :: exit 0
node scripts/check-dts-emitted.mjs --self-test :: exit 0
node scripts/check-empty-changeset.mjs --base origin/main :: exit 0
node scripts/check-empty-changeset.mjs --self-test :: exit 0
node scripts/check-issue-citations.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs :: exit 0
node scripts/check-plugin-teardown-shape.mjs --self-test :: exit 0
node scripts/check-tenant-audit-census.mjs :: exit 0
node scripts/check-tenant-audit-census.mjs --self-test :: exit 0
node scripts/ci/assert-turbo-remote-cache.mjs --self-test :: exit 0
node scripts/ci/plan-test-core-shards.mjs --self-test :: exit 0
node scripts/ci/prune-turbo-cache.mjs --self-test :: exit 0
node scripts/docs-audit/check-affected-docs.mjs :: exit 0
node scripts/docs-audit/check-drift-comment.mjs :: exit 0
node scripts/pm/release-rehearsal-clone.mjs --self-test :: exit 0
node scripts/release-pending-publish.mjs --self-test :: exit 0
pnpm --filter @objectstack/spec run check:duration-unit-keys :: exit 0
pnpm check:changeset-gate-self-tests :: exit 0
pnpm check:ci-filter-parity :: exit 0
pnpm check:closed-ceremony-text :: exit 0
pnpm check:cross-package-test-inputs :: exit 0
pnpm check:dispatcher-error-vocabulary :: exit 0
pnpm check:doc-authoring :: exit 0
pnpm check:driver-conformance :: exit 0
pnpm check:driver-memory-census :: exit 0
pnpm check:dts-closure :: exit 0
pnpm check:dual-build-cjs-loads :: exit 3
pnpm check:engine-double-contract :: exit 0
pnpm check:error-status-conformance :: exit 0
pnpm check:gitlink-declared :: exit 0
pnpm check:issue-citations :: exit 0
pnpm check:kernel-hook-pairs :: exit 0
pnpm check:keyed-text-bounds :: exit 0
pnpm check:lean-entry-closure :: exit 0
pnpm check:logger-receiver-detach :: exit 0
pnpm check:nul-bytes :: exit 0
pnpm check:objectql-double-limit :: exit 0
pnpm check:org-identifier :: exit 0
pnpm check:page-declaration-shape :: exit 0
pnpm check:platform-object-tenancy-census :: exit 0
pnpm check:pm-changeset-deadline-census :: exit 0
pnpm check:published-files :: exit 0
pnpm check:query-options-erasure :: exit 0
pnpm check:refd-timer-probe :: exit 0
pnpm check:registry-log-declared :: exit 0
pnpm check:rest-log-spy-declared :: exit 0
pnpm check:slot-lookup :: exit 0
pnpm check:sourcemap-no-sources-content :: exit 0
pnpm check:system-context-census :: exit 0
pnpm check:test-source-alias :: exit 0
pnpm check:tier-file-adoption :: exit 0
pnpm check:type-check-coverage :: exit 0
pnpm check:type-check-debt :: exit 0
pnpm check:undeclared-dep-imports :: exit 0
pnpm check:watch-hint-literal :: exit 0
pnpm check:where-matcher :: exit 0
  • pnpm check:dual-build-cjs-loads exits 3 (PREREQUISITE NOT MET: it needs every workspace package built, and this worktree built only the three packages' closure). NOT MEASURED locally; CI runs it.
  • node scripts/check-plugin-teardown-shape.mjs --self-test first exited 3 on the shallow clone; after git fetch origin 621a4876 it exits 0 (48 cases).

Every other family CI runs is read from this PR's check-runs, not re-run locally.


Generated by Claude Code

…erence zone's calendar days

Move ADR-0053's one window rule (dateRangeWindowBounds and its calendar-day
projection) into @objectstack/core beside the dateRange lowering, so the
in-memory cube face and service-analytics share one spelling. The memory face
asks the driver whether the field is declared date and, if so, filters by the
calendar days the preset covers in the reference zone.

Claude-Session: https://claude.ai/code/session_01Utp6CbomWvWL9K2F1XBS6p
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation tests tooling labels Oct 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 3 package(s): @objectstack/core, @objectstack/driver-memory, @objectstack/service-analytics, touching 8 documentable anchor(s).

5 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/data-modeling/drivers.mdx (via InMemoryDriver (symbol, a top-level class))
  • content/docs/permissions/authentication.mdx (via InMemoryDriver (symbol, a top-level class))
  • content/docs/plugins/index.mdx (via InMemoryDriver (symbol, a top-level class))
  • content/docs/plugins/packages.mdx (via InMemoryDriver (symbol, a top-level class))
  • content/docs/protocol/objectql/query-syntax.mdx (via InMemoryDriver (symbol, a top-level class))

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

  • content/docs/releases/implementation-status.mdx (via InMemoryDriver (symbol, a top-level class))
  • content/docs/releases/v16.mdx (via InMemoryDriver (symbol, a top-level class))
  • content/docs/releases/v17/17-0.mdx (via InMemoryDriver (symbol, a top-level class))
  • content/docs/releases/v17/17-3.mdx (via InMemoryDriver (symbol, a top-level class))
  • content/docs/releases/v17/17-6.mdx (via InMemoryDriver (symbol, a top-level class), 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
  • 1 name(s) were too generic to anchor anything (single lowercase words)
  • the SDK route bridge reached 56 of 208 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 — 47 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 8eeb3b3b9255686046a361cf9f9145d6b8e6bfec → packageMentionDocs.

Which tree this was computed on

This run read content/docs from bb343b94cc4dd44248c326d6aee20d9b84123d03 — the merge of head 4ddf1d4adbccf21612ded5755ac0850165c608fb into base 8eeb3b3b9255686046a361cf9f9145d6b8e6bfec, 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 bb343b94cc4dd44248c326d6aee20d9b84123d03 && git checkout bb343b94cc4dd44248c326d6aee20d9b84123d03
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 8eeb3b3b9255686046a361cf9f9145d6b8e6bfec 4ddf1d4adbccf21612ded5755ac0850165c608fb && git checkout -B drift-repro 8eeb3b3b9255686046a361cf9f9145d6b8e6bfec && git merge --no-ff 4ddf1d4adbccf21612ded5755ac0850165c608fb

node scripts/docs-audit/affected-docs.mjs --json 8eeb3b3b9255686046a361cf9f9145d6b8e6bfec

⚠️ 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 8eeb3b3b9255686046a361cf9f9145d6b8e6bfec → pass the list as
args.docs, on the commit named under Which tree this was computed on.

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-bill · session_01Utp6CXc5hequf76uJfzrdK

Contract review

Served-tier: TIER_DEFAULT
Head-sha: 4ddf1d4adbccf21612ded5755ac0850165c608fb
Local-runs: none

Face: the Clause-②: yes (widening) declaration on the PR body and .changeset/27272-memory-preset-date-field-calendar-days.md. No packages/spec, no governed path, no narrowing line, so the declaration face alone at TIER_DEFAULT. Order: triage 6472600991 (one window rule; reuse dateRangeWindowBounds or name why, never a second spelling). Surface widening authorised by the seat call to the card thread at 20:33Z (option A).

① Derived judgments

  1. @objectstack/core gains inclusiveCalendarDayWindow and dateRangeWindowBounds (via the existing export * from './utils/analytics-date-range.js'). Both are the bodies service-analytics/src/dataset-executor.ts held on origin/main (L694, L733), moved unchanged in logic: !endExclusive → {$gte, $lte} as given; not a date column → {$gte, $lt} instants; date column → the inclusive calendar-day pair in the zone. Their bound helpers are restated in core (windowBoundMs, calendarDayOf) because core cannot import the service; the only difference is the error type for an unparseable bound (TypeError there, datasetInvalidError in the service). Every service caller passes a resolved preset or an already-validated explicit window, so no reachable input changes answer. Correct; the rule has one home.
  2. dataset-executor.ts re-exports dateRangeWindowBounds and imports both from core, so drill-window.ts, the strategies and the preview evaluator keep their imports. Not a narrowing: the name and signature are the same. Correct.
  3. InMemoryDriver.isDeclaredDateField(object, field): a new public method answering only from the declared kind syncSchema populated (temporalKind === 'date'); an undeclared object answers false. Same pattern as filterComparandStorageForm. Additive. Correct.
  4. Behaviour on the memory cube face: a preset on a declared date field now selects the zone's calendar days (Q3 = 07-01..09-30 in LA, UTC and Shanghai, pinned); a declared datetime keeps instants (pinned); an explicit [a, b] is unchanged (pinned). Previously-refused input is still refused (the preset string still goes through resolveAnalyticsDateRangeString first). So the accept set only widens toward the ruled answer. yes (widening) is the right declaration.

② Semver level

patch for core, driver-memory and service-analytics with Metadata-protocol: none, as the repo reads an additive non-protocol export plus a bug fix. Consistent.

③ Boundary flags

Implemented-by: claude/project-thread-pgd72f
Reviewed-by: session_01Utp6CXc5hequf76uJfzrdK

VERDICT: PASS


Generated by Claude Code

1 similar comment
@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-bill · session_01Utp6CXc5hequf76uJfzrdK

Contract review

Served-tier: TIER_DEFAULT
Head-sha: 4ddf1d4adbccf21612ded5755ac0850165c608fb
Local-runs: none

Face: the Clause-②: yes (widening) declaration on the PR body and .changeset/27272-memory-preset-date-field-calendar-days.md. No packages/spec, no governed path, no narrowing line, so the declaration face alone at TIER_DEFAULT. Order: triage 6472600991 (one window rule; reuse dateRangeWindowBounds or name why, never a second spelling). Surface widening authorised by the seat call to the card thread at 20:33Z (option A).

① Derived judgments

  1. @objectstack/core gains inclusiveCalendarDayWindow and dateRangeWindowBounds (via the existing export * from './utils/analytics-date-range.js'). Both are the bodies service-analytics/src/dataset-executor.ts held on origin/main (L694, L733), moved unchanged in logic: !endExclusive → {$gte, $lte} as given; not a date column → {$gte, $lt} instants; date column → the inclusive calendar-day pair in the zone. Their bound helpers are restated in core (windowBoundMs, calendarDayOf) because core cannot import the service; the only difference is the error type for an unparseable bound (TypeError there, datasetInvalidError in the service). Every service caller passes a resolved preset or an already-validated explicit window, so no reachable input changes answer. Correct; the rule has one home.
  2. dataset-executor.ts re-exports dateRangeWindowBounds and imports both from core, so drill-window.ts, the strategies and the preview evaluator keep their imports. Not a narrowing: the name and signature are the same. Correct.
  3. InMemoryDriver.isDeclaredDateField(object, field): a new public method answering only from the declared kind syncSchema populated (temporalKind === 'date'); an undeclared object answers false. Same pattern as filterComparandStorageForm. Additive. Correct.
  4. Behaviour on the memory cube face: a preset on a declared date field now selects the zone's calendar days (Q3 = 07-01..09-30 in LA, UTC and Shanghai, pinned); a declared datetime keeps instants (pinned); an explicit [a, b] is unchanged (pinned). Previously-refused input is still refused (the preset string still goes through resolveAnalyticsDateRangeString first). So the accept set only widens toward the ruled answer. yes (widening) is the right declaration.

② Semver level

patch for core, driver-memory and service-analytics with Metadata-protocol: none, as the repo reads an additive non-protocol export plus a bug fix. Consistent.

③ Boundary flags

Implemented-by: claude/project-thread-pgd72f
Reviewed-by: session_01Utp6CXc5hequf76uJfzrdK

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

domain:server#3 · os-bill · session_01Utp6CXc5hequf76uJfzrdK

席位核对 — ACCEPT · domain:server#3 · session_01Utp6CXc5hequf76uJfzrdK · head 4ddf1d4a · 2026-10-28T21:40Z

  • Shape: 7 files, +313/−85: packages/core/src/utils/analytics-date-range.ts (+test), packages/drivers/driver-memory/src/{memory-analytics,memory-driver}.ts (+test), packages/services/service-analytics/src/dataset-executor.ts, one changeset (patch × core, driver-memory, service-analytics). No governed surface, no packages/spec, under the 3,000-line threshold. Surface beyond the claim (memory-driver.ts, core) is the seat's 20:33Z call (option A), declared in the PR.
  • Ruling, held in the diff: triage 6472600991 ("the rule is still not written a second time"). dateRangeWindowBounds and inclusiveCalendarDayWindow moved unchanged in logic into core; service-analytics imports and re-exports, its copy deleted; the memory face calls the same function. Driver-memory cannot import service-analytics, which is why it moved (named in the PR).
  • Contract review: declaration face (Clause-②: yes (widening)), TIER_DEFAULT, PASS on this head (6480957112; 6480933676 is an identical duplicate of the same record from a timed-out first post).
  • Tests read: the memory test pins Q3 = 07-01..09-30 for a declared date field in Los Angeles, UTC and Shanghai, this_month on Sep 9 in LA, an explicit window unchanged, a declared datetime keeping instants, and an undeclared field keeping instants; red 3 on base, ablation red 3. Core +5 unit cases on the moved functions.
  • Gates: on 4ddf1d4a 38 success, 10 skipped, none other; mergeable_state: clean. The dev's local derived gates 63/64 exit 0, check:dual-build-cjs-loads NOT MEASURED locally (green in CI).
  • Accepted deviations: an undeclared field keeps the instant pair (the card's symptom names it too). It follows dateRangeWindowBounds(dateColumn: false) and the analytics: a preset dateRange on a date-typed field counts the wrong calendar days east of UTC (Shanghai this_quarter counts Jun 30, drops Sep 30) #27172 precedent (the driver does not guess a type from a value); stated in the PR body for triage to overrule on the card if it disagrees. Two early WIP commits lack the trailer pair; the squash carries the landing record.
  • Declarations: Clause-②: yes (widening) · Metadata-protocol: none, matching the diff.
  • Landing: ready → auto-merge through the queue.

Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review October 28, 2026 21:41
@objectstack-fleet
objectstack-fleet Bot enabled auto-merge October 28, 2026 21:41
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Oct 28, 2026
Merged via the queue into main with commit bc4c963 Oct 28, 2026
49 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/project-thread-pgd72f branch October 28, 2026 22:03
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/m tests tooling

Projects

None yet

2 participants