LearnNewsExamplesServices
Frontmatter
id18606
titleADR 0029 amendment: the perspective-selection contract, before its leaves
stateClosed
labels
documentationenhancementaiarchitecturedashboard
assigneesneo-fable
createdAtSep 12, 2026, 4:01 PM
updatedAtSep 12, 2026, 5:32 PM
githubUrlhttps://github.com/neomjs/neo/issues/18606
authorneo-fable
commentsCount0
parentIssue18605
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[x] 18609 Perspective names come from the declared list; snapshots never define one, [x] 18608 The Workspace publishes dock.perspective.active, modified and pending, [x] 18607 The dock Workspace selects with activePerspective_, carried by the write
closedAtSep 12, 2026, 5:32 PM

ADR 0029 amendment: the perspective-selection contract, before its leaves

Closed v13.2.0/archive-v13-2-0-chunk-20 documentationenhancementaiarchitecturedashboard
neo-fable
neo-fable commented on Sep 12, 2026, 4:01 PM

Leaf of #18605 (parent linked natively) — the first leaf by the record's own rule. Decision Record: Required: ADR 0029 amend · Decision Record impact: amends ADR 0029.

Context

D#18594 graduated with Vega's STEP_BACK verdict (DC_kwDODSospM4BGPHO, acknowledged per row in the author's comment 6): ADR 0029 is amended, not challenged, and the amendment carries four obligations, (a)–(d) below. learn/agentos/decisions/0029-docking-design.md:765 — "a leaf that contradicts a section here amends this ADR first, in its own reviewed change (ADR-0005 lifecycle)" — is why this leaf merges before any other leaf of the epic opens as ready.

The Problem

Three sentences in the record will be retrieved by future sessions as authority, and each contradicts the graduated shape:

  • :68–69 (§2.1, stage one): "This tier is initial-only: reactive declaration reconciliation, perspective/storage configuration and multi-workspace authoring are separate contracts." — the epic puts a reactive selection on the host. A note beside the sentence cannot re-mean it; a retrieval returns the sentence.
  • :117 (§2.1): "Every future docking leaf classifies each new piece of state into exactly one row of this table before implementation." — the epic introduces three pieces of state (the accepted intent, the committed name, the pending request) and none has a row.
  • :211–212 (§2.2): "layoutId is the topology identity; a collection's activeLayoutId must name one entry in its topologies record." — consumers read this as selection (the Workstation controller reads activeLayoutId by hand at apps/workstation/view/WorkspaceController.mjs:53), while under auto-save (persistence/TopologyLibrary.mjs:114–116 → :155, replace: true) the pointer is rewritten on every commit and means nothing about what the user chose.

The §5 decomposition table (:721) lists the leaves the record gates; the epic has no row.

The Architectural Reality

Neo.tab.Container#activeIndex_ (src/tab/Container.mjs:56–59), layout.Card#activeIndex_ (src/layout/Card.mjs:49–51) and toolbar.Breadcrumb#activeKey_ (src/toolbar/Breadcrumb.mjs:23–26) are the idiom the record now admits for the Workspace: a reactive intent config, a committed truth published beside it, an event. The state classes the record's §2.1 table already distinguishes (component-owned reactive config · worker-owned shared truth · transient request state) are exactly the three rows the epic needs.

The Fix

Documentation-only PR against learn/agentos/decisions/0029-docking-design.md plus two JSDoc mirrors:

  • (a) rewrite :68–69 in place; quote the former sentence verbatim in the same paragraph as provenance ("stage one read: …"); the successor sentence names the selection contract as stage two and keeps the bound-catalog tier and multi-workspace authoring as still-separate contracts.
  • (b) add exactly one row each to the §2.1 state-class table: activePerspective_ — accepted intent, component-owned reactive config; the committed perspective name — accepted-write identity, carried in the write's descriptor {workspaceKey, before, after}, published as dock.perspective.active; dock.perspective.pending — request-lifecycle state under the request serial. State that wants two rows is two pieces of state (:117).
  • (c) restate :211–212 as a collection invariant with no selection meaning ("names one entry; it is a consumer-owned pointer, never the selected perspective"), and mirror the sentence in the activeLayoutId docblocks of src/dashboard/dock/persistence/PerspectiveLibrary.mjs and src/dashboard/dock/persistence/TopologyLibrary.mjs (JSDoc lines only).
  • (d) add the epic's row to the §5 decomposition table: leaf #18605, contract sections §2.1 + §2.2, status "graduated from D#18594".
  • §2.2 gains the selection contract in four sentences: the declared list is the only name source at this stage; a saved record is a snapshot that may equal but never define a perspective; the reserved name for the unnamed declared perspective is refused at both persisted write boundaries; origin metadata follows the accepted-write identity, never the published leaf.

Acceptance Criteria

  • :68–69 rewritten in place; the former sentence quoted verbatim as provenance in the same paragraph; no "note" or "see also" substitute.
  • The §2.1 state-class table has one row each for activePerspective_, the committed perspective name and dock.perspective.pending, with the class named per row; a reviewer can place every piece of state the epic's engine leaves introduce into exactly one of them.
  • :211–212 states activeLayoutId as a collection invariant without selection meaning; the two persistence docblocks carry the mirrored sentence; no runtime line changes.
  • §2.2 carries the four-sentence selection contract; §5's decomposition table has the epic's row.
  • The PR touches only the ADR and the two docblocks, and merges before any other #18605 leaf's PR leaves draft (the :765 rule); the epic's other leaves are blocked_by this one natively.
  • Cross-family review from the GPT family (the author's family cannot clear its own graduation's first leaf).

Out of Scope

Any engine, example, app or guide change (own leaves). The bound-catalog tier and saved-name selection (deferred with triggers in #18605).

Avoided Traps

A footnote or "amendment" section that leaves :68–69 intact (retrieval returns the old sentence); a fifth state class instead of the three existing rows; converting activeLayoutId into the selection (the Workstation's auto-save rewrites it on every commit).

Related

#18605 · D#18594 (STEP_BACK DC_kwDODSospM4BGPHO; ack comment 6) · ADR-0005 (amendment lifecycle) · the three engine leaves of #18605 (blocked by this one).

Filed at the graduation of D#18594 as a leaf of #18605; live latest-open sweep at 2026-09-12T13:55:27Z found no equivalent.

Origin Session ID: c42870ab-3721-40f0-a1f8-f385786ffc3c Retrieval Hint: "ADR 0029 amend activePerspective_ state-class rows activeLayoutId collection invariant"

🪢 Mnemosyne (Claude Fable 5.1, Claude Code) · session c42870ab-3721-40f0-a1f8-f385786ffc3c