LearnNewsExamplesServices
Frontmatter
id18149
titleReload hides by default: the recreate fallback needs a host override
stateClosed
labels
enhancementaiarchitecturedashboard
assignees[]
createdAtSep 3, 2026, 12:14 AM
updatedAtSep 3, 2026, 12:56 AM
githubUrlhttps://github.com/neomjs/neo/issues/18149
authorneo-opus-vega
commentsCount1
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 3, 2026, 12:56 AM

Reload hides by default: the recreate fallback needs a host override

Closed v13.2.0/archive-v13-2-0-chunk-18 enhancementaiarchitecturedashboard
neo-opus-vega
neo-opus-vega commented on Sep 3, 2026, 12:14 AM

Context

Operator ruling 2026-09-03: "reload => default action is recreate … MUST BE THERE BY DEFAULT."

Measured on a downstream workspace that overrides nothing: a focused pane's header carries [maximize, close] — no reload node at all. The pane implements no dockReload(), and the workspace overrides no factory, which is precisely the case the default is meant to serve.

The Problem

The ruling is not new. It was given on 2026-08-31 and is recorded in a peer's session memory as: "reload must have a default engine behavior — recreate — rather than existing only for panes implementing dockReload()", with "#17966 is required default behavior, not optional polish." #17966's title carries it: "Default reload fallback: engine-owned recreate when a pane lacks dockReload()".

Its six ACs do not. They cover the two-phase transaction's safety only — phase-1 refusal leaves the live pane untouched, phase-2 preserves slot and action identities, destroy-after-live ordering, reconciler interaction after recreate, teardown mid-transaction. Every one was met, and the ticket closed legitimately via merged PR #17996 ("A user-triggered pane recreate amends the never-destroyed guarantee" — the ADR 0029 §2.6 amendment). The ruling was never encoded as an AC, so nothing failed when the default did not ship. That is the process half of this ticket, and it is worth naming: a ruling that lives in a title and a memory is not a gate.

What shipped is a consumer hook, deliberately and with documentation. The Contract Ledger on #17966 records resolveFreshPane as a "New consumer hook" whose fallback "returns null — recreate unsupported", and hasDockRecreateFallback() as "Prototype-derived — is the capability wired". #18014 AC-7 then scoped the factory to a single host: "Workstation's fresh-pane factory bypasses paneCache … resolveFreshPane always creates independently."

So a workspace that overrides nothing has no recreate capability, and syncDockReloadAction correctly hides reload.

The consumer-hook rationale is real and must survive any fix. From the hook's own JSDoc: an option on resolvePane "would be silently ignored and hand back the very instance the recreate is meant to replace." A distinct hook cannot be accidentally satisfied by a cache. The open question is therefore not "remove the hook" but "what can the engine mint without one".

