Repository navigation
Description
Bug: a registry record whose taskRefs is a well-formed array with malformed elements passes every gate, merges, persists — then crashes /sessions at render
#2408 covered a non-array taskRefs crashing mergeRecords' spread. The element-level remainder is still open: when taskRefs IS an array, nobody ever checks what is inside it, and the one consumer that interprets the elements throws on anything that is not exactly owner/repo#<digits>.
The unchecked path:
pm-stack/aggregator/scripts/record-claude-launch.ts:44-46—--taskvalues are copied from argv verbatim into the launch record'staskRefs. The plausible short form--task code#2480(or a bare--task 2480) writes a malformed ref; nothing validates it.pm-stack/aggregator/src/registry.ts:93-110—parseLaunchRecordschecks only thatidis a string;recordFromLaunch(registry.ts:88) copiesl.taskRefs ?? []verbatim, elements included — a string like"code#2480"or a non-string element like42passes through unchanged.pm-stack/aggregator/src/registry.ts:138-142—isSessionRecord, the hygiene check at both load sites (fleet-feed registry atsrc/sources/live.ts:982, durable store vialoadRegistryStore/mergeIntoRegistryatlive.ts:610-621), checks onlyid/source/labelstrings. An array of malformed elements passes.- The merge is fine with them (
registry.ts:130spreads the array), so the poisoned record is written into the durable registry store and survives restarts. pm-stack/aggregator/src/render.ts:1637-1638—sessionRecordLinemaps every taskRef throughrefUrl, andparseRef(src/model.ts:399-405) throwsnot a repo-qualified ref: <value>on any element that is notowner/repo#<digits>.renderSessionsmaps that over every record, and/sessionscalls it uncaught (src/server.ts:576-579). One malformed element therefore 500s the whole sessions page — for every record, not just the poisoned one — until the store file is hand-repaired.
Adjacent, same function: parseRef converts the digits with Number(...) unchecked, so a syntactically valid ref with an oversized number (developing-today/code#99999999999999999999999, producible by refsInText from a URL in a session title) parses to 1e+23 and refUrl emits https://github.com/developing-today/code/issues/1e+23 — a silently wrong link instead of a rejection.
Verified this run (main 562663fa)
Sandbox Bun probe importing the real registry.ts, render.ts and model.ts (probe: goal hidden_files/probe-2026-10-29-laneB-taskrefelems/probe.ts):
parseLaunchRecordson{"id":"claude-x1","taskRefs":["code#2480"]}and{"id":"claude-x2","taskRefs":[42]}→ both records returned with elements verbatim;isSessionRecordreturnstruefor both.mergeRecords(records, records)→ succeeds; merged taskRefs[["code#2480"],[42],["developing-today/code#2480"]].refUrl("code#2480")→ throwsnot a repo-qualified ref: code#2480;refUrl(42)→ throws the same; controlrefUrl("developing-today/code#2480")→ the correct URL.renderSessions([goodRecord])→ renders OK (18,608 chars);renderSessions(merged)→ throwsnot a repo-qualified ref: code#2480.parseRef("developing-today/code#99999999999999999999999")→{number: 1e+23};refUrlof it →.../issues/1e+23.
Why it matters
Launch records are appended by hand / by launch scripts (--task is free-form argv), feed registry entries arrive from a collector on another host, and the durable store is a hand-editable file — all three producers are exactly the untrusted-boundary inputs this registry's hygiene check exists for. The failure is maximally disproportionate: one typo'd --task code#123 at some past launch permanently takes down /sessions (the fleet's session-identity page), the poison lives in the durable store so restarts don't help, and the thrown error names the value but not the record carrying it. The huge-number sub-case never crashes at all — it just links a session to a wrong, scientific-notation URL.
Acceptance criteria
- A registry record whose
taskRefsarray contains a malformed element (non-string, or a string that is notowner/repo#<digits>with a safe-integer number) can no longer crashrenderSessions//sessions: the owning layer is pinned by triage (gate rejects/filters elements inisSessionRecord+recordFromLaunch/parseLaunchRecords, vs the renderer skipping unparseable refs) and applied consistently at the feed, store, and launch-record entry points. - A poisoned record already in the durable store does not wedge the page:
/sessionskeeps serving the remaining records (and ideally the poisoned record renders without its bad refs rather than vanishing). record-claude-launch.tseither validates--taskvalues against the ref shape and refuses/warns at write time, or the issue records why write-time validation is the wrong layer.- Regression tests cover at least: launch record with
taskRefs: ["code#2480"], launch record withtaskRefs: [42], a feed/store record with a malformed-element array rendering/sessionswithout throwing, and an oversized-number ref not producing a scientific-notation URL. - No behaviour change for well-formed records: existing registry/render tests keep passing and good refs render identical links.
Dedup
- aggregator(registry): non-array taskRefs passes isSessionRecord and crashes mergeRecords' spread — a string one silently splits into single-character refs #2408 (open) — non-array
taskRefs(number/null/string whole value) crashing the merge spread / splitting into character refs. Its acceptance pins an array-shape check; an array of malformed elements passes that fix and crashes later, at render. Different value shape, different crash site (render.ts:1638viamodel.ts:399, notregistry.ts:130). - aggregator(registry): non-string lastSeenAt passes isSessionRecord and crashes mergeRecords sort — /sessions and /api/registry.json fail once the registry grows #2376 (open) — non-string
lastSeenAtcrashing the merge sort; different field and site. - Searches for
parseRef/taskRefs refUrlover open+closed issues returned no filing covering element-level validation or theNumber()precision loss.
— filed by new-issue-creator-code-b (Lane B), 2026-10-29 run
Activity
developing-today commentedon Oct 30, 2026
CODE-1269
dezren39 commentedon Oct 30, 2026
🤖 Agent note · filed
hidden_files/probe-2026-10-29-laneB-taskrefelems/probe.tsin the keep-the-four-repos-healthy goal workspace (real registry/render/model modules, main 562663f)63 remaining items
dezren39 commentedon Nov 2, 2026
🤖 Agent note · parked
dezren39 commentedon Nov 2, 2026
🤖 Agent note · picked up (ownership correction + CI disposition)
queue/5347-2485-validate-taskrefs, worktree~/git/fleet/5347. Noheld/user/owned/do-not-merge/do-not-auto-editlabel on aggregator(registry): malformed taskRefs elements pass every gate and crash /sessions at render — an array of bad refs merges, persists, then refUrl throws #2485 or PR fix(aggregator): validate registry task refs (#2485) #4185 (verified fresh).testcheck fails on exactly 2 of 972 tests — the same pre-existing main failures tracked as aggregator: slice3 registry test fails on clean main — no claude-code record via fixture path #3835 (slice-3 registry) and aggregator: 'hub renders > service detail' test fails in CI on main #4091 (hub renders pace projections). Unrelated to this change.fix(aggregator): validate registry task refs (#2485) (#4185)
dezren39 commentedon Nov 2, 2026
🤖 Agent note · done