Repository navigation
Description
Problem
isFleetFeed (pm-stack/aggregator/src/feed.ts:207-219) never validates
the optional subscriptions section at all — it checks only version,
generatedAt, quota.pools being an array, and attention being an
array. But the live source serves feed.subscriptions verbatim as the
usage view's flat-subscription rows, and the /quota renderer
dereferences every row unguarded, so one malformed row in a feed that
passes the gate crashes the whole Usage & billing page.
Evidence (verified this run, main 5a34fa70)
feed.ts:53-54:FleetFeed.subscriptions?: { name: string; plan: string; renewsAt: string | null }[](same shape atmodel.ts:1280).feed.ts:207-219:isFleetFeedcontains nosubscriptionscheck of
any kind — not array-ness, not entry shape.sources/live.ts:808-815: inusage()'s feed branch (taken whenever
feed.saasis non-empty), the section is passed through verbatim:
subscriptions: feed.subscriptions ?? []— no filter or per-entry
guard, unlike the registry / projects / snapshots consumers in the
same file, which validate or filter first.render.ts:1816-1826: the subscriptions strip maps rows with
esc(s.name)/esc(s.plan)/s.renewsAt— a null row throws on
s.name; a non-array stringsubscriptionsthrows on.map.server.ts:486-487:/quotaawaitssource.usage()and calls
renderQuota(...)with no catch, so the throw becomes a 500 for the
entire usage hub page — not just a missing subscriptions strip.
/api/quota.json(server.ts:656-665) does not crash, but serializes
the malformed rows verbatim to API consumers.
Laptop Bun probe (~/git/fleet/probe-2026-10-29-laneA-feedcharges/, real
feed.ts + render.ts from main 5a34fa70, md5-identical, Bun 1.4.2;
usage view assembled exactly as live.ts:811-815 assembles it):
subscriptions |
isFleetFeed |
renderQuota (the real /quota render) |
|---|---|---|
| valid entry (control) | true |
OK — row renders |
[null] |
true |
throws TypeError: null is not an object (evaluating 's.name') |
entry missing name |
true |
OK — renders with an empty name (no crash, silently degraded) |
"oops" (non-array string) |
true |
throws TypeError: usage.subscriptions.map is not a function |
[valid, null] mixed |
true |
throws — the valid row never renders |
Why it matters
subscriptions is the hub's flat/renewal-only strip (Q5): the collector
emits these rows from the subscription records, so a single drifted or
hand-edited feed row takes down /quota entirely instead of dropping
one row, and the JSON twin silently serves the garbage onward. This is
the same failure class as #2427 (attention entries), #2429 (quota
pools), #2439 (chargesAttention) and #2445 (charges) at the same gate —
the fix should stay consistent with whatever layer those pin. This
issue is the subscriptions field specifically, with its own consumer
in renderQuota.
Acceptance criteria
- A feed passing
isFleetFeedcan no longer crash/quota: either the
gate validatessubscriptions(array of entries with string
name/planandrenewsAtstring-or-null), or the live source
filters/defaults malformed entries before the view is built — the
layer pinned consistently with the 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(feed): isFleetFeed never checks chargesAttention — one malformed entry crashes usage() and takes down /quota + the quota JSON twins #2439/aggregator(feed): isFleetFeed never checks charges — one malformed row crashes /quota, a non-array object silently empties the charges strip #2445 rulings. - Malformed entries are dropped individually; well-formed entries in
the same array still render. - Regression tests cover:
[null]entry, non-arraysubscriptions,
[valid, null]mixed — each rendering the valid remainder, never
throwing; a well-formed feed unchanged, and the collector still
accepts its own emitted feed.
Dedup
gh searches (open + closed) for subscriptions isFleetFeed and feed subscriptions validator returned no covering issue. Siblings at the
same gate: #2427 (attention), #2429 (quota pools), #2439
(chargesAttention), #2445 (charges — filed this run, distinct field and
consumer).
Activity
developing-today commentedon Oct 29, 2026
CODE-1249
dezren39 commentedon Oct 29, 2026
🤖 Agent note · filed
dezren39 commentedon Nov 1, 2026
🤖 Agent note · done
7 remaining items
dezren39 commentedon Nov 1, 2026
🤖 Agent note · done (triage)
quotaDropped(the aggregator(feed): isFleetFeed checks only that quota.pools is an array — burn/choices, pool entries and windows unvalidated, one malformed pool crashes /quota #2429 pattern in the same file);isFleetFeedunchanged because the section is optional and whole-feed rejection would be disproportionate.repo-health:v1:fix:developing-today/code:issue:2446).dezren39 commentedon Nov 1, 2026
🤖 Agent note · picked up
sanitizeSubscriptionsinloadFeed(feed.ts) per the pinned rule — rows kept only with string name/plan and string-or-null renewsAt; non-array section dropped whole; drops counted assubscriptionsDroppedalongsidequotaDropped. PR opened; laptop tests: feed.test.ts 23/23, quota/quota-contract/usage 68/68.Merge pull request #3432 from developing-today/queue/5326-2446-feed-s…