The Architectural Reality

  • src/dashboard/dock/Workspace.mjs:3651 — resolveFreshPane(itemId, item) { return null }, the honest declining default.
  • :3671 — hasDockRecreateFallback() { return this.resolveFreshPane !== Workspace.prototype.resolveFreshPane }. Prototype identity, so the base implementation is excluded by construction: the engine can never be its own fallback under this predicate.
  • Workspace#syncDockReloadAction — hidden = typeof carrier !== 'function' && !this.hasDockRecreateFallback(); hides only when neither delegation nor fallback can serve the item.
  • LayoutAdapter.mjs projects reload with a CONSTANT hidden: true (stable-instance discipline), revealed only by that post-settle sweep.
  • Withdrawn actions carry no DOM node (#18015), so the absence is invisible to inspection.

The Fix

Give the engine a default resolveFreshPane that mints a pane from the item's own persisted record — item is already the hook's second argument — so hasDockRecreateFallback() answers true without a host override, while a host override stays authoritative and dockReload() delegation keeps priority.

The design question to answer before implementing: what does the engine recreate FROM? The item catalog record, versus the pane's own config snapshot, versus a module reference. Whichever is chosen must not resolve through any live-instance cache — that is the exact trap the current hook shape exists to prevent — and hasDockRecreateFallback() must stop being prototype-identity-derived once the base can serve.

Contract Ledger Matrix

Target Surface Source of Authority Proposed Behavior Fallback Docs Evidence
Workspace#resolveFreshPane src/dashboard/dock/Workspace.mjs:3651 engine default mints from the item record instead of returning null a host override wins unchanged JSDoc identity arm: the minted pane is never the live instance
Workspace#hasDockRecreateFallback :3671 no longer prototype-identity-derived; true when the base can serve false only if the engine genuinely cannot mint JSDoc visibility arm on a bare workspace
Workspace#handleDockReloadAction Workspace.mjs unchanged — delegation first, recreate second no live pane → honest error JSDoc #17966's delegation-priority mutation stays green
ADR 0029 §2.6 learn/agentos/decisions/0029-docking-design.md unchanged — user-triggered only; automatic recreation still forbidden — the record existing amendment stands

Decision Record impact

aligned-with ADR 0029 §2.6 — the user-triggered recreate exception already exists and is conditioned on the two-phase guarantee; this ticket widens who can serve it, not when it may run. Successor to #17966 (closed, PR merged): not a reopen.

Acceptance Criteria

  • AC-1 A Neo.dashboard.dock.Workspace subclass that overrides nothing projects a reachable reload on a focused pane whose card implements no dockReload().
  • AC-2 The engine's default recreate mints a pane that is not the live instance it replaces, proven by identity — #17966's live-instance refusal stays red-proof.
  • AC-3 A host override of resolveFreshPane still wins, and dockReload() delegation still preempts recreate; both mutation-proven, with #17966's delegation-priority arm green.
  • AC-4 Every #17966 transaction guarantee holds on the engine-owned path: phase-1 refusal leaves the live pane untouched, phase-2 preserves slot/tab/header/action/overflow identities, destroy happens only after the candidate is live, teardown mid-transaction settles without leaks.
  • AC-5 A declining item still shows the action and settles with a named refusal rather than hiding the button — the shipped behaviour the #17966 ledger records ("hiding would leave a wedged pane with no affordance").
  • AC-6 Red-first witness at the current head asserting reload's presence on a bare workspace; absence asserted as node presence, not display.
  • AC-7 Exact-head unit + component suites green.

Out of Scope

  • Lock and pop-out defaults — the sibling successor filed alongside this one.
  • Automatic (non-user-triggered) recreation: still forbidden by ADR 0029 §2.6.
  • Changing what dockReload() means for panes that implement it.

Avoided Traps

  • Reopening #17966. Its ACs were met and it had a merged PR. The gap is that the ruling lived in the title rather than in an AC — a successor is the correct instrument.
  • Serving recreate through an option on resolvePane. Named and rejected in the hook's own JSDoc: consumers that do not implement it hand back the cached instance the recreate exists to replace, silently.
  • Leaving capability derived from prototype identity once a base exists. The predicate would answer false for every consumer while the engine can in fact serve — the defect this ticket is about, one layer up.

Related

  • #17966 (closed, PR #17996 merged) — the two-phase transaction and the ADR §2.6 amendment; its title carries the ruling its ACs omitted.
  • #18014 AC-7 (closed) — scoped the fresh-pane factory to a single host.
  • #18039 (closed) — reload lost after a rail round trip (the sweep now walks the settled shell).
  • #17948 / PR #17974 — the reload action itself, merged delegation-only.
  • Sibling successor: lock + pop-out defaults, #18148.

Live latest-open sweep: checked the latest 25 open issues at 2026-09-02T22:12Z; A2A in-flight claim sweep over the most recent 12 messages at 22:14Z — no equivalent ticket and no competing claim.

Origin Session ID: a5db4dc8-310d-4702-9bfa-3780d32e932b

Retrieval Hint: "engine-owned recreate default resolveFreshPane hasDockRecreateFallback prototype identity reload hidden bare workspace"