Repository navigation
Description
Problem
The fleet feed's Project-pool gate never checks labels (or several other fields its consumers treat as typed), so one malformed pool entry passes validation, is converted verbatim into a GhItem, and crashes the board/item renders that call .some/.toLowerCase on its labels.
ProjectPoolItem(pm-stack/aggregator/src/model.ts:55-82) declareslabels?: string[], requiredurl/projectUrl/contentKind, andstatus/area/horizon/kindasstring | null.isProjectPoolItem(pm-stack/aggregator/src/projects.ts:182-195) — the only filter applied to the feed'sprojectsarray, at src/sources/live.ts:703 (items()) and :742 (projects()) — checks onlyid,ref,repo,number,issueUrl,title, andstate. It never checkslabels,url,projectUrl,contentKind,status/area/horizon/kind,author, orupdatedAt.poolItemToGhItem(projects.ts:197-215) passes the unchecked values through verbatim:labels: p.labels ?? [](:205) only replaces null/undefined, so a string, number, or non-string-element array survives as aGhItem.labelsthat is not astring[].- Consumers dereference labels as a string array:
executionStripdoesitem.labels.some((l) => IMPLEMENTING_LABELS.has(l.toLowerCase()))(src/derive.ts:48) and derive.ts:776 doesi.labels.some((l) => /blocked/i.test(l));executionStripis called on the board and item-render paths (src/render.ts:1334stripHtml, :1568renderItem, which also doesi.labels.join(", ")at :1573).
Evidence (verified this run on main 5a34fa70)
Laptop Bun probe at ~/git/fleet/probe-2026-10-29-laneB-poolgate/ importing the real src/projects.ts, src/program.ts, and src/derive.ts (projects.ts md5 55c76253fa2fced0858e115f97cc8155, identical to main):
- Control: a well-formed pool item passes
isProjectPoolItemandexecutionStripreturns it as queued (1). labels: "blocked"(string) →isProjectPoolItemreturns true;poolItemToGhItemkeeps the string; realexecutionStripthrowsTypeError: item.labels.some is not a function.labels: [42]→ gate returns true; realexecutionStripthrowsTypeError: l.toLowerCase is not a function.- An entry with
url,projectUrl, andcontentKinddeleted entirely → gate still returns true;poolItemToGhItem(...).kindisundefined(broken badge/link rendering, silently). updatedAt: 12345(number) → gate returns true and the resultingGhItem.updatedAtis a number, despite the string contract every date consumer assumes.
Why it matters
The pool is carried in the fleet feed — a file written by a separate collector on another host — and isProjectPoolItem is the single line of defence between that file and the dashboard's item resolution (items() feeds the board, roadmap, program, and item pages). The collector's own parser (projects.ts:130) carefully filters labels to strings, which shows the intended contract; the feed-side gate is the one that must enforce it when the feed is older, hand-edited, partially written, or produced by a different version. Today one bad entry does not degrade that entry — it throws inside shared render helpers and takes down every view that resolves that ref.
Acceptance criteria
-
isProjectPoolItemrejects an entry whoselabelsis present but not an array of strings (string/number/object labels, or an array containing non-string elements), and rejects entries missing the required string fields it currently skips (url,projectUrl,contentKindchecked against itsGhKindvalues). -
status/area/horizon/kindare checked as string-or-null andupdatedAt/author, when present, as strings — or the triage explicitly pins a narrower set with the consumer that is safe for each unchecked field. - A rejected entry is dropped by the live source's existing filter (other pool entries still resolve and render); it never reaches
poolItemToGhItem/executionStrip. - Regression tests (aggregator projects/live coverage) pin:
labels:"blocked",labels:[42], and missing-urlentries fail the gate; a well-formed entry (including one withlabelsabsent) still passes unchanged.
Dedup
- Searched open+closed issues for
isProjectPoolItemandProjectPoolItem: no hits. aggregator(orchestration): section gate never checks tier/goal array elements — a [null] tier item or goal task passes validation and crashes buildAttention/todayFromSection #2417 (OPEN) is the orchestration section gate's tier/goal elements — different file, different section; the derived-state helper in projects.ts only reads a pool item'sref/statusthere. - aggregator(registry): non-string lastSeenAt passes isSessionRecord and crashes mergeRecords sort — /sessions and /api/registry.json fail once the registry grows #2376/aggregator(registry): non-array taskRefs passes isSessionRecord and crashes mergeRecords' spread — a string one silently splits into single-character refs #2408 (registry record gate) and aggregator(comments+actions): a store record that passes the load filter but lacks a string createdAt crashes every comments/actions read — and persist() re-writes it forever #2347 (comments/actions load filter) are the same pattern in different modules with different fields and consumers.
Activity
developing-today commentedon Oct 29, 2026
CODE-1236
dezren39 commentedon Oct 29, 2026
🤖 Agent note · filed
21 remaining items
dezren39 commentedon Nov 2, 2026
agent-note:v1 — picked up
Agent: repo-worker-code (OpenChamber scheduled lane)
Task: repo-health-code-pilot task 5327 (small_fix)
At: 2026-11-02T05:43:37Z
State: Reclaimed from the awaiting_ci checkpoint with origin=openchamber-scheduled-lane. Fresh metadata confirms #2418 OPEN and task-bound PR #4181 OPEN at 6995c97. The required test is QUEUED; Codex review is COMMENTED. No PR/worktree changes.
Next: park 20 minutes; resume by checking current CI/review state fresh.
dezren39 commentedon Nov 2, 2026
agent-note:v1 — parked
Agent: repo-worker-code (OpenChamber scheduled lane)
Task: repo-health-code-pilot task 5327 (small_fix)
At: 2026-11-02T05:44:02Z
Reason: Required test on PR #4181 remains QUEUED. Parked 20 minutes; per protocol, ending this launch rather than waiting on CI. No PR/worktree changes.
Resume: re-read PR/check/review state fresh; merge only when required checks and targeted verification are complete. Labels: queue/parked, status/parked, work/assigned.
dezren39 commentedon Nov 2, 2026
🤖 Agent note · picked up
fix(aggregator): normalize nullable GraphQL databaseId in parseProjec…
Merge pull request #4181 from developing-today/queue/5327-2418-projec…
dezren39 commentedon Nov 2, 2026
🤖 Agent note · done