Resolves #15197
Establishes owner-keyed root and wrapper class contributions in Neo.component.Base, so reapplying caller-authored cls or wrapperCls no longer erases semantic, layout, plugin, drag, or motion classes. Public getters remain deduplicated aggregate views, while mutation snapshots and recreation configs preserve their authored sources; engine producers now replace or release only their own contribution.
Evidence: L3 (focused unit matrix plus a live Chromium Neural Link journey across pooled component identity, VDOM, DOM, computed style, and geometry) → L3 required (runtime class ownership and pooled-reuse ACs). No residuals.
Deltas from ticket
- Extended the ownership boundary to
wrapperCls, because wrapper and root contributions can share one physical VDOM node and have the same last-writer-wins failure mode.
- Added stable owner APIs plus authored-source accessors, replay-safe mutation snapshots, and
toRecreationConfig() so undo/removal and component recreation do not promote rendered classes into authored config.
- Migrated framework-owned class producers across layouts, dragging, docking, grids/tables, fields, dialogs, buttons, selection, plugins, and the affected applications.
- Preserved compatibility for lightweight layout/drag test doubles through scoped fallback state rather than forcing every consumer to implement the new API immediately.
Test Evidence
- Core ownership, Button semantics, wrapper sharing, deduplication, silent/non-silent updates, getter mutation, and recreation:
npx playwright test ai/InstanceServiceRemoveUndoCapture.spec.mjs ai/InstanceServiceUndoCapture.spec.mjs button/Base.spec.mjs dashboard/DockMotionSignal.spec.mjs draggable/container/SortZone.spec.mjs functional/DuplicateIdBug.spec.mjs grid/RowCellIdMigration.spec.mjs grid/column/Component.spec.mjs tab/BodyContainer.spec.mjs -c test/playwright/playwright.config.unit.mjs --workers=1 — 57 passed on rebased head afe701379.
- Workstation pooled-grid journey:
NEO_E2E_PORT=8124 npx playwright test workstation/WorkstationNL -c test/playwright/playwright.config.e2e.mjs --workers=1 — 1 passed; the same Button identity moved to a different record while owner config, aggregate classes, VDOM, DOM, computed flex-direction, and geometry stayed aligned.
- Broad unit suite:
npm run test-unit — 7,179 passed, 9 failed, 5 skipped, 43 not run. The patch-related suites passed; serial reruns classified the remaining failures as seven sandbox EPERM writes to .neo-ai-data, one tree-lint timeout, and one QueryReRanker flake that passed alone.
- Source gates: all 80 modified
.mjs files parsed; git diff --check, agent-preflight --no-fix, and the full pre-commit hook passed.
- Directly touched app/feature surfaces without a dedicated new spec are covered by the broad unit run plus their migrated owner call sites; no SCSS or theme surface changed.
Depth Floor
A serialized aggregate cls value cannot carry the non-enumerable provenance used by same-process getter-mutate-reassign compatibility. Consequently, a future Neural Link get_instance_properties(cls) → fresh set_instance_properties(cls) round trip can promote rendered tokens into authored input. This PR protects same-process mutation, undo capture, removal/recreation, and the live pooled-grid journey; an explicit authored-source remote projection is the cleaner API-boundary follow-up. This is not a failing close-target AC.
Post-Merge Validation
Evolution
The initial pooled-Button symptom exposed a representation bug rather than a Grid bug: one array was simultaneously treated as authored input and rendered aggregate state. The implementation therefore moved from a consumer repair to a central ownership compositor, then expanded only as far as the engine producers whose direct array mutations could bypass that boundary.
Authored by Emmy (GPT-5.6 Sol Ultra, Codex). Session 2c46ce52-9c69-46a7-a8c9-4db937d2a341.
Resolves #15197
Establishes owner-keyed root and wrapper class contributions in
Neo.component.Base, so reapplying caller-authoredclsorwrapperClsno longer erases semantic, layout, plugin, drag, or motion classes. Public getters remain deduplicated aggregate views, while mutation snapshots and recreation configs preserve their authored sources; engine producers now replace or release only their own contribution.Evidence: L3 (focused unit matrix plus a live Chromium Neural Link journey across pooled component identity, VDOM, DOM, computed style, and geometry) → L3 required (runtime class ownership and pooled-reuse ACs). No residuals.
Deltas from ticket
wrapperCls, because wrapper and root contributions can share one physical VDOM node and have the same last-writer-wins failure mode.toRecreationConfig()so undo/removal and component recreation do not promote rendered classes into authored config.Test Evidence
npx playwright test ai/InstanceServiceRemoveUndoCapture.spec.mjs ai/InstanceServiceUndoCapture.spec.mjs button/Base.spec.mjs dashboard/DockMotionSignal.spec.mjs draggable/container/SortZone.spec.mjs functional/DuplicateIdBug.spec.mjs grid/RowCellIdMigration.spec.mjs grid/column/Component.spec.mjs tab/BodyContainer.spec.mjs -c test/playwright/playwright.config.unit.mjs --workers=1— 57 passed on rebased headafe701379.NEO_E2E_PORT=8124 npx playwright test workstation/WorkstationNL -c test/playwright/playwright.config.e2e.mjs --workers=1— 1 passed; the same Button identity moved to a different record while owner config, aggregate classes, VDOM, DOM, computedflex-direction, and geometry stayed aligned.npm run test-unit— 7,179 passed, 9 failed, 5 skipped, 43 not run. The patch-related suites passed; serial reruns classified the remaining failures as seven sandboxEPERMwrites to.neo-ai-data, one tree-lint timeout, and one QueryReRanker flake that passed alone..mjsfiles parsed;git diff --check,agent-preflight --no-fix, and the full pre-commit hook passed.Depth Floor
A serialized aggregate
clsvalue cannot carry the non-enumerable provenance used by same-process getter-mutate-reassign compatibility. Consequently, a future Neural Linkget_instance_properties(cls)→ freshset_instance_properties(cls)round trip can promote rendered tokens into authored input. This PR protects same-process mutation, undo capture, removal/recreation, and the live pooled-grid journey; an explicit authored-source remote projection is the cleaner API-boundary follow-up. This is not a failing close-target AC.Post-Merge Validation
devunit shards run in a writable CI environment without the local.neo-ai-datasandbox ceiling.Evolution
The initial pooled-Button symptom exposed a representation bug rather than a Grid bug: one array was simultaneously treated as authored input and rendered aggregate state. The implementation therefore moved from a consumer repair to a central ownership compositor, then expanded only as far as the engine producers whose direct array mutations could bypass that boundary.
Authored by Emmy (GPT-5.6 Sol Ultra, Codex). Session 2c46ce52-9c69-46a7-a8c9-4db937d2a341.