Frontmatter
| title | fix(grid): resolve dragged column identity in SortZone onDragEnd (#12878) |
| author | neo-fable |
| state | Merged |
| createdAt | Jun 11, 2026, 2:19 AM |
| updatedAt | Jun 11, 2026, 2:55 AM |
| closedAt | Jun 11, 2026, 2:55 AM |
| mergedAt | Jun 11, 2026, 2:55 AM |
| branches | dev ← agent/12878-sortzone-locked-resort |
| url | https://github.com/neomjs/neo/pull/12881 |

PR Review Summary
Status: Approved
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: This PR is the right merge unit for the #12878 locked-region membership/corruption leaf. The remaining landing-index overshoot is already split to #12880 with a different code home (
src/draggable/container/SortZone.mjs) and an instrumentation-first plan, so Request Changes here would conflate two mechanically separate defects.
Peer-Review Opening: Reviewed at exact head 386c589db3c8224c250e21cb2cdc7b9dbffd5b6d. The patch is narrow, the ticket split is documented on both #12878 and #12880, and the focused unit verification plus CI are green.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: #12878 body/comments, #12880 split ticket, PR changed-file list, current
devsource forsrc/draggable/grid/header/toolbar/SortZone.mjs, sibling parent source insrc/draggable/container/SortZone.mjs,src/grid/Container.mjs/src/grid/Body.mjsdataFieldkey-property usage, focused unit spec, CI status, and SortZone KB grounding. - Expected Solution Shape: A correct #12878 fix should stop non-owning/stale-index lock mutations, resolve the dragged column through the existing grid column identity substrate, use release-region truth without hardcoding devindex widths, and leave parent drag-cadence/index overshoot to the separate parent-class lane. Test isolation should remain focused: existing helper unit coverage plus documented L3 live protocol until #12807 can make the full drag path durable in CI.
- Patch Verdict: Matches and improves the expected shape. The diff re-resolves the dragged column by
dataFieldaftersuper.onDragEnd, applies coordinate-backed region verdicts to every drop instead of only cross-region deltas, snapshots locked-body rects at drag start, and does not patch parentcontainer.SortZonecadence that belongs to #12880.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #12878
- Related Graph Nodes: #12880 (landing-index overshoot split), #12807 (durable drag simulation substrate), #9491 / PR #12792 lineage, #9486 grid multi-body umbrella
🔬 Depth Floor
Challenge OR documented search (per guide §7.1):
Challenge considered: whether dataField is stable enough as the dragged-column identity. Source check supports it for this surface: grid.Container creates the columns collection with keyProperty: 'dataField', grid.Body column positions are also keyed by dataField, and header/body lookup paths already use it as the durable column key. Residual risk is therefore not a blocker for #12878; duplicate/null dataField columns would be a broader grid contract issue, not introduced by this patch.
Rhetorical-Drift Audit (per guide §7.4):
- PR description: framing matches what the diff substantiates. It claims locked-region corruption/membership repair, not full landing-index correctness.
- Anchor & Echo summaries: no new public JSDoc was added; inline comments are localized to the async drag boundary and match the code path.
-
[RETROSPECTIVE]tag: N/A; no retrospective tag in the PR body. - Linked anchors: #12878 establishes the corruption/membership defect; #12880 establishes the mechanically separate landing-index residual.
Findings: Pass.
🧠 Graph Ingestion Notes
[KB_GAP]: N/A.[TOOLING_GAP]: Memory Core raw-memory query degraded during reviewer self-review detection (QUERY_PATH_DEGRADED/chroma-error-finding-id). GitHub author/live state still established this as a peer review.[RETROSPECTIVE]: The right boundary is now clear: grid subclass owns lock-region membership correctness; parentcontainer.SortZoneowns landing cadence. Keeping #12880 separate preserves that ownership instead of hiding parent cadence inside a grid-local patch.
🎯 Close-Target Audit
For every issue named as close-target, verify it does NOT carry the epic label:
- Close-targets identified: #12878 via PR body / GitHub
closingIssuesReferences. - #12878 labels checked:
bug,ai,grid; noepiclabel. - Branch commit message checked with
git log origin/dev..HEAD; no stale extra close-targets found.
Findings: Pass.
📑 Contract Completeness Audit
- Originating ticket contains explicit ACs plus a pre-PR AC2 amendment comment.
- Implemented PR diff matches the amended contract: correct region + stable membership under #12878; landing-index accuracy remains #12880.
Findings: Pass.
🪜 Evidence Audit
- PR body contains an
Evidence:declaration line. - #12878 required L3+ after the prior L2-only miss; the ticket comments document the manual L3 rig, characterization, and post-fix verification matrix.
- Residuals are explicit: durable CI regression waits on #12807, and landing-index accuracy is #12880.
- Review language preserves the evidence boundary: I independently verified exact-head source, CI, and focused unit coverage; I audited the documented L3 evidence but did not replay the live devindex rig because Neural Link had no connected window topology during review.
Findings: Pass with named residuals (#12807, #12880).
📡 MCP-Tool-Description Budget Audit
Findings: N/A — no OpenAPI/MCP tool descriptions touched.
🔗 Cross-Skill Integration Audit
Findings: N/A — routine grid code change; no new workflow convention, skill, MCP surface, or wire format.
🧪 Test-Execution & Location Audit
- Branch checked out locally via
checkout_pull_request. - Canonical Location: no new/moved test files in this PR.
- If code changed: ran related focused unit test locally.
- CI checked via
gh pr checks 12881: Analyze, Classify test scope, CodeQL, integration-unified, lint-pr-body, and unit all pass.
Findings: Tests pass. Local command: npm run test-unit -- test/playwright/unit/draggable/grid/header/toolbar/SortZone.spec.mjs → 3 passed.
📋 Required Actions
No required actions — eligible for human merge.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 95 - 5 points deducted only for the still-open parent cadence residual; the patch itself respects the grid/body split and keeps parent SortZone behavior out of the grid subclass.[CONTENT_COMPLETENESS]: 95 - 5 points deducted because the original issue body still carries the broad AC2 wording, but the pre-PR AC2 amendment and PR body make the split explicit and reviewable.[EXECUTION_QUALITY]: 90 - 10 points deducted because durable L3 CI regression coverage still depends on #12807; exact-head source review, CI, and local focused unit coverage are green, and the documented L3 manual evidence covers the runtime gap for this merge unit.[PRODUCTIVITY]: 95 - 5 points deducted because #12880 remains for visible landing accuracy; this PR does deliver the dangerous silent membership corruption fix.[IMPACT]: 80 - Major release-window grid stability fix on the flagship multi-region drag surface; localized file touch, but high v13 optics and correctness value.[COMPLEXITY]: 55 - Moderate: one file, but the async drag lifecycle crosses toolbar-local index space, global column collection state, locked body geometry, and VDOM/body refresh timing.[EFFORT_PROFILE]: Quick Win - High release value with a narrow patch, while the heavier parent cadence defect remains isolated in #12880.
Approved. This satisfies the cross-family review gate for the #12878 corruption fix; #12880 remains the next visible grid-polish lane, not a blocker for this PR.
Authored by Claude Fable 5 (Claude Code). Session 567191c3-16f6-4235-914c-b51fc94d1514.
Resolves #12878 Related: #12880 Refs #12807 Refs #9486
Fixes the silent locked-region corruption in multi-region column drag-and-drop, operator-flagged as release-blocking ("polish the grid before v13 ships"). Three root causes in
src/draggable/grid/header/toolbar/SortZone.mjs, all live-verified on the devindex 50k-row grid through an agent-driven whitebox rig:onDragEndindexed the globalcolumnscollection with the dragged element'saria-colindex— toolbar-local AND pre-drag-stale — so the lock-inference inspected and mutated an unrelated column (live case: a center-region drag unlocked the locked#index column). Now the dragged column'sdataFieldis captured at drag-start and re-resolved against the global collection after the move applies; lock-logic never runs on an unresolved column.nulland silently unlocked the column. The release-coordinate region is now the verdict for every drop; neighbor inference remains the no-coordinate fallback.Evidence: L3 (live whitebox verification — agent-dispatched MouseEvent sequences on devindex, engine-truth assertions via Neural Link vdom/rect/lock-state queries, fresh session per case, three post-fix runs) → L3 required (#12878 AC3 documented-manual-protocol branch; declared in the ticket). Residual: durable CI regression spec lands via the #12807 simulatability substrate [active lane, @neo-opus-ada].
Deltas from ticket
container.SortZonecadence defect, split to #12880 with full discriminator evidence (+1 short-drag / +3 long-drag deterministic overshoot, masked in small regions by clamping, likely affects single-region toolbars too). Separate code home, separate failure class, instrumentation-first next step.Test Evidence
npx playwright test test/playwright/unit/draggable/grid/header/toolbar/SortZone.spec.mjs -c test/playwright/playwright.config.unit.mjs→ 3 passed (pure helpersgetDropRegion/columnIndexOffset/gridBodyunchanged).[#,Rank,User]→[Rank,User,#]; within-start drag →#EJECTED into center idx 0 (start 3→2 items). Numeric fits to the pixel on the ticket.[#, User, Rank], no ejection, locks stable; center drags → start region untouched, zero lock mutations; DragCoordinator clean after every drop; zero worker console errors.Post-Merge Validation
Commits
386c589db— fix(grid): resolve dragged column identity in SortZone onDragEnd (#12878)