feat(projection): add the fleet reset as a loop over the repository form
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 25s

ADR-012 decision 7 requires the fleet form to share the per-repository
implementation: the rarely-run wide operation must be the frequently-run narrow
one, or the wide one is trusted on the strength of never having been exercised.

Failure behaviour is the substance. A refusal does not stop the pass — aborting
on the first refusal means one unresolved repository blocks reconstruction
everywhere, which in practice means permanently. An error does not stop it
either. Each repository gets its own session so one failure cannot roll back
another's work, and only repositories that applied are committed.

Refs STATE-WP-0083-T04

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 2583210@bnt-lap001
Assistant-Session: f2bff2d5-e9b2-4338-92ca-10282a927006
This commit is contained in:
tegwick 2026-08-26 13:23:08 +02:00
parent 76a7c7ed24
commit 532583ce17
3 changed files with 173 additions and 1 deletions

View file

@ -249,6 +249,9 @@ class _FakeSession:
async def commit(self):
self.committed = True
async def rollback(self):
self.rolled_back = True
class TestCollisionRefusal:
"""A colliding identifier must refuse, not raise (STATE-WP-0083-T03).
@ -299,3 +302,85 @@ class TestCollisionRefusal:
session = _FakeSession(repo=_Repo(), rows=[], foreign=[])
out = await fp.reset_repository_projection(session, "demo", derived=derived)
assert out.status == "applied" and out.created == ["DEMO-WP-0001"]
class TestFleetReset:
"""The fleet form must be a loop over the repository form (T04).
Its value is entirely in what it does with failure: a wide reset that stops
at the first refusal is one nobody can run, because there is always one
unresolved repository somewhere.
"""
class _Factory:
def __init__(self, sessions):
self._sessions = list(sessions)
def __call__(self):
session = self._sessions.pop(0)
class Ctx:
async def __aenter__(self_inner):
return session
async def __aexit__(self_inner, *a):
return False
return Ctx()
@staticmethod
def _derived(rid):
return fp.DerivedProjection(
repo_slug="x", commit="c0ffee",
workplans=[fp.DerivedWorkplan(
record_id=rid, uuid=fp.derived_record_uuid(rid), title=rid,
status="active", relative_path="workplans/a.md", archived=False, tasks=[])])
@pytest.mark.asyncio
async def test_a_refusal_does_not_stop_the_pass(self, monkeypatch):
calls = []
async def fake(session, slug, **kw):
calls.append(slug)
out = fp.ResetOutcome(repo_slug=slug, commit="c0ffee", status="applied")
if slug == "bad":
out.status = "refused"
out.refused.append({"reason": "would be retired", "slug": "x"})
else:
out.updated.append("A-WP-0001")
return out
monkeypatch.setattr(fp, "reset_repository_projection", fake)
s = [_FakeSession(repo=_Repo(), rows=[]) for _ in range(3)]
res = await fp.reset_fleet_projection(self._Factory(s), ["good", "bad", "also-good"])
assert calls == ["good", "bad", "also-good"]
d = res.to_dict()
assert d["by_status"] == {"applied": 2, "refused": 1}
assert d["totals"]["updated"] == 2
@pytest.mark.asyncio
async def test_an_error_does_not_stop_the_pass(self, monkeypatch):
async def fake(session, slug, **kw):
if slug == "boom":
raise RuntimeError("clone failed")
return fp.ResetOutcome(repo_slug=slug, commit="c", status="noop")
monkeypatch.setattr(fp, "reset_repository_projection", fake)
s = [_FakeSession(repo=_Repo(), rows=[]) for _ in range(3)]
res = await fp.reset_fleet_projection(self._Factory(s), ["a", "boom", "b"])
d = res.to_dict()
assert d["errored"] == 1 and "clone failed" in res.errors["boom"]
assert set(res.results) == {"a", "b"}
@pytest.mark.asyncio
async def test_only_applied_repositories_are_committed(self, monkeypatch):
async def fake(session, slug, **kw):
out = fp.ResetOutcome(repo_slug=slug, commit="c", status="applied")
if slug == "refuser":
out.status = "refused"
else:
out.updated.append("A-WP-0001")
return out
monkeypatch.setattr(fp, "reset_repository_projection", fake)
s = [_FakeSession(repo=_Repo(), rows=[]) for _ in range(2)]
await fp.reset_fleet_projection(self._Factory(s), ["applier", "refuser"])
assert s[0].committed is True
assert s[1].committed is False