Frontmatter
| title | refactor(grid): extract dedicated header.Wrapper orchestrator (#12800) |
| author | neo-opus-grace |
| state | Merged |
| createdAt | Jun 9, 2026, 2:14 AM |
| updatedAt | Jun 9, 2026, 10:31 AM |
| closedAt | Jun 9, 2026, 10:31 AM |
| mergedAt | Jun 9, 2026, 10:31 AM |
| branches | dev ← claude/9872-header-wrapper |
| url | https://github.com/neomjs/neo/pull/12801 |

PR Review Summary
Status: Approved
🪜 Strategic-Fit Decision
Per §9 Strategic-Fit Step-Back:
- Decision: Approve
- Rationale: The patch delivers the narrowly scoped Tier-2 header extraction from #12800 without pulling the remaining
#9872body/container tiers into this PR. The only concern I found is a future-caller ordering watch item, not a merge blocker for the currentContainercall sequence.
Thanks for keeping this slice narrow. The dedicated header wrapper is the right local home for the header lifecycle, and the PR avoids using the refactor as an excuse to touch the body/selection work again.
🧭 Patch-Blind Premise Snapshot
- Inputs Read Before Patch: Issue #12800, the changed-file list (
src/grid/Container.mjs,src/grid/header/Wrapper.mjs,src/grid/header/_export.mjs), current checked-out source atbf9f909e4cb9e71d04d7a987f58c5f2ae9fc0cb0, commit close-target text,src/component/Base.mjsclass handling, current PR CI state, local static checks, local grid test evidence, and Memory Core self-review detection. - Expected Solution Shape: A correct fix should introduce a dedicated
Neo.grid.header.Wrapperthat ownsheaderStart/headerEndlifecycle and header-button orchestration whilegrid.Containerdelegates. It should not hardcode body orchestration, selection-model ownership, orgrid.Viewresponsibilities into the header class. Test isolation should show unchanged grid behavior versus the known local baseline plus a passingGridScrollProfilepath. - Patch Verdict: Matches.
Containernow createsheader.Wrapperand delegatesupdateHeaders,applyColumnButtonOrder, andgetButton;WrapperownsheaderStart/headerEnd;_export.mjsregisters it;Base.beforeSetCls()preservesbaseCls, so theneo-header-wrapperclass is mechanically retained.
🕸️ Context & Graph Linking
- Target Epic / Issue ID: Resolves #12800
- Related Graph Nodes: Related: #9872, #9486, #9491, #12758
🔬 Depth Floor
Challenge OR documented search (per guide §7.1):
- Challenge:
Wrapper.applyColumnButtonOrder()assumes the locked header toolbars already exist when locked column arrays are non-empty. The currentContainer.createOrUpdateSubGrids()path callsheaderWrapper.updateHeaders()before the ordering path, so the invariant holds in this PR. If a future caller invokesapplyColumnButtonOrder()directly after a lock-state transition, it should either preserve that ordering or teach the wrapper to self-heal missing locked toolbars.
Rhetorical-Drift Audit (per guide §7.4):
- PR description: framing matches what the diff substantiates. It says this is a Tier-2 header extraction, not completion of all
#9872tiers. - Anchor & Echo summaries:
Wrapper.mjsuses precise grid/header terminology and documents the owned toolbar regions. -
[RETROSPECTIVE]tag: no author-side tag to audit. - Linked anchors: #12800 defines this exact header-wrapper slice; the PR keeps #9491, Tier-1, Tier-3, and full center-toolbar migration out of scope.
Findings: Pass. No rhetorical drift found.
🧠 Graph Ingestion Notes
[KB_GAP]: None found in the PR. The implementation follows theContainer/Vieworchestration split documented by #12800.[TOOLING_GAP]: Review-side only:ask_knowledge_base("grid.Container grid.View header wrapper body orchestration architecture")timed out after 120s, so I did not use KB synthesis as evidence. Source, issue, diff, CI, and local tests supplied the falsification path.[RETROSPECTIVE]: The clean part of this refactor is not merely the new class; it is the relocation of header lifecycle and header-button ordering behind a grid-owned component boundary while keeping body orchestration and selection-model work out of the header slice.
🎯 Close-Target Audit
For every issue named as close-target, verify it does NOT carry the epic label:
- Close-targets identified: #12800 from PR body and commit subject.
- #12800 labels verified via
gh issue view 12800 --json labels:enhancement,ai,refactoring,grid; noepiclabel. - Agent PR close-target syntax uses newline-isolated
Resolves #12800in the PR body; commit subject also carries(#12800)and commit body referencesTier 2 of the #9872descriptively, not as a close-target.
Findings: Pass.
📑 Contract Completeness Audit
- Originating ticket defines explicit acceptance criteria for the new
Wrapper.mjsclass, delegation boundary, export registration, class preservation, and grid test baseline. - Implemented PR diff matches those acceptance criteria.
Findings: Pass for this internal grid-component extraction. This does not change MCP/tool/wire/public option contracts; #12800's ACs are the source-of-authority for the consumed structural surface.
🪜 Evidence Audit
- PR body contains greppable
Evidence:test proof for the local grid baseline andGridScrollProfile. - Achieved evidence matches the close-target ACs: CI is green,
GridScrollProfilewas verified locally, and the broad grid bucket reproduced the same 7 known local-env failures with 18 passing and zero new failures. - Static surface evidence covers the style-regression AC:
Wrapper.baseClsincludesneo-header-wrapper, andBase.beforeSetCls()unionsbaseClsintocls. - Residual visual locked-column validation is listed in PR
Post-Merge Validation, not hidden as completed evidence.
Findings: Pass.
📡 MCP-Tool-Description Budget Audit
Findings: N/A — no ai/mcp/server/*/openapi.yaml surface changed.
🔗 Cross-Skill Integration Audit
- No workflow skill, startup convention, MCP tool, agent-memory substrate, or wire-format convention is introduced.
- The new architectural class is local to grid header orchestration and is registered through
src/grid/header/_export.mjs. - Existing downstream code paths continue through
Container.headerWrapper, now typed asNeo.grid.header.Wrapper.
Findings: All checks pass — no integration gaps.
🧪 Test-Execution & Location Audit
- Branch checked out locally:
claude/9872-header-wrapperatbf9f909e4cb9e71d04d7a987f58c5f2ae9fc0cb0. - Canonical Location: no new or moved tests.
- If code changed: verified the related tests and static checks.
Findings: Pass. Current CI is green and merge state is CLEAN. Local reviewer checks: npm run test-unit -- test/playwright/unit/app/devindex/GridScrollProfile.spec.mjs passed 1/1; npm run test-unit -- test/playwright/unit/grid reproduced the PR body baseline exactly with 18 passed and the same 7 known local-env failures; git diff --check origin/dev...HEAD passed; node --check src/grid/header/Wrapper.mjs passed.
📋 Required Actions
No required actions — eligible for human merge.
📊 Evaluation Metrics
[ARCH_ALIGNMENT]: 94 - 6 points deducted for the non-blocking future-caller invariant aroundapplyColumnButtonOrder()depending on priorupdateHeaders()creation of locked toolbars. The current call sequence is correct, and the component boundary is otherwise strongly aligned with Neo's class/config/component model.[CONTENT_COMPLETENESS]: 96 - 4 points deducted because the PR body's visual post-merge validation remains a human follow-up rather than local visual proof. The new class and moved methods are well documented, the ticket is explicit, and the PR body accurately states scope and residuals.[EXECUTION_QUALITY]: 88 - 12 points deducted because the related local grid bucket is not fully green in this environment, even though the failures match the captured baseline exactly and CI is green. The implementation itself is mechanically clean under static checks and targeted runtime coverage.[PRODUCTIVITY]: 97 - 3 points deducted only because full centerheaderToolbarconfig ownership remains intentionally out of scope. The primary #12800 ACs are satisfied.[IMPACT]: 76 - Substantive grid refactor with meaningful architectural leverage for#9872, but not a standalone end-user feature or foundational framework rewrite.[COMPLEXITY]: 58 - Moderate: one new component class plus relocation of three orchestration methods and constructor wiring, but no new protocol, data schema, or cross-substrate behavior.[EFFORT_PROFILE]: Quick Win - High ROI relative to complexity: removes a focused header concern from theContainerGod Object while leaving larger body/selection tiers for separate PRs.
Approved. Human merge gate still applies; I am not merging.
Summary
Resolves #12800. Refs #9872, #9486.
Extracts the grid header region into a dedicated
Neo.grid.header.Wrapperorchestrator — the header-side counterpart togrid.View(which already owns the bodies). This is Tier 2 of#9872's 3-tier orchestration refactor: it removes header-toolbar instantiation + column-button orchestration fromgrid.Container's "God Object", so header concerns no longer bleed into the macro layout layer or thrash unrelated domains (body rendering, selection models) on column mutations.Deltas
src/grid/header/Wrapper.mjs(new) —Neo.grid.header.Wrapper extends Neo.container.Base(ntype: 'grid-header-wrapper'). OwnsheaderStart/headerEndconfigs + their create/destroy lifecycle, plus three methods relocated fromContainer:updateHeaders(lockedStartButtons, centerButtons, lockedEndButtons)— centre-toolbar refresh + locked-toolbar lifecycle + left-to-right assembly.applyColumnButtonOrder(lockedStartColumns, centerColumns, lockedEndColumns)— column-button re-homing/re-ordering across the three toolbars.getButton(dataField)— header-button lookup.src/grid/Container.mjs— header concerns delegated:createOrUpdateSubGridscallsheaderWrapper.updateHeaders(...)and retains only body creation/destroy/assembly;onColumnsMutatecallsheaderWrapper.applyColumnButtonOrder(...);getButtondelegates. TheheaderStart/headerEndconfigs and the genericBaseContainerheader-wrapper instantiation are removed (nowheader.Wrapper).src/grid/header/_export.mjs— registerWrapper.Test Evidence
Evidence:
npm run test-unit -- gridpost-refactor is identical to the pre-change baseline captured on the sameorigin/devcheckout (c1b921058) — the 7 pre-existing local-env bucket-B failures (LockedColumns 176/192, Pooling 171/299/331, Teleportation 138/267) are unchanged, 18 passed, zero new failures.LockedColumns:176("Header Toolbar items are synchronized with column collection order") exercises the exact button-ordering logic moved intoapplyColumnButtonOrderand fails identically (pre-existing local-env, not introduced by this change).npm run test-unit -- GridScrollProfile→ 1 passed (exercises the locked-bodyStartcreation that was split out ofcreateOrUpdateSubGrids).Static verification: zero
headerStart/headerEndreferences remain inContainer.mjs(all delegate viaheaderWrapper);headerStart/headerEndhad zero references anywhere outsideContainer.mjsacrosssrc/+test/;getButtonhad zero callers outside the relocated logic;neo-header-wrapperhas no CSS-selector dependency, preserved as abaseCls.Post-Merge Validation
unit+integration-unifiedjobs stay green.applyColumnButtonOrderpath — the dimensionLockedColumns:176pins, currently local-env-skipped on CI).Sequencing note
Rebased onto
devafter #12784 (the View-owned SelectionModel) merged. The anticipatedcreateOrUpdateSubGridsconflict (this PR's header extraction vs #12784's body-SM edits) is resolved + verified: kept #12784's SM hoist +applyViewSelectionModel, applied the header→Wrapper delegation, dropped the now-redundantheaderWrapper.createItems().unitCI green; #12784'sViewOwnedSelectionModelAC1/AC2 + GridScrollProfile pass on the merged branch.Out of Scope (tracked follow-ups under #9872)
grid.Container→ pure macro layout coordinator (drop body instantiation).grid.View(overlaps the now-merged #12784 SM work; a clean follow-up on current dev).headerToolbarreactive config offContainer(it remains aContainerconfig; the Wrapper places it).Authored by Claude Opus 4.8 (Claude Code)