Frontmatter
| title | refactor(draggable): align SortZone drag-end overrides (#12895) |
| author | neo-gpt |
| state | Merged |
| createdAt | Jun 12, 2026, 1:38 PM |
| updatedAt | Jun 12, 2026, 2:45 PM |
| closedAt | Jun 12, 2026, 2:45 PM |
| mergedAt | Jun 12, 2026, 2:45 PM |
| branches | dev ← codex/12895-sortzone-process-drag-end |
| url | https://github.com/neomjs/neo/pull/12989 |

PR Review Summary
Status: Approved
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: All six #12895 ACs are substantively delivered. The one deviation from the ticket's prescription — the dashboard shape question — is resolved by superseding evidence rather than by one of the pre-framed shapes (a)/(b)/(c), and that evidence is documented in the PR body and verified by this review against current
dev. No follow-up ticket is warranted: the residual observation (below) concerns pre-#12892latch semantics this PR does not touch.
Peer-Review Opening: Clean, evidence-driven migration — the source review that invalidated the ticket's shape-(c) prior instead of mechanically executing it is exactly the right reviewer-resistant move. Notes below.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: Ticket #12895 (full body incl. the dashboard (a)/(b)/(c) framing), current
devsource ofcontainer/SortZone.mjs:362-376(the latch),dashboard/SortZone.mjs:262/:273/:312/:349,tab/header/toolbar/SortZone.mjs:40,table/header/toolbar/SortZone.mjs:58, sibling precedentgrid/header/toolbar/SortZone.mjs(migrated in#12892), changed-file list. - Expected Solution Shape: tab/table = pure mechanical
processDragEndrenames withsuper.processDragEnd; dashboard = the AC-mandated self-call dependency analysis before a shape choice, with no base-pipeline fragments copy-pasted into the subclass; the touched dashboard spec should pin the latch interaction deterministically. - Patch Verdict: Improves on the expected shape. tab/table match exactly (tab even preserves its schedule-before-await ordering for the 300ms animation-cls restore). Dashboard contradicts the ticket's pre-framed shapes but with superseding evidence I independently verified: on current
dev, the:312/:349self-calls already routeme.onDragEnd({})→ dashboard override →super.onDragEnd= the base latch — so the cleanup was already latch-routed before this PR, the ticket's "a naive rename would change their semantics" concern doesn't materialize, and the PR preserves the routing while moving the coordinator signal inside the latch window. Two strengths the body undersells: (1) the previously fire-and-forgetsuper.onDragEnd(data)is nowawait super.processDragEnd(data)— the:349"remove from old parent" step no longer races a still-running base pipeline; (2) duplicate realdrag:enddeliveries can no longer double-signalDragCoordinator(the new spec pins exactly this).
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #12895
- Related Graph Nodes:
#12892(latch origin),#12894(moveNode context),#12883(ancestor defect family)
🔬 Depth Floor
Challenge (per guide §7.1): AC4-ii's race surface — a cleanup self-call and a real drag:end overlapping on the same instance — has no spec: the new test covers duplicate real deliveries (coordinator dedup), not self-call-vs-real contention, where the per-instance latch would silently drop whichever arrives second. My read of the flows says this is benign-by-construction (the self-call sites gate on isRemoteDragging, and cross-zone deliveries hit different instances with independent latches) — and crucially these semantics are pre-existing #12892 reality unchanged by this PR — so it is an observation, not a Required Action. Tagged explicitly: hypothesis — needs V-B-A before any implementation; if forensics ever show a dropped cleanup, a debug-level "latch dropped a delivery" log would be the cheap instrument.
Rhetorical-Drift Audit (per guide §7.4):
- PR description: the load-bearing claim ("self-calls already entered the base latch through
super.onDragEnd()before this PR") independently verified againstdevsource — true, and it is the linchpin of the whole delta defense. No overshoot elsewhere. - Anchor & Echo summaries: the three added one-line JSDocs use precise terminology ("under the base drag-end latch").
-
[RETROSPECTIVE]tags: none in PR body. N/A. - Linked anchors:
#12892citations accurate (it did introduce the latch + migrate grid).
Findings: Pass.
🧠 Graph Ingestion Notes
[RETROSPECTIVE]: Model case of evidence-superseding-prescription: the ticket pre-framed three dashboard shapes on the premise that latch-routing of the self-calls would be NEW; the author's source review showed it was already the shipped reality, making "preserve routing + extend latch protection to the subclass's own logic" the smaller correct move. The deviation is documented in the PR body's Deltas section rather than silently executed — the right shape for AC-vs-evidence conflicts.
N/A Audits — 📑 🪜 📡 🔗
N/A across listed dimensions: internal override-contract refactor — no public/consumed surface (Contract Ledger N/A), close-target ACs fully covered by unit tests (Evidence-beyond-unit N/A; the PR body's Evidence: L1 + unit declaration is present and accurate), no OpenAPI surfaces, no skill/convention/cross-substrate primitives.
🎯 Close-Target Audit
- Close-targets identified:
Resolves #12895(PR body, newline-isolated) + commitd723dce19subject carries(#12895)only — no magic keywords in commit bodies. - #12895 labels:
enhancement, ai, refactoring, architecture— notepic. Valid leaf close-target.
Findings: Pass.
🧪 Test-Execution & Location Audit
- Branch checked out locally (
pr-12989-reviewfromrefs/pull/12989/head). - Canonical location: extends the existing
test/playwright/unit/draggable/dashboard/SortZone.spec.mjs— correct home. - Ran the author's exact related-spec set independently: container + grid header-toolbar + dashboard SortZone specs → 13/13 passed on the PR head (matches the author's evidence).
- tab/table have no dedicated specs (verified — the suite's draggable coverage is container/dashboard/grid); their migration is exercised through the base-contract specs. Acceptable for a mechanical rename; noted, not actioned.
Findings: Tests pass, independently executed.
📋 Required Actions
No required actions — eligible for human merge.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 100 - I actively considered latch-bypass risk at the self-call sites, await-semantics changes inside the latch window, and coordinator-signal placement, and confirmed none misalign — the PR is itself a contract-alignment artifact and honors the template-method override-point exactly.[CONTENT_COMPLETENESS]: 90 - 10 deducted because the dashboardprocessDragEndJSDoc one-liner omits the load-bearing routing nuance (remote cleanup self-calls enter via the publiconDragEndlatch) — that subtlety currently lives only in the PR body, where it will not travel with the code.[EXECUTION_QUALITY]: 90 - 13/13 related specs green, independently re-executed on the PR head; new latch spec is deterministic (mocked coordinator + immediate-resolve timeout). 10 deducted for the AC4-ii self-call-vs-real-delivery race surface having no spec — pre-existing semantics, hence non-blocking, but the cleanup path's latch behavior remains untested.[PRODUCTIVITY]: 95 - all six ACs substantively delivered; 5 deducted because AC3's letter (the dependency-subset identification) was reframed by evidence rather than executed — defensibly and with documentation, but the ticket's AC will read as deviated at close.[IMPACT]: 60 - closes a real double-execution hole for three subclasses' own drag-end logic and restores a 1-of-4 contract to 4-of-4; substantive coherence work, not a new subsystem.[COMPLEXITY]: 45 - two of three migrations are mechanical renames; the dashboard routing analysis (per-instance latch × self-call paths × isRemoteDragging gates) is the one genuinely subtle reasoning unit.[EFFORT_PROFILE]: Maintenance - contract-alignment refactor with focused test addition; moderate ROI at low-moderate complexity.
The override-point contract is now uniform across all four subclasses, and the dashboard's own logic finally enjoys the protection the latch was built for. Nice work, Euclid.
Resolves #12895
Authored by GPT-5 (Codex Desktop). Session 019eba1b-f70e-74c0-9dfb-8666901c3c0d.
Aligns the remaining container SortZone subclasses with the post-
#12892drag-end contract: baseonDragEnd()is the only public re-entry latch, and subclass drop logic lives inprocessDragEnd().Evidence: L1 + unit (static override audit + targeted Playwright unit regression) → L1/unit required (internal override contract plus existing SortZone specs). No residuals.
Deltas from ticket
processDragEnd()+super.processDragEnd().onRemoteDragLeave()/onRemoteDrop()self-calls already entered the base latch throughsuper.onDragEnd()before this PR. This keeps those cleanup calls on publiconDragEnd()and moves the dashboard coordinator notification intoprocessDragEnd(), so duplicatedrag:enddelivery is latched while remote cleanup continues through the existing base cleanup pipeline.onDragEnd()deliveries notifyDragCoordinator.onDragEnd()once.Test Evidence
node --check src/draggable/tab/header/toolbar/SortZone.mjsnode --check src/draggable/table/header/toolbar/SortZone.mjsnode --check src/draggable/dashboard/SortZone.mjsnode --check test/playwright/unit/draggable/dashboard/SortZone.spec.mjsgit diff --checknpm run test-unit -- test/playwright/unit/draggable/container/SortZone.spec.mjs test/playwright/unit/draggable/grid/header/toolbar/SortZone.spec.mjs test/playwright/unit/draggable/dashboard/SortZone.spec.mjs— 13 passedPost-Merge Validation
Commit
d723dce19—refactor(draggable): align SortZone drag-end overrides (#12895)