LearnNewsExamplesServices
Frontmatter
title>-
authorneo-opus-vega
stateOpen
createdAt12:36 AM
updatedAt12:44 AM
closedAt
mergedAt
branchesdevagent/14899-fleet-store-model-rebuild
urlhttps://github.com/neomjs/neo/pull/14905
contentTrust
projected
quarantined0
signals[]
Open
neo-opus-vega
neo-opus-vega commented on 12:36 AM

Resolves #14899

Related: #13015 (parent epic) · #14611 (B4/C2 honest-state contract, preserved) · #14802 (registry↔identity producer, out of scope) · #14868 (loadActivity fail-closed sibling)

Rebuilds the FM cockpit fleet view on Neo's data primitives instead of against them: one AgentOS.store.FleetRoster Store (singleton) of AgentOS.model.FleetAgent records is now the roster SSOT — the plain-array agents_ path, the hand-mapped fixture flow, and all seven-per-fleet per-card state.Provider instances are gone. Cards render from their record (applyRecord, in-place reference updates); the grid routes the store's reactivity (load re-derives, recordChange re-ranks on session-state change and updates the one affected card in place otherwise); the C2 lifecycle adapter writes pendingAction/controlReason via record.set(), so control state re-renders through the same store seam — never optimistic. CSS-in-JS is fully killed across the fleet primitives (StateDot, HealthSwatch, EventChip, FamilyRail) via class→token resolvers (stateClass/kindClass/familyClass) bound in the component SCSS, and AGENTS.md gains the operator-directed app-work required-context gate.

Evidence: L2 (live dev-server mount + computed-style/interaction probes + local unit runs; sandbox ceiling = local runtime) → L2 required (AC 8 mount-verified at live scale; all other ACs covered by unit/static L1). No residuals.

Deltas from ticket

  • Anchor drift: the ticket's FleetCockpit.mjs:168/:194 loadRoster anchors described the abandoned feat/fm-cockpit-live-roster branch; dev reality was FIXTURE_ROSTER-only. loadRoster() is implemented fresh on the Store path mirroring loadActivity's fail-closed routing: first wired payload populates the store (replacing the sample seed), later payloads merge onto records (record.set(row) per known agentId, add for a joiner), wired-but-EMPTY / not-wired / absent / thrown keep the last-known roster.
  • keyProperty shadow (framework observation): collection.Base defaults keyProperty: 'id' (truthy), so Store.getKeyProperty()'s this.keyProperty || this.model.keyProperty fallback can never reach a model-level key. FleetRoster mirrors keyProperty: 'agentId' at store level with an explanatory comment. Left un-ticketed per this ticket's own "no new tickets" AC — surfacing here for the swarm to route.
  • CSS-in-JS audit widened per the ticket's "full audit required" clause: beyond StateDot.style['--fm-dot'], the sweep found HealthSwatch (vdom node style), EventChip (--fm-chip) and FamilyRail (--fm-rail) — all four converted to the uniform class→token idiom with SCSS bindings per component.
  • Dock blueprint reshaped: fleetCardFactory emits record: {field bag} instead of stateProvider: {data} (a restored card would otherwise re-mint a provider). AgentCard accepts a live record or a plain snapshot bag through the same applyRecord path; the factory is spec-only-consumed today.
  • Honest sample label: FleetGrid.adapterState gains 'sample' — the seeded fixture renders "sample roster" instead of passing as live.
  • AGENTS.md byte budget: the file sat 9 bytes under the 24KB harness cap, so the gate addition is paired with semantics-preserving compressions (net result: 26 bytes below the pre-PR size, 35 below the cap) — see slot rationale.

Substrate slot rationale (AGENTS.md)

  • Added: the app-work context gate as an extension of the existing §edge_case_triggers "Authoring Discipline" row (no new row/section). 3-Axis: trigger-frequency = every apps/** writing/reviewing turn; failure-severity = high (the operator-verdict full-rejection class this ticket cleans up — the anti-pattern shipped twice past review); enforceability = discipline-only today, MACHINE-ENFORCEABLE-CANDIDATE. Retirement trigger stated in-row: retire once a mechanical apps/** data-path/style lint enforces it.
  • Modified (byte-budget enablers, semantics preserved, Atlas detail verified present before compressing): critical gate 6 tightened; gate 7's Maintainer-Polish sentence compressed to its pull-request-workflow.md §10 pointer (gate enumeration verified there); gate 8's release-mutation sentence tightened; §pre_commit_gates Gates 1–2 tightened; the Skill-Adherence pre-flight tail tightened; §edge_case_triggers Wake/Heartbeat + Ticket-Creation-Freshness rows tightened. File: 24,567 → 24,541 bytes (cap 24,576).

Test Evidence

  • Local unit runs (canonical unit config minus its Chroma webServer — which cannot start in this clone — --workers=1 matching CI): 75 passed across the 11 fleet spec files, plus cockpitDockDocument.spec.mjs 5 passed. The 3 remaining failures are a pre-existing cross-file worker-reuse transient: reproduced IDENTICALLY on origin/dev in a clean worktree under the same runner (eventChip:27, fleetGrid below-threshold, healthSwatch:42 — dev: 57 passed/3 failed; this branch: 75 passed/3 failed, +13 net-new tests). CI's canonical runner passes these specs on dev.
  • Direct node import probes: FleetRoster seeds 7 records keyed agentId; record.set() fires recordChange with exact field arrays; no-op sets suppressed by the deep-equal guard; controlReason object fields survive typed parsing; store.add returns records.
  • Live mount (AC 8): dev-server apps/agentos#/fleet — zero console errors; header "FLEET · 7 AGENTS sample roster"; health legend 5 working / 1 idle / 0 wedged / 0 rate-limited / 1 benched matches the record seed; tiers ranked online (agentId-sorted) → idle → benched with the benched card showing only the state-reachable ▶.
  • Class→token colors (AC 3, live): after build-themes, computed styles resolve fm-state-okrgb(52,211,153), fm-state-idlergb(245,181,68), chip fm-kind-pr/fm-kind-a2a and rails fm-family-claude/fm-family-gpt to distinct token colors; zero inline --fm-* styles anywhere in the cockpit DOM (querySelectorAll('[style*="--fm"]') → 0).
  • Live control round-trip (AC 5): clicking card controls with the dev bridge's endpoint absent renders "⚠ rejected: Failed to fetch" per card — the full chain button → controller (record reads) → lifecycleIntent → cockpit controller → C2 adapter → record.set → store recordChange → in-place applyRecord, retry open, no optimistic success.
  • AC 3 grep evidence: rg -n "\.style\s*=|\.style\[|style:\s*\{" apps/agentos/view/fleet/*.mjs → zero code hits (comment mentions only).
  • node --check green on all touched .mjs; check-block-alignment green; npm run agent-preflight -- --no-fix green with this body.

Post-Merge Validation

  • Deployed/portal build renders the fleet cockpit from FleetRoster with class→token colors + the "sample roster" label after its theme build.
  • First real fleetRoster bridge payload (the #14802 producer) populates the store and flips the grid live through loadRoster's first-wire path.

Commits

  • 4018ca323 — the full rebuild: FleetAgent model + FleetRoster store, record-driven card/grid/cockpit, C2 adapter record seam, CSS-in-JS kill + SCSS class→token bindings, 9 spec rewrites (+13 net-new tests), AGENTS.md app-work gate with paired compressions.

Authored by Vega (Claude Fable 5, Claude Code). Session d2fbbdb4-404b-47e1-bbb3-1b9e0330894b.