Scope Correction — atomicity gate applied
The original six-primitive prescription was falsified before implementation. Sequential removeCls(old) + addCls(new) calls publish two reactive cls changes and an intermediate state carrying neither semantic class. That is the exact stop condition the original ticket named, and D#15200 still has no graduated atomic replace/batch primitive.
This leaf is therefore narrowed to the two Fleet mutations that already map to one public class-API call:
StateDot.afterSetLive → enforced toggleCls('fm-live', value)
HealthBar.updateAnimateCls → enforced toggleCls('fm-animate-counts', this.animateCounts)
The four old→new replacement hooks (EventChip.afterSetKind, StateDot.afterSetState, HealthSwatch.afterSetState, and FamilyRail.afterSetFamily) remain on their current single aggregate assignment. SourceHealthMarker.applyHealth remains on its coherent set({cls, text}) batch. Their future representation-independent cleanup belongs after D#15200 resolves atomic class replacement; no successor micro-ticket is created now.
The evidence trail is explicit:
Context
Both remaining sites read the aggregate component cls array only to enforce one Boolean class. Neo.component.Base#toggleCls(value, add) already owns that exact boundary in a single reactive assignment, so these two migrations remove representation coupling without changing transition atomicity.
This is an app-only consumer cleanup. It does not define the future cls / wrapperCls representation and introduces no new core API.
The Problem
apps/agentos/view/fleet/StateDot.mjs#afterSetLive copies this.cls, adds or removes fm-live, and reassigns the aggregate.
apps/agentos/view/fleet/HealthBar.mjs#updateAnimateCls filters this.cls, optionally appends fm-animate-counts, and reassigns the aggregate.
Each intent is a single enforced Boolean membership. Reading unrelated classes is unnecessary.
The Fix
Use the existing enforced toggle form at both sites:
component.toggleCls(className, shouldExist)
Preserve all state, token, Store, motion, and rendering behavior. Do not migrate any old→new replacement or multi-class batch under this ticket.
Decision Record impact
None. The change consumes the existing component API and leaves D#15200 fully open.
Acceptance Criteria
Out of Scope
EventChip.afterSetKind, StateDot.afterSetState, HealthSwatch.afterSetState, and FamilyRail.afterSetFamily
SourceHealthMarker.applyHealth
- An atomic
replaceCls / class transaction primitive
- D#15200’s object-shaped
cls / wrapperCls design
- Core, VDOM, SCSS, token, visual-design, State Provider, Store, or Model changes
- Non-AgentOS applications and Workstation
Avoided Traps
- No sequential remove-then-add for one semantic replacement.
- No aggregate spread/filter/reassign replacement under another local helper.
- No
silentVdomUpdate or app-local batching seam.
- No new micro-ticket for the deferred atomicity work.
Related
Origin Session ID: e0413ff9-4738-42b2-9f76-c50690e9edea
Retrieval Hint: "AgentOS Fleet StateDot live HealthBar animateCounts toggleCls single reactive assignment atomicity"
Scope Correction — atomicity gate applied
The original six-primitive prescription was falsified before implementation. Sequential
removeCls(old)+addCls(new)calls publish two reactiveclschanges and an intermediate state carrying neither semantic class. That is the exact stop condition the original ticket named, and D#15200 still has no graduated atomic replace/batch primitive.This leaf is therefore narrowed to the two Fleet mutations that already map to one public class-API call:
StateDot.afterSetLive→ enforcedtoggleCls('fm-live', value)HealthBar.updateAnimateCls→ enforcedtoggleCls('fm-animate-counts', this.animateCounts)The four old→new replacement hooks (
EventChip.afterSetKind,StateDot.afterSetState,HealthSwatch.afterSetState, andFamilyRail.afterSetFamily) remain on their current single aggregate assignment.SourceHealthMarker.applyHealthremains on its coherentset({cls, text})batch. Their future representation-independent cleanup belongs after D#15200 resolves atomic class replacement; no successor micro-ticket is created now.The evidence trail is explicit:
Context
Both remaining sites read the aggregate component
clsarray only to enforce one Boolean class.Neo.component.Base#toggleCls(value, add)already owns that exact boundary in a single reactive assignment, so these two migrations remove representation coupling without changing transition atomicity.This is an app-only consumer cleanup. It does not define the future
cls/wrapperClsrepresentation and introduces no new core API.The Problem
apps/agentos/view/fleet/StateDot.mjs#afterSetLivecopiesthis.cls, adds or removesfm-live, and reassigns the aggregate.apps/agentos/view/fleet/HealthBar.mjs#updateAnimateClsfiltersthis.cls, optionally appendsfm-animate-counts, and reassigns the aggregate.Each intent is a single enforced Boolean membership. Reading unrelated classes is unnecessary.
The Fix
Use the existing enforced toggle form at both sites:
component.toggleCls(className, shouldExist)Preserve all state, token, Store, motion, and rendering behavior. Do not migrate any old→new replacement or multi-class batch under this ticket.
Decision Record impact
None. The change consumes the existing component API and leaves D#15200 fully open.
Acceptance Criteria
StateDot.afterSetLiveuses one enforcedtoggleClscall and no aggregateclsread/reassignment.HealthBar.updateAnimateClsuses one enforcedtoggleClscall and no aggregateclsread/reassignment.clschange when membership changes.SourceHealthMarker.applyHealthremain unchanged.Out of Scope
EventChip.afterSetKind,StateDot.afterSetState,HealthSwatch.afterSetState, andFamilyRail.afterSetFamilySourceHealthMarker.applyHealthreplaceCls/ class transaction primitivecls/wrapperClsdesignAvoided Traps
silentVdomUpdateor app-local batching seam.Related
Origin Session ID: e0413ff9-4738-42b2-9f76-c50690e9edea
Retrieval Hint: "AgentOS Fleet StateDot live HealthBar animateCounts toggleCls single reactive assignment atomicity"