ADR-082: /experiment vs /experiments — accept the URL split, make it visible
Status: Accepted Date: 2026-04-24 Resolves: ADR-081 §10 open-question #2 Related: ADR-054 (three-mode routes), ADR-058 (five-mode platform UI), ADR-081 (UI shell consolidation grammar)
Context
ADR-081 §10 left one open question on the Top-10 trickle's critical
path: the /experiment (singular, Experiment mode-home) vs
/experiments (plural, fleet catalogue) URL collision flagged in
ai-frontend/docs/ui-consolidation-audit-2026-04-24.md (PR #191).
Same name, one letter apart, different scopes, different palettes.
ADR-081 listed two candidates — rename /experiments to
/experiments/catalogue, or accept the split and document it — and
escalated the decision to the first trickle PR whose scope touched
the mode-pill (task #227).
Investigation in the trickle PR (ai-frontend #200) found:
/experiment(singular) is the Experiment mode-home. It is wired intomode-store.svelte.ts(MODE_HOMES.experiment = '/experiment'and themodeFromPathmapping), theModeSwitchercomponent (clicking "Experiment" navigates to/experiment), the keyboard shortcut registry (shortcuts.tsbindse→/experiment), theSideRailplaceholder note, and ten-plus.test.tsfiles that pin the singular path as part of the mode-pill contract./experiments(plural) owns five live sub-routes:[number]/+page.svelte,blocks/+page.svelte,blocks/[id]/+page.svelte,blocks/new/+page.svelte, andmaze-demo/+page.svelte. A rename to/experiments/cataloguewould leave the sub-tree at its current prefix (fine) or force a matching rename on every sub-route (expensive) — and inbound links from/build/+page.svelte,/[buildID]/+page.svelte, and the catalogue's own back-arrows already assume the plural prefix.
A redirect in either direction is strictly more destructive than a
visible cross-link: redirecting /experiments → /experiment kills
the catalogue's own identity (which is richer than the mode-home's
charts-first dashboard) and surprises bookmark holders; redirecting
/experiment → /experiments breaks the mode-pill contract and
the e shortcut. Neither candidate from ADR-081's shortlist wins
on a least-destruction basis.
Decision
Accept the URL split. Make the split visible rather than mute the routes.
Four concrete moves, shipped in ai-frontend PR #200:
/experiment(mode-home) renames its<h1>and document title to "Experiment mode" (was "Experiments")./experiments(fleet catalogue) renames its<h1>and document title to "Experiment catalogue" (was "Experiments").- Mode-home hero gains a trailing inline link labelled
"Browse the full catalogue →" pointing at
/experiments, with adata-testid="catalogue-link"anchor and a unit test that pins the href. - Catalogue hero gains a trailing inline link labelled
"Back to Experiment mode →" pointing at
/experiment, with adata-testid="mode-home-link"anchor and a unit test that pins the href.
The /experiments route is hereby named as a Fleet-scope route
(per ADR-081 §1). It sits alongside /projects, /playbooks,
/docs, /pipelines, /operators, /assets, /build, and
/patches, with the same dim mode-pill treatment and Home-inherited
calm-slate palette that ADR-081 §1 prescribes for Fleet.
This ADR does not modify ADR-081 in place. ADR-081 §1's Fleet
row does not list /experiments explicitly; this ADR fills that
gap by reference rather than by editing the immutable prior record.
ADR-081 §10 QS2 remains on the books as an open question; this ADR
is the answer.
Consequences
Easier:
- Top-10 trickle PRs that touch the mode-pill no longer hit a collision decision they have to make mid-flight.
- Operators who land on either URL get a one-click path to its sibling, so "wrong URL in my head" recovers without a router round-trip.
- The distinction is legible in the browser tab + tab-history (document titles differ) and in navigation history (h1 strings differ), not only in the URL bar.
Harder:
- Two URLs remain one letter apart, which is a naming hazard for any future audit re-run. The cross-links are the mitigation, but they only work when operators are already on the page. External links (slides, runbooks, Slack) still have to pick one URL correctly.
- Any future rename of
/experiments(e.g. to/experiments/catalogueif UX pressure grows) now also touches the cross-link on/experimentand the h1/title strings on both pages. Small blast radius, but non-zero.
Signed up to operate:
- Keep the two cross-links in lockstep. If either page's hero is
refactored (e.g. into a shared
PageHerocomponent), the link survives the refactor — the unit tests inai-frontend/src/lib/homes/ExperimentHome.test.tsandai-frontend/src/routes/experiments/page.test.tspin the contract. - Keep the h1 strings distinct. If a future design pass retitles either page, pick a string that remains visibly different from its sibling — "Experiments" in isolation on either page re-opens the collision the operator was protected from.
Alternatives considered
- Rename
/experimentsto/experiments/catalogue. Candidate from ADR-081 §10 QS2. Rejected: would either leave five sub-routes at the old prefix (noise) or require matching renames on[number],blocks,blocks/[id],blocks/new,maze-demoplus every inbound link in/build,/[buildID], and the catalogue's own back-arrows. Pure disruption for a naming clean-up. - Redirect
/experiments→/experiment(308). Rejected: kills the catalogue's quick-links (blocks, maze-demo) and grouped-by- category table, which the charts-first mode-home does not replace. SvelteKit sub-routes would survive (the redirect would only fire on the exact/experimentspath), but the index itself is load-bearing content, not a duplicate. - Redirect
/experiment→/experiments(308). Rejected: breaks the mode-pill contract inmode-store.svelte.ts, theekeyboard shortcut, theModeSwitchernavigation target, and ten-plus tests. The mode-home is the entry-point the palette- flip depends on; removing it would leave Experiment mode with no room of its own. - Accept the split without cross-links. The literal ADR-081 candidate. Rejected: "document it" in isolation is a docs-only fix for a UX problem. An operator who lands on the wrong URL has no recovery path except retyping.
Supersedes: none. Superseded by: leave blank until a later ADR reverses this one.
Reference PRs: ai-frontend #200 (cross-link implementation + unit tests), task #227.