ra8-firmware 0.1.0
Bare-metal firmware for the Renesas RA8 family (RA8D2 / RA8P1)
Loading...
Searching...
No Matches
hil_convergence_safety_semantic_mutations.py
Go to the documentation of this file.
1# SPDX-License-Identifier: MIT
2# Copyright (c) 2026 Brighton Sikarskie
3"""Semantic mutation and raw-path fixtures for image-lock convergence."""
4
5from __future__ import annotations
6
7import ast
8import hashlib
9from collections.abc import Callable
10
11import hil_convergence_safety_image_lock_digest as image_lock_digest
12import hil_convergence_safety_image_process_analysis as image_process_analysis
13import hil_convergence_safety_process_mutations as process_mutations
14import hil_convergence_safety_raw_digest_runtime as raw_digest_runtime
15import hil_convergence_safety_runtime_cleanup as runtime_cleanup
16import hil_convergence_safety_runtime_escape as runtime_escape
17import hil_convergence_safety_runtime_fixtures as runtime_fixtures
18import hil_convergence_safety_runtime_mutations as runtime_mutations
19
20Mutation = tuple[str, str, str, str]
21Scan = Callable[[dict[str, str]], list[str]]
22
23
24class SemanticMutationError(ValueError):
25 """A semantic mutation fixture no longer has one exact authority."""
26
27
28def _scoped_owner_authority(key: str, owner: str) -> str | None:
29 """Map one scoped Python owner to its production diagnostic authority."""
30 return {
31 (
32 "devcontainer_image_selftest_supervisor",
33 "_load_cases_dispatch",
34 ): "devcontainer image supervisor",
35 (
36 "devcontainer_image_selftest_supervisor",
37 "_read_cases_source",
38 ): "devcontainer image supervisor",
39 (
40 "devcontainer_image_selftest_supervisor_cases",
41 "_closed_controller_command",
42 ): "devcontainer image supervisor cases",
43 (
44 "devcontainer_image_selftest_supervisor_cases",
45 "_closed_controller_descriptor_selftest",
46 ): "devcontainer image supervisor cases",
47 (
48 "devcontainer_image_selftest_supervisor_cases",
49 "_refused_controller_launch",
50 ): "devcontainer image supervisor cases",
51 (
52 "devcontainer_image_selftest_supervisor_cases",
53 "_watchdog_expiry_runner",
54 ): "devcontainer image supervisor cases",
55 }.get((key, owner))
56
57
58def _owner_mutation_findings(label: str) -> tuple[str, ...] | None:
59 """Return exact diagnostics for owner absence and ambiguity fixtures."""
60 prefixes = ("semantic owner renamed: ", "semantic owner duplicated: ")
61 prefix = next((value for value in prefixes if label.startswith(value)), None)
62 if prefix is None:
63 return None
64 key, separator, owner = label.removeprefix(prefix).partition(":")
65 if not separator or not key or not owner:
66 return None
67 if (authority := _scoped_owner_authority(key, owner)) is not None:
68 finding = f"{authority}: {owner} scoped owner is missing, ambiguous, or unparseable"
69 else:
70 finding = (
71 f"devcontainer image source policy: {key}:{owner} "
72 "owner is missing, ambiguous, or unparseable"
73 )
74 extras = {
75 ("semantic owner renamed: devcontainer_image_selftest_supervisor:_load_cases_dispatch"): (
76 "devcontainer image supervisor: required process-authority token "
77 "is not unique: def _load_cases_dispatch(descriptor: int)",
78 ),
79 (
80 "semantic owner renamed: "
81 "devcontainer_image_selftest_supervisor:_open_suite_root_authority"
82 ): (
83 "devcontainer image supervisor: required process-authority token "
84 "is not unique: def _open_suite_root_authority(",
85 ),
86 (
87 "semantic owner renamed: "
88 "devcontainer_image_selftest_supervisor_cases:_closed_controller_command"
89 ): (
90 "devcontainer image supervisor cases: required process-authority token "
91 "is not unique: def _closed_controller_command(",
92 ),
93 }
94 return (*extras.get(label, ()), finding)
95
96
97def semantic_image_findings(label: str, key: str) -> tuple[str, ...] | None:
98 """Return the exact non-digest diagnostics required for one helper mutation."""
99 if (owner_findings := _owner_mutation_findings(label)) is not None:
100 return owner_findings
101 expected = image_process_analysis.semantic_process_findings(label)
102 if label == "closed-controller command definition removed" and expected is not None:
103 return (
104 *expected,
105 "devcontainer image supervisor cases: _closed_controller_command "
106 "scoped owner is missing, ambiguous, or unparseable",
107 )
108 if label == "suite anchor identity binding removed" and expected is not None:
109 expected = (
110 "devcontainer image lifecycle selftest: required process-authority token "
111 'is not unique: "$(file_identity "$anchor")" == '
112 '"$SELFTEST_SUITE_ANCHOR_IDENTITY"',
113 *expected,
114 )
115 order_labels = {
116 "bound-exit supervisor pre-spawn signal block removed",
117 "bound-exit parent-death polling removed",
118 "bound-exit controller group cleanup reduced to controller PID",
119 "supervisor interruption-handler definition removed",
120 "supervisor interruption-handler call removed",
121 }
122 if label in order_labels and expected is not None:
123 return (*expected, "devcontainer image supervisor: cleanup proof order drifted")
124 if label == "supervisor launcher bound entry environment removed" and expected is not None:
125 return (
126 *expected,
127 "devcontainer image lifecycle selftest: required process-authority token "
128 "is not unique: "
129 'RA8_SELFTEST_BOUND_ENTRY="$1"',
130 )
131 if expected is not None:
132 return expected
133 if key == "devcontainer_image_lock_receipts" and label.endswith(" early success refused"):
134 function = label.removeprefix("image lock ").removesuffix(" early success refused")
135 expected = (f"image lock receipt: {function} can return success before its proof",)
136 else:
137 expected = runtime_fixtures.semantic_findings(label, key)
138 return expected
139
140
141def _rebind_main_mutation(inputs: dict[str, str]) -> dict[str, str]:
142 """Rebind a mutated main image script into the raw digest authority."""
143 changed = dict(inputs)
144 main_digest = hashlib.sha256(changed["devcontainer_image"].encode("utf-8")).hexdigest()
145 current_main = image_lock_digest.DEVCONTAINER_IMAGE_RAW_SHA256
146 changed["image_lock_digest"] = _replace_exact_pin(
147 changed["image_lock_digest"], current_main, main_digest, "the raw main pin"
148 )
149 return changed
150
151
152def rebind_helper_mutation(inputs: dict[str, str], key: str) -> dict[str, str]:
153 """Rebind one mutated image authority so only its semantic detector can fire."""
154 if key == "devcontainer_image":
155 return _rebind_main_mutation(inputs)
156 current_pins = {
157 "devcontainer_image_selftest": image_lock_digest.DEVCONTAINER_IMAGE_SELFTEST_RAW_SHA256,
158 "devcontainer_image_bound_exit_selftest": (
159 image_lock_digest.DEVCONTAINER_IMAGE_BOUND_EXIT_SELFTEST_RAW_SHA256
160 ),
161 "devcontainer_image_lock_receipts": (
162 image_lock_digest.DEVCONTAINER_IMAGE_LOCK_RECEIPTS_RAW_SHA256
163 ),
164 "devcontainer_image_lock_selftest": (
165 image_lock_digest.DEVCONTAINER_IMAGE_LOCK_SELFTEST_RAW_SHA256
166 ),
167 "devcontainer_image_selftest_cases": (
168 image_lock_digest.DEVCONTAINER_IMAGE_SELFTEST_CASES_RAW_SHA256
169 ),
170 "devcontainer_image_signal_selftest": (
171 image_lock_digest.DEVCONTAINER_IMAGE_SIGNAL_SELFTEST_RAW_SHA256
172 ),
173 "devcontainer_image_selftest_process": (
174 image_lock_digest.DEVCONTAINER_IMAGE_SELFTEST_PROCESS_RAW_SHA256
175 ),
176 "devcontainer_image_selftest_supervisor": (
177 image_lock_digest.DEVCONTAINER_IMAGE_SELFTEST_SUPERVISOR_RAW_SHA256
178 ),
179 "devcontainer_image_selftest_supervisor_cases": (
180 image_lock_digest.DEVCONTAINER_IMAGE_SELFTEST_SUPERVISOR_CASES_RAW_SHA256
181 ),
182 }
183 current_helper = current_pins.get(key)
184 if current_helper is None:
185 message = f"unsupported semantic helper mutation authority: {key}"
186 raise SemanticMutationError(message)
187 helper_digest = hashlib.sha256(inputs[key].encode("utf-8")).hexdigest()
188 changed = dict(inputs)
189 if key == "devcontainer_image_selftest_supervisor_cases":
190 return _rebind_supervisor_cases_mutation(changed, helper_digest)
191 if key == "devcontainer_image_selftest_process":
192 return _rebind_process_mutation(changed, helper_digest)
193 if changed["devcontainer_image"].count(current_helper) != 1:
194 message = "semantic helper mutation cannot rebind the main helper pin"
195 raise SemanticMutationError(message)
196 changed["devcontainer_image"] = changed["devcontainer_image"].replace(
197 current_helper,
198 helper_digest,
199 )
200 main_digest = hashlib.sha256(changed["devcontainer_image"].encode("utf-8")).hexdigest()
201 authority = changed["image_lock_digest"]
202 current_main = image_lock_digest.DEVCONTAINER_IMAGE_RAW_SHA256
203 if authority.count(current_helper) != 1 or authority.count(current_main) != 1:
204 message = "semantic helper mutation cannot rebind the raw digest authority"
205 raise SemanticMutationError(message)
206 changed["image_lock_digest"] = authority.replace(
207 current_helper,
208 helper_digest,
209 ).replace(current_main, main_digest)
210 return changed
211
212
213def _replace_exact_pin(source: str, old: str, new: str, context: str) -> str:
214 """Replace one and only one raw digest in a named authority."""
215 if source.count(old) != 1:
216 message = f"semantic cases mutation cannot rebind {context}"
217 raise SemanticMutationError(message)
218 return source.replace(old, new)
219
220
221def _rebind_supervisor_cases_mutation(changed: dict[str, str], cases_digest: str) -> dict[str, str]:
222 """Cascade one cases mutation through supervisor, main, and raw authority."""
223 old_cases = image_lock_digest.DEVCONTAINER_IMAGE_SELFTEST_SUPERVISOR_CASES_RAW_SHA256
224 old_supervisor = image_lock_digest.DEVCONTAINER_IMAGE_SELFTEST_SUPERVISOR_RAW_SHA256
225 old_main = image_lock_digest.DEVCONTAINER_IMAGE_RAW_SHA256
226 supervisor = _replace_exact_pin(
227 changed["devcontainer_image_selftest_supervisor"],
228 old_cases,
229 cases_digest,
230 "the supervisor cases pin",
231 )
232 changed["devcontainer_image_selftest_supervisor"] = supervisor
233 supervisor_digest = hashlib.sha256(supervisor.encode("utf-8")).hexdigest()
234 main = _replace_exact_pin(
235 changed["devcontainer_image"], old_cases, cases_digest, "the main cases pin"
236 )
237 main = _replace_exact_pin(main, old_supervisor, supervisor_digest, "the main supervisor pin")
238 changed["devcontainer_image"] = main
239 main_digest = hashlib.sha256(main.encode("utf-8")).hexdigest()
240 authority = _replace_exact_pin(
241 changed["image_lock_digest"], old_cases, cases_digest, "the raw cases pin"
242 )
243 authority = _replace_exact_pin(
244 authority, old_supervisor, supervisor_digest, "the raw supervisor pin"
245 )
246 changed["image_lock_digest"] = _replace_exact_pin(
247 authority, old_main, main_digest, "the raw main pin"
248 )
249 return changed
250
251
252def _rebind_process_mutation(changed: dict[str, str], process_digest: str) -> dict[str, str]:
253 """Cascade one process mutation through supervisor, main, and raw authority."""
254 old_process = image_lock_digest.DEVCONTAINER_IMAGE_SELFTEST_PROCESS_RAW_SHA256
255 old_supervisor = image_lock_digest.DEVCONTAINER_IMAGE_SELFTEST_SUPERVISOR_RAW_SHA256
256 old_main = image_lock_digest.DEVCONTAINER_IMAGE_RAW_SHA256
257 supervisor = _replace_exact_pin(
258 changed["devcontainer_image_selftest_supervisor"],
259 old_process,
260 process_digest,
261 "the supervisor process pin",
262 )
263 changed["devcontainer_image_selftest_supervisor"] = supervisor
264 supervisor_digest = hashlib.sha256(supervisor.encode("utf-8")).hexdigest()
265 main = _replace_exact_pin(
266 changed["devcontainer_image"], old_process, process_digest, "the main process pin"
267 )
268 main = _replace_exact_pin(main, old_supervisor, supervisor_digest, "the main supervisor pin")
269 changed["devcontainer_image"] = main
270 main_digest = hashlib.sha256(main.encode("utf-8")).hexdigest()
271 authority = _replace_exact_pin(
272 changed["image_lock_digest"], old_process, process_digest, "the raw process pin"
273 )
274 authority = _replace_exact_pin(
275 authority, old_supervisor, supervisor_digest, "the raw supervisor pin"
276 )
277 changed["image_lock_digest"] = _replace_exact_pin(
278 authority, old_main, main_digest, "the raw main pin"
279 )
280 return changed
281
282
283def process_authority_mutations() -> tuple[Mutation, ...]:
284 """Return every process mutation from its focused catalog."""
285 return process_mutations.process_authority_mutations()
286
287
288def _replace_image_lock_authority(source: str, name: str, value: str | None) -> str:
289 """Replace or remove one direct raw-byte authority assignment in source text."""
290 tree = ast.parse(source)
291 matches = [
292 node
293 for node in tree.body
294 if isinstance(node, ast.Assign)
295 and len(node.targets) == 1
296 and isinstance(node.targets[0], ast.Name)
297 and node.targets[0].id == name
298 ]
299 if len(matches) != 1:
300 message = f"raw-byte authority assignment is not unique: {name}"
301 raise SemanticMutationError(message)
302 node = matches[0]
303 lines = source.splitlines(keepends=True)
304 replacement = [] if value is None else [f'{name} = "{value}"\n']
305 lines[node.lineno - 1 : node.end_lineno] = replacement
306 return "".join(lines)
307
308
309def image_lock_digest_pin_cases(inputs: dict[str, str], scan: Scan) -> list[tuple[str, bool]]:
310 """Drive wrong and missing raw-byte pins through the production scan."""
311 cases = []
312 source = inputs["image_lock_digest"]
313 for name in image_lock_digest.pin_names():
314 wrong = dict(inputs)
315 wrong["image_lock_digest"] = _replace_image_lock_authority(source, name, "0" * 64)
316 cases.append((f"wrong image-lock raw pin fires: {name}", bool(scan(wrong))))
317 missing = dict(inputs)
318 missing["image_lock_digest"] = _replace_image_lock_authority(source, name, None)
319 cases.append((f"missing image-lock raw pin fires: {name}", bool(scan(missing))))
320 return cases
321
322
323def _semantic_aggregator_tokens() -> tuple[tuple[str, str, str], ...]:
324 """Bind the semantic coordinator to every focused mutation provider."""
325 semantic = "semantic_mutations", "hil convergence semantic selftest"
326 return (
327 (*semantic, "import " + "hil_convergence_safety_raw_digest_runtime as raw_digest_runtime"),
328 (*semantic, "import " + "hil_convergence_safety_runtime_cleanup as runtime_cleanup"),
329 (*semantic, "import " + "hil_convergence_safety_runtime_escape as runtime_escape"),
330 (*semantic, "import " + "hil_convergence_safety_runtime_mutations as runtime_mutations"),
331 (*semantic, "import " + "hil_convergence_safety_process_mutations as process_mutations"),
332 (
333 *semantic,
334 "import " + "hil_convergence_safety_image_process_analysis as image_process_analysis",
335 ),
336 (*semantic, "raw_digest_runtime." + "cases(inputs)"),
337 (*semantic, "runtime_cleanup." + "cases(inputs)"),
338 (*semantic, "runtime_escape." + "cases(inputs)"),
339 (*semantic, "runtime_mutations." + "runtime_cases(inputs)"),
340 (*semantic, "process_mutations." + "process_authority_mutations()"),
341 (*semantic, "image_process_analysis." + "semantic_process_findings(label)"),
342 )
343
344
345def _runtime_aggregator_tokens() -> tuple[tuple[str, str, str], ...]:
346 """Bind runtime cleanup, mutation, and escape providers."""
347 return (
348 (
349 "runtime_cleanup",
350 "runtime cleanup",
351 "import hil_convergence_safety_runtime_loader as runtime_loader",
352 ),
353 ("runtime_cleanup", "runtime cleanup", "runtime_loader.cases(inputs)"),
354 (
355 "runtime_mutations",
356 "runtime mutations",
357 "import hil_convergence_safety_runtime_launcher as runtime_launcher",
358 ),
359 (
360 "runtime_mutations",
361 "runtime mutations",
362 "import hil_convergence_safety_runtime_sources as runtime_sources",
363 ),
364 ("runtime_mutations", "runtime mutations", "_write_sources = runtime_sources.publish"),
365 ("runtime_mutations", "runtime mutations", "runtime_launcher.launch("),
366 (
367 "runtime_escape",
368 "runtime escape",
369 "from hil_convergence_safety_runtime_mutations import (",
370 ),
371 )
372
373
374def _process_aggregator_tokens() -> tuple[tuple[str, str, str], ...]:
375 """Bind process analysis and mutation catalogs to their dependencies."""
376 return (
377 (
378 "image_process_analysis",
379 "image process analysis",
380 "import hil_convergence_safety_image_process_policy as catalog",
381 ),
382 (
383 "image_process_analysis",
384 "image process analysis",
385 "import hil_convergence_safety_image_subreaper_policy as subreaper_policy",
386 ),
387 (
388 "image_process_analysis",
389 "image process analysis",
390 "CROSS_LANGUAGE_SCOPED_TOKENS = catalog.CROSS_LANGUAGE_SCOPED_TOKENS",
391 ),
392 (
393 "image_process_analysis",
394 "image process analysis",
395 "subreaper_policy.errors(supervisor, process_source)",
396 ),
397 (
398 "process_mutations",
399 "process mutation catalog",
400 "import hil_convergence_safety_process_source_fixtures as process_source_fixtures",
401 ),
402 (
403 "process_mutations",
404 "process mutation catalog",
405 "import hil_convergence_safety_runtime_fixtures as runtime_fixtures",
406 ),
407 (
408 "process_mutations",
409 "process mutation catalog",
410 "import hil_convergence_safety_source_fixtures as source_fixtures",
411 ),
412 (
413 "process_mutations",
414 "process mutation catalog",
415 "process_source_fixtures.process_authority_mutations()",
416 ),
417 (
418 "process_mutations",
419 "process mutation catalog",
420 "runtime_fixtures.process_authority_mutations()",
421 ),
422 (
423 "process_mutations",
424 "process mutation catalog",
425 "*source_fixtures.process_authority_mutations(),",
426 ),
427 )
428
429
430def _image_dispatch_aggregator_tokens() -> tuple[tuple[str, str, str], ...]:
431 """Bind the image harness, its public consumer, and the shell loader."""
432 return (
433 (
434 "image_harness_policy",
435 "image harness policy",
436 "import " + "hil_convergence_safety_image_process_analysis as image_process_analysis",
437 ),
438 (
439 "image_harness_policy",
440 "image harness policy",
441 "image_process_analysis.supervisor_errors(supervisor, cases_source, process_source)",
442 ),
443 (
444 "image_harness_policy",
445 "image harness policy",
446 "image_process_analysis.source_errors(inputs)",
447 ),
448 (
449 "hil_convergence_entry",
450 "image harness consumer",
451 "import hil_convergence_safety_image_harness_policy as image_harness_policy",
452 ),
453 (
454 "hil_convergence_entry",
455 "image harness consumer",
456 "image_harness_policy.errors(inputs)",
457 ),
458 (
459 "devcontainer_image",
460 "bound-exit helper loader",
461 "SELFTEST_BOUND_EXIT_RAW_SHA256=",
462 ),
463 (
464 "devcontainer_image",
465 "bound-exit helper loader",
466 'source_approved_selftest_helper "$SCRIPT_DIR/'
467 'devcontainer_image_bound_exit_selftest.bash"',
468 ),
469 (
470 "devcontainer_image",
471 "bound-exit helper loader",
472 '"$SELFTEST_BOUND_EXIT_RAW_SHA256"',
473 ),
474 )
475
476
477def _aggregator_tokens() -> tuple[tuple[str, str, str], ...]:
478 """Return exact split-module consumer tokens and their checker labels."""
479 return (
480 *_semantic_aggregator_tokens(),
481 *_runtime_aggregator_tokens(),
482 *_process_aggregator_tokens(),
483 *_image_dispatch_aggregator_tokens(),
484 )
485
486
487def aggregator_cases(inputs: dict[str, str], scan: Scan) -> list[tuple[str, bool]]:
488 """Prove every split runtime test import and dispatch remains load-bearing."""
489 cases = []
490 for key, authority, token in _aggregator_tokens():
491 source = inputs[key]
492 if source.count(token) != 1:
493 message = f"semantic aggregator fixture is not unique: {token}"
494 raise SemanticMutationError(message)
495 changed = dict(inputs)
496 changed[key] = source.replace(token, "", 1)
497 if key == "devcontainer_image":
498 changed = _rebind_main_mutation(changed)
499 expected = f"{authority}: required process-authority token is not unique: {token}"
500 cases.append((f"semantic aggregator removal fires: {token}", scan(changed) == [expected]))
501 return cases
502
503
504def wsl_clock_cases(inputs: dict[str, str], scan: Scan) -> list[tuple[str, bool]]:
505 """Prove WSL clock and keep-alive regressions cannot pass."""
506 key = "wsl_role"
507 source = inputs[key]
508 safe = "waitsync 0 0 0 1"
509 start = "- name: Start the Windows autostart task now"
510 if source.count(safe) != 1 or source.count(start) != 1:
511 message = "WSL clock or keep-alive fixture is not unique"
512 raise SemanticMutationError(message)
513 clock_changed = dict(inputs)
514 clock_changed[key] = source.replace(safe, "waitsync 0 0.1 0.0 1", 1)
515 start_changed = dict(inputs)
516 start_changed[key] = source.replace(start, f"{start} later", 1)
517 return [
518 ("WSL slew-safe readiness threshold restoration fires", bool(scan(clock_changed))),
519 ("WSL immediate keep-alive start removal fires", bool(scan(start_changed))),
520 ]
521
522
523def _fleet_import_cases() -> tuple[tuple[str, str, str, str, str], ...]:
524 """Return split fleet import-removal mutation specifications."""
525 return (
526 ("fleet", "import fleet_capacity_client as fcc", "", "capacity import", "runtime imports"),
527 (
528 "fleet_reconcile",
529 "import fleet_reconcile_process as frp",
530 "",
531 "process import",
532 "runtime imports",
533 ),
534 (
535 "fleet_reconcile",
536 "import fleet_reconcile_arc_selftest as fras",
537 "",
538 "ARC selftest import",
539 "runtime imports",
540 ),
541 )
542
543
544def _fleet_selftest_source_cases() -> tuple[tuple[str, str, str, str, str], ...]:
545 """Return split fleet selftest definition and dispatch mutations."""
546 return (
547 (
548 "fleet_capacity_client",
549 "def run_selftest(data: dict[str, Any]) -> list[str]:",
550 "def _removed_selftest(data: dict[str, Any]) -> list[str]:",
551 "capacity selftest definition",
552 "executable selftests",
553 ),
554 (
555 "fleet_reconcile_process",
556 "def run_selftest() -> list[str]:",
557 "def _removed_selftest() -> list[str]:",
558 "process selftest definition",
559 "executable selftests",
560 ),
561 (
562 "fleet_reconcile_arc_selftest",
563 "def run(apply_host: ApplyHost) -> list[str]:",
564 "def _removed_run(apply_host: ApplyHost) -> list[str]:",
565 "ARC selftest definition",
566 "executable selftests",
567 ),
568 (
569 "fleet_reconcile",
570 " failures.extend(frp.run_selftest())\n",
571 "",
572 "process selftest call",
573 "executable selftests",
574 ),
575 (
576 "fleet_reconcile",
577 " failures.extend(fras.run(apply_host))\n",
578 "",
579 "ARC selftest call",
580 "executable selftests",
581 ),
582 )
583
584
585def fleet_split_cases(inputs: dict[str, str], scan: Scan) -> list[tuple[str, bool]]:
586 """Prove split fleet runtime imports, sources, and selftests are load-bearing."""
587 results = []
588 for key, token, replacement, label, diagnostic in (
589 *_fleet_import_cases(),
590 *_fleet_selftest_source_cases(),
591 ):
592 if inputs[key].count(token) != 1:
593 message = f"split fleet mutation fixture is not unique: {key}:{token}"
594 raise SemanticMutationError(message)
595 changed = dict(inputs)
596 changed[key] = inputs[key].replace(token, replacement, 1)
597 expected = f"fleet split modules: {diagnostic} are not exact"
598 results.append((f"fleet split {label} removal fires", expected in scan(changed)))
599 return results
600
601
602def fleet_guard_dispatch_cases(inputs: dict[str, str], scan: Scan) -> list[tuple[str, bool]]:
603 """Prove guarded re-entry derives and passes its guardian capability."""
604 mutations = (
605 (
606 " guardian = _bench_guard_subprocess_kwargs("
607 "args.command, fml.guardian_subprocess_kwargs)\n",
608 " guardian = {}\n",
609 "guardian derivation",
610 ),
611 (
612 " return _run(guard, cwd=fm.REPO_ROOT, subprocess_kwargs=guardian)\n",
613 " return _run(guard, cwd=fm.REPO_ROOT)\n",
614 "guardian pass-through",
615 ),
616 )
617 expected = "fleet.py: selector/extra-var refusal is not before lock and inventory"
618 results = []
619 for old, new, label in mutations:
620 if inputs["fleet"].count(old) != 1:
621 message = f"guard dispatch mutation fixture is not unique: {label}"
622 raise SemanticMutationError(message)
623 changed = dict(inputs)
624 changed["fleet"] = inputs["fleet"].replace(old, new, 1)
625 results.append((f"fleet {label} mutation fires", expected in scan(changed)))
626 return results
627
628
629def fleet_activation_cases(inputs: dict[str, str], scan: Scan) -> list[tuple[str, bool]]:
630 """Prove ARC activation check and final restore order stay load-bearing."""
631 mutations = (
632 (
633 " clean, changed = inspect_activation_host(data, host, run)\n",
634 " clean, changed = inspect_host(data, host, run)\n",
635 "activation check",
636 ),
637 (
638 ' restore = run(fleet_command(host, "restore"))\n',
639 ' restore = run(fleet_command(host, "check"))\n',
640 "final restore",
641 ),
642 )
643 expected = "fleet reconciliation: ARC activation/check/restore order is not exact"
644 source = inputs["fleet_reconcile"]
645 start = source.index("def _activate_arc(")
646 end = source.index("\ndef apply_host(", start)
647 activation = source[start:end]
648 results = []
649 for old, new, label in mutations:
650 if activation.count(old) != 1:
651 message = f"ARC order mutation fixture is not unique: {label}"
652 raise SemanticMutationError(message)
653 changed = dict(inputs)
654 changed["fleet_reconcile"] = source[:start] + activation.replace(old, new) + source[end:]
655 results.append((f"fleet ARC {label} mutation fires", expected in scan(changed)))
656 return results
657
658
659def digest_cases(inputs: dict[str, str], scan: Scan) -> list[tuple[str, bool]]:
660 """Return raw digest pin and path-identity mutation cases."""
661 return (
662 image_lock_digest_pin_cases(inputs, scan)
663 + raw_digest_runtime.cases(inputs)
664 + runtime_mutations.runtime_cases(inputs)
665 + runtime_cleanup.cases(inputs)
666 + runtime_escape.cases(inputs)
667 )