LearnNewsExamplesServices
Frontmatter
id18429
titleDemo-B''s copied geometry block drops every root edge chip: it unwraps the zone descriptor by hand while the engine uses the canonical helper
stateClosed
labels
bug
assigneesneo-opus-grace
createdAtSep 7, 2026, 3:40 AM
updatedAtSep 7, 2026, 8:29 AM
githubUrlhttps://github.com/neomjs/neo/issues/18429
authorneo-opus-grace
commentsCount1
parentIssue18304
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 7, 2026, 8:29 AM

Demo-B's copied geometry block drops every root edge chip: it unwraps the zone descriptor by hand while the engine uses the canonical helper

neo-opus-grace
neo-opus-grace commented on Sep 7, 2026, 3:40 AM

Serves: #18304 — this is that Epic's re-derivation finding arriving as a live defect rather than a count.

Context

The #18304 closeout measured DemoBWorkspace re-implementing twelve dock-specific methods while extending Container rather than the dock Workspace. That was a number. @neo-opus-vega found what the number costs, while fixing #18421, and handed it over rather than widening her lane.

Every link below re-verified independently at dev@a4dc6c9441 before filing.

The Problem

examples/dashboard/crossWindow/DemoBCrossWindowStage.mjs carries a hand-copied measureGeometry whose root unwrap reads the zone descriptor directly:

rootId = nodes[document.root]?.type === 'edge-zone'
    ? (nodes[document.root].zones?.center ?? document.root)
    : document.root,

Under the v13.2 schema zones.center is an object descriptor, not an id. demoBPerspectives.mjs:37 ships exactly that shape:

root: {type: 'edge-zone', zones: {center: {nodeId: 'workbench-tabs'}, right: {nodeId: 'side-tabs'}}}

So rootId becomes {nodeId: 'workbench-tabs'}, and the block then publishes root: {nodeId: rootId, rect: hostRect}. Two guards in PreviewProducer fail closed on precisely that, and it is worth naming which does what — produceCandidates:376 is the one that drops the chips, while resolveRootEdge:175 is the separate pointer-inference path (@neo-gpt, measured):

// produceCandidates — the chip set
if (root && typeof root.nodeId === 'string' && root.nodeId && root.rect && root.nodeId !== zone.nodeId) { … }
// else rootOut stays null

and the pointer-inference guard:

if (!(band > 0) || typeof root?.nodeId !== 'string' || !root.nodeId || !rect || !pointer) return null;

Every root edge chip in Demo-B is off, silently, with no error. Reachable rather than theoretical: the perspective above ships in the demo, and DemoBWorkspace.mjs:4524 carries a popup-root shell of the same shape.

The Architectural Reality

The engine fixed this and the copy never heard. src/dashboard/dock/interaction/DragAffordances.mjs:150 carries the repair and a comment naming this exact regression:

"…id guard from silently dropping every root edge chip"

rootId = nodes[…root]?.type === 'edge-zone'
    ? (WorkspaceDocument.getZoneNodeId(nodes[…root].zones?.center) ?? …root)
    : …root;

Census, one instrument across both files:

file getZoneNodeId uses
src/dashboard/dock/interaction/DragAffordances.mjs 1
examples/dashboard/crossWindow/DemoBCrossWindowStage.mjs 0

⭐ This is the cost of re-derivation stated as a defect instead of a line count. One file got the repair; its duplicate kept the bug; nothing in the tree could tell them apart, because the duplicate is not in the inheritance chain that would have carried the fix.

The same block also pairs a centre nodeId with the host's rect — the #18421 mismatch @neo-opus-vega fixed engine-side in PR #18426. The duplicate holds both defects.

The Fix

Call the canonical unwrap. WorkspaceDocument.getZoneNodeId is a static, so the stage can use it without inheriting from DockWorkspace — this de-duplicates the decision, not merely the current copy, so the two cannot diverge on this point again.

What this ticket deliberately does not claim to fix. The duplication itself. Demo-B remains a standalone stage that re-derives twelve dock methods, and the engine's newer boundary seam (resolveDockableRoot()) still cannot reach it. That disposition belongs to #18304 and is named in its seam account. Repairing the symptom here is worth doing because the defect is live and user-visible; it is not worth pretending it addresses the cause.

Acceptance Criteria

  • DemoBCrossWindowStage.mjs resolves the root zone through WorkspaceDocument.getZoneNodeId; a getZoneNodeId census of that file returns ≥ 1.
  • rootId is a string for the shipped demoBPerspectives edge-zone root — asserted, with a red arm on the current code proving the object form.
  • A root edge chip is produced for Demo-B's edge-zone root where none is produced today. Former AC-4 (matching root ID and measured boundary) is outside this ticket's close target, as the author clarified. It remains owned by open #18304 in the recorded residual, following #18426. This ticket delivers AC-1 through AC-3.

Constraints

  • Symptom-level by design. Do not fold the decomposition in; #18304 owns it and widening a lane mid-flight is what cost the team five hours on #18383.
  • No engine change. The existing DragAffordances descriptor unwrap and WorkspaceDocument.getZoneNodeId are the reference for this bounded repair; #18426 owns the engine's separate boundary alignment.

Decision Record impact

none — this restores conformance to the existing WorkspaceDocument contract rather than changing it.

Evidence and framing from @neo-opus-vega, re-verified before filing.