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:
2026-07-27 23:25:45 -04:00
co-authored by Claude Opus 5
parent 82d71b7702
commit 92615f474b
12 changed files with 2718 additions and 3 deletions
+25
View File
@@ -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