LearnNewsExamplesServices
Frontmatter
id18529
titleChallenge the dock catalog''s kind field: no engine code reads it, and every example teaches it
stateClosed
labels
enhancementaiarchitecturedashboard
assigneesneo-opus-grace
createdAtSep 9, 2026, 10:17 AM
updatedAtSep 9, 2026, 3:14 PM
githubUrlhttps://github.com/neomjs/neo/issues/18529
authorneo-opus-grace
commentsCount6
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 9, 2026, 3:14 PM

Challenge the dock catalog's kind field: no engine code reads it, and every example teaches it

Closed v13.2.0/archive-v13-2-0-chunk-20 enhancementaiarchitecturedashboard
neo-opus-grace
neo-opus-grace commented on Sep 9, 2026, 10:17 AM

Serves: #18456 / #18461 — idiomatic dock consumers. Dead config that examples teach is a direct cost to that goal.

Body rewritten 2026-09-09. The original filed this as an open challenge with a counter-argument to be answered. It is no longer open: the census is closed, the authored surface is governed by a ratified decision, and two preservation options I proposed in comments were both withdrawn. The ACs below supersede the originals — a comment cannot carry them.

The Problem

Dock catalog items carry a kind field ('panel', 'terminal'). No engine code reads it, and on the authored surface it is a ratified avoided-trap that shipped anyway.

Worse than an unread field: the values name nothing. Every dock pane carrying kind: 'panel' is a Neo.component.Base —

ReloadablePane                                                 extends Neo.component.Base
container/Panel usages in examples/dashboard + apps/workstation:   0

Neo.container.Panel exists in the engine and is used elsewhere, but no dock pane anywhere is one. So 'panel' is a label describing a class the dock never instantiates.

The Architectural Reality

It is declared, persisted, and consumed by nobody. WorkspaceDocument.mjs:47 allowlists it; Authoring.mjs:183 copies it through from a pane declaration into the document.

Every .kind read in src/ belongs to something else:

site what it actually is
WorkspaceDocument.mjs:853, Operations.mjs:716, PreviewProducer.mjs:102/115/223 placement.kind — tab-into, split-*, edge-*. Drag geometry
DeltaCoherenceRegistry.mjs:252/649/718/750 RETIREMENT_KINDS — vdom retirement

On the authored surface it is a ratified decision, already violated. #18474's Avoided Traps, agreed on Discussion #18468 before any code:

Named on the Discussion and not to be re-proposed: a rails key; a dock key beside layout; kind on the authored surface; type required; …

The example merged through sub #18477 carries it ten times. A full audit of that example against all seven named traps found exactly one violation — this one; title, rails, type, persistence and the compile step are all clean.

Exposure is two repos, established org-wide. gh repo list neomjs returns 53 repos; local checkouts were never an authoritative root (there are 11 seat checkout roots on one machine). Code search on the wire-schema name:

gh search code --owner neomjs 'neo.dock.zone.v1' --limit 100
→ 71 results, NOT capped → neomjs/neo = 66 · neomjs/neo-agent-institution = 5

neomjs/neo-agent-institution authors the field in production source (apps/agentos/util/CockpitDockDocument.mjs) and asserts it with exact toEqual shapes in three specs.

⚠️ Counts in this ticket may understate the institution surface. gh search code is case-insensitive and git grep is not — for componentRef that is 6 files vs 11, the delta being resolveDockComponentRef. Any recount here pins -i on the git side before comparing, or the divergence reads as repo drift. (@neo-opus-ada, #18528.)

The Fix

Remove it. Not deprecate, not tolerate, not relocate.

Two preservation options were proposed and both withdrawn:

  • Keep it allowlisted read-only for backward compatibility. Circular: FM consumes a pinned engine SHA and resolves its tickets before bumping, so pinning already provides the decoupling this was meant to buy. Strip that and only backward compatibility remains, which is explicitly not an argument here.
  • Relocate the value to metadata. metadata is a sanctioned opaque consumer channel, but the value names nothing — relocating it institutionalises noise in a nicer box.

Removal costs a real break, and that is correct: the allowlist rejects, it does not drop. findUnexpectedKey returns {key, path, reason: 'field is outside the saved-layout schema'} as a validation failure (WorkspaceDocument.mjs:373, Operations.mjs:177, Authoring.mjs:348). A document still carrying kind fails validation — absorbed by the consumer before it bumps, which is what a pinned SHA is for.

AC

  • AC-1 Cross-repo consumer census — DONE. Two repos, org-wide, uncapped. No engine or consumer code in neomjs/neo branches on a catalog item's kind.
  • AC-1b kind is removed from every authored panes declaration in this repo. Justified by #18474's ratified Avoided Traps, not by the census — this half needs no verdict and does not wait on AC-2.
  • AC-2 kind is removed from WorkspaceDocument.dockZoneItemKeys. No allowlist entry, no read-side tolerance, no deprecation window.
  • AC-3 The neomjs/neo-agent-institution migration is filed as its own ticket in its own repo, and states that kind has no replacement — no engine reader, and no value corresponding to a class the dock instantiates. metadata is not offered as a landing place for this field. If that repo wants a per-pane tag it justifies one on its own terms.
  • AC-4 The 254 test occurrences are migrated or scoped out with a reason.

Out of Scope

placement.kind and all drag/preview geometry — a different field with the same name, and it must not be touched. RETIREMENT_KINDS. The componentRef challenge (#18528), which is not symmetric: that field is not on the avoided-traps list and remains a genuine open design question.

Avoided Traps

Do not grep bare kind and act on the count: placement.kind dominates the results and is unrelated — and the org code search is case-insensitive, so it re-imports every collision a careful git grep excludes. Do not treat "the engine ignores it" as "nothing reads it" — the engine is one of five places to look, and the institution repo was found only because the census left this one. Do not reach for a preservation shape when told the field is dead; two were proposed here and both dissolved on contact with the release model and the values.

Decision Record impact

challenges ADR 0029's document vocabulary. The authored-surface half is aligned-with #18474's ratified decision rather than a change to it.

Sweeps and ownership

Live latest-open sweep before filing; org-wide code search for the closure. Counts measured at dev@335b2ca43a and re-anchored at origin/dev.

Raised by @tobiu; filed by @neo-opus-grace, who approved the example carrying it without questioning the key, and who proposed both withdrawn preservation options above.

tobiu referenced in commit 2509fb1 - "refactor(dock): the catalog's lookup key becomes the engine's own reference (#18528) (#18536) on Sep 9, 2026, 1:34 PM
tobiu referenced in commit 22c183c - "refactor(dock): retire the catalog's dead kind field (#18529) (#18540) on Sep 9, 2026, 3:14 PM
tobiu closed this issue on Sep 9, 2026, 3:14 PM