ADR-0046: Tracker links on runs¶
Status: Accepted Date: 2026-10-18 Deciders: Alex Nodeland
Builds phase 3 of lattice RFC-0004, portalr, later: a run shows the tracker items it was filed as, and the item a tracker: event was about links to it. relayr's side is relayr ADR-0039.
Context¶
The RFC speaks of "a run's page" and "a tracker event's page". portalr has neither: the app's runs and admin's are rows in a list (ADR-0018, ADR-0032), and no screen shows an event of the log by itself. relayr keeps what a run filed by the run's chain, not by its id; the app's reflexr stream carries every event, tracker: ones among them, and admin's asks for run events alone, so the bodies of the application's events never reach it (ADR-0032). A tracker: event carries its item's name and link; what it says of the item, a title or a comment, is what a person wrote in the tracker.
Decision¶
- On the run's row, in both UIs, under its rule: Filed as, the items its chain filed or commented on, from relayr's read of what the workspace's runs filed, matched by the chain the run's
run_startedenvelope names; and, in the app, From, the items whosetracker:events its firing matched. A run's row is where a run is shown; a page of its own would hold nothing else yet. admin has no From: its stream asks for run events alone, and stays so. - The store keeps each run's chain, and, for each
tracker:event, its item's tracker, name and link, byseq, and for each firing the items it matched, each once: never what the event said. Only an event whose actor is relayr's followers' source,tracker, names an item: the namespace is reserved to it only where the application passes reflexrpublishers=, and a link anyone else published could lead anywhere under a tracker's name. An event that names no item, or whose fields aren't strings, is ignored as other events are. - One read per workspace,
queries.relayr.filed, made stale when an attempt ends: a run succeeding, retrying or dead-lettered, after which it may have filed something. With no relayr surface it answers none, and nothing is asked. - Where relayr is is
config.json'ssurfaces.relayr: an origin, as grantr's is, since relayr's paths carry/relayr/v1;nullby default, for an application without a tracker. - The kit's
TrackerItemsdraws them: a label, then each item as a link that opens in a new tab withrel="noopener noreferrer", named aloud as "OPS-12, in Plane, opens in a new tab", with an icon beside the name; an item a person deleted is its name struck through and deleted in words; an address that isn't http or https is a name and never a link.safeHref, which Tools wrote for its links (ADR-0035), moves to the kit for it. Opening a link is not recorded: unlike Tools' links, it leads past no gate of portalr's, and the tracker checks its own access. - The switch doesn't hide them.
tracker:planeortracker:vikunjaoff in a workspace stops relayr filing and reading there; what was filed stays filed, and its link still opens. - Scenarios run against a fake tracker in the test application: a triage run that is dead-lettered is filed, keyed by the run, by
portalr:file-dead-letter;POST /tracker/edits, which only the test application has, renames an item as a person would, andportalr:follow-upruns on the change.trackers.featurehas a scenario for each link, the RFC's exit criterion for the phase.
Options considered¶
| Choice | Option | For | Against |
|---|---|---|---|
| Where | The run's row (chosen) | Where runs are; no new route | A row carries more |
| A page per run, and per event | The RFC's words | Pages with one line each; an event page would show what a person wrote in the tracker | |
| Matching a run | By its chain (chosen) | What relayr records; a later run of the same rule that comments on the item shows it too | Every run of a chain shows the item, the failed run and the one that filed it alike |
| By run id | Exact | relayr keeps no run id on the key, and a chain is the item's story (relayr ADR-0021) | |
| admin's From | None (chosen) | Its stream stays to run events | A run in admin doesn't say a tracker set it off |
Ask for tracker: events too |
The link | What people wrote in the tracker would reach admin's browser |
Consequences¶
- A run filed in a tracker links to the item from both UIs, and a run a tracker's change set off links to it from the app, with no request per run.
config.jsongrowssurfaces.relayr, and the client a typed relayr client, from relayr's committed document.- An application must mount relayr's reads and name them in
Surfaces(relayr=...)for the links to show; stackr's template doesn't yet.