Skip to content

feat: Persist and serve fleet roster order - #3200

Merged
mabry1985 merged 3 commits into
mainfrom
feat/bd-7nck-persist-and-serve-fleet-roster-order
Aug 27, 2026
Merged

feat: Persist and serve fleet roster order#3200
mabry1985 merged 3 commits into
mainfrom
feat/bd-7nck-persist-and-serve-fleet-roster-order

Conversation

@mabry1985

Copy link
Copy Markdown
Member

Summary

Adds a persisted, id-based, hub-scoped fleet roster display order to the fleet control plane (ADR 0042).

  • graph/fleet/supervisor.py — new set_roster_order() / get_roster_order() plus a roster.json store (sibling of fleet.json, not mixed into its {id: record} map). set_roster_order() validates a submission as a complete permutation of the current member ids (host + local + remote) under the existing _state_lock(), rejecting non-list / non-string / blank / duplicate / unknown / missing ids without ever mutating saved order. status() now applies the saved order via _apply_roster_order(), which reconciles live membership: ordered members first, any member added since (or never ordered) keeps discovery order and follows, and a removed member simply drops out — every current member appears exactly once.
  • operator_api/fleet_routes.pyPUT /api/fleet/order accepting {order: [id, …]}, offloaded to a thread, mapping FleetError → HTTP 400; subsequent GET /api/fleet returns members in the saved order.
  • Tests — supervisor tests cover set/apply, restart survival, local/remote/host add-remove reconciliation, invalid-payload rejection with saved order intact, and the unset default; route tests cover persist-and-reorder, bad-payload 400s, and new-member reconciliation.
  • changelog.d/3197.added.md — release note.

Ordering is presentation metadata only: immutable ids, member configuration, remote credentials, the state-lock discipline, and the hub-only routing contract are all unchanged.

Fixes #3197

Add an id-based, hub-scoped fleet reorder API (PUT /api/fleet/order)
backed by a lock-protected persisted roster order in the supervisor.
status()/list reads apply the saved order while reconciling every known
member (host/local/remote) so additions/removals never lose members or
create duplicates. The submitted order is validated as a complete
permutation of current member ids under the state lock; duplicate,
unknown, missing, or malformed lists are rejected without mutating saved
order. Immutable ids, member config, remote credentials, and the
hub-only routing contract are unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 5 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 9bed28b5-7f2b-4e24-8295-0c81126db1a6

📥 Commits

Reviewing files that changed from the base of the PR and between fec18fe and 53cdfa8.

📒 Files selected for processing (6)
  • changelog.d/3196.fixed.md
  • changelog.d/3197.added.md
  • graph/fleet/supervisor.py
  • operator_api/fleet_routes.py
  • tests/test_fleet_routes.py
  • tests/test_fleet_supervisor.py

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@protoreview protoreview Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA panel review — PASS

code-review-structural · head 2abddee9944a · formal

No defects were found by the panel, and the verifier confirmed the empty result. However, confidence is reduced on two angles: the cross-file review report was truncated (incomplete coverage of inter-module interactions), and the protoPatch structural pass failed to complete (no automated structural analysis was available). These are process gaps, not findings — they mean the panel's "clean" verdict carries less weight than it would with full coverage. No prior requests to disposition. If this PR touches non-trivial cross-module logic or structural invariants, consider a targeted re-review of those areas before merging.

No findings — the review came back clean.

findings JSON (machine-readable)
[]

mabry1985 and others added 2 commits August 27, 2026 12:42
Review finding: _load_roster_order() called p.read_text() but caught only
OSError / json.JSONDecodeError. Invalid UTF-8 bytes in roster.json raise
UnicodeDecodeError from read_text() (before json.loads runs), which escaped the
loader — 500-ing status() and GET /api/fleet instead of falling back to unsaved
(discovery) order. Catch UnicodeDecodeError alongside OSError/JSONDecodeError,
matching the _load_archetype_catalog posture already used elsewhere in this file.

Adds a regression test writing raw invalid-UTF-8 bytes and asserting neither the
loader nor status() raises, plus broader corrupt/wrong-shape tolerance coverage.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@protoreview protoreview Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA panel review — PASS

code-review-structural · head 2b5def151836 · formal

⚠️ PR advanced 1 commit(s) during this round (2b5def15183653cdfa81d04f); 0 finding(s) in the delta were demoted to possibly addressed.

