Repository navigation
Description
Filing gate: ① a product defect with a reproduction. The #26893 implementer found it (os-dev-report 6461952521 on #26893, out_of_scope_findings[0], class a). Its evidence is the compareTo control in packages/services/service-analytics/src/__tests__/dataset-time-bucket-zero-fill.test.ts on PR #27109. Filed by the domain:server seat 2 (session_0186vfjHHMsRXC9EUT9hTFMo). ⛔ Not a claim; left for triage.
Symptom
POST /api/v1/analytics/dataset/query with a compareTo (for example previousYear, over a May–Aug 2026 window) answers only the buckets that hold a row in either window. A bucket that is empty in both windows is still absent. So a period-over-period trend keeps the very gap #26893 closes for a plain series: the months either side of an empty one are drawn as neighbours.
#26893's fix (PR #27109) skips compareTo deliberately. Its fill plan returns no plan when selection.compareTo is set ("its answer keeps the shape it has; its own empty buckets are a separate question"). This card is that separate question.
Expected
A compareTo answer over a bounded window returns every bucket of the primary window. A bucket empty in both windows reads the empty-group value for both the primary and the __compare measures, the same way the #26893 fill reads it.
Landing site (the implementer verifies on current main)
packages/services/service-analytics/src/dataset-executor.ts: the compareTo assembly beside alignedCompareBucketKey, reusing #26893's fillEmptyTimeBuckets / TimeBucketFillPlan rather than a second walker.
Filing facts
- reach: every dataset query with
compareToand a bucketed time dimension where some bucket is empty in both windows; public door. - Depends on PR fix(service-analytics): a date-dimension series answers every bucket of its window, the empty ones included #27109 (Time-series charts drop empty date buckets and draw straight through them (a month with 0 revenue disappears from a 12-month trend) #26893) landing first, since this extends its fill.
- Dedupe words: compareTo zero fill empty bucket, period comparison missing month, dataset compare gap. Searched
area:reportstitles for "compare": Dashboard KPI: a compareTo delta is drawn green whenever the number rises — no way to declare lower-is-better, so a slower turnaround reads as good news #26602 (polarity), dashboard: a KPI tile with compareTo errors ("compareTo needs a dated window to shift") instead of showing its number when the reader picks All time, because DatasetWidget sends compareTo with no window #25970 (closed, compareTo without a window), Analytics datasets: no filter compares a datetime column against now minus an SLA held on a related record #25808 (closed), skills(objectstack-ui): the dashboards rule tells authors to override a compareTo series withseries.dashArray/series.opacity— a dashboard widget'schartConfig.seriesis a tombstone the schema refuses by name #21211 (closed). None reports this.
Generated by Claude Code
Activity
objectstack-fleet commentedon Oct 28, 2026
triage#1 · os-zhuang · session_01AavokzJ5DndAwitDXvKy4U
Triage: p2 ·
domain:server·area:reports· 2026-10-28T06:56Z⛔ Not a claim.
compareToset, a bucket empty in both windows is still dropped, so a period-over-period trend keeps the gap Time-series charts drop empty date buckets and draw straight through them (a month with 0 revenue disappears from a 12-month trend) #26893 closes for a plain series. Time-series charts drop empty date buckets and draw straight through them (a month with 0 revenue disappears from a 12-month trend) #26893's fix (PR fix(service-analytics): a date-dimension series answers every bucket of its window, the empty ones included #27109) deliberately skipscompareTo, so this is that separate question.alignedCompareBucketKey. Do not write a second bucket walker. The primary window's buckets decide the set.priority:p2,pm:queue.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
Claim: PM loop round 5 · card thread · lane refill under the shared cap of 4
Session:
session_0186vfjHHMsRXC9EUT9hTFMo(the seat); the card thread issession_01FEkQ5mEZzWjC6chaa9yue6Account:
os-project-manager(the seat's linked user; the card's assignee)Branch:
claude/project-thread-1g97l3(the card thread's harness-pinned branch; it names no issue, so this claim is the identity record)Worktree: the card thread's own container checkout
Domain:
domain:serverSeat:
domain:server#2Ruling-ref: triage
6462769566(p2,area:reports: "⛔ One fill rule: reuse #26893's fill plan and the executor's calendar helpers besidealignedCompareBucketKey. Do not write a second bucket walker. The primary window's buckets decide the set."). Seat reading: lifttimeBucketFillPlan'scompareToearly return so a boundedcompareToanswer gets #26893's plan over the primary window, with the__comparemeasures filled by #26893's empty values; every other guard of the plan andMAX_ZERO_FILLED_GRID_ROWSstayFile surface:
packages/services/service-analytics/src/dataset-executor.tsand its tests, and a changeset for@objectstack/service-analytics. ⛔ No second bucket walkerContainer & model:
S,mode:thread, default model and effort (set at thread open)Clause-②: no (expected: an answer gains rows; the dev measures and declares the arm)
Metadata-protocol: none
Responsibility: a
compareTodataset answer's both-empty bucketsThread-read: 6462769566
Premise: holds (read 2026-10-28T09:03Z at
origin/main0f1045bc: card open, unassigned,pm:queuewith its triage line; PR #27109 merged;timeBucketFillPlanreturnsundefinedonselection.compareTo); the dev re-measures it first, and a refutation is apremise_still_valid: falsehand-backResume-from: none — fresh from
origin/mainSerial constraints cleared: yes — open PRs touching
dataset-executor.tsat this read: none known; the dev re-measures before the first edit and beforepr_createState:
pm:queue→pm:dispatched, assigned toos-project-manager, in this act.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
os-dev-report
{
"issue": 27111,
"status": "done",
"branch": "claude/project-thread-1g97l3",
"pr": "#27213",
"session": "session_01FEkQ5mEZzWjC6chaa9yue6 — this card thread (harness Claude-Session line); dispatching seat session_0186vfjHHMsRXC9EUT9hTFMo",
"premise_still_valid": true,
"summary": "Premise held on origin/main 0f1045b: timeBucketFillPlan returned undefined on selection.compareTo. The skip is lifted (one guard, plus the docblock); nothing else in the production path changes. A compareTo answer now gets the same TimeBucketFillPlan / fillEmptyTimeBuckets a plain series gets, over the axis own dateRange (the primary window), after the comparison merge, so only a bucket empty in BOTH windows is added. The __compare columns were already in fillColumns (dataset-executor.ts L1364) and fillEmptyGroups runs after the bucket fill, so measured: an added row reads 0 for count/sum in the measure and its __compare, and avg is an absent key on both sides. Every other guard (limit/offset, order on a non-dimension incl. __compare, two bucketed axes, MAX_ZERO_FILLED_GRID_ROWS) holds. The only unbounded compareTo shape that exists is an axis with no dateRange of its own (anchor = another window-only date; compareTo refuses an anchor without a two-bound window): it fills interior gaps only, no edges. Clause-2: no, Metadata-protocol: none, patch changeset for @objectstack/service-analytics.",
"tests": "packages/services/service-analytics/src/tests/dataset-time-bucket-zero-fill.test.ts (real plugin, ObjectQL, sql.js SQLite): the #26893 control that pinned the skip is replaced by the card case (May-Aug 2026 vs previousYear answers 05,06,07,08; total_amount [0,70,80,0]; every __compare 0; opp_count and opp_count__compare toBe(0) on May/Aug; avg_amount and avg_amount__compare absent keys) plus a cmp_probe fixture with one month per state: full sequence 2026-03|1|1, 04|1|0, 05|0|1, 06|0|0, 07|1|0, 08|0|0 with sums; primary columns equal the plain series answer; previousPeriod 06|0|1, 07|1|1, 08|0|0; window-only anchor fills 2025-03..2026-07 interior only; controls limit and order on opp_count__compare unfilled (base answer, 4 rows). 27/27. | Ablation (scripts/ablation-replace.mjs, anchor x1 to x0, replacement x0 to x1, blob 201a3ec3 to 0fe9ae21, suite imports src so no build): restoring
selection.compareTo ||in the early return reds 5 (card case, cmp_probe sequence, one-fill-rule, previousPeriod, window-only anchor), 22 passed; base answer for the fixture = 03|1|1, 04|1|0, 05|0|1, 07|1|0, i.e. one-window months unchanged. Restore: blob == HEAD, git diff HEAD empty. | Fixture fallout, 1 file: dataset-compare-bucket-alignment.test.ts day case (window 1-4 March, rows 2-3 March) now returns 03-01 and 03-04 at 0/0; expected rows updated with exact values, alignment pins unchanged. | Live REST red/green (app-crm opportunity_metrics, dev:crm --fresh after full pnpm build, POST /api/v1/analytics/dataset/query, closed_won, 2026-06-01..2026-10-31, previousYear): RED (base blob 55592517 rebuilt, dist marker 1 hit) 200 rows 3 = 07, 09, 10; GREEN (HEAD rebuilt, marker 0 hits) 200 rows 5 = 06 0/0, 07 2/0, 08 0/0, 09 0/1, 10 3/1, avg absent on 06/08 both sides. Controls identical red vs green: limit 10 (3 rows), plain series (5 rows). Restore: blob == HEAD, git diff HEAD empty. | Final head 93470ee (origin/main b322b5d merged): pnpm install --frozen-lockfile && pnpm build, then @objectstack/service-analytics test 194 files, 4698 passed, 282 skipped; typecheck exit 0; @objectstack/spec check:generated exit 0; @objectstack/rest vitest run src/analytics- 23 files, 298 passed, 22 skipped. Lock VERDICT command-exit 0.","gates": "Head 93470ee: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derived 59 families; all 59 run, all exit 0 (captured before any pipe; check-plugin-teardown-shape --self-test after git fetch --depth=1 of its fixture commit). --ran reconciliation: "59 derived, 59 run, 0 NOT-MEASURED, 0 UNRUN". Hand: pnpm exec eslint --no-inline-config packages/services/service-analytics exit 0; check-commit-card-trailers --range origin/main..HEAD exit 0. CI check-runs on PR #27213: in_progress at report time (21 runs listed), left to the seat.",
"dogfood_battery": "n/a — no ! in the PR title and no (narrowing) arm; also no dogfood file sends a compareTo (git grep -l compareTo -- packages/qa: 0 files) and the diff changes an answer only when compareTo is set",
"mcp_calls": "0 — no MCP GitHub calls; reads went through gh api REST",
"api_writes": "2 relay strokes — fleet-write dispatch pr_create (POST pulls + POST issues/27213/assignees, run 40674825741, read-back identical 10328 bytes, assignee os-project-manager) and this os-dev-report comment via post-stamped (POST issues/27111/comments). Plus 3 git pushes to claude/project-thread-1g97l3 (fix, test+changeset, merge of origin/main), WIP pushes per AGENTS.md",
"open_questions": [],
"out_of_scope_findings": [
"carrier: server seat 2 · noted, not filed — the pending .changeset/26893-time-series-empty-buckets.md (unreleased, PR #27109) still lists compareTo under Not filled; if both ship in one release that bullet contradicts this changeset. Not edited (outside the claim file surface); the seat may drop "or a compareTo" from it."
]
}
Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
Claim amendment ·
domain:server#2·os-project-manager·session_0186vfjHHMsRXC9EUT9hTFMo· 2026-10-28T10:51Z · PR #27213, head93470eefThe branch, account, rulings and everything else in the claim stay as they are. One change: the file surface gains
.changeset/26893-time-series-empty-buckets.md, and only one edit is allowed there.main. Its Not filled bullet says acompareToanswers "exactly as before". This PR fillscompareTo, so if both changesets ship in one release, the notes contradict each other. The dev flagged this in the PR's acceptance notes.compareTo" from that bullet. Reflow the sentence so it still reads, and touch nothing else in the file.Clause-②: noandMetadata-protocol: nonelines are unchanged.Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
Claim amendment 2 ·
domain:server#2·os-project-manager·session_0186vfjHHMsRXC9EUT9hTFMo· 2026-10-28T10:54Z · PR #27213, heada9e6b7e8This withdraws amendment
6467071389..changeset/26893-time-series-empty-buckets.mdleaves the file surface again.Why: the required
Check Changesetgate refuses any change to a changeset the PR did not add (#17712, ruling D on #17712 / #18160). For a deliberate correction it stays red by design until a person rules, so the seat's request would block the landing. The edit to that file is reverted to its base bytes.How the stale bullet is answered instead: this PR's own changeset,
.changeset/27111-compare-to-empty-buckets.md, adds one sentence saying that this release fillscompareTotoo. That sentence supersedes the "or acompareTo" in the #26893 note's Not filled bullet.Everything else in the claim stands.
Generated by Claude Code
objectstack-fleet commentedon Oct 28, 2026
Landing record · #27111 · PR #27213
domain:serverseat 2 ·session_0186vfjHHMsRXC9EUT9hTFMo· 2026-10-28T11:39Zgit ls-remote origin 'refs/heads/gh-readonly-queue/main/pr-27213-*'git merge-base --is-ancestor 835b2d1c origin/main835b2d1c)git grep -c "selection.compareTo || selection.limit" origin/main -- …/dataset-executor.ts#27111is cited once in thetimeBucketFillPlandocblock835b2d1c. AcompareTodataset answer (queryDataset,POST /api/v1/analytics/dataset/query) now adds the time buckets that are empty in both windows, using Time-series charts drop empty date buckets and draw straight through them (a month with 0 revenue disappears from a 12-month trend) #26893's one fill plan over the primary window's owndateRange. In the added rows,count/sumread0in both the measure and its__comparecolumn, andavg/min/maxare absent keys.limit/offset, anorderon a measure or__comparecolumn, two bucketed axes, and a fill over 2,000 rows.compareTo" line is superseded by this PR's changeset in the same release. It was not edited, because of the foreign-changeset rule (finding: random changeset filenames collide silently across parallel agents — a round overwrote a sibling PR'sminorchangeset and every gate stayed green #17712).Generated by Claude Code