Files
Gitea-Tools/tests/test_issue_978_instance_fleet_snapshot.py
T
sysadminandClaude Opus 4.8 4a1cc63e94 feat(controller): expose instance-level fleet identity and health snapshots
Add a pure fleet snapshot assessor and a read-only controller/reconciler
MCP tool so multi-instance fleets can be enumerated by client_instance_id
without treating shared client_type as duplication. Trusted launcher
instance IDs, classification (missing/unmanifested/collisions/historical),
registry schema extensions, docs, and regression tests for #978.

Closes #978

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-30 03:14:15 -04:00

1055 lines
36 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Instance-level fleet identity and health snapshots (#978).
Covers the acceptance criteria for multi-instance fleets: two Codex launches
remain distinct, ten workers attribute correctly, same client_type is not a
duplicate, collisions fail closed, historical rows stay non-blocking, and the
controller/reconciler read surface grants no mutation authority.
All identifiers are synthetic.
"""
from __future__ import annotations
import inspect
import json
import os
import tempfile
import unittest
from datetime import datetime, timedelta, timezone
from typing import Any
from unittest import mock
import mcp_fleet_snapshot as fleet
import mcp_worker_identity as mwi
NOW = datetime(2026, 7, 30, 7, 0, 0, tzinfo=timezone.utc)
TTL = 900.0
NAMESPACES = ("author", "reviewer", "merger", "controller", "reconciler")
def _registry() -> mwi.WorkerRegistry:
handle, path = tempfile.mkstemp(suffix=".sqlite3")
os.close(handle)
os.unlink(path)
return mwi.WorkerRegistry(path)
def _alive(_pid: int | None) -> bool:
return True
def _dead(_pid: int | None) -> bool:
return False
def _register(
registry: mwi.WorkerRegistry,
*,
client: str,
instance: str,
session: str,
generation: str,
namespace: str,
pid: int,
now: datetime = NOW,
ttl: float = TTL,
fleet_run_id: str | None = "run-canary",
status_after: str | None = None,
worker_identity: str | None = None,
process_identity: str | None = None,
repository_binding: str = "/repo/Gitea-Tools",
startup_revision: str = "rev-live",
instance_id_provenance: str = fleet.INSTANCE_ID_PROVENANCE_TRUSTED,
role: str | None = None,
) -> dict[str, Any]:
identity = worker_identity or mwi.generate_worker_identity(
client, f"{session}-{namespace}", now=now, nonce=f"{pid}-{namespace}"
)
outcome = registry.register(
worker_identity=identity,
client_name=client,
client_instance_id=instance,
session_id=session,
generation_id=generation,
role=role or namespace,
profile=f"prgs-{namespace}",
namespace=namespace,
remote="prgs",
repository_binding=repository_binding,
pid=pid,
heartbeat_ttl_seconds=ttl,
now=now,
fleet_run_id=fleet_run_id,
process_identity=process_identity or f"pid-{pid}",
startup_revision=startup_revision,
loaded_revision=startup_revision,
parity_revision=startup_revision,
live_revision=startup_revision,
instance_id_provenance=instance_id_provenance,
pid_alive_probe=_alive,
)
outcome["identity"] = identity
if status_after and outcome.get("registered"):
if status_after == mwi.STATUS_RELEASED:
registry.release(worker_identity=identity, now=now)
elif status_after == mwi.STATUS_SUPERSEDED:
# Direct status write for historical rows.
with registry._tx() as conn: # noqa: SLF001 — test-only fixture
conn.execute(
"UPDATE worker_registrations SET status = ? WHERE worker_identity = ?",
(mwi.STATUS_SUPERSEDED, identity),
)
return outcome
def _five_workers(
registry: mwi.WorkerRegistry,
*,
client: str,
instance: str,
session_prefix: str,
gen_prefix: str,
pid_base: int,
now: datetime = NOW,
) -> list[dict[str, Any]]:
out = []
for i, ns in enumerate(NAMESPACES):
out.append(
_register(
registry,
client=client,
instance=instance,
session=f"{session_prefix}-{ns}",
generation=f"{gen_prefix}-{ns}",
namespace=ns,
pid=pid_base + i,
now=now,
)
)
return out
class InstanceIdentityTests(unittest.TestCase):
def test_generate_distinct_instance_ids(self):
a = fleet.generate_client_instance_id("codex", launch_nonce="a", now=NOW)
b = fleet.generate_client_instance_id("codex", launch_nonce="b", now=NOW)
self.assertNotEqual(a, b)
self.assertTrue(a.startswith("inst-codex-"))
self.assertTrue(fleet.assess_instance_identity(a)["trusted"])
def test_legacy_pid_fallback_not_trusted(self):
a = fleet.assess_instance_identity("pid-1234")
self.assertFalse(a["trusted"])
self.assertEqual(a["provenance"], fleet.INSTANCE_ID_PROVENANCE_LEGACY)
def test_missing_instance_id(self):
a = fleet.assess_instance_identity(None)
self.assertFalse(a["complete"])
self.assertEqual(a["provenance"], fleet.INSTANCE_ID_PROVENANCE_MISSING)
class TwoCodexInstancesTests(unittest.TestCase):
"""AC1AC3: two Codex instances, ten workers, same type not duplicate."""
def test_two_codex_instances_ten_workers(self):
registry = _registry()
inst_a = fleet.generate_client_instance_id("codex", launch_nonce="A", now=NOW)
inst_b = fleet.generate_client_instance_id("codex", launch_nonce="B", now=NOW)
_five_workers(
registry,
client="codex",
instance=inst_a,
session_prefix="sessA",
gen_prefix="genA",
pid_base=1000,
)
_five_workers(
registry,
client="codex",
instance=inst_b,
session_prefix="sessB",
gen_prefix="genB",
pid_base=2000,
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(status=None),
now=NOW + timedelta(seconds=10),
pid_alive_probe=_alive,
canonical_repository="/repo/Gitea-Tools",
expected_live_revision="rev-live",
)
self.assertEqual(snap["live_worker_count"], 10)
self.assertEqual(snap["instance_count"], 2)
self.assertIn("codex", snap["multi_instance_same_client_type"])
self.assertEqual(len(snap["multi_instance_same_client_type"]["codex"]), 2)
self.assertTrue(snap["same_client_type_not_duplicate"])
# No finding that treats same client_type alone as a duplicate.
for f in snap["active_blockers"]:
self.assertNotIn("client_type alone", f["detail"].lower())
by_inst = {i["client_instance_id"]: i for i in snap["instances"]}
self.assertEqual(by_inst[inst_a]["worker_count"], 5)
self.assertEqual(by_inst[inst_b]["worker_count"], 5)
self.assertEqual(set(by_inst[inst_a]["namespaces"]), set(NAMESPACES))
self.assertEqual(set(by_inst[inst_b]["namespaces"]), set(NAMESPACES))
self.assertTrue(snap["live_fleet_safe"])
def test_mixed_client_types(self):
registry = _registry()
_five_workers(
registry,
client="codex",
instance="inst-codex-1",
session_prefix="c",
gen_prefix="gc",
pid_base=10,
)
_five_workers(
registry,
client="claude_code",
instance="inst-claude-1",
session_prefix="l",
gen_prefix="gl",
pid_base=20,
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(),
now=NOW + timedelta(seconds=5),
pid_alive_probe=_alive,
)
self.assertEqual(snap["instance_count"], 2)
self.assertTrue(snap["live_fleet_safe"])
class CollisionTests(unittest.TestCase):
def test_live_reuse_of_instance_id_different_client_types(self):
registry = _registry()
shared = "inst-shared-collision"
_register(
registry,
client="codex",
instance=shared,
session="s1",
generation="g1",
namespace="author",
pid=1,
)
# Second client type forced onto same instance id via direct SQL to
# bypass register's same-namespace guard and exercise classification.
with registry._tx() as conn: # noqa: SLF001
conn.execute(
"""
INSERT INTO worker_registrations (
worker_identity, client_name, client_instance_id, session_id,
generation_id, role, profile, namespace, remote,
repository_binding, pid, transport, token_fingerprint,
started_at, last_heartbeat_at, heartbeat_ttl_seconds,
fencing_epoch, status, process_identity, instance_id_provenance
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
""",
(
"claude_code-20260730T070000Z-aaaaaaaaaaaa",
"claude_code",
shared,
"s2",
"g2",
"author",
"prgs-author",
"reviewer",
"prgs",
"/repo/Gitea-Tools",
2,
None,
None,
mwi._ts(NOW), # noqa: SLF001
mwi._ts(NOW), # noqa: SLF001
TTL,
1,
mwi.STATUS_ACTIVE,
"pid-2",
fleet.INSTANCE_ID_PROVENANCE_TRUSTED,
),
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(),
now=NOW + timedelta(seconds=1),
pid_alive_probe=_alive,
)
classes = {f["classification"] for f in snap["active_blockers"]}
self.assertIn(fleet.CLASS_INSTANCE_ID_COLLISION, classes)
self.assertFalse(snap["live_fleet_safe"])
def test_duplicate_namespace_within_instance(self):
registry = _registry()
inst = "inst-dup-ns"
first = _register(
registry,
client="codex",
instance=inst,
session="s-a",
generation="g-a",
namespace="author",
pid=11,
)
self.assertTrue(first["registered"], first)
# Direct second author under same instance (bypass register guard).
with registry._tx() as conn: # noqa: SLF001
conn.execute(
"""
INSERT INTO worker_registrations (
worker_identity, client_name, client_instance_id, session_id,
generation_id, role, profile, namespace, remote,
repository_binding, pid, transport, token_fingerprint,
started_at, last_heartbeat_at, heartbeat_ttl_seconds,
fencing_epoch, status, process_identity, instance_id_provenance
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
""",
(
"codex-20260730T070000Z-bbbbbbbbbbbb",
"codex",
inst,
"s-b",
"g-b",
"author",
"prgs-author",
"author",
"prgs",
"/repo/Gitea-Tools",
12,
None,
None,
mwi._ts(NOW), # noqa: SLF001
mwi._ts(NOW), # noqa: SLF001
TTL,
2,
mwi.STATUS_ACTIVE,
"pid-12",
fleet.INSTANCE_ID_PROVENANCE_TRUSTED,
),
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(),
now=NOW + timedelta(seconds=1),
pid_alive_probe=_alive,
)
classes = {f["classification"] for f in snap["active_blockers"]}
self.assertIn(fleet.CLASS_DUPLICATE_NAMESPACE, classes)
def test_register_refuses_duplicate_namespace(self):
registry = _registry()
inst = "inst-reg-dup"
a = _register(
registry,
client="codex",
instance=inst,
session="s1",
generation="g1",
namespace="author",
pid=21,
)
self.assertTrue(a["registered"])
b = _register(
registry,
client="codex",
instance=inst,
session="s2",
generation="g2",
namespace="author",
pid=22,
)
self.assertFalse(b["registered"])
self.assertEqual(b.get("collision_kind"), "duplicate_namespace_worker")
def test_reused_worker_identity(self):
registry = _registry()
identity = mwi.generate_worker_identity("codex", "sess", now=NOW, nonce="fixed")
first = _register(
registry,
client="codex",
instance="inst-1",
session="s1",
generation="g1",
namespace="author",
pid=31,
worker_identity=identity,
)
self.assertTrue(first["registered"])
second = registry.register(
worker_identity=identity,
client_name="codex",
client_instance_id="inst-2",
session_id="s2",
generation_id="g2",
namespace="author",
pid=32,
now=NOW,
pid_alive_probe=_alive,
)
self.assertFalse(second["registered"])
self.assertTrue(second["collision"])
def test_reused_session_identity(self):
registry = _registry()
_register(
registry,
client="codex",
instance="inst-1",
session="shared-session",
generation="g1",
namespace="author",
pid=41,
)
_register(
registry,
client="codex",
instance="inst-2",
session="shared-session",
generation="g2",
namespace="reviewer",
pid=42,
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(),
now=NOW + timedelta(seconds=1),
pid_alive_probe=_alive,
)
classes = {f["classification"] for f in snap["active_blockers"]}
self.assertIn(fleet.CLASS_SESSION_COLLISION, classes)
def test_reused_generation_across_instances(self):
registry = _registry()
_register(
registry,
client="codex",
instance="inst-1",
session="s1",
generation="gen-shared",
namespace="author",
pid=51,
)
_register(
registry,
client="codex",
instance="inst-2",
session="s2",
generation="gen-shared",
namespace="author",
pid=52,
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(),
now=NOW + timedelta(seconds=1),
pid_alive_probe=_alive,
)
classes = {f["classification"] for f in snap["active_blockers"]}
self.assertIn(fleet.CLASS_GENERATION_COLLISION, classes)
def test_reused_pid_and_process_identity(self):
registry = _registry()
_register(
registry,
client="codex",
instance="inst-1",
session="s1",
generation="g1",
namespace="author",
pid=61,
process_identity="proc-same",
)
_register(
registry,
client="codex",
instance="inst-2",
session="s2",
generation="g2",
namespace="reviewer",
pid=61,
process_identity="proc-same",
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(),
now=NOW + timedelta(seconds=1),
pid_alive_probe=_alive,
)
classes = {f["classification"] for f in snap["active_blockers"]}
self.assertIn(fleet.CLASS_PID_COLLISION, classes)
self.assertIn(fleet.CLASS_PROCESS_COLLISION, classes)
def test_ownership_fencing_collision(self):
registry = _registry()
_register(
registry,
client="codex",
instance="inst-1",
session="s1",
generation="gen-fence",
namespace="author",
pid=71,
)
# Second instance forced onto same generation+epoch via SQL.
with registry._tx() as conn: # noqa: SLF001
conn.execute(
"""
INSERT INTO worker_registrations (
worker_identity, client_name, client_instance_id, session_id,
generation_id, role, profile, namespace, remote,
repository_binding, pid, transport, token_fingerprint,
started_at, last_heartbeat_at, heartbeat_ttl_seconds,
fencing_epoch, status, process_identity, instance_id_provenance
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
""",
(
"codex-20260730T070000Z-cccccccccccc",
"codex",
"inst-2",
"s2",
"gen-fence",
"reviewer",
"prgs-reviewer",
"reviewer",
"prgs",
"/repo/Gitea-Tools",
72,
None,
None,
mwi._ts(NOW), # noqa: SLF001
mwi._ts(NOW), # noqa: SLF001
TTL,
1, # same epoch as first (first got epoch 1)
mwi.STATUS_ACTIVE,
"pid-72",
fleet.INSTANCE_ID_PROVENANCE_TRUSTED,
),
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(),
now=NOW + timedelta(seconds=1),
pid_alive_probe=_alive,
)
classes = {f["classification"] for f in snap["active_blockers"]}
self.assertTrue(
{fleet.CLASS_OWNERSHIP_COLLISION, fleet.CLASS_GENERATION_COLLISION}
& classes
)
class ManifestAndEdgeTests(unittest.TestCase):
def test_missing_and_unmanifested(self):
registry = _registry()
live = "inst-live"
expected = "inst-expected-missing"
_register(
registry,
client="codex",
instance=live,
session="s",
generation="g",
namespace="author",
pid=81,
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(),
expected_manifest=[
{"client_instance_id": expected, "client_type": "codex"},
],
now=NOW + timedelta(seconds=1),
pid_alive_probe=_alive,
)
self.assertEqual(snap["missing_expected_instance_ids"], [expected])
self.assertEqual(snap["unmanifested_instance_ids"], [live])
classes = {f["classification"] for f in snap["active_blockers"]}
self.assertIn(fleet.CLASS_MISSING, classes)
self.assertIn(fleet.CLASS_UNMANIFESTED, classes)
def test_unknown_client(self):
registry = _registry()
with registry._tx() as conn: # noqa: SLF001
conn.execute(
"""
INSERT INTO worker_registrations (
worker_identity, client_name, client_instance_id, session_id,
generation_id, role, profile, namespace, remote,
repository_binding, pid, transport, token_fingerprint,
started_at, last_heartbeat_at, heartbeat_ttl_seconds,
fencing_epoch, status, process_identity, instance_id_provenance
) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)
""",
(
"unknown_client-20260730T070000Z-dddddddddddd",
"unknown_client",
"inst-unk",
"s",
"g",
"author",
"prgs-author",
"author",
"prgs",
"/repo/Gitea-Tools",
91,
None,
None,
mwi._ts(NOW), # noqa: SLF001
mwi._ts(NOW), # noqa: SLF001
TTL,
1,
mwi.STATUS_ACTIVE,
"pid-91",
fleet.INSTANCE_ID_PROVENANCE_TRUSTED,
),
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(),
now=NOW + timedelta(seconds=1),
pid_alive_probe=_alive,
)
classes = {f["classification"] for f in snap["active_blockers"]}
self.assertIn(fleet.CLASS_UNKNOWN_CLIENT, classes)
def test_foreign_repository(self):
registry = _registry()
_register(
registry,
client="codex",
instance="inst-fr",
session="s",
generation="g",
namespace="author",
pid=101,
repository_binding="/other/repo",
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(),
now=NOW + timedelta(seconds=1),
pid_alive_probe=_alive,
canonical_repository="/repo/Gitea-Tools",
)
classes = {f["classification"] for f in snap["active_blockers"]}
self.assertIn(fleet.CLASS_FOREIGN_REPOSITORY, classes)
def test_old_revision(self):
registry = _registry()
_register(
registry,
client="codex",
instance="inst-old",
session="s",
generation="g",
namespace="author",
pid=111,
startup_revision="rev-old",
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(),
now=NOW + timedelta(seconds=1),
pid_alive_probe=_alive,
expected_live_revision="rev-live",
)
classes = {f["classification"] for f in snap["active_blockers"]}
self.assertIn(fleet.CLASS_OLD_REVISION, classes)
def test_stale_worker(self):
registry = _registry()
_register(
registry,
client="codex",
instance="inst-stale",
session="s",
generation="g",
namespace="author",
pid=121,
now=NOW,
ttl=60.0,
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(),
now=NOW + timedelta(seconds=120),
pid_alive_probe=_dead,
)
classes = {f["classification"] for f in snap["active_blockers"]}
self.assertIn(fleet.CLASS_STALE_WORKER, classes)
def test_historical_not_active_blocker(self):
registry = _registry()
_register(
registry,
client="codex",
instance="inst-hist",
session="s",
generation="g",
namespace="author",
pid=131,
status_after=mwi.STATUS_RELEASED,
)
_register(
registry,
client="codex",
instance="inst-live",
session="s2",
generation="g2",
namespace="author",
pid=132,
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(status=None),
now=NOW + timedelta(seconds=1),
pid_alive_probe=_alive,
)
self.assertEqual(snap["historical_worker_count"], 1)
self.assertTrue(
all(f["classification"] == fleet.CLASS_HISTORICAL for f in snap["historical_findings"])
)
# Only historical finding for the dead row — live fleet otherwise safe.
hist_blockers = [
f
for f in snap["active_blockers"]
if f["classification"] == fleet.CLASS_HISTORICAL
]
self.assertEqual(hist_blockers, [])
self.assertTrue(snap["live_fleet_safe"])
def test_legacy_incomplete_identity(self):
registry = _registry()
_register(
registry,
client="codex",
instance="pid-999",
session="s",
generation="g",
namespace="author",
pid=141,
instance_id_provenance=fleet.INSTANCE_ID_PROVENANCE_LEGACY,
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(),
now=NOW + timedelta(seconds=1),
pid_alive_probe=_alive,
)
classes = {f["classification"] for f in snap["active_blockers"]}
self.assertIn(fleet.CLASS_LEGACY_INCOMPLETE, classes)
self.assertFalse(snap["mutation_safe"])
class LifecycleTests(unittest.TestCase):
def test_heartbeat_continuity_across_snapshots(self):
registry = _registry()
outcome = _register(
registry,
client="codex",
instance="inst-hb",
session="s",
generation="g",
namespace="author",
pid=151,
)
identity = outcome["identity"]
earlier = fleet.snapshot_instance_fleet(
registry.list_workers(),
now=NOW + timedelta(seconds=1),
pid_alive_probe=_alive,
)
later_time = NOW + timedelta(seconds=30)
registry.heartbeat(
worker_identity=identity,
fencing_epoch=outcome["fencing_epoch"],
now=later_time,
expected_session_id="s",
expected_generation_id="g",
expected_client_name="codex",
expected_pid=151,
)
later = fleet.snapshot_instance_fleet(
registry.list_workers(),
now=later_time + timedelta(seconds=1),
pid_alive_probe=_alive,
)
cmp = fleet.compare_snapshot_heartbeats(earlier, later)
self.assertTrue(cmp["stable_ownership"])
self.assertEqual(cmp["shared_live_workers"], [identity])
self.assertTrue(cmp["continuity"][0]["heartbeat_non_decreasing"])
def test_worker_reconnect_preserves_instance(self):
"""Reconnect = heartbeat renew; same instance/worker/session."""
registry = _registry()
o = _register(
registry,
client="codex",
instance="inst-rc",
session="s-rc",
generation="g-rc",
namespace="author",
pid=161,
)
before = registry.get(o["identity"])
registry.heartbeat(
worker_identity=o["identity"],
fencing_epoch=o["fencing_epoch"],
now=NOW + timedelta(seconds=20),
expected_session_id="s-rc",
expected_generation_id="g-rc",
expected_client_name="codex",
expected_pid=161,
)
after = registry.get(o["identity"])
self.assertEqual(before["client_instance_id"], after["client_instance_id"])
self.assertEqual(before["session_id"], after["session_id"])
self.assertEqual(before["worker_identity"], after["worker_identity"])
def test_worker_restart_new_worker_same_instance(self):
registry = _registry()
inst = "inst-restart"
old = _register(
registry,
client="codex",
instance=inst,
session="s-old",
generation="g-old",
namespace="author",
pid=171,
)
registry.release(worker_identity=old["identity"], now=NOW + timedelta(seconds=5))
new = _register(
registry,
client="codex",
instance=inst,
session="s-new",
generation="g-new",
namespace="author",
pid=172,
now=NOW + timedelta(seconds=10),
)
self.assertTrue(new["registered"])
self.assertNotEqual(old["identity"], new["identity"])
snap = fleet.snapshot_instance_fleet(
registry.list_workers(status=None),
now=NOW + timedelta(seconds=15),
pid_alive_probe=_alive,
)
live = [w for w in snap["live_workers"] if w["client_instance_id"] == inst]
self.assertEqual(len(live), 1)
self.assertEqual(live[0]["worker_identity"], new["identity"])
self.assertEqual(snap["historical_worker_count"], 1)
def test_full_application_restart_new_instance(self):
registry = _registry()
old_inst = fleet.generate_client_instance_id("codex", launch_nonce="old", now=NOW)
new_inst = fleet.generate_client_instance_id(
"codex", launch_nonce="new", now=NOW + timedelta(hours=1)
)
for ns, pid in zip(NAMESPACES, range(181, 186)):
o = _register(
registry,
client="codex",
instance=old_inst,
session=f"old-{ns}",
generation=f"oldg-{ns}",
namespace=ns,
pid=pid,
)
registry.release(worker_identity=o["identity"], now=NOW + timedelta(minutes=1))
for ns, pid in zip(NAMESPACES, range(191, 196)):
_register(
registry,
client="codex",
instance=new_inst,
session=f"new-{ns}",
generation=f"newg-{ns}",
namespace=ns,
pid=pid,
now=NOW + timedelta(minutes=2),
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(status=None),
now=NOW + timedelta(minutes=3),
pid_alive_probe=_alive,
)
live_ids = {i["client_instance_id"] for i in snap["instances"]}
self.assertEqual(live_ids, {new_inst})
self.assertEqual(snap["historical_worker_count"], 5)
class PermissionAndSingleClientTests(unittest.TestCase):
def test_single_client_still_supported(self):
registry = _registry()
_five_workers(
registry,
client="codex",
instance="inst-single",
session_prefix="solo",
gen_prefix="solo-g",
pid_base=300,
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(),
now=NOW + timedelta(seconds=1),
pid_alive_probe=_alive,
)
self.assertEqual(snap["instance_count"], 1)
self.assertEqual(snap["live_worker_count"], 5)
self.assertTrue(snap["live_fleet_safe"])
self.assertTrue(snap["mutation_safe"])
def test_snapshot_has_consistency_token(self):
registry = _registry()
_register(
registry,
client="codex",
instance="inst-tok",
session="s",
generation="g",
namespace="author",
pid=400,
)
snap = fleet.snapshot_instance_fleet(
registry.list_workers(),
now=NOW + timedelta(seconds=1),
pid_alive_probe=_alive,
)
self.assertTrue(snap["consistency_token"].startswith("fleetrev-"))
self.assertEqual(snap["consistency_token"], snap["registry_revision"])
self.assertIn("snapshot_at", snap)
def test_tool_role_gate_denies_author(self):
import gitea_mcp_server as server
with mock.patch.object(server, "_profile_operation_gate", return_value=[]):
with mock.patch.object(
server,
"get_profile",
return_value={
"profile_name": "prgs-author",
"role": "author",
"allowed_operations": ["gitea.read"],
"forbidden_operations": [],
},
):
result = server.gitea_snapshot_instance_fleet(remote="prgs")
self.assertFalse(result.get("success"))
self.assertEqual(result.get("denied_role"), "author")
self.assertTrue(result.get("read_only"))
self.assertFalse(result.get("mutation_performed", True))
def test_tool_allows_controller(self):
import gitea_mcp_server as server
registry = _registry()
_register(
registry,
client="codex",
instance="inst-ctrl",
session="s",
generation="g",
namespace="controller",
pid=410,
)
with mock.patch.object(server, "_profile_operation_gate", return_value=[]):
with mock.patch.object(
server,
"get_profile",
return_value={
"profile_name": "prgs-controller",
"role": "controller",
"allowed_operations": ["gitea.read"],
"forbidden_operations": [],
},
):
with mock.patch.object(server, "_worker_registry", return_value=registry):
with mock.patch.object(
server, "_current_master_parity", return_value={}
):
result = server.gitea_snapshot_instance_fleet(remote="prgs")
self.assertTrue(result.get("success"), result)
self.assertTrue(result.get("read_only"))
self.assertTrue(result["permission_scope"]["denied_unrelated_mutations"])
self.assertEqual(result["permission_scope"]["granted_operations"], ["gitea.read"])
def test_tool_allows_reconciler(self):
import gitea_mcp_server as server
registry = _registry()
with mock.patch.object(server, "_profile_operation_gate", return_value=[]):
with mock.patch.object(
server,
"get_profile",
return_value={
"profile_name": "prgs-reconciler",
"role": "reconciler",
"allowed_operations": ["gitea.read"],
"forbidden_operations": [],
},
):
with mock.patch.object(server, "_worker_registry", return_value=registry):
with mock.patch.object(
server, "_current_master_parity", return_value={}
):
result = server.gitea_snapshot_instance_fleet(remote="prgs")
self.assertTrue(result.get("success"), result)
def test_capability_map_entry(self):
import task_capability_map as tcm
entry = tcm.TASK_CAPABILITY_MAP["snapshot_instance_fleet"]
self.assertEqual(entry["permission"], "gitea.read")
self.assertEqual(entry["role"], "controller")
self.assertIn("gitea_snapshot_instance_fleet", tcm.TASK_CAPABILITY_MAP)
def test_fleet_run_env_allowlisted(self):
import gitea_config
self.assertIn("GITEA_MCP_FLEET_RUN_ID", gitea_config.RECOGNIZED_GITEA_ENV_KEYS)
def test_docs_exist(self):
root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
path = os.path.join(root, "docs", "instance-fleet-identity.md")
self.assertTrue(os.path.isfile(path))
with open(path, encoding="utf-8") as fh:
text = fh.read()
self.assertIn("client_instance_id", text)
self.assertIn("exactly_one_per_profile", text)
self.assertIn("gitea_snapshot_instance_fleet", text)
def test_tool_is_registered(self):
import gitea_mcp_server as server
self.assertTrue(callable(server.gitea_snapshot_instance_fleet))
src = inspect.getsource(server.gitea_snapshot_instance_fleet)
self.assertIn("controller", src)
self.assertIn("reconciler", src)
class ClientHintsTests(unittest.TestCase):
def test_client_hints_legacy_when_unset(self):
import gitea_mcp_server as server
env = {"GITEA_MCP_CLIENT": "codex"}
with mock.patch.dict(os.environ, env, clear=False):
# Ensure instance key is absent.
os.environ.pop("GITEA_MCP_CLIENT_INSTANCE", None)
hints = server._client_identity_hints()
self.assertFalse(hints["instance_identity_trusted"])
self.assertTrue(
str(hints["client_instance_id"]).startswith("legacy-pid-")
or hints["instance_id_provenance"] == fleet.INSTANCE_ID_PROVENANCE_LEGACY
)
def test_client_hints_trusted_when_set(self):
import gitea_mcp_server as server
inst = fleet.generate_client_instance_id("codex", launch_nonce="t", now=NOW)
with mock.patch.dict(
os.environ,
{
"GITEA_MCP_CLIENT": "codex",
"GITEA_MCP_CLIENT_INSTANCE": inst,
"GITEA_MCP_FLEET_RUN_ID": "run-1",
},
clear=False,
):
hints = server._client_identity_hints()
self.assertTrue(hints["instance_identity_trusted"])
self.assertEqual(hints["client_instance_id"], inst)
self.assertEqual(hints["fleet_run_id"], "run-1")
if __name__ == "__main__":
unittest.main()