Skip to content

A ref that cannot link is plain text — one ref contract for the aggregator (design) #6242

Closed
6 of 6 issues completed

Description

@dezren39

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 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.

  1. One validity rule — isValidRef only, in both apps; no local regex re-derives it. parseRef keeps throwing for internally constructed refs.
  2. 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.
  3. Producers mint only valid refs — every scrape/derivation routes candidates through isValidRef; an unfiltered ref scrape is a defect per se.
  4. Rendering and deriving never throw on a ref — stored/feed-derived refs reach refUrl only 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.
  5. 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)

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

  1. added
    epicMeta-issue grouping multiple items (org Project Type=Epic)
    app/aggregatorPackage/app: aggregator (pm-stack in this repo)
    on Nov 5, 2026
  2. developing-today commented on Nov 5, 2026

    @developing-today
    Contributor
  3. added
    kind/designA design proposal (design-architect): multi-step, phased, decision-bearing
    on Nov 5, 2026
  4. dezren39 commented on Nov 5, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · design filed

  5. added
    priority/normalOrdinary priority (the default)
    effort/lLarge: multi-day or open-ended; expect staged check-ins
    on Nov 5, 2026
  6. 38 remaining items

  7. developing-today commented on Nov 8, 2026

    @developing-today
    Contributor

    🤖 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.ts beside 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.

  8. dezren39 commented on Nov 8, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  9. dezren39 commented on Nov 8, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · parked

  10. dezren39 commented on Nov 8, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · parked

  11. developing-today commented on Nov 8, 2026

    @developing-today
    Contributor

    🤖 Agent note · parked

  12. dezren39 commented on Nov 8, 2026

    @dezren39
    MemberAuthor
  13. dezren39 commented on Nov 8, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · parked

  14. developing-today commented on Nov 8, 2026

    @developing-today
    Contributor

    🤖 Agent note · done — all four phases closed, design contract landed

  15. added and removed
    status/parkedDeliberately paused with a recorded resume condition
    queue/parkedQueue task is parked (waiting on CI, time, or an event)
    work/assignedQueue worker lifecycle state
    status/triagedTriaged and routed (see agent note)
    on Nov 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    app/aggregatorPackage/app: aggregator (pm-stack in this repo)effort/lLarge: multi-day or open-ended; expect staged check-insepicMeta-issue grouping multiple items (org Project Type=Epic)kind/designA design proposal (design-architect): multi-step, phased, decision-bearingmeta/enrichedIntake review done: acceptance criteria, links, and normalized labels addedpriority/normalOrdinary priority (the default)source/design-architectFiled by the repo design architect jobstatus:donetask/triageQueue packet kind: verify, reproduce, and route this item

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions