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:
@@ -252,6 +252,31 @@ Helpers: `scripts/worktree-start`, `scripts/worktree-review`,
|
||||
- Never place raw tokens in LLM/MCP config.
|
||||
- Use `gitea_whoami` and `gitea_resolve_task_capability` before mutating.
|
||||
|
||||
## Fleet inventory
|
||||
|
||||
`gitea_whoami`, `gitea_get_runtime_context` and `gitea_assess_master_parity` each
|
||||
describe only the server answering the call. Five namespaces independently
|
||||
reporting the same revision never proved that five processes exist, that no sixth
|
||||
exists, or that all five belong to one client cohort.
|
||||
|
||||
`gitea_assess_fleet_inventory` is the read-only capability that does prove it. It
|
||||
takes no evidence parameters: it combines the control-plane runtime registry,
|
||||
which each server writes about itself at native transport bind, with a process
|
||||
observation the answering server performs. Classification is a pure function of
|
||||
that snapshot, so `gitea-controller` and `gitea-reconciler` return the same
|
||||
verdict for the same fleet.
|
||||
|
||||
Consume `mutation_gate_satisfied`. When it is false, report `blocked_reason`
|
||||
verbatim and stop — `missing_members`, `duplicate_members`, `unexpected_members`,
|
||||
`unregistered_processes`, `mixed_cohort` and `mixed_revision` are reported
|
||||
separately because each needs a different operator action. Treat
|
||||
`inventory_complete: false` and `single_cohort: null` as *unknown*, never as
|
||||
healthy. The capability never terminates a duplicate process, restarts,
|
||||
reconnects, or touches a lease.
|
||||
|
||||
Details, field meanings, and the gate-consumption sequence:
|
||||
[`docs/mcp-fleet-inventory.md`](../../docs/mcp-fleet-inventory.md) (#949).
|
||||
|
||||
## Tool inventory
|
||||
|
||||
[`docs/mcp-tool-inventory.md`](../../docs/mcp-tool-inventory.md) is the canonical
|
||||
|
||||
Reference in New Issue
Block a user