refactor(ci): declare the README replay as a service edge instead of a matrix flag

The replay used to be decided by the matrix: utils.roles.guide picked a
variant, instructions.mark wrote a flag, and the workflow appended
web-app-docs to the row's apps. Now each invokable role declares the
guide host in meta/services.yml with bond 0.5 behind the group gate and
switches it on in exactly one variant, and the deploy pulls it in
through that edge. The matrix only reads the declaration back.

web-app-docs was no provider at all: the registry requires shared,
provides or an alias on the role's own entity entry and it had none, so
the 188 declarations would have been decorative. Registering it brings
a structural contract with it -- a dynamically loaded service role must
set its run_once flag as its very first task or load/app.yml includes
it again -- so the role body moves into tasks/00_core.yml and main.yml
becomes the guard.

smallest_variant no longer counts the guide. It picks the variant that
then switches the guide on, which on the next read would make that
variant one service larger and no longer the smallest; without the
exclusion 30 of 188 roles disagreed with the file.

The job title loses the marker. The row is an ordinary deploy of its
variant, so nothing distinguishes it any more -- which is why the unit
test now asserts the label is left alone.

Measured against the previous tree, the sweep marks 68 rows where it
marked 88. Twenty roles declare the guide on a variant this sweep does
not run in the guide's mode, and by decision the declaration is
absolute: the file is the truth and those roles carry no replay in such
a run. The old code instead searched the deployed variants, which hid
the mismatch behind a fallback.

The same measurement found that the predicate first read the group gate
as a declaration -- the gate renders to a non-empty string, so every
variant looked like the declaring one. It now tests for a literal true.
Neither defect showed up in the unit tests, which check the new logic
rather than its difference to the old.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
kevinveenbirkenbachandClaude Opus 5 committed 2026-10-23 02:30:56 +02:00
1 parent 75a2726389
commit 74a91e7adf
385 files changed
+2057 -135

No files matched your search

@@ -1,12 +1,13 @@
"""The README replay rides on a row that runs in the guide's own mode.
"""The README replay rides on the variant that declares the guide host.
The replay is a second deploy on the same runner. On a swarm row that runner
would have to bring a compose stack up beside a swarm one, so the marker has to
land on a compose row for a role that ships a stack, and on a host row for one
installed onto the machine.
Which row carries it is read out of ``meta/variants.yml``, not computed, so a
role moves the replay by moving the ``docs`` service between its variants.
The trap this guards is quiet: marking the wrong row still produces a 📖 in a
job title, so a run looks like it replayed the instructions either way.
Two traps this guards. The row still has to run in the guide's own mode: the
replay is a second deploy on the same runner, and on a swarm row that runner
would have to bring a compose stack up beside a swarm one. And the job title
carries no marker any more, so a wrongly marked row is invisible in the run
list; only these assertions catch it.
"""
from __future__ import annotations
@@ -34,32 +35,28 @@ def _entry(app: str, variant: str, mode: str, **overrides: str) -> dict[str, str
_VARIANTS = {
"web-app-x": [
{"services": {"a": {"enabled": True}, "b": {"enabled": True}}},
{"services": {"a": {"enabled": True}}},
{"services": {"a": {"enabled": True}, "docs": {"enabled": True}}},
]
}
def _mark(entries, mode="compose", variants=None):
"""Mark with the guide's mode forced, leaving the variant search real.
Both bindings are patched: ``mark`` resolved the name at import, while
``guide_variant`` calls its own module's copy.
"""
with (
patch.object(instructions, "guide_deployable", return_value=mode),
patch("utils.roles.guide.guide_deployable", return_value=mode),
):
with patch.object(instructions, "guide_deployable", return_value=mode):
return instructions.mark(
entries, variants if variants is not None else _VARIANTS
)
class TestInstructionsMode(unittest.TestCase):
def test_the_compose_row_of_the_smallest_variant_carries_it(self) -> None:
def test_the_variant_that_declares_the_guide_carries_it(self) -> None:
marked = _mark([_entry("web-app-x", "1", "compose")])
self.assertEqual("compose", marked[0]["instructions"])
def test_a_variant_without_the_guide_carries_nothing(self) -> None:
marked = _mark([_entry("web-app-x", "0", "compose")])
self.assertEqual("", marked[0]["instructions"])
def test_a_swarm_row_never_carries_a_compose_replay(self) -> None:
marked = _mark([_entry("web-app-x", "1", "swarm")])
self.assertEqual("", marked[0]["instructions"])
@@ -70,12 +67,6 @@ class TestInstructionsMode(unittest.TestCase):
)
self.assertEqual(["", "compose"], [e["instructions"] for e in marked])
def test_a_variant_deployed_only_in_swarm_does_not_win_the_search(self) -> None:
marked = _mark(
[_entry("web-app-x", "1", "swarm"), _entry("web-app-x", "0", "compose")]
)
self.assertEqual(["", "compose"], [e["instructions"] for e in marked])
def test_a_host_role_carries_it_on_its_host_row(self) -> None:
marked = _mark([_entry("web-app-x", "1", "host")], mode="host")
self.assertEqual("host", marked[0]["instructions"])
@@ -90,12 +81,15 @@ class TestInstructionsMode(unittest.TestCase):
)
self.assertEqual(["compose", ""], [e["instructions"] for e in marked])
def test_the_label_gains_the_glyph_only_where_it_is_marked(self) -> None:
def test_an_unknown_variant_index_carries_nothing(self) -> None:
marked = _mark([_entry("web-app-x", "9", "compose")])
self.assertEqual("", marked[0]["instructions"])
def test_the_label_is_left_alone(self) -> None:
marked = _mark(
[_entry("web-app-x", "1", "swarm"), _entry("web-app-x", "1", "compose")]
)
self.assertEqual("web-app-x#1", marked[0]["label"])
self.assertNotEqual("web-app-x#1", marked[1]["label"])
self.assertEqual(["web-app-x#1", "web-app-x#1"], [e["label"] for e in marked])
if __name__ == "__main__":