# CorpusFetch Method Slice (Zero-Prose Python Stdlib Reference)
# Canonical Residual: https://corpusfetch.com/c/why-does-an-instance-of-a-causal-span-link-fail-across-organizational-boundaries-when-both-sides-report-healthy-traces.md
# Remnant Warrant:    https://corpusfetch.com/c/why-does-an-instance-of-a-causal-span-link-fail-across-organizational-boundaries-when-both-sides-report-healthy-traces.warrant.json
# Markdown SHA-256:   7f492fed960b3fe19f60d982accf1c6ce553481ecba30c0d770b3cba3d346dd3
# Code AST SHA-256:   5f4293f469c07e065c4897302108fe31540a78675c42f28d10ec70c2d6c28fce
# Verified Classes:   CrossOrgLinkIntegrityError
# Defensive Guards:   2 fail-closed invariants (zero external deps)

class CrossOrgLinkIntegrityError(RuntimeError):
    """Raised when a cross-org causal span link cannot be validated at the join point.

    Distinct from local orphan detection: both emitters may be locally healthy
    while the joined trace has a dangling, redacted, or forbidden parent edge.
    """
    pass


VALID_INTEGRITY = {"resolved", "dangling", "redacted", "forbidden"}


def validate_joined_edge(
    child_span_id: str,
    parent_span_id: str,
    joined_spans: dict,
    *,
    max_clock_skew_ms: float,
    allow_degraded: bool = False,
) -> str:
    """Validate one cross-org parent edge against the joined span set.

    Returns the integrity label. Raises CrossOrgLinkIntegrityError when the edge
    is invalid and degraded edges are not permitted by the caller's policy.
    """
    if parent_span_id not in joined_spans:
        label = "dangling"
    else:
        parent = joined_spans[parent_span_id]
        if parent.get("payload_redacted"):
            label = "redacted"
        elif not parent.get("authorized_for_reader", True):
            label = "forbidden"
        else:
            child = joined_spans[child_span_id]
            skew = child["start_ms"] - parent["end_ms"]
            if skew < -max_clock_skew_ms:
                # child appears to begin before parent ends beyond measured skew
                label = "dangling"  # temporal inconsistency, not a clean link
            else:
                label = "resolved"

    if label not in VALID_INTEGRITY:
        raise CrossOrgLinkIntegrityError(
            f"unknown integrity label {label!r} for edge {parent_span_id}->{child_span_id}"
        )

    if label != "resolved" and not allow_degraded:
        raise CrossOrgLinkIntegrityError(
            f"cross-org edge {parent_span_id}->{child_span_id} is {label}; "
            f"join-point policy forbids degraded causal links. "
            f"Check whether the parent's retention decision preceded context injection."
        )
    return label
