Repository navigation
Description
Design doc: files/designs/code-aggregator-ref-contract.md in the keep-the-four-repos-healthy goal workspace. Filed by the design architect (pass 55, 2026-11-05). Audit HEAD: origin/main 955ed23. No user/review — the validity rule is already landed (#4952) and the layer rulings below are the architect's recorded decisions.
The defect
The aggregator's pages are stitched together with GitHub refs (owner/repo#N). A ref is typed RefKey — a promise that parseRef accepts it — but producers and gates make that promise without checking it, and renderers trust it: parseRef throws, and one bad ref takes down a whole page (/today #6174, /search + project pages #6211, pool project pages + briefs #6224), a whole board (#6210), or silently deletes the board's entire in-flight lane (#6195 — the throw is swallowed by an unlogged catch). Six filings in two days, one per producer/consumer pair, each leaving the same question open — which layer owns it? — and answering it differently: drop at a named boundary (#6174), "a design call for one deliberate pass" (#6195), both layers (#6211), gate-drops-the-item preferred (#6224). This issue is that deliberate pass: it records the disposition contract once and adopts the five open origins as its phases. Per-site fixes that choose different layers will diverge; this design is the layer ruling they share.
Evidence
- The validity rule exists.
pm-stack/aggregator/src/model.ts:454-467—parseRefthrows on non-owner/repo#Nshapes and unsafe-integer numbers; the comment states the pm-stack(aggregator): validateNewComment/validateNewAction accept ref keys parseRef rejects (GH_REF number unbounded) #4952 rule (boundary validators route throughisValidRef, never a local regex).refsInTextcomplies (:484; pm-stack(aggregator): refsInText emits ref keys parseRef rejects — builder-made session records fail isSessionRecord and drop whole #4890 closed today was its producer instance). Render embodiments exist: PR fix(aggregator): Needs-you render no longer 500s on a tier item with a non-GitHub ref #6149'sisValidRefguard → plain text (render.ts:2489-2524),sessionRecordLine's try/catch (render.ts:1912). The overview instance pm-stack(aggregator): overview front door throws on a lined-up task with a non-GitHub ref — reconciledInProgress reaches refUrl unguarded #6205 closed in-window today. - Gates shape-check or ignore ref fields.
isLinedUpElement(feed.ts:223) never checksref;assembleWorkItemscopies it verbatim (work-items.ts:191).isProjectPoolItem(projects.ts:280-283) checksparentRef/epicRefas any string,blockedBy/blocksas any string array. - Producers mint/carry unvalidated refs.
titleRefs(sources/live.ts:538) adds its explicit-ref and alias-shorthand candidates unguarded (pm-stack(aggregator): board render throws on a lined-up task whose title carries an unsafe-integer ref in a non-first link — titleRefs mints refs parseRef rejects, inFlightBoardItems validates only the first #6210).projectorLinks(sources/live.ts:484) maps registry rows verbatim (pm-stack(aggregator): a projector registry link with an unsafe-integer ref 500s /search and the project page — projector gate admits it, projectorLinks never validates, renderSearch + roadmap rows call refUrl unguarded #6211), and the projector'sPOST /linksgate is a local regex whose\d+admits unsafe numbers (pm-stack/projector/src/server.ts:246) — the anti-pattern the pm-stack(aggregator): validateNewComment/validateNewAction accept ref keys parseRef rejects (GH_REF number unbounded) #4952 comment names, in the sibling app; legacypmp_linksrows bypass even that. - Consumers throw. 25
refUrl(sites inpm-stack/aggregator/src; the filed unguarded ones on stored/feed-derived refs are atrender.ts:3368(pm-stack(aggregator): /today render 500s on a lined-up task with a non-GitHub ref — objectives call refUrl unguarded and the section gate never checks linedUp ref #6174),render.ts:1506+ assemblyparseRefatsources/live.ts:634(pm-stack(aggregator): one lined-up task with a non-GitHub ref silently deletes the board's whole in-flight lane — inFlightBoardItems parseRef throws into an unlogged catch #6195/pm-stack(aggregator): board render throws on a lined-up task whose title carries an unsafe-integer ref in a non-first link — titleRefs mints refs parseRef rejects, inFlightBoardItems validates only the first #6210),render.ts:510+:6741(pm-stack(aggregator): a projector registry link with an unsafe-integer ref 500s /search and the project page — projector gate admits it, projectorLinks never validates, renderSearch + roadmap rows call refUrl unguarded #6211),brief.ts:96+render.ts:6479/:6590/:6645/:6648(pm-stack(aggregator): a pool item with an invalid blockedBy/blocks edge 500s the project page and throws the brief — isProjectPoolItem validates edges by shape only, brief + pool-graph + epic-graph call refUrl unguarded #6224). The board lane's catch (sources/live.ts:1177-1184) swallows the throw with no log line.
The contract (recorded decisions)
A ref is an annotation, not identity; validity is isValidRef; an invalid ref is dropped where it is consumed and rendered as plain text where it is displayed — it never throws, and it never takes its item with it.
- One validity rule —
isValidRefonly, in both apps; no local regex re-derives it.parseRefkeeps throwing for internally constructed refs. - Drop the ref, keep the item (stored/feed data) — at the consumption step beside each gate (
normalizeOrchestrationSection,poolFromFeed,projectorLinks), not inside the pure predicates: an invalid ref field/edge is removed, the element survives, the drop is counted/logged. This pins against pm-stack(aggregator): a pool item with an invalid blockedBy/blocks edge 500s the project page and throws the brief — isProjectPoolItem validates edges by shape only, brief + pool-graph + epic-graph call refUrl unguarded #6224's drop-the-whole-item gate option (an edge is an element of the item; design(aggregator): ingest, fail-closed — one validation contract for every document the PM stack consumes #2576's drop-element-and-count applies at that granularity — item loss over an annotation is the pm-stack(aggregator): one lined-up task with a non-GitHub ref silently deletes the board's whole in-flight lane — inFlightBoardItems parseRef throws into an unlogged catch #6195 harm). Direct user POSTs whose ref is the subject keep pm-stack(aggregator): validateNewComment/validateNewAction accept ref keys parseRef rejects (GH_REF number unbounded) #4952's landed reject behaviour. - Producers mint only valid refs — every scrape/derivation routes candidates through
isValidRef; an unfiltered ref scrape is a defect per se. - Rendering and deriving never throw on a ref — stored/feed-derived refs reach
refUrlonly through one safe-link helper (valid → anchor, invalid → plain text, the fix(aggregator): Needs-you render no longer 500s on a tier item with a non-GitHub ref #6149 treatment); derive paths treat an invalid edge as absent. Defence in depth behind rule 2, not a substitute. - Drops are observable — every rule-2/3 drop leaves a log line or provenance/count note; assembly catches that can hide a lane log what they swallowed.
Phases (each independently landable; origins adopted, no parallels)
- Phase 1 — the lined-up seam: pm-stack(aggregator): /today render 500s on a lined-up task with a non-GitHub ref — objectives call refUrl unguarded and the section gate never checks linedUp ref #6174 + pm-stack(aggregator): one lined-up task with a non-GitHub ref silently deletes the board's whole in-flight lane — inFlightBoardItems parseRef throws into an unlogged catch #6195 (consumption-step drop + objectives/board consumption via the safe treatment + lane catch logging). pm-stack(aggregator): /today render 500s on a lined-up task with a non-GitHub ref — objectives call refUrl unguarded and the section gate never checks linedUp ref #6174's "chosen layer" question is answered by the contract: consumption step + render helper, not the predicate, not render alone.
- Phase 2 — minting and the registry: pm-stack(aggregator): board render throws on a lined-up task whose title carries an unsafe-integer ref in a non-first link — titleRefs mints refs parseRef rejects, inFlightBoardItems validates only the first #6210 + pm-stack(aggregator): a projector registry link with an unsafe-integer ref 500s /search and the project page — projector gate admits it, projectorLinks never validates, renderSearch + roadmap rows call refUrl unguarded #6211 (
titleRefsfiltering;projectorLinksconsumption filtering incl. legacy rows; projectorPOST /linksgate aligned; /search + roadmap rendering via the helper). - Phase 3 — pool edges: pm-stack(aggregator): a pool item with an invalid blockedBy/blocks edge 500s the project page and throws the brief — isProjectPoolItem validates edges by shape only, brief + pool-graph + epic-graph call refUrl unguarded #6224 (edges drop / single-ref fields clear at
poolFromFeed, item retained per rule 2; brief + graph consumers per rule 4). Its dispatch implementation copy Fix #6224: pm-stack(aggregator): a pool item with an invalid blockedBy/blocks edge #6226 is this phase's vehicle if its holder takes the pin; otherwise triage reconciles the two so the fix lands once. - Phase 4 — closing inventory (filed with this design, gated on Phases 1–3): every
refUrl/parseRefconsumer and ref-carrying gate/scrape/store path in aggregator + projector dispositioned; contract statement inmodel.tsbeside the pm-stack(aggregator): validateNewComment/validateNewAction accept ref keys parseRef rejects (GH_REF number unbounded) #4952 comment; named dispositions forqueue-view.ts,sources/live.ts:368,comments.ts:494,render.ts:3574+:6756, thesessionRecordLinealignment, and a stored-pmp_linksrecount.
Open questions stay in the design doc (a typed non-GitHub ref kind for goal ids; drop-count surfacing — shared with #2576's dashboard question; projector bulk-import paths).
Acceptance
- All four phases closed or dispositioned with evidence.
- No stored/feed-derived ref can throw a render or a derivation anywhere in the aggregator (Phase 4 inventory is the proof).
- Every invalid-ref drop in the filed surfaces leaves the trace rule 5 requires.
Acceptance criteria
- One ref contract is documented for the aggregator: when a ref can link, when it must render as plain text, and why.
- Every surface that renders refs (board, in-flight, detail views) follows the same contract, with tests for a linkable ref and an unlinkable ref.
- No ref renders as a dead/broken link anywhere the contract applies.
Activity
developing-today commentedon Nov 5, 2026
CODE-1825
dezren39 commentedon Nov 5, 2026
🤖 Agent note · design filed
isValidRef); drop the ref at the consumption step, keep the item (pinning against pm-stack(aggregator): a pool item with an invalid blockedBy/blocks edge 500s the project page and throws the brief — isProjectPoolItem validates edges by shape only, brief + pool-graph + epic-graph call refUrl unguarded #6224's drop-the-item option); producers mint only valid refs; rendering/deriving via one safe-link helper, never a throw; drops observable.files/designs/code-aggregator-ref-contract.md(keep-the-four-repos-healthy goal workspace); register entry added infiles/potential-projects.md.38 remaining items
developing-today commentedon Nov 8, 2026
🤖 Agent note · Phase 4 closing state (from task 13017, lane code-g)
Phase 4 of this design (issue #6243) is complete except for merge: the closing inventory is posted on #6243 (every refUrl/parseRef consumer and ref-carrying gate/scrape/store path in pm-stack/aggregator + pm-stack/projector dispositioned, none unclassified; all seven required dispositions recorded; pmp_links recount 0 rows / 0 invalid), the contract statement is landed in
pm-stack/aggregator/src/model.tsbeside the #4952 comment, and disposition 5 (sessionRecordLine) is aligned to the rule-4 plain-text treatment — all in PR #7642 with pinning tests (18 pass). No follow-up issues were owed by the inventory. When PR #7642 merges, all four phases of the ref contract are closed out.dezren39 commentedon Nov 8, 2026
🤖 Agent note · picked up
dezren39 commentedon Nov 8, 2026
🤖 Agent note · parked
dezren39 commentedon Nov 8, 2026
🤖 Agent note · parked
developing-today commentedon Nov 8, 2026
🤖 Agent note · parked
direct(row 4635), so this lane resumed it from the priorverifiedcheckpointdezren39 commentedon Nov 8, 2026
🤖 Agent note · parked
dezren39 commentedon Nov 8, 2026
🤖 Agent note · parked
developing-today commentedon Nov 8, 2026
🤖 Agent note · done — all four phases closed, design contract landed