Low-risk change: the PR reorders status() output fields, which is a no-op when no order is saved. All five finders returned zero findings, and the verifier confirmed the empty array with no claims to ground. No panel disagreement, no verification changes, and no coverage gaps — the structural pass ran clean. Nothing to fix; the PR is clear to merge.

No findings — the review came back clean.

findings JSON (machine-readable)
[]

@protoreview protoreview Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QA panel review — PASS

code-review-structural · head 53cdfa81d04f · formal

The panel produced no findings to verify — the verifier explicitly reports that no findings array was delivered to it, so the structural verification pass was skipped entirely. There is no dedup, no re-ranking, and no disposition work to do. The practical effect is a clean pass with zero coverage: no finder surfaced a defect, and the verifier could not confirm or refute anything. If this PR is non-trivial, treat the absence of findings as a coverage gap rather than a signal of correctness.

No findings — the review came back clean.

findings JSON (machine-readable)
[]

@protoreview protoreview Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Promoting the PASS verdict for head 53cdfa81d04f: all checks terminal-green, zero unresolved review threads. (approve-on-green)

@mabry1985
mabry1985 merged commit ad4ff54 into main Aug 27, 2026
17 checks passed
@mabry1985
mabry1985 deleted the feat/bd-7nck-persist-and-serve-fleet-roster-order branch August 27, 2026 20:27
mabry1985 added a commit that referenced this pull request Aug 28, 2026
Add manual roster reordering to FleetManagerPanel (Settings ▸ Agents) atop
the PR #3200 backend: native HTML5 drag-and-drop plus keyboard-accessible
move-up/move-down controls, submitting the complete immutable-id permutation
to the hub-scoped PUT /api/fleet/order. Optimistic update with reconcile
through queryKeys.fleet on both success and failure.

Refs #3197

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mabry1985 added a commit that referenced this pull request Aug 28, 2026
)

Render manual roster reordering in the Settings → Agents fleet panel: a
per-row drag handle (native HTML5 DnD) plus explicit move-up / move-down
controls as the accessible, non-pointer equivalent. Reorders submit the
COMPLETE immutable-FleetAgent.id permutation to the hub-scoped
PUT /api/fleet/order (backend from #3200), update React Query optimistically,
and invalidate queryKeys.fleet on both success and failure so the polling
query stays authoritative.

The pinned host ("this instance") carries no reorder controls — it stays a
zero-button row (fixing the fleet.spec.ts:45 E2E regression) but remains a
valid drop slot. No name, URL, token, process state, or immutable id is
changed. Ordering math, boundary/busy disabling, accessible labels, and
failure-safe reconciliation are exported as pure helpers and unit-tested.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
mabry1985 added a commit that referenced this pull request Aug 28, 2026
…3227)

* feat(console): drag-and-drop + accessible fleet roster reordering (#3197)

Render manual roster reordering in the Settings → Agents fleet panel: a
per-row drag handle (native HTML5 DnD) plus explicit move-up / move-down
controls as the accessible, non-pointer equivalent. Reorders submit the
COMPLETE immutable-FleetAgent.id permutation to the hub-scoped
PUT /api/fleet/order (backend from #3200), update React Query optimistically,
and invalidate queryKeys.fleet on both success and failure so the polling
query stays authoritative.

The pinned host ("this instance") carries no reorder controls — it stays a
zero-button row (fixing the fleet.spec.ts:45 E2E regression) but remains a
valid drop slot. No name, URL, token, process state, or immutable id is
changed. Ordering math, boundary/busy disabling, accessible labels, and
failure-safe reconciliation are exported as pure helpers and unit-tested.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(console): guard the fleet drag path while a reorder save is pending (#3197)

The move-up/move-down controls already disabled via moveDisabled(...,
reorder.isPending), but the drag handle stayed live during a pending save,
so a second drag's drop could fire a concurrent full-order PUT — whose
out-of-order completion, or an earlier failure's optimistic roll-back, could
overwrite a later drag's intended order.

Route both reorder paths through a single pure choke point, shouldSubmitOrder
(order actually changed AND no save in flight), and disable the drag handle
itself while pending (draggable={false}, dimmed, guarded onDragStart) to match
the move buttons. Unit-test the new guard.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow drag-and-drop reordering of fleet members in Fleet Config

1 participant