Skip to content

symfonic.services.switching.release

release

LIB-CR / LIB-OV — what one library release ships, and which side of the retirement boundary it sits on.

The two version markers are the normative names from T1.2.6. They live here as module constants because release automation, the migration error text, and the construction-time rejection all have to agree on them; three copies of "10.4" in three files is how a version boundary quietly becomes two boundaries.

ReleaseProfile dataclass

ReleaseProfile(package_version: str, static_vectors: Mapping[str, GenerationVector], constraints: ConstraintSet, override_table: Mapping[str, Sequence[GenerationVector]] = dict())

Everything the hermetic library backend is allowed to know (DMC-3).

A profile is compiled data: the static vector the release was validated against, the constraint set it shipped, and the documented override table. There is no field for "where to look this up", because looking anything up is precisely what library mode may not do.

release_line

release_line(version: str) -> tuple[int, ...]

version's release line, at the width the two markers are written to.

The markers name lines, so a version is compared by its line: 10.4.1 is on 10.4 and is honoured exactly as 10.4 is. Comparing the full parsed tuple instead put every patch of the last honouring line strictly inside the forbidden band — parse_version("10.4.1") > (10, 4) — so the first patch on the very line this programme is cutting could not construct a profile at all. That was inert while nothing in src/ built one; TA8.54 put ReleaseProfile on SymfonicAgent.__init__'s unconditional path, which is what made it reachable.

Truncation is deliberately only to the marker width. 10.5 still narrows to (10, 5) and still falls in the band, because LIB-OV-6 means exactly that: a line between the markers neither honours nor rejects a pin, and the remedy is to move both markers together by amending the ADR, not to widen the comparison until the band disappears.

Source code in src/symfonic/services/switching/release.py
def release_line(version: str) -> tuple[int, ...]:
    """``version``'s release line, at the width the two markers are written to.

    The markers name lines, so a version is compared by its line: ``10.4.1``
    is *on* ``10.4`` and is honoured exactly as ``10.4`` is. Comparing the full
    parsed tuple instead put every patch of the last honouring line strictly
    inside the forbidden band — ``parse_version("10.4.1") > (10, 4)`` — so the
    first patch on the very line this programme is cutting could not construct
    a profile at all. That was inert while nothing in ``src/`` built one;
    TA8.54 put ``ReleaseProfile`` on ``SymfonicAgent.__init__``'s unconditional
    path, which is what made it reachable.

    Truncation is deliberately *only* to the marker width. ``10.5`` still
    narrows to ``(10, 5)`` and still falls in the band, because LIB-OV-6 means
    exactly that: a line between the markers neither honours nor rejects a pin,
    and the remedy is to move both markers together by amending the ADR, not to
    widen the comparison until the band disappears.
    """
    parsed = parse_version(version)
    return (parsed + (0,) * _MARKER_WIDTH)[:_MARKER_WIDTH]