feat(control-plane): add authoritative native MCP fleet inventory (#949)
Every pre-existing runtime surface is per-process. gitea_get_runtime_context and gitea_assess_master_parity describe only the server answering the call, and gitea_assess_mcp_namespace_health accepts process, probe_result and registered_tools from the caller, so it cannot constrain the caller. The control-plane sessions table records allocator task sessions, not server processes. Five self-reports of one revision therefore never proved that five processes exist, that no sixth exists, or that all five share one client cohort. Add gitea_assess_fleet_inventory, a strictly read-only capability that takes no evidence parameters. It combines two sources that must agree: - a control-plane runtime registry row each server writes about itself, from the official entrypoint, immediately after the native transport bind; - a process observation the answering server performs, never the caller. A member is running only when both agree and the observed process predates its registration, so configuration alone never counts and a recycled PID cannot impersonate an exited server. Classification is a pure function of the snapshot, so gitea-controller and gitea-reconciler return the same verdict. Missing, duplicate, unexpected, stale and unregistered members are reported in separate fields rather than collapsed into one error, because each needs a different operator action. single_cohort is derived only from recorded cohort identity and stays null when unknown, so matching revisions never establish a cohort. Any unreadable registry, unavailable listing, unregistered process, unknown cohort or unknown revision sets inventory_complete and mutation_gate_satisfied false with a specific blocked_reason. The capability performs no restart, reconnect, drain, lease mutation, issue mutation or process termination, never terminates a duplicate, and never manufactures restart evidence. The only signal sent is signal 0. Also resolves the fleet namespace from the expected roster: role_namespace_gate.infer_mcp_namespace recognises only author and reviewer and echoes the profile name otherwise, which would have mislabelled the controller, merger and reconciler members. Widening that shared helper is controller role-metadata work owned by #950 and is deliberately not done here. Schema v5 to v6 is additive and idempotent: one new table, no existing table, tool signature or result field changed. Two control-plane tests that pinned the schema version to the literal 5 now assert against SCHEMA_VERSION. #950 (controller role metadata), #951 (restart receipts) and #952 (stale-lease consistency) remain separate and untouched. Tests: 118 new across tests/test_mcp_fleet_inventory.py, tests/test_control_plane_db_server_runtimes.py and tests/test_fleet_inventory_tool.py, covering every acceptance criterion including the multi-LLM duplicate-server regression that motivated the issue. Closes #949 Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01NBaQKcRRKeKbZuhk72MhEf
This commit is contained in:
@@ -14,6 +14,7 @@ from control_plane_db import (
|
||||
ControlPlaneError,
|
||||
InvalidWorkKindError,
|
||||
LeaseRequiredError,
|
||||
SCHEMA_VERSION,
|
||||
WORK_KINDS,
|
||||
_ts,
|
||||
_utc_now,
|
||||
@@ -37,7 +38,10 @@ class ControlPlaneDBTest(unittest.TestCase):
|
||||
rows = dict(conn.execute("SELECT key, value FROM schema_meta").fetchall())
|
||||
finally:
|
||||
conn.close()
|
||||
self.assertEqual(rows["schema_version"], "5")
|
||||
# Pinned to the constant, not a literal: every additive migration bumps
|
||||
# SCHEMA_VERSION, and the invariant under test is that the meta row
|
||||
# records the version the code actually wrote (#949 added v6).
|
||||
self.assertEqual(rows["schema_version"], str(SCHEMA_VERSION))
|
||||
self.assertIn("DB coordinates", rows["architecture"])
|
||||
self.assertIn("bridge", rows["architecture"].lower())
|
||||
|
||||
@@ -868,7 +872,7 @@ class SessionCheckpointTest(unittest.TestCase):
|
||||
conn.close()
|
||||
self.assertIn("session_checkpoints", names)
|
||||
record = self._write()
|
||||
self.assertEqual(record["checkpoint_schema_version"], 5)
|
||||
self.assertEqual(record["checkpoint_schema_version"], SCHEMA_VERSION)
|
||||
|
||||
# AC2 — checkpoints written for multi-role session fixtures.
|
||||
def test_multi_role_fixtures_each_get_a_row(self) -> None:
|
||||
|
||||
Reference in New Issue
Block a user