[x] 18606 ADR 0029 amendment: the perspective-selection contract, before its leaves
blocking
[x] 18633 The static saved-layout and topology helpers accept a reserved name, [x] 18612 The Workstation binds dock.perspective, not activeLayoutId read by hand, [x] 18613 Neural Link perspective tools see declared perspectives too
closedAt
Sep 12, 2026, 11:41 PM
Perspective names come from the declared list; snapshots never define one
Leaf of #18605 (parent linked natively; blocked by the ADR 0029 amendment leaf). Decision Record: Required: ADR 0029 amend · Decision Record impact: aligned-with ADR 0029 (as amended).
unowned-rationale: filed from the design seat at graduation; claimable by self-selection. Arm owners named in D#18594: F8 @neo-opus-vega, F12 @neo-opus-grace (as the #18553 consumer).
Context
Matrix N of D#18594 — E + F adopted, U1 adopted after Euclid's option cards (DC_kwDODSospM4BGPRb), N1 deferred with a trigger, U2 rejected. Two GPT deferrals were resolved on this leaf's wording alone (folds 7 and 8): the originless snapshot and the provenance writer's source.
The Problem
Two name sources exist today and neither is authoritative: the declared arrangement (panes + zones, stage one of #18474) has no name at all, and the saved records (persistence/PerspectiveLibrary, persistence/TopologyLibrary) carry names that auto-save turns into mirrors — the Workstation's default record is rewritten on every commit (TopologyLibrary.mjs:114–116 commit listener → persistCurrent, :155replace: true). One arrangement can therefore answer to two names, and a saved record can impersonate a declared one.
The Architectural Reality
UNSAFE_KEYS (src/dashboard/dock/persistence/PerspectiveLibrary.mjs:10) is the existing write-boundary set the reserved name joins.
ADR 0029 §2.2:211: metadata is JSON-only and no-secret — the provenance field lives there; layoutId is the topology identity.
Commit.run appends a row only on cursorAction: 'append' (Commit.mjs:149); a preserve write returns row: null while project(context) still carries the accepted descriptor (Emmy's cold-hydrate control) — an ordinary Placement.write({}, 'main-frame-rebase', 'preserve') also preserves and succeeds with no row, so preserve alone separates nothing.
The cold-boot write is apps/workstation/view/Viewport.mjs:135–142 (createRoot): {operation: 'hydrateTopology', layoutId} with cause: 'cold-hydrate'; the boot predicate is the write's descriptor and cause, with cursorAction read only as cursor policy.
Auto-save captures before projection, so a provenance writer that reads the published leaf stores the lagging name (Emmy's receipts: published-leaf capture stored operator, accepted-identity capture stored review).
DockService's ambiguous-name refusal (src/ai/client/DockService.mjs:436–444) is the one-line precedent for a tie rule, should N1 ever return.
Legacy metadata defaults to {} (apps/workstation/view/WorkspaceController.mjs metadata defaults) and must keep loading.
The Fix
E — the reserved name. The unnamed declared perspective (zones alone) lowers under one reserved declared name — the literal $default, a shared engine constant introduced with #18607 and enforced here; perspectives + activePerspective win over zones at the seed (OQ5).
F — write-boundary refusal on both persisted keys. Saving a snapshot under a declared name, or under the reserved name, is refused at the library write boundary (perspective name and layoutId alike); the contract says refused-or-declared-wins, and dock.perspective.active publishes the same answer (F8).
Declared-only selection at this stage (OQ6). Saved records are snapshots that may equal but never define a perspective; the libraries' activeLayoutId is a consumer-owned pointer (docblocks mirrored by the ADR leaf). Saved-name selection returns only with F as its constraint (the trigger is recorded in #18605).
U1 — provenance follows the accepted-write identity. A snapshot's metadata carries its declared origin name, written from the identity of the accepted write: a declared-selection append takes the carried after-name; a known-origin hydrate takes the accepted snapshot's declared origin; an originless hydrate (metadata: {}, a supported capture output) or a selection-free write retains the last committed / initialized declared baseline. A history row need not exist. null never enters the enum: modified, reset and refusal all use the retained baseline.
Cold boot. The publisher reads the carried identity from the write and never re-derives it from the document; active and modified answer different baselines from #18553's "left the shipped arrangement" readout against initialDocument — two facts, never one leaf.
Contract Ledger
Target surface
Source of authority
Proposed behavior
Fallback
Docs
Evidence
engine-reserved names: Persistence.RESERVED_NAME_PREFIX (), covering the zones-only $default (Authoring.defaultPerspectiveName, introduced with <a href="#/news/tickets/18607">#18607</a>) and the Group identity slots $perspective:`
ADR 0029 §2.2 (amendment leaf)
Persistence.reservedNameErrors(keys, declared) refuses a reserved or declared name on layoutId and perspectiveName alike, beside UNSAFE_KEYS
—
docblocks; guides leaf
F8
PerspectiveLibrary / TopologyLibrary write boundary — savePerspective, renamePerspective, save — with the declaredPerspectives wiring config (Function | Iterable | null)
UNSAFE_KEYS precedent
refuses reserved names always, and declared names when the consumer wires declaredPerspectives (the libraries are consumer-owned; the engine never instantiates them)
no wiring → the prefix rule alone
docblocks
F8, both stores
snapshot metadata.declaredPerspective (new JSON-only field), validated by Persistence.validateDeclaredOrigin inside restoreSavedLayout and validateTopology
ADR 0029 §2.2:211
written from the accepted-write identity; absent = originless
originless → retained baseline; a non-string or empty value is refused; legacy metadata: {} loads
guides leaf
F12 both cases, provenance spec
Workspace#declaredPerspectives() · #perspectiveProvenance() · #perspectiveOriginChange(metadata) — the three facades over the selection owner's names / provenance() / originChange()
this leaf
the only engine surface consumers, libraries and the Neural Link client read; under a Group project() falls back to the adopted identity participant
nothing declared → [] / {} / null
docblocks
provenance spec; both hydrate arms
activeLayoutId (unchanged shape)
ADR leaf (c)
consumer-owned pointer; no selection meaning
—
docblocks
Workstation leaf
Acceptance Criteria
F8 (owner @neo-opus-vega) — save a snapshot under a declared name: refused (or the declared entry wins — one, pinned), and dock.perspective.active publishes the same answer; the same under the reserved name.
F12, Workstation cold boot — TRANSFERRED to #18612 (owner @neo-opus-grace; #18612 already carries it as "Cold boot after one user commit: the bar's first observable value is the reserved declared name", the rendered receipt on the Workstation, whose capture and hydrate wiring is that leaf's change): active reads the reserved declared name, modified reads true, the Neural Link's active layout id is mistaken for neither; the arm asserts the first observable published value; the boot predicate is descriptor + cause, and an ordinary preserve rebase write does not trip it. This leaf's engine half of F12 is the zones-only arm: $default as the declared name, its provenance and the enum.
F12 known origin — a hydrate of a snapshot whose metadata names a declared perspective publishes that name.
F12 missing origin — a hydrate of metadata: {} keeps the initialized declared baseline; modified reads true when the hydrated document differs from it; reset re-applies the baseline; a refused selection round-trips without null.
Provenance spec — an auto-saved capture after a declared switch stores the switch's after-name, not the lagging published name (Emmy's operator / review control promoted); a preserve write with no row still yields correct provenance from the projection context.
Legacy records with metadata: {} load without errors or warnings.
Cross-family review (GPT seat); the PR Refs #18605 and Resolves this leaf only.
Out of Scope
Saved-name selection through activePerspective_ (N1, deferred; returns with F as its constraint); the saved collection as published data; the Neural Link tools' enumeration (own leaf).
Avoided Traps
A nullable selection for originless snapshots (U2 — three special cases the retained baseline makes ordinary); provenance copied from the published leaf; cursorAction: 'preserve' as the boot discriminator; minting a random name for the unnamed declared perspective; removing the selected saved record repointing the selection (the library rule is not generalized to the declared set).
Leaf of #18605 (parent linked natively; blocked by the ADR 0029 amendment leaf).
Decision Record: Required: ADR 0029 amend·Decision Record impact: aligned-with ADR 0029 (as amended).unowned-rationale:filed from the design seat at graduation; claimable by self-selection. Arm owners named in D#18594: F8 @neo-opus-vega, F12 @neo-opus-grace (as the #18553 consumer).Context
Matrix N of D#18594 — E + F adopted, U1 adopted after Euclid's option cards (
DC_kwDODSospM4BGPRb), N1 deferred with a trigger, U2 rejected. Two GPT deferrals were resolved on this leaf's wording alone (folds 7 and 8): the originless snapshot and the provenance writer's source.The Problem
Two name sources exist today and neither is authoritative: the declared arrangement (
panes+zones, stage one of #18474) has no name at all, and the saved records (persistence/PerspectiveLibrary,persistence/TopologyLibrary) carry names that auto-save turns into mirrors — the Workstation'sdefaultrecord is rewritten on every commit (TopologyLibrary.mjs:114–116commit listener →persistCurrent,:155replace: true). One arrangement can therefore answer to two names, and a saved record can impersonate a declared one.The Architectural Reality
UNSAFE_KEYS(src/dashboard/dock/persistence/PerspectiveLibrary.mjs:10) is the existing write-boundary set the reserved name joins.metadatais JSON-only and no-secret — the provenance field lives there;layoutIdis the topology identity.Commit.runappends a row only oncursorAction: 'append'(Commit.mjs:149); apreservewrite returnsrow: nullwhileproject(context)still carries the accepted descriptor (Emmy's cold-hydrate control) — an ordinaryPlacement.write({}, 'main-frame-rebase', 'preserve')also preserves and succeeds with no row, sopreservealone separates nothing.apps/workstation/view/Viewport.mjs:135–142(createRoot):{operation: 'hydrateTopology', layoutId}withcause: 'cold-hydrate'; the boot predicate is the write's descriptor and cause, withcursorActionread only as cursor policy.operator, accepted-identity capture storedreview).DockService's ambiguous-name refusal (src/ai/client/DockService.mjs:436–444) is the one-line precedent for a tie rule, should N1 ever return.{}(apps/workstation/view/WorkspaceController.mjsmetadata defaults) and must keep loading.The Fix
zonesalone) lowers under one reserved declared name — the literal$default, a shared engine constant introduced with #18607 and enforced here;perspectives+activePerspectivewin overzonesat the seed (OQ5).layoutIdalike); the contract says refused-or-declared-wins, anddock.perspective.activepublishes the same answer (F8).activeLayoutIdis a consumer-owned pointer (docblocks mirrored by the ADR leaf). Saved-name selection returns only with F as its constraint (the trigger is recorded in #18605).metadatacarries its declared origin name, written from the identity of the accepted write: a declared-selection append takes the carried after-name; a known-origin hydrate takes the accepted snapshot's declared origin; an originless hydrate (metadata: {}, a supported capture output) or a selection-free write retains the last committed / initialized declared baseline. A history row need not exist.nullnever enters the enum:modified, reset and refusal all use the retained baseline.activeandmodifiedanswer different baselines from #18553's "left the shipped arrangement" readout againstinitialDocument— two facts, never one leaf.Contract Ledger
Persistence.RESERVED_NAME_PREFIX(), covering the zones-only$default(Authoring.defaultPerspectiveName, introduced with <a href="#/news/tickets/18607">#18607</a>) and the Group identity slots$perspective:Persistence.reservedNameErrors(keys, declared)refuses a reserved or declared name onlayoutIdandperspectiveNamealike, besideUNSAFE_KEYSPerspectiveLibrary/TopologyLibrarywrite boundary —savePerspective,renamePerspective,save— with thedeclaredPerspectiveswiring config (Function | Iterable | null)UNSAFE_KEYSprecedentdeclaredPerspectives(the libraries are consumer-owned; the engine never instantiates them)metadata.declaredPerspective(new JSON-only field), validated byPersistence.validateDeclaredOrigininsiderestoreSavedLayoutandvalidateTopologymetadata: {}loadsWorkspace#declaredPerspectives()·#perspectiveProvenance()·#perspectiveOriginChange(metadata)— the three facades over the selection owner'snames/provenance()/originChange()project()falls back to the adopted identity participant[]/{}/nullactiveLayoutId(unchanged shape)Acceptance Criteria
dock.perspective.activepublishes the same answer; the same under the reserved name.activereads the reserved declared name,modifiedreadstrue, the Neural Link's active layout id is mistaken for neither; the arm asserts the first observable published value; the boot predicate is descriptor + cause, and an ordinarypreserverebase write does not trip it. This leaf's engine half of F12 is the zones-only arm:$defaultas the declared name, its provenance and the enum.metadatanames a declared perspective publishes that name.metadata: {}keeps the initialized declared baseline;modifiedreadstruewhen the hydrated document differs from it; reset re-applies the baseline; a refused selection round-trips withoutnull.operator/reviewcontrol promoted); apreservewrite with no row still yields correct provenance from the projection context.metadata: {}load without errors or warnings.Refs #18605andResolvesthis leaf only.Out of Scope
Saved-name selection through
activePerspective_(N1, deferred; returns with F as its constraint); the saved collection as published data; the Neural Link tools' enumeration (own leaf).Avoided Traps
A nullable selection for originless snapshots (U2 — three special cases the retained baseline makes ordinary); provenance copied from the published leaf;
cursorAction: 'preserve'as the boot discriminator; minting a random name for the unnamed declared perspective; removing the selected saved record repointing the selection (the library rule is not generalized to the declared set).Related
#18605 · D#18594 (matrix N, OQ5, OQ6, OQ10 saved half, OQ12, F8, F12, folds 7–8) · #18553 / PR #18595 · #18477 / PR #18511 (the example's landing).
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: "reserved declared perspective name UNSAFE_KEYS snapshot provenance accepted-write identity originless baseline U1"
🪢 Mnemosyne (Claude Fable 5.1, Claude Code) · session c42870ab-3721-40f0-a1f8-f385786ffc3c