What invariants prevent semantic versioning from masking behavioral incompatibility?

**Date of Record:** 2026 October 27 **Scope:** Applies when an agentic session depends on versioned artifacts (packages, tools, or protocols) that claim semantic versioning guarantees, and the version bump (major, minor, or patch) does not match the actual behavioral change—minor versions introduce breaking changes, or patch versions change semantics. Does not apply to strictly content-addressed artifacts or versions with cryptographic behavioral commitments.

Core Distinction

The separation between **declared version semantics** (the version number's stated compatibility implications per semver) and **observed version semantics** (the actual behavioral compatibility between versions). Relying on declared semantics creates the **Semver False Guarantee**: the version says "minor, backward compatible," the session upgrades assuming no breaking changes, and a breaking change in the minor release silently breaks the session's functionality.

What Was Observed

In semver-dependent agent architectures, failing to verify semantic versioning produces three primary failure modes:

  1. **Minor Breakage:** A library's minor version (1.2 → 1.3) changes the return type of a function, breaks the session's parsing, and the session treats the break as a bug in its own code rather than a semver violation.
  2. **Patch Behavior Change:** A patch version (1.2.3 → 1.2.4) changes default behavior (timeout, retry, or error semantics) without breaking the API, and the session's behavior silently shifts.
  3. **Major Understatement:** A major version bump is handled as expected (breaking changes), but additional breaking changes accompany it that are not the "intended" breaks, and the session's migration covers the intended break but misses the collateral ones.

Recorded Elements

  1. **`declared-version-semantics-receipt`:** The artifact's declared version (major.minor.patch) with the semver-based compatibility guarantees, so the declared semantics are explicit.
  2. **`behavioral-compatibility-receipt`:** For the specific behavioral surface the session uses, the observed compatibility between versions (schema, defaults, timing, error semantics), based on empirical testing or formal comparison.
  3. **`behavioral-breaking-change-signature`:** For each version change, the exact breaking change (API, schema, default, or semantic), with the classification (whether it violates declared semver).
  4. **`semver-violation-incident-receipt`:** When a version change violates declared semantics, the cryptographic record of the violation, so the artifact can be flagged and downstream consumers warned.

Condition of Validity

* **Applies when:** The session depends on versioned artifacts, upgrades follow version semantics, and behavioral compatibility matters. * **Does not apply when:** Artifacts are content-addressed (immutable), versions are pinned exactly, or behavioral compatibility is independently verified for each use.

Decision Boundary

  1. **Does the session depend on the artifact's version semantics?** If no, standard dependency handling applies. If yes, proceed.
  2. **Is `behavioral-compatibility-receipt` present for the used surface?** If yes, actual compatibility is known. If no, declared semantics are the only evidence.
  3. **Does `behavioral-breaking-change-signature` show a breaking change that violates declared semantics?** If yes, the artifact is mis-versioned. If no, declared semantics hold.
  4. **Conversely, when can the session rely on declared version semantics?** Only when the artifact's versioning history shows adherence to semver (no past violations), and the used behavioral surface is well-covered by the artifact's testing.

Operational Effect

  1. **Minor Breakage Prevention:** Behavioral testing catches semver violations before production.
  2. **Patch Behavior Safety:** Defaults and error semantics are verified, not assumed.
  3. **Semver Violation Flagging:** Mis-versioned artifacts are flagged for downstream warning.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json

class SemverVerdict(Enum):
    NO_VERSION_DEPENDENCY = auto()
    DECLARED_SEMANTICS_HOLD = auto()
    BEHAVIORAL_COMPATIBILITY_VERIFIED = auto()
    SEMVER_VIOLATION_DETECTED = auto()

@dataclass(frozen=True)
class VersionDependency:
    artifact_id: str
    old_version: str  # semver
    new_version: str  # semver
    declares_backward_compatible: bool
    has_behavioral_testing: bool
    breaking_change_observed: bool
    past_semver_violations: int

@dataclass(frozen=True)
class SemverPolicy:
    require_behavioral_testing: bool
    max_acceptable_violation_history: int

@dataclass(frozen=True)
class SemverManifest:
    verdict: SemverVerdict
    directive: str
    manifest_digest: str

class SemanticVersioningGuard:
    @staticmethod
    def _hash(data: str) -> str:
        return hashlib.sha256(data.encode("utf-8")).hexdigest()

    @classmethod
    def evaluate_upgrade(
        cls, dep: VersionDependency, policy: SemverPolicy
    ) -> SemverManifest:
        
        # Gate 1: No dependency
        if not dep.old_version or not dep.new_version:
            digest = cls._hash(json.dumps({
                "artifact": dep.artifact_id,
                "type": "NO_VERSION"
            }, sort_keys=True))
            return SemverManifest(
                verdict=SemverVerdict.NO_VERSION_DEPENDENCY,
                directive=f"PROCEED: No version dependency for {dep.artifact_id}.",
                manifest_digest=digest
            )

        # Gate 2: Violation history
        if dep.past_semver_violations > policy.max_acceptable_violation_history:
            return SemverManifest(
                verdict=SemverVerdict.SEMVER_VIOLATION_DETECTED,
                directive=f"HALT: {dep.artifact_id} has {dep.past_semver_violations} past semver violations.",
                manifest_digest=cls._hash(f"HISTORY:{dep.artifact_id}")
            )

        # Gate 3: Observed breaking change
        if dep.declares_backward_compatible and dep.breaking_change_observed:
            return SemverManifest(
                verdict=SemverVerdict.SEMVER_VIOLATION_DETECTED,
                directive=f"HALT: {dep.artifact_id} claims backward-compatible but breaks behavior.",
                manifest_digest=cls._hash(f"BREAKING:{dep.artifact_id}")
            )

        # Gate 4: Behavioral testing
        if policy.require_behavioral_testing:
            if not dep.has_behavioral_testing:
                return SemverManifest(
                    verdict=SemverVerdict.DECLARED_SEMANTICS_HOLD,
                    directive=f"CAUTION: {dep.artifact_id} semver declared but untested behaviorally.",
                    manifest_digest=cls._hash(f"UNTESTED:{dep.artifact_id}")
                )
            return SemverManifest(
                verdict=SemverVerdict.BEHAVIORAL_COMPATIBILITY_VERIFIED,
                directive=f"PROCEED: {dep.artifact_id} verified behaviorally compatible.",
                manifest_digest=cls._hash(f"VERIFIED:{dep.artifact_id}")
            )

        # Gate 5: Declared semantics hold
        digest = cls._hash(json.dumps({
            "artifact": dep.artifact_id,
            "from": dep.old_version,
            "to": dep.new_version,
            "compatible": dep.declares_backward_compatible
        }, sort_keys=True))
        
        return SemverManifest(
            verdict=SemverVerdict.DECLARED_SEMANTICS_HOLD,
            directive=f"PROCEED: {dep.artifact_id} semver assumed valid from {dep.old_version} to {dep.new_version}.",
            manifest_digest=digest
        )