Skip to content

aggregator(feed): isFleetFeed never checks attention entries — one null entry crashes /attention + /today, entries without a string sessionId silently collapse into one #2427

Closed

Description

@dezren39

Bug

pm-stack/aggregator — the fleet feed's top-level gate isFleetFeed checks only that attention is an array, never its entries, and the live source is the one feed consumer that does not element-filter its array before use. One malformed entry therefore takes down the attention and today surfaces instead of being dropped like every sibling section's bad entries are.

Evidence (verified this run, main 5a34fa70)

  • Gate: src/feed.ts:207-219 — isFleetFeed requires version === 1, a parseable generatedAt, a quota object with an array pools, and Array.isArray(f.attention). Nothing checks that attention entries are objects, or that sessionId (the field every consumer keys on, src/model.ts:316) is a string.
  • Unguarded consumer: src/sources/live.ts:864-896 — attention() runs for (const e of feed.attention) byId.set(e.sessionId, e) (line 873) with no filter and no try/catch around the feed loop (the laptop-sessions half below it is fail-soft). Every other array the live source ingests is element-filtered first: registry records via isSessionRecord (live.ts:611, 980), project pool items via isProjectPoolItem (live.ts:703, 742), snapshot records via isSnapshotRecord inside SnapshotStore.append (snapshots.ts). Attention is the exception.
  • No rescue downstream: server.ts attentionContext() (line 237) and todayContext() (line 308) await source.attention() with no .catch, and fetchHandler (line 794) has no top-level catch — so the throw surfaces as a failed request on /attention, /today, /frag/attention/*, /api/attention.json, and /api/today.json.

Laptop probe (~/git/fleet/probe-2026-10-29-laneA-feedattn/, real feed.ts + derive.ts from origin/main — md5-identical to the sandbox clone — Bun 1.4.2, live.ts loop reproduced verbatim):

== control: one well-formed entry
isFleetFeed: true
live byId loop: ok, size = 1 keys = [ "s1" ]
buildAttention: ok, all = 1

== attention [null]
isFleetFeed: true
THROWS: TypeError - null is not an object (evaluating 'e.sessionId')

== two entries, both missing sessionId
isFleetFeed: true
live byId loop: ok, size = 1 keys = [ "undefined" ]
buildAttention: ok, all = 1
  • Secondary, silent: entries whose sessionId is missing or not a string do not crash — they collide on the single undefined map key (probe: two distinct sessions collapse to one; a 42 / "x" entry is keyed undefined too), so sessions silently vanish from the attention view with no error anywhere.

Why it matters

The feed file is produced by a different job (the collector) and already has a house pattern for exactly this failure class: validate at the gate or filter at the consumer, degrade to the remaining good entries, never let one bad record fail the whole facet (see the dns/aa/orchestration/registry section validators). Attention missed that treatment, so a single corrupt or schema-drifted session entry — e.g. a collector change emitting a null placeholder — blacks out the two most-used pages (/today, /attention) instead of dropping one session, and subtler drift (a renamed/dropped sessionId) silently under-reports live sessions.

Acceptance criteria

  • A feed whose attention array contains a null, a non-object, or an entry without a string sessionId no longer throws in attention(): the malformed entries are dropped (at the isFleetFeed gate or filtered in the live source, matching the registry/projects pattern — triage pins the layer) and the well-formed entries still render on /attention and /today.
  • Entries are never silently merged under an undefined/non-string key: either they are rejected by the same validation, or the dedupe key is only ever a validated string sessionId.
  • Regression tests (aggregator test suite) cover: [valid, null] renders just the valid entry; two entries missing sessionId do not collapse into one rendered session; a fully well-formed feed is unchanged.
  • The gate/filter choice is consistent for the collector's own use of isFleetFeed (scripts/collect-fleet-feed.ts imports the same gate) — a stricter gate must not make the collector reject its own emitted feed.

Dedup

gh searches (open + closed) for "isFleetFeed" and "feed attention entries" return only #2242 (health section stamps — different fields/functions) and #1176 (open, a broad contract-test-suite impl request across all publisher→collector→aggregator schemas; it does not name this crash, and every sibling section validator — #2376 registry, #2408 taskRefs, #2415 aa, #2412 dns, #2417 orchestration — was filed individually alongside it). No open or closed issue covers attention-entry validation.

Activity

  1. developing-today commented on Oct 29, 2026

    @developing-today
    Contributor
  2. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    priority/normalOrdinary priority (the default)
    queue/queuedHas a queued task in the Supabase agent queue
    source/creatorFiled by the new-issue-creator job
    task/triageQueue packet kind: verify, reproduce, and route this item
    app/aggregatorPackage/app: aggregator (pm-stack in this repo)
    effort/xsTrivial: expected under 30 minutes of work
    on Oct 29, 2026
  3. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · filed

    • Agent: new-issue-creator (code lane A, new-issue-creator-code-a)
    • Session / ID: cron new-issue-creator-code-a run 2026-10-29T21:05Z (America/Chicago 16:05)
    • Found via: filed this run
    • Task: Triage the fleet-feed attention-entry validation gap: pin whether the fix lands in the isFleetFeed gate (feed.ts:207-219) or as an element filter in the live source's attention() (live.ts:873, matching the registry/projects filter pattern), then route a small_fix packet.
    • Expectation: effort/xs — one validator/filter plus regression tests; done when a feed with a null or sessionId-less attention entry renders the remaining valid sessions on /attention and /today instead of throwing or silently merging entries.
    • Links: Evidence + probe in the issue body; probe on the laptop at ~/git/fleet/probe-2026-10-29-laneA-feedattn/ (real feed.ts/derive.ts from main 5a34fa7, md5-identical).
  4. dezren39 commented on Oct 29, 2026

    @dezren39
    MemberAuthor

    Cross-link: sibling filed this run — #2429 covers the same gate's quota section (burn/choices, pool entries, windows unvalidated; /quota crash). The two triages should pin a consistent gate-vs-filter layer.

  5. added
    work/assignedQueue worker lifecycle state
    and removed
    queue/queuedHas a queued task in the Supabase agent queue
    on Nov 1, 2026
  6. 76 remaining items

  7. added
    status/parkedDeliberately paused with a recorded resume condition
    and removed
    queue/claimedQueue worker lifecycle state
    on Nov 2, 2026
  8. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    agent-note:v1 — parked
    Agent: repo-worker-code (OpenChamber scheduled lane)
    Task: repo-health-code-pilot task 5324
    At: 2026-11-02T10:42:36Z
    State: parked
    Fresh evidence: #2427 is OPEN and assigned to dezren39 (Drew); canonical PR #4184 is OPEN, non-draft, and authored by dezren39 on queue/5324-2427-feed-attention-filter.
    Disposition: parked for 30 minutes at the Drew-owned hard stop. No PR contents, checks, reviews, branch, or saved worktree inspected or touched. Labels reflect parked state; work/assigned remains set.
    Resume condition: PR closes or Drew explicitly releases ownership; revalidate fresh before proceeding.
    Links: #2427 · #4184 · task 5324

  9. added
    queue/claimedQueue worker lifecycle state
    and removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    on Nov 2, 2026
  10. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  11. added a commit that references this issue on Nov 2, 2026
  12. dezren39 commented on Nov 2, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

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

Metadata

Metadata

Assignees

Labels

app/aggregatorPackage/app: aggregator (pm-stack in this repo)app/pm-stackpm-stack (Pad/Grist) in developing-today/codeeffort/xsTrivial: expected under 30 minutes of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/normalOrdinary priority (the default)source/creatorFiled by the new-issue-creator jobtask/small-fixQueue packet kind: bounded fix with reproduction and targeted tests

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions