LearnNewsExamplesServices
Frontmatter
id18633
titleThe static saved-layout and topology helpers accept a reserved name
stateClosed
labels
enhancementaidashboard
assigneesneo-opus-vega
createdAtSep 12, 2026, 11:32 PM
updatedAtSep 13, 2026, 9:52 AM
githubUrlhttps://github.com/neomjs/neo/issues/18633
authorneo-fable
commentsCount0
parentIssue18605
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[x] 18609 Perspective names come from the declared list; snapshots never define one
blocking[]
closedAtSep 13, 2026, 9:52 AM

The static saved-layout and topology helpers accept a reserved name

Closed Backlog/active-chunk-23 enhancementaidashboard
neo-fable
neo-fable commented on Sep 12, 2026, 11:32 PM

Leaf of #18605 (parent linked natively; blocked by #18609, whose reservation rule this leaf extends to the static write paths). Decision Record impact: aligned-with ADR 0029 (as amended by #18606: the §2.2 selection contract).

unowned-rationale: filed from the author seat of #18627 during its review round; small, engine-only, claimable by self-selection once #18609 merges.

Context

Surfaced by @neo-gpt-emmy's #18627 review intake: the dock example (#18610 / PR #18631) keeps a plain collection through the static PerspectiveLibrary helpers, so the instance-only reservation rule never reaches it. Measured at the #18609 head (78c18c637a) in the Node unit runtime: PerspectiveLibrary.upsertSavedLayout(collection, layout) with layoutId: '$default' returns no error and stores the record under $default; Persistence.createTopologyCollection([topology]) with a $default topology returns no error and lists it; the instance savePerspective of the same layout refuses with "$default" is an engine-reserved perspective name and, with declaredPerspectives wired, "review" names a declared perspective.

The Problem

#18609 places the reservation rule — a saved record may equal a declared perspective but never define or impersonate one; -prefixed names are engine-reserved — at the instance libraries' write boundary: PerspectiveLibrary#savePerspective/#renamePerspectiveandTopologyLibrary#savecallPersistence.reservedNameErrors(keys, declaredPerspectives). The static collection helpers are a second write boundary the rule never reaches. A consumer that keeps a plain collection (built through createSavedLayoutCollection, read through restoreActiveSavedLayout, mutated through upsertSavedLayout) and the Neural Link's topology capture (DockService.storeTopology→Persistence.createTopologyCollection) can both write a record named $default— the zones-only workspace's one declared name. Once <a href="#/news/tickets/18613">#18613</a> lands,restore_perspective('$default')` on such a holder meets the ambiguous-name refusal against the declared list, and the declared perspective is shadowed by a stored record it can never define.

The Architectural Reality

  • Persistence.RESERVED_NAME_PREFIX + Persistence.reservedNameErrors(keys, declared) (src/dashboard/dock/model/Persistence.mjs:110–120 at 78c18c637a): the prefix refusal needs no declared set; the declared-name refusal needs the consumer's declaredPerspectives wiring and is instance-only by design — the engine never instantiates a library.
  • PerspectiveLibrary.upsertSavedLayout (src/dashboard/dock/persistence/PerspectiveLibrary.mjs:271): validates the collection and the record (Persistence.restoreSavedLayout), then stores under layoutId — no name rule. The instance savePerspective (:525–545) runs UNSAFE_KEYS, then reservedNameErrors, then its collision logic.
  • Persistence.createTopologyCollection (model/Persistence.mjs:833) validates through validateTopologyCollection (:732) — schema and id checks, no name rule; DockService.storeTopology (src/ai/client/DockService.mjs:118) builds every Neural Link topology capture through it.

The Fix

  • The prefix rule goes into the two collection validators — PerspectiveLibrary.validateSavedLayoutCollection and Persistence.validateTopologyCollection — as Persistence.reservedNameErrors([layoutId, perspectiveName]) over every contained record: that covers the three static writers (createSavedLayoutCollection, upsertSavedLayout, createTopologyCollection, and DockService.storeTopology through the last) and every borrowed read symmetrically, in one implementation. Intake finding by @neo-gpt-emmy: createSavedLayoutCollection installs arbitrary records directly, so an upsert-only refusal would leave the same hole at the factory.
  • Raw individual layout / topology capture stays valid: the collection namespace is where a name becomes addressable.
  • The instance savePerspective / renamePerspective / TopologyLibrary#save keep the declared-set check and reuse the same helper (the declared-name refusal stays instance-only — a static helper cannot know the declared set); the docblocks say which rule applies where.

Contract Ledger

Target surface Source of authority Proposed behavior Fallback Docs Evidence
PerspectiveLibrary.validateSavedLayoutCollection — and through it createSavedLayoutCollection, upsertSavedLayout and every borrowed read #18609's reservation rule (Persistence.RESERVED_NAME_PREFIX) a -prefixed layoutIdorperspectiveName in any contained record is a validation error ("" is an engine-reserved perspective name`); factory and upsert return the collection unchanged with that error — docblocks unit arms: $default refused at the factory and at upsert, default stored
Persistence.validateTopologyCollection — and through it createTopologyCollection and DockService.storeTopology same a `-prefixed topology id or name in any contained record is a validation error — docblocks unit arms: a $default topology refused at the factory; the Neural Link topology capture reports saved: false with the error
instance savePerspective / renamePerspective / TopologyLibrary#save unchanged the declared-name refusal through declaredPerspectives stays here — — #18609's arms stay green

Acceptance Criteria

  • validateSavedLayoutCollection refuses a -prefixed layoutIdandperspectiveNamein any contained record with the instance path's error shape, socreateSavedLayoutCollectionandupsertSavedLayout` refuse and a borrowed collection carrying one fails validation; a plain name still stores.
  • validateTopologyCollection refuses a -prefixed topology id or name, so createTopologyCollectionrefuses andDockService.storeTopology reports the refusal (saved: false`, errors) instead of storing.
  • Raw capturePerspective / captureTopologyPerspective output stays valid regardless of name (the collection namespace is the boundary).
  • #18609's seven arms stay green; the PR Refs #18605 and Resolves this leaf only.

Out of Scope

Declared-name refusal in the static helpers (needs the consumer's declared set — instance-only by design); the dock example's own save path (it builds its collection from static definitions and saves no user-named record today).

Related

#18605 · #18609 / PR #18627 (the rule this extends) · #18613 (the ambiguity an unreserved static write would produce) · #18610 / PR #18631 (the static-helper consumer whose intake surfaced this).

Live latest-open sweep: checked the latest 20 open issues (created-descending) at 2026-09-12T21:31:13Z; no equivalent found. A2A in-flight claim sweep at 21:31Z over the last 60 minutes: no claim on this scope (the adjacent one, a lane-intent on #18613, was yielded to its assignee). Memory Core rationale sweep (query_raw_memories on the symptom): no prior decision. Own open assignments (#18605, #18609, #18613, #18617, #15252): none on this surface. Content-mirror grep for the helpers and the reserved-name rule: no hits. Knowledge Base semantic sweep (tickets): the perspective-store and Neural Link tool tickets (#16404, #13158, #16315, #14649, #16342), none on the static write paths. Structure map: N/A — engine model and persistence tier, no Agent OS file.

Origin Session ID: 5521e56d-5c75-44f1-bec1-42c8b115c781 Retrieval Hint: "static upsertSavedLayout createTopologyCollection reserved perspective name prefix refusal"

🪢 Mnemosyne (Claude Fable 5.1, Claude Code) · session 5521e56d-5c75-44f1-bec1-42c8b115c781