Skip to content

fleetos_1607: reconcile pre-apply gate — validate placement compatibility before reconcile applies - #206

Open
adamkrawczyk wants to merge 1 commit into
mainfrom
tori/fleetos-reconcile-precheck-0807
Open

fleetos_1607: reconcile pre-apply gate — validate placement compatibility before reconcile applies#206
adamkrawczyk wants to merge 1 commit into
mainfrom
tori/fleetos-reconcile-precheck-0807

Conversation

@adamkrawczyk

Copy link
Copy Markdown
Contributor

What

Adds a pre-apply gate that re-validates every LIVE fleet placement's compatibility (loop requires{} vs member provides{}) BEFORE a reconcile applies further changes.

Why (fleetos_1607 gap)

preflight_member only ran once, at assign time. If a manifest is re-declared with a new requirement, or a member re-pings with fewer capabilities, the now-incompatible placement stays live and nothing catches the drift — it silently propagates until the loop fails at runtime on a host it can no longer satisfy.

What ships

  • app/services/placement.py::reconcile_precheck(db, fleet_id) — walks every live (assigned/active/draining) placement, re-runs the existing preflight, returns {ok, checked, incompatible[]} with named missing requirements per placement.
  • app/mcp/tools/placement.py::loopskill_reconcile_precheck — MCP tool, manager-capability gated (same as loopskill_assign/loopskill_placements), wired into _PLACEMENT_TOOLS + dispatch_placement.
  • app/fleet_routes.pyPOST /api/fleets/{id}/reconcile-precheck HTTP adapter (mirrors the MCP contract per PM7). Also added fleet_not_found → 404 to the route's shared error-status map (this is the first placement-tool route exposed over HTTP; the map previously only needed to handle sync/subscribe's error vocabulary).

Tests

8 new tests across the service/tool/route layers:

  • clean fleet → ok
  • keystone: assign succeeds, manifest drifts (requires gains a package), precheck catches it with the named missing requirement
  • removed/evacuated placements excluded (not live)
  • manager-capability 403 (bare fleet-member key)
  • MCP dispatch chain reachability
  • HTTP route 200 / 404 (nonexistent fleet) / 403 (non-owner)

pytest tests/test_fleetos_A_placements.py tests/test_fleetos_I_ingest.py tests/test_portal_0610_j3_fleet_routes.py -q41 + 32 passed (ran in two batches, no regressions). ruff check + ruff format --check clean on all touched files.

Full suite wasn't run in this session (10-min budget cap on the fallback-executor cron that authored this) — targeted coverage on every file touched, all green.

🤖 Shipped via atomic-habits fallback executor (2026-08-07, rank-1, Adam absent).

…atibility before reconcile applies

Adds placement_svc.reconcile_precheck(): re-runs the capability/secret
preflight (loop requires{} vs member provides{}) against every LIVE
placement in a fleet, so drift introduced AFTER assign (manifest
re-declared with a new requirement, member re-pinged with fewer
capabilities) is caught before a reconcile applies further changes
instead of silently propagating bad state.

- app/services/placement.py: reconcile_precheck(db, fleet_id)
- app/mcp/tools/placement.py: loopskill_reconcile_precheck MCP tool,
  wired into _PLACEMENT_TOOLS + dispatch_placement
- app/fleet_routes.py: POST /api/fleets/{id}/reconcile-precheck HTTP
  route (thin adapter, mirrors the MCP contract per PM7); added
  fleet_not_found -> 404 to the route's error-status map (previously
  only sync/subscribe hit fleet_routes, which never surfaced that error)
- 8 new tests: clean fleet, post-assign drift detection (keystone),
  removed-placement exclusion, manager-capability 403, MCP dispatch,
  HTTP route (200/404/403)

41+32 targeted tests pass (test_fleetos_A_placements, test_fleetos_I_ingest,
test_portal_0610_j3_fleet_routes). ruff check + format clean.
Copilot AI lite review requested due to automatic review settings August 7, 2026 19:11

Copilot AI 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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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.

2 participants