4"""Continuously converge ordinary CI runner hosts from one trusted snapshot."""
6from __future__
import annotations
17from collections.abc
import Callable, Sequence
18from dataclasses
import dataclass
19from pathlib
import Path
20from threading
import Thread
21from typing
import Any, TextIO
23sys.path.insert(0, str(Path(__file__).resolve().parent))
25import fleet_model
as fm
26import fleet_mutation_lock
as fml
27import fleet_reconcile_arc_selftest
as fras
28import fleet_reconcile_process
as frp
29import fleet_reconcile_selftest
as frs
32SOURCE_DIGEST_FILE =
".ra8-source-sha256"
33STATE_FILE =
"state.json"
34DEFAULT_FULL_INTERVAL = 7 * 24 * 60 * 60
35DEFAULT_PRODUCER_INTERVAL = 24 * 60 * 60
36PRIVATE_DIRECTORY_MODE = 0o700
37SELFTEST_CHANGED_TOTAL = 3
38SELFTEST_SIGNAL_BOUND = 8
41PRODUCER_CHECK_NOISE = 2
44PRODUCER_HELD_CHECK_NOISE = 1
45SHA256_RE = re.compile(
r"[0-9a-f]{64}")
46ANSI_RE = re.compile(
r"\x1b\[[0-9;]*m")
50 r"^\s*([A-Za-z0-9_.-]+)\s+:\s+ok=(\d+)\s+changed=(\d+)\s+"
51 r"unreachable=(\d+)\s+failed=(\d+)\s+skipped=(\d+)\s+"
52 r"rescued=(\d+)\s+ignored=(\d+)\s*$"
56@dataclass(frozen=True)
57class ReconcileOptions:
58 """Runtime policy for one fleet reconciliation."""
65 producer_interval: int
69CommandRunner = Callable[[Sequence[str]], frp.CommandResult]
72def recap_identity(data: dict[str, Any], host: str) -> str:
73 """Return Ansible's recap name without changing the fleet control identity."""
74 transport = fm.CLASSES[data[
"hosts"][host][
"class"]].transport
75 return "localhost" if transport ==
"wsl" else host
78def runner_hosts(data: dict[str, Any]) -> list[str]:
79 """Return capacity-managed hosts in producer-before-consumer order."""
80 names = [name
for name, host
in data[
"hosts"].items()
if host.get(
"runners")]
81 producer = str(data[
"runner_image"][
"source_host"])
82 if producer
not in names:
83 msg =
"runner_image.source_host is not a capacity-managed host"
85 return [producer, *[name
for name
in names
if name != producer]]
88def parse_changed(output: str, host: str, expected_plays: int) -> int:
89 """Return changed tasks from exact successful Ansible recap rows."""
90 rows: list[tuple[int, int, int]] = []
91 for raw
in output.splitlines():
92 match = RECAP_RE.fullmatch(ANSI_RE.sub(
"", raw))
93 if match
is None or match.group(1) != host:
95 changed = int(match.group(3))
96 unreachable = int(match.group(4))
97 failed = int(match.group(5))
98 rows.append((changed, unreachable, failed))
99 if len(rows) != expected_plays:
100 msg = f
"{host}: expected {expected_plays} Ansible recap row(s), found {len(rows)}"
101 raise ValueError(msg)
102 if any(unreachable
or failed
for _, unreachable, failed
in rows):
103 msg = f
"{host}: Ansible recap reported a failed or unreachable play"
104 raise ValueError(msg)
105 return sum(changed
for changed, _, _
in rows)
108def fleet_command(host: str, verb: str) -> list[str]:
109 """Build one command against the fleet entry point in this snapshot."""
111 arguments = [
"check", host]
112 elif verb ==
"parked-check":
113 arguments = [
"reconcile-parked-check", host]
114 elif verb ==
"activate":
115 arguments = [
"reconcile-activate", host]
116 elif verb ==
"activation-check":
117 arguments = [
"reconcile-activation-check", host]
118 elif verb ==
"parked-apply":
119 arguments = [
"reconcile-parked-apply", host]
120 elif verb ==
"quarantine":
121 arguments = [
"capacity-quarantine", host]
122 elif verb ==
"restore":
123 arguments = [
"capacity-restore", host]
125 msg = f
"unsupported fleet reconcile verb: {verb}"
126 raise ValueError(msg)
127 return [sys.executable, str(fm.REPO_ROOT /
"scripts/dev/fleet.py"), *arguments]
130def emit_result(result: frp.CommandResult, stream: TextIO = sys.stdout) ->
None:
131 """Emit captured evidence without losing stderr attribution."""
132 stream.write(result.stdout)
133 sys.stderr.write(result.stderr)
136def load_state(path: Path) -> dict[str, Any]:
137 """Read prior receipts, refusing malformed state."""
138 if not path.exists():
139 return {
"version": 1,
"hosts": {}}
140 if path.is_symlink()
or not path.is_file():
141 msg = f
"state path is not a regular file: {path}"
142 raise ValueError(msg)
143 document = json.loads(path.read_text(encoding=
"ascii"))
144 if not isinstance(document, dict)
or document.get(
"version") != 1:
145 msg =
"fleet reconciliation state has an unsupported schema"
146 raise ValueError(msg)
147 hosts = document.get(
"hosts")
148 if not isinstance(hosts, dict):
149 msg =
"fleet reconciliation state has no host receipt map"
154def save_state(path: Path, document: dict[str, Any]) ->
None:
155 """Atomically publish reconciliation receipts."""
156 encoded = (json.dumps(document, indent=2, sort_keys=
True) +
"\n").encode(
"ascii")
157 fd, raw = tempfile.mkstemp(prefix=f
".{path.name}.", dir=path.parent)
158 temporary = Path(raw)
161 with os.fdopen(fd,
"wb")
as stream:
162 stream.write(encoded)
164 os.fsync(stream.fileno())
165 temporary.replace(path)
166 directory = os.open(path.parent, os.O_RDONLY | os.O_DIRECTORY)
172 temporary.unlink(missing_ok=
True)
175def full_apply_due(receipt: object, options: ReconcileOptions, interval: int) -> bool:
176 """Decide whether periodic full convergence is due for one host."""
177 if options.force
or not isinstance(receipt, dict):
179 if receipt.get(
"source_digest") != options.source_digest:
181 applied = receipt.get(
"full_applied_at")
182 return not isinstance(applied, int)
or options.now - applied >= interval
186 data: dict[str, Any], host: str, run: CommandRunner, *, parked: bool =
False
187) -> tuple[bool, int]:
188 """Run a read-only host check and return success plus drift count."""
189 verb =
"parked-check" if parked
else "check"
190 result = run(fleet_command(host, verb))
192 if result.status == fw.APPLY_REQUIRED_STATUS:
197 changed = parse_changed(
198 result.stdout, recap_identity(data, host), len(data[
"hosts"][host][
"provisions"])
200 except ValueError
as error:
201 print(f
"fleet-reconcile: {error}", file=sys.stderr)
206def inspect_activation_host(
207 data: dict[str, Any], host: str, run: CommandRunner
208) -> tuple[bool, int]:
209 """Check declared ARC authority while its rendered live ceiling stays zero."""
210 result = run(fleet_command(host,
"activation-check"))
215 changed = parse_changed(
217 recap_identity(data, host),
218 len(data[
"hosts"][host][
"provisions"]),
220 except ValueError
as error:
221 print(f
"fleet-reconcile: {error}", file=sys.stderr)
226def quarantine(host: str, run: CommandRunner) ->
None:
227 """Drain a host after failed mutation so it cannot accept new work."""
228 print(f
"fleet-reconcile: quarantining {host} at zero capacity", file=sys.stderr)
229 result = run(fleet_command(host,
"quarantine"))
233 f
"fleet-reconcile: WARNING: could not quarantine {host} (rc={result.status})",
239 data: dict[str, Any], host: str, run: CommandRunner, expected_changes: int
240) -> tuple[bool, int]:
241 """Validate declared ARC authority at zero before the sole capacity opener."""
242 activation = run(fleet_command(host,
"activate"))
243 emit_result(activation)
244 if activation.status
or frp.interrupted_status():
245 quarantine(host, run)
247 clean, changed = inspect_activation_host(data, host, run)
248 if not clean
or frp.interrupted_status()
or changed != expected_changes:
249 quarantine(host, run)
250 return False, changed
251 restore = run(fleet_command(host,
"restore"))
253 if restore.status
or frp.interrupted_status():
254 quarantine(host, run)
260 data: dict[str, Any], host: str, run: CommandRunner, *, expected_check_changes: int
261) -> tuple[bool, int]:
262 """Apply one host and prove the resulting declaration is idempotent."""
263 result = run(fleet_command(host,
"parked-apply"))
265 if result.status
or frp.interrupted_status():
266 quarantine(host, run)
268 clean, changed = inspect_host(data, host, run, parked=
True)
269 if not clean
or changed != expected_check_changes
or frp.interrupted_status():
271 f
"fleet-reconcile: {host} did not reach an idempotent parked state "
272 f
"(remaining changed={changed})",
275 quarantine(host, run)
276 return False, changed
277 host_class = fm.CLASSES[data[
"hosts"][host][
"class"]]
278 if host_class.capacity_kind ==
"k8s":
279 return _activate_arc(data, host, run, expected_check_changes)
280 restore = run(fleet_command(host,
"restore"))
282 if restore.status
or frp.interrupted_status():
283 quarantine(host, run)
285 clean, changed = inspect_host(data, host, run)
286 if not clean
or changed != expected_check_changes
or frp.interrupted_status():
287 quarantine(host, run)
288 return False, changed
293 data: dict[str, Any],
296 options: ReconcileOptions,
298) -> tuple[bool, dict[str, Any]]:
299 """Inspect and optionally converge one normal runner host."""
300 clean, changed = inspect_host(data, host, run)
301 if not clean
or frp.interrupted_status():
303 producer = host == data[
"runner_image"][
"source_host"]
304 interval = options.producer_interval
if producer
else options.full_interval
305 due = full_apply_due(receipt, options, interval)
306 expected_changes = PRODUCER_CHECK_NOISE
if producer
else 0
307 actionable_changes = changed != expected_changes
308 if options.mode ==
"check":
309 state =
"CHECK-NOISE" if producer
and not actionable_changes
else "CURRENT"
310 if actionable_changes:
312 print(f
"fleet-reconcile: {host}: {state} (changed={changed})")
313 return not actionable_changes, {}
314 if not actionable_changes
and not due:
315 print(f
"fleet-reconcile: {host}: current; no full converge due")
316 previous = receipt
if isinstance(receipt, dict)
else {}
317 return True, {**previous,
"checked_at": options.now}
318 why =
"drift" if actionable_changes
else "periodic full verification"
319 print(f
"fleet-reconcile: {host}: applying ({why}, changed={changed})")
320 held_arc = producer
and fm.CLASSES[data[
"hosts"][host][
"class"]].capacity_kind ==
"k8s"
321 held_changes = PRODUCER_HELD_CHECK_NOISE
if held_arc
else expected_changes
322 applied, _ = apply_host(data, host, run, expected_check_changes=held_changes)
326 "checked_at": options.now,
327 "full_applied_at": options.now,
328 "source_digest": options.source_digest,
333 data: dict[str, Any], options: ReconcileOptions, run: CommandRunner = frp.command_runner
335 """Reconcile producer then consumers, preserving dependency safety."""
336 state_path = options.state_dir / STATE_FILE
337 document = load_state(state_path)
338 receipts = document[
"hosts"]
340 producer_failed =
False
341 for index, host
in enumerate(runner_hosts(data)):
342 if frp.interrupted_status():
344 if index
and producer_failed:
345 print(f
"fleet-reconcile: {host}: BLOCKED by producer failure", file=sys.stderr)
349 receipt_invalidated =
False
351 def transaction_run(argv: Sequence[str], target: str = host) -> frp.CommandResult:
352 nonlocal receipt_invalidated
353 verb, _command_host = _command_identity(argv)
354 if verb ==
"parked-apply" and not receipt_invalidated:
355 receipts.pop(target,
None)
356 save_state(state_path, document)
357 receipt_invalidated =
True
360 ok, receipt = reconcile_host(data, host, receipts.get(host), options, transaction_run)
361 if ok
and options.mode ==
"apply":
362 receipts[host] = receipt
365 producer_failed = index == 0
and options.mode ==
"apply"
366 if options.mode ==
"apply":
367 receipts.pop(host,
None)
368 save_state(state_path, document)
369 if options.mode ==
"apply":
370 save_state(state_path, document)
371 return 1
if failures
else 0
374def validate_installed_authority(root: Path) -> str:
375 """Authenticate the root-owned snapshot selected by the systemd unit."""
376 lexical = root.absolute()
377 resolved = lexical.resolve(strict=
True)
378 metadata = lexical.lstat()
379 if lexical != resolved
or stat.S_ISLNK(metadata.st_mode)
or not stat.S_ISDIR(metadata.st_mode):
380 msg =
"installed reconciliation source is linked or not a real directory"
381 raise ValueError(msg)
382 if metadata.st_uid != 0
or metadata.st_mode & 0o022:
383 msg =
"installed reconciliation source must be root-owned and not group/world writable"
384 raise ValueError(msg)
385 marker = root / SOURCE_DIGEST_FILE
386 marker_metadata = marker.lstat()
388 stat.S_ISLNK(marker_metadata.st_mode)
389 or not stat.S_ISREG(marker_metadata.st_mode)
390 or marker_metadata.st_uid != 0
391 or marker_metadata.st_mode & 0o222
393 msg =
"installed reconciliation source digest has unsafe ownership or mode"
394 raise ValueError(msg)
395 digest = marker.read_text(encoding=
"ascii").strip()
396 if SHA256_RE.fullmatch(digest)
is None:
397 msg =
"installed reconciliation source digest is malformed"
398 raise ValueError(msg)
402def prepare_state_dir(path: Path) ->
None:
403 """Create or validate the caller-private state directory."""
404 path.mkdir(mode=0o700, parents=
True, exist_ok=
True)
405 metadata = path.lstat()
406 if path.is_symlink()
or not path.is_dir()
or metadata.st_uid != os.getuid():
407 msg = f
"state directory is not caller-owned: {path}"
408 raise ValueError(msg)
409 if stat.S_IMODE(metadata.st_mode) != PRIVATE_DIRECTORY_MODE:
410 msg = f
"state directory is not mode 0700: {path}"
411 raise ValueError(msg)
414def _recap(host: str, changed: int = 0, failed: int = 0, unreachable: int = 0) -> str:
415 """Build one Ansible recap fixture line."""
417 f
"{host} : ok=9 changed={changed} unreachable={unreachable} failed={failed} "
418 "skipped=1 rescued=0 ignored=0\n"
422def _selftest_data() -> dict[str, Any]:
423 """Return one fleet containing a producer, consumer, and excluded bench."""
425 "runner_image": {
"source_host":
"producer"},
428 "class":
"docker_wsl",
429 "runners": {
"instances": 1},
430 "provisions": [
"one"],
432 "bench": {
"class":
"hil_bench",
"provisions": [
"bench"]},
434 "class":
"docker_linux",
435 "runners": {
"instances": 1},
436 "provisions": [
"one",
"two"],
442def _selftest_options(state_dir: Path, *, mode: str =
"apply") -> ReconcileOptions:
443 """Return deterministic policy inputs for controller tests."""
444 return ReconcileOptions(
447 source_digest=
"a" * 64,
450 producer_interval=50,
455def _command_identity(argv: Sequence[str]) -> tuple[str, str]:
456 """Return the fleet verb and host from a generated test command."""
458 "capacity-quarantine":
"quarantine",
459 "capacity-restore":
"restore",
460 "reconcile-parked-apply":
"parked-apply",
461 "reconcile-parked-check":
"parked-check",
462 "reconcile-activate":
"activate",
463 "reconcile-activation-check":
"activation-check",
465 return identities.get(argv[2], argv[2]), argv[-1]
468def _check_result(data: dict[str, Any], host: str, changed: int = 0) -> frp.CommandResult:
469 """Return a successful check with the declared recap-row count."""
470 rows = [_recap(recap_identity(data, host), changed)]
471 rows.extend(_recap(recap_identity(data, host))
for _
in data[
"hosts"][host][
"provisions"][1:])
472 return frp.CommandResult(0,
"".join(rows),
"")
475def _clean_check_result(data: dict[str, Any], host: str) -> frp.CommandResult:
476 """Return the exact accepted check result for one host class."""
477 producer = host == data[
"runner_image"][
"source_host"]
478 return _check_result(data, host, PRODUCER_CHECK_NOISE
if producer
else 0)
481def _selftest_wsl_status_mapping(data: dict[str, Any], failures: list[str]) ->
None:
482 """Prove safe WSL drift is actionable while probe failures stay fatal."""
484 def apply_required(_argv: Sequence[str]) -> frp.CommandResult:
485 return frp.CommandResult(fw.APPLY_REQUIRED_STATUS,
"",
"")
487 if inspect_host(data,
"consumer", apply_required) != (
True, 1):
488 failures.append(
"authenticated WSL stage drift did not request an apply")
490 def fatal_probe(_argv: Sequence[str]) -> frp.CommandResult:
491 return frp.CommandResult(5,
"",
"")
493 if inspect_host(data,
"consumer", fatal_probe) != (
False, 0):
494 failures.append(
"fatal WSL inspection error was treated as repairable drift")
497def _selftest_order_parsing_and_schedule(failures: list[str]) ->
None:
498 """Prove runner selection, strict recap parsing, and interval decisions."""
499 data = _selftest_data()
500 if runner_hosts(data) != [
"producer",
"consumer"]:
501 failures.append(
"producer ordering or non-runner exclusion drifted")
503 parse_changed(_recap(
"producer", 1) + _recap(
"producer", 2),
"producer", 2)
504 != SELFTEST_CHANGED_TOTAL
506 failures.append(
"changed recap sum drifted")
507 wsl_calls: list[tuple[str, str]] = []
509 def wsl_run(argv: Sequence[str]) -> frp.CommandResult:
510 wsl_calls.append(_command_identity(argv))
511 return _check_result(data,
"consumer")
513 if inspect_host(data,
"consumer", wsl_run) != (
True, 0):
514 failures.append(
"WSL localhost recap was not mapped to its fleet identity")
516 _selftest_wsl_status_mapping(data, failures)
517 if wsl_calls != [(
"check",
"consumer")]:
518 failures.append(
"WSL recap mapping changed its declared control identity")
520 parse_changed(_recap(
"consumer"), recap_identity(data,
"consumer"), 1)
521 failures.append(
"WSL declared name was accepted as its local recap identity")
525 parse_changed(_recap(
"producer", failed=1),
"producer", 1)
526 failures.append(
"failed recap was accepted")
530 parse_changed(_recap(
"producer", unreachable=1),
"producer", 1)
531 failures.append(
"unreachable recap was accepted")
534 options = _selftest_options(Path(
"/unused"))
535 fresh = {
"source_digest":
"a" * 64,
"full_applied_at": 950}
536 if full_apply_due(fresh, options, options.full_interval):
537 failures.append(
"fresh matching receipt forced a full apply")
538 if not full_apply_due(fresh, options, options.producer_interval):
539 failures.append(
"expired producer receipt skipped its full apply")
540 stale_source = {**fresh,
"source_digest":
"b" * 64}
541 if not full_apply_due(stale_source, options, options.full_interval):
542 failures.append(
"source change did not force a full apply")
545def _selftest_check_mode(failures: list[str]) ->
None:
546 """Prove producer staging noise is tolerated but consumer drift is not."""
547 data = _selftest_data()
548 with tempfile.TemporaryDirectory(prefix=
"ra8-fleet-reconcile-")
as raw:
549 state_dir = Path(raw)
550 options = _selftest_options(state_dir, mode=
"check")
551 calls: list[tuple[str, str]] = []
553 def fake_run(argv: Sequence[str]) -> frp.CommandResult:
554 verb, host = _command_identity(argv)
555 calls.append((verb, host))
556 return _clean_check_result(data, host)
558 if reconcile(data, options, fake_run):
559 failures.append(
"producer check-mode staging noise failed the controller")
560 if calls != [(
"check",
"producer"), (
"check",
"consumer")]:
561 failures.append(
"check mode invoked a mutation or reordered hosts")
563 def drift_run(argv: Sequence[str]) -> frp.CommandResult:
564 _, host = _command_identity(argv)
565 if host ==
"consumer":
566 return _check_result(data, host, 1)
567 return _clean_check_result(data, host)
569 if reconcile(data, options, drift_run) != 1:
570 failures.append(
"consumer drift passed check mode")
572 def producer_drift_run(argv: Sequence[str]) -> frp.CommandResult:
573 _, host = _command_identity(argv)
574 return _check_result(data, host, 3
if host ==
"producer" else 0)
576 if reconcile(data, options, producer_drift_run) != 1:
577 failures.append(
"producer drift was mistaken for staging noise")
580def _selftest_apply_and_receipt(failures: list[str]) ->
None:
581 """Prove drift is applied, rechecked, and recorded atomically."""
582 data = _selftest_data()
583 with tempfile.TemporaryDirectory(prefix=
"ra8-fleet-reconcile-")
as raw:
584 state_dir = Path(raw)
585 options = _selftest_options(state_dir)
586 receipt = {
"source_digest":
"a" * 64,
"full_applied_at": 975}
588 state_dir / STATE_FILE,
589 {
"version": 1,
"hosts": {
"producer": receipt,
"consumer": receipt}},
591 calls: list[tuple[str, str]] = []
594 def fake_run(argv: Sequence[str]) -> frp.CommandResult:
595 nonlocal consumer_checks
596 verb, host = _command_identity(argv)
597 calls.append((verb, host))
598 if verb ==
"check" and host ==
"consumer":
600 return _check_result(data, host, 2
if consumer_checks == 1
else 0)
601 if verb
in {
"check",
"parked-check"}:
602 return _clean_check_result(data, host)
603 return frp.CommandResult(0,
"",
"")
605 if reconcile(data, options, fake_run):
606 failures.append(
"repairable consumer drift failed reconciliation")
608 (
"check",
"producer"),
609 (
"check",
"consumer"),
610 (
"parked-apply",
"consumer"),
611 (
"parked-check",
"consumer"),
612 (
"restore",
"consumer"),
613 (
"check",
"consumer"),
615 if calls != expected:
616 failures.append(
"consumer repair did not follow check/apply/recheck order")
617 stored = load_state(state_dir / STATE_FILE)[
"hosts"][
"consumer"]
618 if stored.get(
"full_applied_at") != options.now:
619 failures.append(
"successful repair did not publish a receipt")
622def _selftest_failure_quarantine(failures: list[str]) ->
None:
623 """Prove failed producer mutation drains it and blocks every consumer."""
624 data = _selftest_data()
625 with tempfile.TemporaryDirectory(prefix=
"ra8-fleet-reconcile-")
as raw:
626 options = _selftest_options(Path(raw))
627 calls: list[tuple[str, str]] = []
629 def fake_run(argv: Sequence[str]) -> frp.CommandResult:
630 verb, host = _command_identity(argv)
631 calls.append((verb, host))
632 if verb
in {
"check",
"parked-check"}:
633 return _clean_check_result(data, host)
634 return frp.CommandResult(1
if verb ==
"parked-apply" else 0,
"",
"")
636 if reconcile(data, options, fake_run) != 1:
637 failures.append(
"producer mutation failure did not fail reconciliation")
639 (
"check",
"producer"),
640 (
"parked-apply",
"producer"),
641 (
"quarantine",
"producer"),
643 if calls != expected:
644 failures.append(
"producer failure did not drain before blocking consumers")
647def _selftest_failed_repair_retries(failures: list[str]) ->
None:
648 """Prove a failed repair invalidates success and forces the next full apply."""
649 data = _selftest_data()
650 with tempfile.TemporaryDirectory(prefix=
"ra8-fleet-reconcile-")
as raw:
651 state_dir = Path(raw)
652 options = _selftest_options(state_dir)
653 receipt = {
"source_digest": options.source_digest,
"full_applied_at": 975}
655 state_dir / STATE_FILE,
656 {
"version": 1,
"hosts": {
"producer": receipt,
"consumer": receipt}},
660 class SimulatedHardKill(BaseException):
661 """Model controller death at the parked-child launch boundary."""
663 def fail_repair(argv: Sequence[str]) -> frp.CommandResult:
664 nonlocal consumer_checks
665 verb, host = _command_identity(argv)
666 if verb ==
"check" and host ==
"consumer":
668 return _check_result(data, host, 1)
669 if verb
in {
"check",
"parked-check"}:
670 return _clean_check_result(data, host)
671 if verb ==
"parked-apply":
672 persisted = load_state(state_dir / STATE_FILE)[
"hosts"]
673 if "consumer" in persisted:
674 failures.append(
"receipt was still successful when parked apply began")
675 raise SimulatedHardKill
676 return frp.CommandResult(0,
"",
"")
679 reconcile(data, options, fail_repair)
680 failures.append(
"simulated hard kill returned through reconciliation")
681 except SimulatedHardKill:
683 stored = load_state(state_dir / STATE_FILE)[
"hosts"]
684 if "consumer" in stored:
685 failures.append(
"failed repair retained its prior success receipt")
686 retry_calls: list[tuple[str, str]] = []
688 def retry(argv: Sequence[str]) -> frp.CommandResult:
689 verb, host = _command_identity(argv)
690 retry_calls.append((verb, host))
691 if verb
in {
"check",
"parked-check"}:
692 return _clean_check_result(data, host)
693 return frp.CommandResult(0,
"",
"")
695 if reconcile(data, options, retry):
696 failures.append(
"immediate retry after failed repair did not converge")
697 if (
"parked-apply",
"consumer")
not in retry_calls:
698 failures.append(
"missing receipt did not force the next consumer repair")
701def _selftest_postcheck_quarantine(failures: list[str]) ->
None:
702 """Prove a consumer that remains changed is drained after its repair."""
703 data = _selftest_data()
704 with tempfile.TemporaryDirectory(prefix=
"ra8-fleet-reconcile-")
as raw:
705 state_dir = Path(raw)
706 options = _selftest_options(state_dir)
707 receipt = {
"source_digest":
"a" * 64,
"full_applied_at": 975}
709 state_dir / STATE_FILE,
710 {
"version": 1,
"hosts": {
"producer": receipt,
"consumer": receipt}},
712 calls: list[tuple[str, str]] = []
714 def fake_run(argv: Sequence[str]) -> frp.CommandResult:
715 verb, host = _command_identity(argv)
716 calls.append((verb, host))
717 if verb
in {
"check",
"parked-check"}:
719 _check_result(data, host, 1)
720 if host ==
"consumer"
721 else _clean_check_result(data, host)
723 return frp.CommandResult(0,
"",
"")
725 if reconcile(data, options, fake_run) != 1:
726 failures.append(
"non-idempotent consumer repair passed")
728 (
"parked-apply",
"consumer"),
729 (
"parked-check",
"consumer"),
730 (
"quarantine",
"consumer"),
732 if calls[-3:] != expected_tail:
733 failures.append(
"non-idempotent consumer was not quarantined")
736def _selftest_restore_quarantine(failures: list[str]) ->
None:
737 """Prove a failed capacity restore is driven back to zero before return."""
738 data = _selftest_data()
739 with tempfile.TemporaryDirectory(prefix=
"ra8-fleet-reconcile-")
as raw:
740 options = _selftest_options(Path(raw))
741 calls: list[tuple[str, str]] = []
743 def fake_run(argv: Sequence[str]) -> frp.CommandResult:
744 verb, host = _command_identity(argv)
745 calls.append((verb, host))
746 if verb
in {
"check",
"parked-check"}:
747 return _clean_check_result(data, host)
748 return frp.CommandResult(1
if verb ==
"restore" else 0,
"",
"")
750 if reconcile(data, options, fake_run) != 1:
751 failures.append(
"failed capacity restore passed reconciliation")
753 (
"parked-apply",
"producer"),
754 (
"parked-check",
"producer"),
755 (
"restore",
"producer"),
756 (
"quarantine",
"producer"),
758 if calls[-4:] != expected_tail:
759 failures.append(
"failed restore did not drive the host back to zero")
762def _selftest_state_safety(failures: list[str]) ->
None:
763 """Prove private state, symlink refusal, locking, and atomic round trips."""
764 with tempfile.TemporaryDirectory(prefix=
"ra8-fleet-reconcile-")
as raw:
765 state_dir = Path(raw)
766 prepare_state_dir(state_dir)
767 receipt = {
"source_digest":
"a" * 64,
"full_applied_at": 950}
768 state_path = state_dir / STATE_FILE
769 save_state(state_path, {
"version": 1,
"hosts": {
"producer": receipt}})
770 if load_state(state_path)[
"hosts"][
"producer"] != receipt:
771 failures.append(
"atomic receipt round trip drifted")
773 target = state_dir /
"target.json"
774 target.write_text(
'{"version": 1, "hosts": {}}\n', encoding=
"ascii")
775 state_path.symlink_to(target)
777 load_state(state_path)
778 failures.append(
"linked state file was accepted")
783def _selftest_timeout(failures: list[str]) ->
None:
784 """Prove a timed-out mutation returns through the quarantine path."""
785 result = frp.command_runner(
789 "import sys,time; print('partial stdout', flush=True); "
790 "print('partial stderr', file=sys.stderr, flush=True); time.sleep(60)",
792 timeout_seconds=0.05,
794 if result.status != frp.TIMEOUT_STATUS
or "command timed out" not in result.stderr:
795 failures.append(
"a timed-out fleet command escaped fail-closed handling")
796 if result.stdout !=
"partial stdout\n":
797 failures.append(
"timed-out command evidence was discarded")
800def _selftest_signal_quarantine(failures: list[str]) ->
None:
801 """Prove TERM stops the owned process group and completes quarantine."""
802 data = _selftest_data()
803 with tempfile.TemporaryDirectory(prefix=
"ra8-fleet-reconcile-signal-")
as raw:
804 marker = Path(raw) /
"quarantine"
805 ready = marker.with_suffix(
".ready")
807 def request_stop() -> None:
808 deadline = time.monotonic() + 5
809 while not ready.exists()
and time.monotonic() < deadline:
812 os.kill(os.getpid(), signal.SIGTERM)
814 def fake_run(argv: Sequence[str]) -> frp.CommandResult:
815 verb, _host = _command_identity(argv)
816 if verb ==
"parked-apply":
818 "import pathlib,signal,subprocess,sys,time; "
819 "child=subprocess.Popen(['sleep','60']); "
820 "signal.signal(signal.SIGTERM, lambda *_: "
821 "(child.terminate(), child.wait(), sys.exit(143))); "
822 f
"pathlib.Path({str(ready)!r}).write_text(str(child.pid)); "
825 return frp.command_runner(
826 [sys.executable,
"-c", code], timeout_seconds=SELFTEST_SIGNAL_BOUND
828 if verb ==
"quarantine":
829 marker.write_text(
"quarantined", encoding=
"ascii")
830 return frp.CommandResult(0,
"",
"")
831 return frp.CommandResult(2,
"",
"unexpected signal selftest command\n")
833 sender = Thread(target=request_stop, name=
"fleet-reconcile-signal-selftest")
835 started = time.monotonic()
836 with frp.stop_handlers():
837 applied, _changed = apply_host(data,
"consumer", fake_run, expected_check_changes=0)
838 elapsed = time.monotonic() - started
839 sender.join(timeout=1)
840 if sender.is_alive():
841 failures.append(
"signal selftest sender did not finish")
842 if applied
or frp.interrupted_status() != 128 + signal.SIGTERM:
843 failures.append(
"TERM did not preserve the interrupted service status")
844 if elapsed >= SELFTEST_SIGNAL_BOUND:
845 failures.append(
"interrupted controller did not finish its handler")
846 if not marker.exists():
847 failures.append(
"TERM during a mutation did not route through quarantine")
848 if not ready.exists():
849 failures.append(
"signal selftest mutation never started")
851 worker_pid = int(ready.read_text(encoding=
"ascii"))
852 deadline = time.monotonic() + 2
853 while time.monotonic() < deadline:
855 os.kill(worker_pid, 0)
856 except ProcessLookupError:
860 failures.append(
"TERM left an owned mutation child running")
863def selftest() -> int:
864 """Exercise the unattended controller's safety-critical decisions."""
865 failures: list[str] = []
866 _selftest_order_parsing_and_schedule(failures)
867 _selftest_check_mode(failures)
868 _selftest_apply_and_receipt(failures)
869 _selftest_failure_quarantine(failures)
870 _selftest_failed_repair_retries(failures)
871 _selftest_postcheck_quarantine(failures)
872 _selftest_restore_quarantine(failures)
873 failures.extend(fras.run(apply_host))
874 _selftest_state_safety(failures)
875 failures.extend(fml.run_selftest())
876 _selftest_timeout(failures)
877 failures.extend(frp.run_selftest())
878 _selftest_signal_quarantine(failures)
879 failures.extend(frs.runtime_inventory_selftest(fm))
880 failures.extend(frs.run(fm.REPO_ROOT))
881 for failure
in failures:
882 print(f
"fleet_reconcile.py --selftest: FAIL: {failure}", file=sys.stderr)
885 print(
"fleet_reconcile.py --selftest: PASS")
889def parse_args(argv: Sequence[str] |
None =
None) -> argparse.Namespace:
890 """Parse the offline selftest and live reconciliation boundaries."""
891 parser = argparse.ArgumentParser(description=__doc__)
892 parser.add_argument(
"--selftest", action=
"store_true")
893 parser.add_argument(
"--mode", choices=(
"apply",
"check"), default=
"apply")
894 parser.add_argument(
"--force", action=
"store_true")
895 parser.add_argument(
"--require-installed-authority", action=
"store_true")
896 parser.add_argument(
"--state-dir", type=Path)
897 parser.add_argument(
"--full-interval", type=int, default=DEFAULT_FULL_INTERVAL)
898 parser.add_argument(
"--producer-interval", type=int, default=DEFAULT_PRODUCER_INTERVAL)
899 return parser.parse_args(argv)
902def _early_status(args: argparse.Namespace) -> int |
None:
903 """Handle non-live modes and reject invalid option combinations."""
906 if args.force
and args.mode !=
"apply":
907 print(
"fleet-reconcile: --force requires --mode apply", file=sys.stderr)
909 if args.full_interval <= 0
or args.producer_interval <= 0:
910 print(
"fleet-reconcile: convergence intervals must be positive", file=sys.stderr)
915def main(argv: Sequence[str] |
None =
None) -> int:
916 """Enter selftest or one locked reconciliation transaction."""
917 args = parse_args(argv)
918 early_status = _early_status(args)
919 if early_status
is not None:
923 validate_installed_authority(fm.REPO_ROOT)
924 if args.require_installed_authority
925 else "manual-operator-checkout"
927 state_dir = args.state_dir
or Path.home() /
".local/state/ra8-fleet-reconcile"
928 prepare_state_dir(state_dir)
929 if args.require_installed_authority:
930 fm.validate_runtime_inventory(state_dir)
932 options = ReconcileOptions(
938 args.producer_interval,
941 if args.mode ==
"check":
942 with frp.stop_handlers():
943 status = reconcile(data, options)
944 return frp.interrupted_status()
or status
946 fml.mutation_lock(data, installed_local=args.require_installed_authority),
950 def guarded_runner(argv: Sequence[str]) -> frp.CommandResult:
951 return frp.command_runner(argv, guardian=
True)
953 status = reconcile(data, options, guarded_runner)
954 return frp.interrupted_status()
or status
955 except fml.MutationLockBusyError
as error:
956 print(f
"fleet-reconcile: {error}", file=sys.stderr)
957 return fml.LOCK_BUSY_STATUS
959 fml.MutationLockError,
963 json.JSONDecodeError,
966 print(f
"fleet-reconcile: FATAL: {error}", file=sys.stderr)
970if __name__ ==
"__main__":
void main(void)
The application entry point Reset_Handler hands control to.