Context
GitHub's current beginner guidance explicitly points newcomers toward the good first issue label. Neo's only open ticket with that label, #15124, is already assigned, so there is no unassigned entry point for the next contributor.
This ticket creates another deliberately bounded, self-contained contribution outside the v13.2 release work.
[!IMPORTANT]
Reserved for first-time Neo.mjs contributors. The Neo maintainer team will not claim, edit, or implement this ticket through 2026-07-22. A first-time contributor may claim it immediately; contributor questions and PR reviews remain welcome during the reservation window.
Please comment before starting. A maintainer will assign the ticket to one contributor to avoid duplicate work.
Problem
Neo.util.Style.compareStyles() is the pure utility that turns old and new style values into the compact style delta consumed by Neo's VDOM diffing path. It accepts object or string inputs, reports added and changed properties, represents removed properties with null, and returns null when no delta exists.
The method has no focused unit spec. This is not theoretical risk: the regression documented in #6859 broke style removal when the new style value became absent, affecting UI filtering and producing misleading VDOM symptoms. The runtime fix landed, but its edge remains unpinned.
Architectural reality
- Runtime source:
src/util/Style.mjs
- String normalization source:
src/core/Util.mjs#createStyleObject()
- Direct runtime consumer: the
style branch in src/vdom/Helper.mjs#compareAttributes()
- Canonical test destination:
test/playwright/unit/util/Style.spec.mjs
- Established sibling pattern:
test/playwright/unit/util/Json.spec.mjs and String.spec.mjs
- Structural pre-flight: fast-path; the proposed spec exactly matches the existing
test/playwright/unit/util/ sibling role and naming pattern.
- This is pure single-thread utility coverage. It does not require a browser DOM, component instance, or VDOM integration fixture.
Fix
Add one focused Playwright unit spec for the existing Neo.util.Style.compareStyles() contract. Pin the current documented and runtime behavior without changing src/util/Style.mjs.
Contract ledger
| Target surface |
Source of authority |
Existing behavior to pin |
Fallback / edge case |
Docs |
Evidence |
Style.compareStyles(newStyle, oldStyle) with object inputs |
src/util/Style.mjs; src/vdom/Helper.mjs style-delta consumer |
Return only added or changed new properties and encode removed old properties as null, without mutating either input |
Return null when both objects express the same styles |
No change |
Focused object-delta and immutability assertions in Style.spec.mjs |
| Missing-side semantics |
src/util/Style.mjs; historical regression #6859 |
With no old style, return a shallow clone of the new style; with no new style, return a delta that maps every old key to null |
Return null when both inputs are absent |
No change |
Focused identity, absent-input, and #6859 regression assertions |
| String normalization |
src/util/Style.mjs; src/core/Util.mjs#createStyleObject() |
Normalize string inputs before comparison, including kebab-case property names and numeric values |
Object/string mixed inputs follow the same delta semantics |
No change |
String/string and mixed-input assertions |
Acceptance criteria
Out of scope
- Refactoring or changing
Neo.util.Style
- Changing
Neo.core.Util#createStyleObject()
- Browser CSS validation or computed-style assertions
- Adding VDOM integration coverage
- Removing unrelated imports or performing broad utility cleanup
- Adding coverage for
Neo.util.Date
- Any v13.2 release, Agent OS, Fleet Manager, or docking-feature work
Avoided traps
- Do not run default
npx playwright test; Neo requires its custom unit configuration.
- Do not use browser DOM or
toHaveCSS() assertions for this pure delta utility.
- Do not broaden the ticket into CSS parsing or normalization changes.
- Do not change runtime behavior merely to make a test easier.
Prior art and duplicate sweep
- Template precedents: #15117, #15118, and #15124.
- Historical regression anchor: #6859.
- Live latest-20-open, exact GitHub, Knowledge Base, local issue/discussion, and recent all-state A2A claim sweeps were refreshed immediately before creation at 2026-07-15T15:08:33.095Z; no equivalent ticket or active lane was found.
Decision Record impact
None. This ticket pins an existing public utility contract and introduces no architectural decision.
Origin Session ID: 2c46ce52-9c69-46a7-a8c9-4db937d2a341
Context
GitHub's current beginner guidance explicitly points newcomers toward the
good first issuelabel. Neo's only open ticket with that label,#15124, is already assigned, so there is no unassigned entry point for the next contributor.This ticket creates another deliberately bounded, self-contained contribution outside the v13.2 release work.
Problem
Neo.util.Style.compareStyles()is the pure utility that turns old and new style values into the compact style delta consumed by Neo's VDOM diffing path. It accepts object or string inputs, reports added and changed properties, represents removed properties withnull, and returnsnullwhen no delta exists.The method has no focused unit spec. This is not theoretical risk: the regression documented in
#6859broke style removal when the new style value became absent, affecting UI filtering and producing misleading VDOM symptoms. The runtime fix landed, but its edge remains unpinned.Architectural reality
src/util/Style.mjssrc/core/Util.mjs#createStyleObject()stylebranch insrc/vdom/Helper.mjs#compareAttributes()test/playwright/unit/util/Style.spec.mjstest/playwright/unit/util/Json.spec.mjsandString.spec.mjstest/playwright/unit/util/sibling role and naming pattern.Fix
Add one focused Playwright unit spec for the existing
Neo.util.Style.compareStyles()contract. Pin the current documented and runtime behavior without changingsrc/util/Style.mjs.Contract ledger
Style.compareStyles(newStyle, oldStyle)with object inputssrc/util/Style.mjs;src/vdom/Helper.mjsstyle-delta consumernull, without mutating either inputnullwhen both objects express the same stylesStyle.spec.mjssrc/util/Style.mjs; historical regression #6859nullnullwhen both inputs are absent#6859regression assertionssrc/util/Style.mjs;src/core/Util.mjs#createStyleObject()Acceptance criteria
test/playwright/unit/util/Style.spec.mjsusing the existing util-spec setup and import pattern.src/Neo.mjs,src/core/_export.mjs, andsrc/util/Style.mjs.#6859regression edge: an absent new style maps every old style key tonull.null.src/util/Style.mjs.npm run test-unit -- test/playwright/unit/util/Style.spec.mjsnpm run test-unitOut of scope
Neo.util.StyleNeo.core.Util#createStyleObject()Neo.util.DateAvoided traps
npx playwright test; Neo requires its custom unit configuration.toHaveCSS()assertions for this pure delta utility.Prior art and duplicate sweep
Decision Record impact
None. This ticket pins an existing public utility contract and introduces no architectural decision.
Origin Session ID:
2c46ce52-9c69-46a7-a8c9-4db937d2a341