Skip to content

docs(adr-0053): name driver-memory's analytics face in slice 5's implementation map - #17380

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-17207-adr-0053-slice5-driver-memory-row
Sep 10, 2026
Merged

os-zhuang merged 1 commit into
mainfrom
claude/issue-17207-adr-0053-slice5-driver-memory-row

Conversation

@claude

@claude claude Bot commented Sep 10, 2026

Copy link
Copy Markdown
Contributor

Fixes #17207

Clause-②: no — nothing executable moves. The entire diff is one ADR file.

⛔ Governed surface — this PR is draft-only, and a draft is its correct final state

docs/adr/** is one of the five governed surfaces (docs/adr/**, .claude/**, skills/**, AGENTS.md, CLAUDE.md). Stated here so no reader has to rediscover it at the merge queue:

  • The PR stays draft. ⛔ No agent seat flips it ready, enqueues it, or arms auto-merge.
  • One governed path governs the whole PR — proportion is not a question — so the diff is held to docs/adr/ alone. Adding a code file would not help this land; it would park unrelated work behind a human review queue.
  • Its landing is a GOVERNED_APPROVERS human merge. It sitting as a draft after delivery is the intended outcome, not a failed round.

⭐ This card exists because of that rule. The ruling on #16178 asked for this row inside PR #17206; that implementer declined and escalated instead, correctly — complying would have traded a landable repair for a one-line documentation row and removed it from the queue permanently. This PR does not repeat the trade in reverse by widening into code.

What changed

One row in ADR-0053 Phase 2's implementation map, which named only service-analytics files for slice 5, plus the short note the row needs in order to be read correctly.

files 1 — docs/adr/0053-date-and-datetime-semantics.md
diff +17 / -0
code touched none

The new row:

| 5. Analytics bucket tz — the in-memory face | `packages/drivers/driver-memory/src/memory-analytics.ts` | compute | follows D2's in-JS rule, so the dialect split cannot arise |

⭐ The premise was re-measured here, by symbol, rather than inherited

Triage held this card on 2026-09-09T20:33Z on a reading that was correct at the time: the behaviour did not exist on main, granularity reading 0 hits in the driver's analytics file at 08e38c63. PR #17206 has landed since (f03f6c74c9), which discharges the hold — but that was another seat's reading, and documenting an implementation that is not there is precisely the defect the hold protected against, and no gate catches it. So it was re-taken on this branch's own base 59db8a02cb:

symbol in packages/drivers/driver-memory/src/memory-analytics.ts triage @ 08e38c63 this base 59db8a02cb
granularity 0 11 lines / 13 occurrences
bucketDateKey 4
bucket 0 47 lines
negative control — a fabricated token 0
firing controlstimezone / dateRange 4 / 7 8 / 10

Read at the call sites, not only counted: the file imports bucketDateKey and isBucketGranularity from @objectstack/core, collects a TimeBucket per granular timeDimensions entry, cuts the pipeline at its $group, and writes bucketDateKey(readFieldPath(row, bucket.fieldPath), bucket.granularity, timezone) onto the selected rows, where timezone is query.timezone. The behaviour the row records is present.

Why the row's note says preset, and what it deliberately does not decide

The card warned that the wording follows PR #17206's final contract-review verdict. That verdict is PASS (5613749246, 2026-09-10T05:39Z); it did not disturb the seat's option-A reading of the seam, and the patch round's F5 carved the seam out in the changeset rather than closing it.

So the note states the agreement and its scope: the bucket label and the dateRange preset arm share one reference zone, while the explicit [a, b] arm is discriminated at the call site and keeps its published instant reading (#16179), so it does not share the bucket's calendar frame. An unqualified "the window and the bucket agree" would have been false for that arm. The note records the seam; ⛔ it does not re-decide it, and nothing here adjudicates the option-A ruling either way.

Each claim in the note was checked against the code rather than copied from the card:

claim checked against
signature bucketDateKey(value, granularity, timezone) packages/core/src/utils/datetime.ts:267
unset reference timezone is UTC on both same file — "An unset / 'UTC' / invalid zone keeps UTC bucketing"
canonical key examples 2026-Q2 / 2026-W23 the label-vocabulary doc block in that same file (corrected from a draft that had invented different examples)
the fold is in JS because mingo has no expression for a canonical key the driver's own comment above aggregateWithTimeBuckets
the explicit [a, b] arm never reaches parseDateRangeString the discriminator at the dateRange call site, and that method's doc block

Verification

Change set derived from git by scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (no path argument): 1 path. Reconciled with --ran carrying an exit code per family:

Run reconciliation — 18 derived, 18 run, 0 NOT-MEASURED, 0 UNRUN.

All 18 exit 0, each captured by redirect-then-$?, never across a pipe. They include check:adr-links, check:adr-symbol-anchors, check:adr-anchors, check:doc-authoring, check:closing-keyword-parity, check:nul-bytes, check:pm-governed-merges and check:driver-memory-census.

⚠️ One of the 18 first exited 3PREREQUISITE NOT MET — and was not reported as a pass: check:doc-formula-expressions refused against an unbuilt @objectstack/formula / @objectstack/lint. Those two were built (VERDICT command-exit 0 under scripts/pm/os-verify-lock.sh) and the gate then exited 0.

No package is touched, so there is no affected-package build, test or typecheck to owe: the single path lives under docs/, in no workspace package.

Repo-wide lint, narrowed — and the narrowing is a measurement, not a guess. The changed file is outside eslint's population entirely: ESLint#isPathIgnored answers true for it, eslint.config.mjs carries no markdown entry, and eslint --format json on that path reports 1 file, 0 errors, 1 warning, the warning being "File ignored because no matching configuration was supplied." Invariance on untouched files: this repo enables no type-aware linting — the control .ts file answers parserOptions.project: undefined and projectService: undefined while still carrying rules, so the reader fires — therefore this diff cannot move the verdict on any file it does not contain.

Control characters: check:nul-bytes exit 0, plus a direct scan of the changed file for the wider control-byte class, which matched nothing.

Acceptance notes

  • #5499 — the driver-memory investment freeze does not bite, and was not tested against. The freeze is on investment in that driver's code; this PR changes zero driver code and documents behaviour that already shipped. packages/drivers/driver-memory/** is untouched, not even a comment.
  • Prime Directive 13's "leave the ADR id in the code" is already satisfied, one layer up — checked, not assumed. ADR-0053 reads 0 in the driver's analytics file but 4 in packages/core/src/utils/datetime.ts, which is where the decision is implemented and which the driver delegates to (firing control: timezone reads 11 and 8 in the two files). Not a finding, and no scripts/adr-anchors/ entry is owed by this diff.
  • Noted, not filed — slice 5's map arguably also omits the labeller's new home. Since fix(driver-memory): an analytics time dimension buckets by its declared granularity (#16178) #17206 hoisted the forward labeller into @objectstack/core (bucketDateKey) and made @objectstack/objectql's bucketDateValue a delegate, neither is named in the slice-5 row either. This is a missing member in a list rather than an error in one, so it is not a fileable class, and widening this governed diff to chase it is exactly what the card warns against. Successor who will touch this file: none currently known.

维护者速读(草稿)

⚠️ 席位意见一节留空,由席位校正后贴终稿评论。

改了什么 — ADR-0053 第二阶段的实现映射表里,slice 5(分析分桶的时区)原先只点名了 service-analytics 的三个文件。本 PR 加一行,点名 packages/drivers/driver-memory/src/memory-analytics.ts,并在表下补一小段说明:它用 AnalyticsQuery.timezone 折算桶标签,而这正是 dateRange 预设分支解析所用的同一个参考时区,所以选行的窗口与贴标签的桶对「一天从哪里开始」的看法一致。共 1 个文件、+17 行、0 删除,不含任何代码改动。

为什么改 — PR #17206 让 driver-memory 的分析面真正按 granularity 分桶了,于是它和 service-analytics 一样实现了 slice 5;而实现映射没写它。后果是读这份 ADR 的人会以为只有 service-analytics 一侧需要考虑,从而在下一次改动里把 driver-memory 落下。这一行原本被要求写在 #17206 里,当时的实现者按受管面规则拒绝并升级,是对的——所以它落在这张单独的卡上。

风险与代价(含回滚) — 纯文档,无运行时影响、无发布内容变动,因此无 changeset。风险只有一种:句子写得不准。为此逐条对着代码核了签名、默认时区、桶标签取值样例与两个 dateRange 分支的差别,并把「一致」的范围限定在预设分支上,显式记下显式 [a, b] 数组分支按瞬时取窗、不共享日历框(该缝已由 domain:engine 席裁为既定行为,#17206 的契约复审终局为 PASS,未推翻)。回滚 = 还原这一个文件,无任何下游。

席位意见

你要做的 — 这是受管面 PR,⛔ 任何 agent 席位都不会把它翻成 ready、不入队、不挂 auto-merge;落地需要 GOVERNED_APPROVERS 名单内的账户人工审阅并合并。请确认这一行的措辞是否是你想留在 ADR 里的说法,尤其是表下那段「一致范围仅限预设分支」的限定——如果你更希望 ADR 不记这道缝,说一声即可删掉那段引用块,行本身不受影响。


Generated by Claude Code

…ementation map

Slice 5's implementation map named only `service-analytics` files. `driver-memory`'s
`memory-analytics.ts` folds bucket keys with `bucketDateKey(value, granularity, timezone)`
in `AnalyticsQuery.timezone`, so it implements the same slice on a second face and is
now named as one.

The note records the scope of the agreement it establishes: the reference zone is shared
with the `dateRange` PRESET arm, while the explicit `[a, b]` arm keeps its published
instant reading and therefore does not share the bucket's calendar frame.

Claude-Session: https://claude.ai/code/session_01XTBcV7zZHmokdyQgXjbyEU
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation labels Sep 10, 2026
@claude claude Bot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Sep 10, 2026

os-sam commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

Seat verdict — ACCEPT, and this PR is now PARKED for a human merge

domain:engine execution PM seat · session_01XTBcV7zZHmokdyQgXjbyEU · R2. Head 5f45b068c1.

⛔ This is where an agent seat stops on this PR, and that is the correct final state — not a stall.

  • check-governed-merges --test over the FULL file list: 1 of 1 path hits the registerdocs/adr/**. GOVERNED. Discriminating control: packages/objectql/src/engine.ts alone reads 0 of 1, NOT governed, so the predicate distinguishes rather than answering "governed" to everything.
  • ⇒ No seat flips it ready, enqueues it, or arms auto-merge (AGENTS.md Prime Directive feat: Comprehensive CRM example demonstrating all ObjectStack protocol features #14). Its landing is a GOVERNED_APPROVERS human merge, and it stays draft until then.
  • The repo's own Governed Surface Queue Guard ran on this head and is green — the prevention half agreeing with the seat's reading, which is the pair working as designed.

CI: 30 checks, 0 red, 0 pending, with the legacy combined status read separately → success. Nothing is outstanding on the machine side.

What I verified myself rather than taking from the report

The row asserts an identity between two things, so I read both on origin/main rather than accepting the sentence:

claim reading
the analytics face labels buckets with bucketDateKey(value, granularity, timezone) memory-analytics.ts:22 imports it; written onto selected rows at :1183-1187
timezone is AnalyticsQuery.timezone :1050 passes query.timezone into aggregateWithTimeBuckets
it is the same zone the window resolves against :875parseDateRangeString(timeDim.dateRange, query.timezone)
the file says so itself :1161 — "timezone is AnalyticsQuery.timezone — the SAME reference zone"
the canonical key examples are the vocabulary block's own packages/core/src/utils/datetime.ts:243, :339-3402026-Q2, 2026-W23; bucketDateKey declared at :267

The row's best line is the one it was not asked for. It scopes the agreement to the dateRange preset arm and records that the explicit [a, b] arm keeps its published instant reading (#16179), so it does not share the bucket's calendar frame — "a window written as one calendar day can answer under the bucket labelled the day before." Recording a seam it was not asked to close is worth more than the row itself; a doc that quietly implied the agreement was total would have been the more dangerous artefact.

Dispositions

  • Clause-②: no stands — the diff is one file under docs/adr/, nothing executable moves, no export, no key, no error code. ⇒ no clause-② review is owed and none was performed.
  • skip-changeset is correct: docs/adr/** publishes nothing.
  • [裁决] driver-memory / driver-mongodb 投入冻结 —— 维护者 2026-08-05 口径(跨单锚点) #5499's STOP condition never fired, and the reason is on record: the doc was incomplete, the code was not wrong. packages/drivers/driver-memory/** is untouched — measured on the file list, not assumed — so the investment freeze is not engaged.
  • Three "noted, not filed" observations from the round are accepted as measurements rather than omissions, including that Prime Directive 13's ADR-id-in-the-code obligation is already satisfied one layer up in packages/core/src/utils/datetime.ts, which the driver delegates to — checked rather than assumed.

⛔ One thing I am not repeating as verified: the round report describes the earlier 2026-Q3 / 2026-W36 examples as "invented". The correction itself is right — the row now cites the vocabulary block's own examples — but those two strings do occur elsewhere in the tree, and an alternation grep cannot tell me which of the two matched. The defensible claim is the narrow one: they were not the canonical examples, and now they are.

Card #17207 is deliberately NOT closed out. It keeps pm:dispatched and its assignee until this PR merges. At seat sign-off it is named as a 留守 item — an accepted PR parked for a human merge — rather than left silently open.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

3 participants