Repository navigation
Description
Bug
isSnapshotRollup (pm-stack/aggregator/src/snapshots.ts:91) checks the rollup's record count with only:
if (typeof r.count !== "number" || !(r.count >= 1)) return false;That refuses 0 and negatives, but a count is a count of raw records — it must be a positive integer. Today the gate also passes:
- a fractional count (
1.5), and - an infinite count (
Infinity— reachable from a real JSONL line, becauseJSON.parse("1e999")isInfinity, so a rollup line carrying"count": 1e999parses to an infinite count).
count is not decorative. Compaction trusts it arithmetically: compactSnapshotMonth sums existing rollup counts into the report (snapshots.ts:725 per-service before += r.count; :732 recordsBefore: records.length + existing.reduce((n, r) => n + r.count, 0)), and re-compaction seeds from existing rollups and increments the carried count (aggAddRecord, :154 o.count += 1), so a fractional count stays fractional forever (1.5 → 2.5 → 3.5 …) and an infinite count makes every later compaction report for that month report recordsBefore: Infinity. Rollup files are kept indefinitely, so the corrupted count is re-seeded on every compaction rather than aging out.
This is the one numeric field #3365's hardening of this same gate did not cover: its acceptance pins the timestamp, limit/resetsAt, pair-value, and month rules, but not count.
Evidence (verified this run, main ba2e97d)
Laptop probe ~/git/fleet/probe-2026-11-01-laneA-gendates/probe.ts (Bun 1.4.2), importing the real snapshots.ts from the laptop clone — snapshots.ts md5 298f32a5, identical on sandbox main ba2e97d and the probe clone; output in probe.txt in the same directory:
- Control rollup (
count: 3, otherwise well-formed) →isSnapshotRollup=true. count: 1.5→true.count: Infinity→true; the same value reached viaJSON.parseof a line with"count":1e999→Infinity→ gatetrue.- Controls on the existing rule:
count: 0→false,count: -2→false(already refused).
Why it matters
The snapshot store is the usage hub's history, and its compaction reports ("records before/after", per-service counts) are the operator's evidence that retention is lossless. A count the gate certifies but no writer can produce silently falsifies those reports for the affected month — permanently, because rollups never expire and each compaction re-seeds from the last. Every other count-like field in this codebase's gates is checked as an integer (e.g. isRepoCensus uses Number.isInteger); this one was left as a bare range check.
Acceptance criteria
isSnapshotRolluprefuses acountthat is not a positive integer (Number.isInteger(count) && count >= 1): fractional, infinite, and NaN counts fail the gate, alongside the already-refused 0/negatives.- A rollup line with
"count": 1e999or"count": 1.5in a rollup file is dropped byreadAll/rollupKeys/compaction seeding like any other malformed rollup line; a well-formed rollup still passes and expands unchanged. - Regression tests beside the existing snapshot gate tests pin each refused shape plus the valid control.
- No change to the rollup file format or to compaction output for valid rollups.
Distinct from #3365 (same function's timestamp/limit/resetsAt/pair/month rules — its acceptance does not mention count), #3364 (coverage census counts), #3285 (compact script --months argument).
Activity
developing-today commentedon Nov 1, 2026
CODE-1314
dezren39 commentedon Nov 1, 2026
🤖 Agent note · filed
dezren39 commentedon Nov 2, 2026
agent-note:v1 — picked up — OpenChamber scheduled lane
4 remaining items
dezren39 commentedon Nov 2, 2026
agent-note:v1 — handed off and done — OpenChamber scheduled lane
199a7d27c024976aee716c8bcc7f6087a1303164confirms count 1.5, Infinity, and parsed 1e999 passisSnapshotRollup; 0, -2, and NaN fail./home/user/git/fleet/5354dezren39 commentedon Nov 2, 2026
🤖 Agent note · picked up
typeof r.count !== "number" || !(r.count >= 1)— accepts 1.5 and Infinity.dezren39 commentedon Nov 2, 2026
🤖 Agent note · done
Number.isInteger(count) && count >= 1, with regression tests for fractional/Infinity/1e999/NaN counts and stored malformed rollup lines dropped by readAll/rollupKeys. Codex review on PR fix(aggregator): reject non-integer snapshot rollup counts #4453 completed with no findings; targeted tests 18 pass on the laptop; aggregator typecheck unchanged from baseline (23 pre-existing errors, none in snapshots files). The one queued PR check (metered-provider guard) is the fleet Actions stall; merging proceeded under the standing merge authority with the fix verified.