Repository navigation
Description
Design from the design-architect pass 18 (2026-10-30). Full design doc: files/designs/code-aggregator-ingest-contract.md in the keep-the-four-repos-healthy goal workspace. The first non-mcpx design in this series — the mcpx conformance inventory is nearly exhausted, and this is the largest un-designed cluster in the repo: over 100 open issues carry aggregator in the title, the validation family is thirty-plus strong, and eight more were filed today alone. Audited against origin/main 7c52ced1.
Problem
The aggregator (pm-stack/aggregator) consumes documents it does not produce — fleet feed files, quota exports, snapshot history, session lists, GitHub billing, Cloudflare exports — and almost every boundary validates some of its document and trusts the rest. The failure modes of the unvalidated remainder:
- One bad element discards everything — a mapper throws on a null element and the whole source vanishes for the run (aggregator(opencode): mapServerSessions never validates the v2 wire containers — a non-array data on any endpoint, or one null session entry, throws out of the mapper and drops every session from that server; an id-less entry fabricates an undefined-sessionId row #2543 sessions, aggregator(jules): mapJulesItems never validates the keeper state — one null or mistyped item discards every Jules session for the run, a string items fabricates phantom sessions #2531 Jules, aggregator(github-billing): billing items never validated — an impossible date discards the whole month's collection, negative amounts are stored as usage #2493 a whole month of billing over one impossible date).
- Malformed input is fabricated into rows — an id-less entry becomes an
undefined-sessionId row (aggregator(opencode): mapServerSessions never validates the v2 wire containers — a non-array data on any endpoint, or one null session entry, throws out of the mapper and drops every session from that server; an id-less entry fabricates an undefined-sessionId row #2543); a stringitemsfabricates phantom sessions (aggregator(jules): mapJulesItems never validates the keeper state — one null or mistyped item discards every Jules session for the run, a string items fabricates phantom sessions #2531); a failed R2 enumeration publishesr2Buckets: []in astatus: okdocument (cloudflare-export: R2 bucket enumeration fails open — a 500/malformed buckets response publishes r2Buckets: [] in a status:ok document, and pagination past 100 buckets is silently dropped #2574). - Out-of-range values publish as live measured data — a window at −20% or 1e9% appended as percent history against
limit: 100(aggregator(quota-gather-map): entryFromResult and entryFromFileExport never range-check percent — a window at -20% or 1e9% publishes as live ok data and appends as pct history against limit:100 #2571, aggregator(snapshot-records): recordsFromProviderUsage never range-checks used_percent — a Codex window at -20% or 1e9% is appended as measured pct history against its own limit:100 #2568); negative counts as live data (aggregator(quota-gather-map): entryFromTurso never range-checks usage — negative counts publish as live ok data and append negative snapshot records #2553); an unpriced addon as measured $0 (supabase-export: billedFromAddons silently zeroes unpriced selected addons (and accepts negative amounts) — a paid addon reports as measured $0 #2492); impossible dates (2026-02-30, 2026-13-01) stored and string-sorted into wrong positions (aggregator(snapshot-records): shape-only dates win freshestDate — a 2026-13-01 'Last checked' silently drops a pool's snapshot history, 2026-02-30 is stored as a phantom March date #2264, aggregator(dns): parseDnsRegister accepts impossible renewal dates — 2026-02-30 and 2026-13-01 enter the register, count toward the annual total, and string-sort into wrong positions #2199, aggregator(quota-extract): Claude and Meta reset dates are never validated — an impossible date is emitted verbatim as resetsAt, and a Feb-30 Meta reset silently discards the pool's whole read #2470).
Each fix is effort/xs and re-derives the same guards privately, so the guards disagree and the next boundary repeats the pattern — the same shape already designed away in sibling repos (openchamber #802, nix #1145).
Evidence
- The contract already exists for one family:
pm-stack/aggregator/src/token-adapter.ts:1-26(feat(quota-plane): browser-token adapter framework — managed credentials, fail-closed dashboard-API clients #1153) — parsing is fail-closed, a drifted payload raisesAdapterError("unparseable"), the previous value is retained and ages, never becomes a zero. Store loaders repeat it (actions.ts:436,comments.ts:116). Nothing else states or shares the rule. isFleetFeed(src/feed.ts:207-219) checks only the top level — pools/windows/attention elements unvalidated;isAaSection(src/aa.ts:1092),isDnsSection(src/dns.ts:73),isOrchestrationSection(src/feed.ts:144) likewise (aggregator(dns): isDnsSection never checks totalAnnualListUsd or entry priceUsd — a feed section that passes validation crashes /dns and the usage view on .toFixed #2412, aggregator(aa): isAaSection never checks thresholds or sources — a section missing them passes the gate and crashes the /aa render instead of degrading #2415, aggregator(orchestration): section gate never checks tier/goal array elements — a [null] tier item or goal task passes validation and crashes buildAttention/todayFromSection #2417, aggregator(feed): isFleetFeed checks only that quota.pools is an array — burn/choices, pool entries and windows unvalidated, one malformed pool crashes /quota #2429, aggregator(feed): isFleetFeed never checks attention entries — one null entry crashes /attention + /today, entries without a string sessionId silently collapse into one #2427, aggregator(registry): malformed taskRefs elements pass every gate and crash /sessions at render — an array of bad refs merges, persists, then refUrl throws #2485).- No shared validation module exists in
src/;Number.isFiniteappears 27 times scattered;Date.parserolls impossible dates over, and several extractors check shape only (aggregator(snapshot-records): shape-only dates win freshestDate — a 2026-13-01 'Last checked' silently drops a pool's snapshot history, 2026-02-30 is stored as a phantom March date #2264, aggregator(quota-extract): ClinePass extractor's measured-date anchor is never validated — an impossible measured date or a huge 'resets in' offset silently discards the pool's whole read, and a Feb-30 anchor rolls into March #2383, aggregator(quota-extract): Claude and Meta reset dates are never validated — an impossible date is emitted verbatim as resetsAt, and a Feb-30 Meta reset silently discards the pool's whole read #2470). - Snapshot history is append-first: records are appended un-range-checked (aggregator(snapshot-records): recordsFromProviderUsage never range-checks used_percent — a Codex window at -20% or 1e9% is appended as measured pct history against its own limit:100 #2568, aggregator(quota-gather-map): entryFromTurso never range-checks usage — negative counts publish as live ok data and append negative snapshot records #2553, aggregator(snapshots): isSnapshotRecord never checks limit/resetsAt — a numeric resetsAt crashes the service history chart, a non-numeric limit NaNs it #2483) and
SnapshotStoreaccepts records it can never read back (aggregator: SnapshotStore accepts records it can never read back — non-finite estimate pairs and non-ISO timestamps pass validation, append reports success, and dedupe never sees them #1540) — render-time checks cannot un-append.
Decisions recorded
- One contract, validated once at ingest: shape, element types, ranges, and real calendar dates, before any consumer, store append, or render. Failure yields exactly one sanctioned outcome per boundary — reject and retain (feed sections, quota entries, store files), drop the element and count it (independent collection elements: sessions, billing items, pool entries), or refuse the append (snapshot records). Forbidden everywhere: throwing across the boundary discarding valid siblings; fabricating rows/ids/zeros; publishing out-of-range values as live data. A genuine zero is a successful validation whose value is 0 (the feat(quota-plane): browser-token adapter framework — managed credentials, fail-closed dashboard-API clients #1153 wording).
- One primitives module,
pm-stack/aggregator/src/validate.ts, no new dependency:isRecord,isNonEmptyString,isFiniteNumber,isNonNegative,isPercentOf(limit), strictisIsoDate/parseIsoDate(component re-check — 2026-02-30 fails instead of rolling into March),arrayOf(guard)(drop-and-count), and a future-timestamp bound (5-minute skew; a futuregeneratedAtis invalid, not maximally fresh, aggregator(health): dns/aa section stamps are never validated — a garbage dns generatedAt renders ageMs NaN/null, a future aa generatedAt renders age 0 (maximally fresh) for as long as the skew lasts #2242). - Phases absorb the per-site queue: each phase names its issue family and closes, with evidence, whichever are still open when it starts; any already closed per-site is verified against the primitives (converted if it hand-rolled), never re-fixed.
Phases
- Phase 1:
validate.tsprimitives + the contract inpm-stack/aggregator/README.md+ a malformed-input probe matrix intest/+isFleetFeed's date check converted to the strict date as proof consumer. Independent. - Phase 2: feed and snapshot boundaries on the primitives — named scope aggregator(dns): isDnsSection never checks totalAnnualListUsd or entry priceUsd — a feed section that passes validation crashes /dns and the usage view on .toFixed #2412 aggregator(aa): isAaSection never checks thresholds or sources — a section missing them passes the gate and crashes the /aa render instead of degrading #2415 aggregator(orchestration): section gate never checks tier/goal array elements — a [null] tier item or goal task passes validation and crashes buildAttention/todayFromSection #2417 aggregator(feed): isFleetFeed never checks attention entries — one null entry crashes /attention + /today, entries without a string sessionId silently collapse into one #2427 aggregator(feed): isFleetFeed checks only that quota.pools is an array — burn/choices, pool entries and windows unvalidated, one malformed pool crashes /quota #2429 aggregator(registry): malformed taskRefs elements pass every gate and crash /sessions at render — an array of bad refs merges, persists, then refUrl throws #2485 aggregator: SnapshotStore accepts records it can never read back — non-finite estimate pairs and non-ISO timestamps pass validation, append reports success, and dedupe never sees them #1540 aggregator(snapshot-records): shape-only dates win freshestDate — a 2026-13-01 'Last checked' silently drops a pool's snapshot history, 2026-02-30 is stored as a phantom March date #2264 aggregator(snapshots): isSnapshotRecord never checks limit/resetsAt — a numeric resetsAt crashes the service history chart, a non-numeric limit NaNs it #2483 aggregator(health): dns/aa section stamps are never validated — a garbage dns generatedAt renders ageMs NaN/null, a future aa generatedAt renders age 0 (maximally fresh) for as long as the skew lasts #2242. Gated on Phase 1.
- Phase 3: extractor and mapper boundaries — named scope aggregator(quota-extract): ClinePass extractor's measured-date anchor is never validated — an impossible measured date or a huge 'resets in' offset silently discards the pool's whole read, and a Feb-30 anchor rolls into March #2383 aggregator(quota-extract): Claude and Meta reset dates are never validated — an impossible date is emitted verbatim as resetsAt, and a Feb-30 Meta reset silently discards the pool's whole read #2470 supabase-export: billedFromAddons silently zeroes unpriced selected addons (and accepts negative amounts) — a paid addon reports as measured $0 #2492 aggregator(saas-extract): applySupabaseExport never range/type-checks the export doc — negative used/limit/billedUsd are stamped as live measured data, and an array measuredAt passes the freshness gate and corrupts the card #2508 aggregator(token-adapter): file-export adapter's fail-closed window check throws on a null window element and accepts a non-string window name, instead of returning the retained unparseable outcome #2551 aggregator(quota-gather-map): entryFromTurso never range-checks usage — negative counts publish as live ok data and append negative snapshot records #2553 aggregator(snapshot-records): recordsFromProviderUsage never range-checks used_percent — a Codex window at -20% or 1e9% is appended as measured pct history against its own limit:100 #2568 aggregator(quota-gather-map): entryFromResult and entryFromFileExport never range-check percent — a window at -20% or 1e9% publishes as live ok data and appends as pct history against limit:100 #2571 aggregator(github-billing): billing items never validated — an impossible date discards the whole month's collection, negative amounts are stored as usage #2493 aggregator(jules): mapJulesItems never validates the keeper state — one null or mistyped item discards every Jules session for the run, a string items fabricates phantom sessions #2531 aggregator(feed): isFleetFeed never checks registry — a non-array container crashes registry()/coverage(), and coverage()'s unfiltered .filter throws on a [null] entry that registry() handles fine #2539 aggregator(opencode): mapServerSessions never validates the v2 wire containers — a non-array data on any endpoint, or one null session entry, throws out of the mapper and drops every session from that server; an id-less entry fabricates an undefined-sessionId row #2543 cloudflare-export: R2 bucket enumeration fails open — a 500/malformed buckets response publishes r2Buckets: [] in a status:ok document, and pagination past 100 buckets is silently dropped #2574. Gated on Phase 1, independent of Phase 2.
Open questions stay in the design doc (projector/meta-exporter adoption across the ts/mjs boundary; a config clause for #2161; whether drop counts surface on the dashboard — watchdog #1260 is the candidate consumer; #1684's UTC-day ageing is a correctness bug in valid data, deliberately out of scope).
🤖 Design-architect pass 18 — agent note: this issue is the design of record; phases are its sub-issues. Do not re-derive the decisions above in triage; flag genuine blockers here instead.
Acceptance criteria
- The three implementation phases stay linked from this design issue until each merges or is explicitly declined with rationale recorded here.
- Each phase meets its own acceptance criteria; no phase widens scope beyond the design doc (
files/designs/code-aggregator-ingest-contract.md). - When all phases have landed or been declined, this issue closes with a summary linking the merged PRs.
Activity
dezren39 commentedon Oct 30, 2026
🤖 agent-note:v1 — design-architect pass 18 (2026-10-30): this is the design of record for the aggregator ingest-validation family (30+ open per-site issues). Phases #2577 (primitives + contract + probe matrix), #2578 (feed/snapshot boundaries, gated on #2577), #2579 (extractor/mapper boundaries, gated on #2577) are linked as sub-issues. The named per-site issues are absorbed by the phases per the design named-scope rule — triage of a named issue should check its phase before fixing per-site. Decisions are recorded in the body; do not re-derive in triage.
dezren39 commentedon Oct 31, 2026
Program tending (2026-10-31, via #1352): Phase 1 of this epic, #2577 (validate.ts primitives — the ingest contract in the README), closed today (2026-10-31T11:04Z), recording it here since this thread's last activity (2026-10-30T05:34Z) predates the landing.
Remaining per the epic's own phase list: #2578 and #2579, both still OPEN. Nothing blocked is recorded on the closed phase. Sub-issue state verified this run via
gh issue view(3 sub-issues: 1 closed, 2 open).dezren39 commentedon Oct 31, 2026
Program tending (2026-10-31): Phase 2 (#2578, feed and snapshot boundaries) closed 2026-10-31T14:32Z, following Phase 1 (#2577) earlier today. Progress: 2 of 3 phases closed. Remaining: Phase 3 #2579 (extractor and mapper boundaries), still open. Not a completion proposal — just keeping the parent current.
3 remaining items
dezren39 commentedon Nov 3, 2026
🤖 Agent note · handed off
dezren39 commentedon Nov 3, 2026
🤖 Agent note · handed off
dezren39 commentedon Nov 3, 2026
🤖 Agent note · handed off (re-handoff)
dezren39 commentedon Nov 3, 2026
🤖 Agent note · handed off
opencode. Handed off — the laptop claimant launches the bound opencode session, which drives the task to its terminal state. Claim labels already synced. NOTE: this claim is attempt 6/6 at the max_attempts cap — if this lease expires unlaunched, task 7961 will need a max_attempts raise to become claimable again.dezren39 commentedon Nov 4, 2026
🤖 Agent note · handed off
opencode; handed off to the laptop claimant (opencode-claimant), which launches the bound opencode session to drive task 7961 to its terminal state.dezren39 commentedon Nov 4, 2026
agent-note:v1
state: picked-up
queue: repo-health-code-pilot
task: 7961 (triage, worker repo-worker-code, lane c8b9355e-319a-4640-8096-7ad19480d151)
by: openchamber-scheduled-lane
at: 2026-11-04T03:05Z
Fresh pickup check: issue #2576 is OPEN; no hard-stop labels are present and its claim labels (
queue/claimed,status/in-progress,work/assigned) are already in sync. The design body says not to re-derive its decisions; I’m checking the current state of the three named phase issues and any live fix ownership before recording the triage verdict. No worktree or code changes started.dezren39 commentedon Nov 4, 2026
agent-note:v1
state: triaged
queue: repo-health-code-pilot
task: 7961 (triage, worker repo-worker-code, lane c8b9355e-319a-4640-8096-7ad19480d151)
by: openchamber-scheduled-lane
at: 2026-11-04T03:11Z
Triage verdict against current
mainheadece818d386cebcacfdb591e1d41695036d211d43(latest commit 2026-11-04T02:55:49Z): issue #2576 remains LIVE because Phase 3 #2579 is still OPEN. Phase 1 #2577 is CLOSED (PR #3014 merged); Phase 2 #2578 is CLOSED (PRs #3035 and #3070 merged). The current claimed-task list shows existing implementation task #6900 for #2579, so no duplicate follow-up task was created.Ordering note for the Phase 3 owner: open PR #5126 (
fix(aggregator): reject invalid file-export measuredAt, for #5118) also editspm-stack/aggregator/src/quota-gather-map.tsand its test, adding anisIsoDategate forentryFromFileExport. It is authored bydezren39; this triage did not modify it. Reconcile that adjacent change before touching the same mapper/function so the measuredAt guard is not duplicated or conflicted. PR #5126 currently reports checks SUCCESS with one lower-severity bot thread unresolved.Action: design decisions were not re-derived; #2576 stays open pending Phase 3. No branch/worktree/code edits/tests or new task were made. This triage is complete; lifecycle labels for task 7961 will be removed.
dezren39 commentedon Nov 4, 2026
agent-note:v1
state: done
queue: repo-health-code-pilot
task: 7961 (triage, worker repo-worker-code, lane c8b9355e-319a-4640-8096-7ad19480d151)
by: openchamber-scheduled-lane
at: 2026-11-04T03:12Z
outcome: triaged. Issue #2576 stays OPEN pending Phase 3 #2579. Phase 1 #2577 and Phase 2 #2578 are CLOSED; existing implementation task #6900 remains the fix path. The parent issue's claim labels (
queue/claimed,status/in-progress,work/assigned) were removed after task completion. Related PR #5126 was noted for coordination and left untouched. No follow-up task was created.bookkeeping: deferred to fleet reconciliation per the scheduled-lane protocol.