Skip to content

symfonic.platform.fallback_capture

fallback_capture

Bind the fallback attribution journal that a zero verdict depends on.

The dispatch-outcome journal does not see every legacy body reach: the ask_user missing-scope guard is the first statement of the legacy body and records dispatch approved_rejection with selected_route="none". Only the kernel_fallback_attribution.v1 rows written by FallbackJournal record that turn as a fallback. A zero verdict therefore needs every replica's fallback journal files declared in expected_fallback_journals, selected for the report, and the only source of fallback rows. Each declared file must hold fallback rows only: one that is also declared as a native, dispatch or route-attempt journal, or that contains any other row kind, is fallback_journal_kind_mismatch, because a file of another kind proves nothing about whether a FallbackJournal wrote to it.

The fallback capture snapshot in capture_boundaries (snapshot) is checked by :mod:symfonic.platform.capture_boundary; this module binds files. A manifest without expected_fallback_journals stays readable, and the report says undeclared rather than raising an issue.

fallback_capture_bindings

fallback_capture_bindings(manifest: Mapping[str, Any] | None, selected: set[Path], resolve: Callable[[Path], Path]) -> tuple[list[str], set[Path] | None]

Return binding issues and the declared files, None when undeclared.

Source code in src/symfonic/platform/fallback_capture.py
def fallback_capture_bindings(
    manifest: Mapping[str, Any] | None,
    selected: set[Path],
    resolve: Callable[[Path], Path],
) -> tuple[list[str], set[Path] | None]:
    """Return binding issues and the declared files, ``None`` when undeclared."""
    if manifest is None or "expected_fallback_journals" not in manifest:
        return [], None
    replicas = manifest.get("expected_replicas")
    journals = (
        _inventory(manifest["expected_fallback_journals"], replicas)
        if isinstance(replicas, list) and replicas
        and all(isinstance(replica, str) for replica in replicas) else None
    )
    if journals is None:
        return ["missing_fallback_journal_inventory"], set()
    issues: list[str] = []
    bound: set[Path] = set()
    for replica in replicas:
        for spec in journals[replica].values():
            for name in spec["files"]:
                try:
                    path = resolve(Path(name))
                except (OSError, RuntimeError, ValueError):
                    issues.append("fallback_journal_file_inventory_mismatch")
                    continue
                if path not in selected:
                    issues.append("fallback_journal_file_inventory_mismatch")
                if path in bound:
                    issues.append("fallback_journal_file_overlap")
                bound.add(path)
    return issues, bound

fallback_capture_status

fallback_capture_status(bound: set[Path] | None, issues: list[str], *, stray_rows: bool, failures: int | None, active_end: int | None, unreadable: bool, foreign_rows: bool = False) -> str

healthy only for a declared, bound, unfailed and drained capture.

Source code in src/symfonic/platform/fallback_capture.py
def fallback_capture_status(
    bound: set[Path] | None, issues: list[str], *, stray_rows: bool,
    failures: int | None, active_end: int | None, unreadable: bool,
    foreign_rows: bool = False,
) -> str:
    """``healthy`` only for a declared, bound, unfailed and drained capture."""
    if bound is None:
        return "undeclared"
    if (issues or stray_rows or foreign_rows or unreadable
            or failures != 0 or active_end != 0):
        return "incomplete"
    return "healthy"

fallback_kind_issues

fallback_kind_issues(bound: set[Path] | None, other_kinds: set[Path]) -> list[str]

fallback_journal_kind_mismatch when a fallback file is another journal's.

Source code in src/symfonic/platform/fallback_capture.py
def fallback_kind_issues(bound: set[Path] | None, other_kinds: set[Path]) -> list[str]:
    """``fallback_journal_kind_mismatch`` when a fallback file is another journal's."""
    return ["fallback_journal_kind_mismatch"] if bound and bound & other_kinds else []