Repository navigation
Description
Summary
A projector registry link whose issue number is an unsafe integer breaks two aggregator pages at render. The chain has three mismatched gates:
- The projector admits it.
POST /linksvalidation (pm-stack/projector/src/server.ts:246) is a shape regex,/^[\w.-]+\/[\w.-]+#\d+$/. Its\d+admits numbersparseRefrejects — exactly the anti-pattern model.ts:463-466 documents for the shared rule (pm-stack(aggregator): validateNewComment/validateNewAction accept ref keys parseRef rejects (GH_REF number unbounded) #4952).developing-today/code#99999999999999999999(20 digits) passes with a 201. - The aggregator trusts it.
projectorLinks(pm-stack/aggregator/src/sources/live.ts:484-488) mapsrow.refverbatim — noisValidReffilter — andenrichWithRegistryLinks(live.ts:496-507) merges those links into board items (live.ts:1164) and roadmap rows (live.ts:1198). Legacy/seeded rows inpmp_linksbypass the POST gate entirely and take the same path. - Two render sites call
refUrlunguarded on those links.parseRef(model.ts:454-460) throwsref number is not a safe integer, so:/search:renderSearch(render.ts:510) callsrefUrl(ref)for every result ref not in the items map — a registry ref is unresolved by construction, so any search matching that board item or roadmap row 500s.- Project pages:
renderProgramProject's roadmap rows (render.ts:6741) callrefUrl(l.ref)for every link on the row, so one bad registry link on one roadmap row takes down that project's whole page.
Evidence (verified this run, laptop main 04f7869)
Probe ~/git/fleet/probe-search-ref/probe.ts (bun, real aggregator sources from the laptop checkout):
projector POST /links gate regex admits BAD: true
isValidRef(BAD): false | isValidRef(GOOD): true
search(board BAD): THREW: ref number is not a safe integer: developing-today/code#99999999999999999999
search(board GOOD control): OK len=19929 hasGoodLink=true
search(roadmap BAD): THREW: ref number is not a safe integer: developing-today/code#99999999999999999999
project(roadmap BAD): THREW: ref number is not a safe integer: developing-today/code#99999999999999999999
project(roadmap GOOD control): OK len=29872 hasGoodLink=true
BAD is developing-today/code#99999999999999999999 carried as a registry Link — the exact shape enrichWithRegistryLinks produces from a pmp_links row. GOOD is the same cases with #123.
Why it matters
Registry links are the cross-repo linking mechanism the board and roadmap are built around ("the registry is where a code task's link to an opencode PR lives", live.ts:492-495), and they are writable through an authenticated API whose only ref check disagrees with the consumer. One stored link — a typo'd or pasted oversized number is enough — permanently 500s /search for matching queries and the affected project page until someone deletes the row from the database by hand. Siblings already filed cover other producers of the same throw (lined-up task titles, #6210 board strip at render.ts:1506 — the same registry links also reach that strip; objectives #6174; reconciled overview #6205); this is the registry-link producer plus the two render sites none of them cover. The session-record path (render.ts:1912) already shows the house pattern for untrusted refs: try/catch and omit only the bad link.
Acceptance criteria
- A registry link whose ref fails
isValidRefnever reaches a renderer: filter (with a logged/provenance-visible drop) inprojectorLinksorenrichWithRegistryLinks, and/or align the projector'sPOST /linksgate withparseRefsemantics (safe-integer number) so the row cannot be stored in the first place. Both layers preferred — stored legacy rows must not stay lethal. /searchand project-page roadmap rendering survive a board item / roadmap row carrying such a link: the item renders, the bad link is omitted or shown unlinked, sibling good links still render as links.- Regression tests alongside the existing aggregator suites (search-view / program rendering) pinning: unsafe-integer registry ref on a board item, on a roadmap row, and a non-GitHub-shaped stored ref if the filter is the chosen layer.
- Existing behavior unchanged for valid refs (controls above).
Related: #6210 (same links reach the board strip via titleRefs; registry links are a second producer), #6174, #6205, #3766 (project-config validator, same gate-disagreement shape, different gate), #2485 (session taskRefs, fixed with the try/catch pattern at render.ts:1912).
Activity
developing-today commentedon Nov 5, 2026
CODE-1812
dezren39 commentedon Nov 5, 2026
🤖 Agent note · filed
18 remaining items
developing-today commentedon Nov 5, 2026
🤖 Agent note · state: picked up
dezren39 commentedon Nov 5, 2026
🤖 Agent note · adopted as a design phase
files/designs/code-aggregator-ref-contract.md(keep-the-four-repos-healthy goal workspace).Merge pull request #6244 from developing-today/queue/10561-6211-unsaf…
developing-today commentedon Nov 5, 2026
🤖 Agent note · state: done
projectorLinksin the aggregator now drops stored refs failingisValidRef(with a[registry] WARNINGlog naming the ref and subject), and the projector'sPOST /linksgate now requires a safe-integer ref number viaisStorableLinkRef— theparseRefrule (pm-stack(aggregator): validateNewComment/validateNewAction accept ref keys parseRef rejects (GH_REF number unbounded) #4952) at both boundaries.