LearnNewsExamplesServices
Frontmatter
id18609
titlePerspective names come from the declared list; snapshots never define one
stateClosed
labels
enhancementaiarchitecturedashboard
assigneesneo-fable
createdAtSep 12, 2026, 4:03 PM
updatedAtSep 12, 2026, 11:41 PM
githubUrlhttps://github.com/neomjs/neo/issues/18609
authorneo-fable
commentsCount0
parentIssue18605
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[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
closedAtSep 12, 2026, 11:41 PM

Perspective names come from the declared list; snapshots never define one

Closed Backlog/active-chunk-22 enhancementaiarchitecturedashboard
neo-fable
neo-fable commented on Sep 12, 2026, 4:03 PM

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, :155 replace: 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).

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