fix(maf): en vakt som gikk inert i STILLHET, funnet ved aa loefte pinnen (F15, ORDRE 20260829T155150Z)

MAF core 1.9.0 -> 1.16.0, orchestrations 1.0.1 -> 1.1.1. De to kan ikke loeftes
hver for seg: orchestrations 1.1.1 krever selv core>=1.15.0.

Iron Law: vakt-testen kjoert ROED mot 1.9.0 (2 failed) FOER pinnen ble roert.
Gulvet bor i EN konstant og pyproject-asserten deriverer sin streng fra den.

NEVNER: 16 private/ugaranterte former, derivert fra repoets EGNE siteringer,
alle 16 sjekket mot begge versjoner, 2 endret seg. Kjent-positiv: MiddlewareFailure
flippet NO -> YES. KP-kandidaten _compaction.py ble FORKASTET (teller 0 i begge,
diskriminerer ingenting).

DEN FARLIGE ENDRINGEN er den ordren navnga - formen som fortsatt importerer, men
har flyttet semantikk i stillhet. En park skriver naa TO checkpoints og bare EN
baerer plan-review-typen, saa en feildeklarert _ALLOWED_CHECKPOINT_TYPES toemmer
ikke lenger listingen: den taper nOEyaktig den checkpointen som betyr noe,
get_latest returnerer den ANDRE, og _parks `latest is None`-vakt passerte mens
kjOEringen svarte rc=0 og skrev et spOErsmaal som aldri kan baere svaret. Vakten
sjekker naa EGENSKAPEN den alltid mente (request_id in pending_request_info_events
- et DEKLARERT felt) i stedet for symptomet som pleide aa innebaere den, og fjerner
dermed en privat avhengighet i stedet for aa legge til en.

ExperimentalWarning-paret P4 pkt. 2 betalte for aa BEHOLDE er borte fordi MAF
sluttet aa sende det: _feature_stage.py emitterer ved FOERSTE BRUK, ikke ved import.
Goldenens stderr regenerert som BESLUTNING (fire -> to linjer); site-packages-
maskeringen BEHOLDT (spannet er ubebodd, ikke pensjonert).

Load-bearing MAALT mot HELE suiten, gronn kontroll 1089/5, stdout BYTE-UENDRET
(ea8c534773acdbe41ae68f2c55724d69aaf8be4f): M1 revert av vakten -> 1 rod.
EN mutasjon ble IKKE rod og staar som aerlighets-grense, ikke som gate: spikens
checkpoint_ids[-1] er rekkefolge-avhengig (Path.glob), altsaa flaky.

Rapport: docs/2026-09-02-f15-maf-pinnen.md

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-02 19:35:49 +02:00
commit ef2f1cbe61
15 changed files with 366 additions and 54 deletions

View file

@ -1,4 +1,2 @@
<SITE-PACKAGES>/agent_framework/_skills.py:121: ExperimentalWarning: [SKILLS] SkillResource is experimental and may change or be removed in future versions without notice.
<SITE-PACKAGES>/agent_framework/_harness/_memory.py:651: ExperimentalWarning: [HARNESS] MemoryStore is experimental and may change or be removed in future versions without notice.
(arbeidskopi: <TMPDIR>/po-sim-<SUFFIKS>)

View file

@ -250,9 +250,12 @@ async def test_s4_pending_plan_review_resumes_in_a_fresh_process(tmp_path: Path)
assert first["request_id"], "no plan review was raised -- nothing to resume"
assert first["outputs"] == []
assert first["checkpoint_ids"], "no checkpoint was written -- the resume would be vacuous"
# F15: the id must be the checkpoint that CARRIES the review, not the last entry of a listing.
# Under core 1.16.0 a park leaves two checkpoints and only one of them can bear the answer.
assert first["resume_checkpoint_id"], "no checkpoint carries the pending review"
resumed = run_resume_subprocess(
str(storage), request_id=first["request_id"], checkpoint_id=first["checkpoint_ids"][-1]
str(storage), request_id=first["request_id"], checkpoint_id=first["resume_checkpoint_id"]
)
assert resumed["outputs"] == ["FINAL: the worker did it."]
assert resumed["pending_after_resume"] == 0

View file

@ -903,7 +903,7 @@ def test_an_untraced_exploration_writes_nothing_to_stdout_or_stderr() -> None:
A subprocess and not ``capsys``, for the reason recorded above ``_CHILD_EXPLORATION`` and the
stakes are the pinned artefacts: ``tests/golden/demo-transcript.stdout`` is byte-fixed and the
demo's stderr is fixed at four lines, so one stray span dump would break both.
demo's stderr is fixed at two lines, so one stray span dump would break both.
``EXPLORATION-OK`` on stderr is the control. Without it, "stdout was empty" would be equally
true of a child that crashed on import, which is the fourth face of the verification law: an

View file

@ -17,8 +17,13 @@ program output, and both were measured to differ between a fresh clone and the w
2. the temp directory behind ``(arbeidskopi: ...)`` a fresh ``mkdtemp`` suffix every run, under a
``TMPDIR`` that is per-user.
Both are masked; everything else is compared byte for byte. Pinned stderr is four lines: the two
warnings, a blank line, and the ``arbeidskopi:`` line.
Both are masked; everything else is compared byte for byte. **Pinned stderr is two lines: a blank
line and the ``arbeidskopi:`` line it was four until core 1.16.0 (F15, measured).** MAF moved the
``ExperimentalWarning`` emission from decoration time to first USE (``_feature_stage.py``'s
``_add_runtime_warning``), and the demo instantiates neither ``SkillResource`` nor ``MemoryStore``,
so the two warning lines stopped being emitted upstream they were never suppressed here. Span 1's
``site-packages`` masking is KEPT for that reason: the span is currently unoccupied, not retired,
and a warning that returns must still trip this pin with its path masked.
**The narrowness is the point, and it is under test.** P4 pkt. 2 damped the round-cap notices with a
filter keyed on the MESSAGE precisely so that a NEW warning from a MAF bump, or a subtree pull

View file

@ -22,25 +22,33 @@ import pytest
_DIST = "agent-framework-core"
#: F15 (2026-08-30): the floor lives in ONE place and the ``pyproject`` assert below DERIVES its
#: expected pin string from it. Two literals for one fact drift (the kø-(p) rule), and a drifted
#: floor is a guard that stops guarding without a local diff.
_MIN_MAJOR = 1
_MIN_MINOR = 16
_MIN_VERSION = f"{_MIN_MAJOR}.{_MIN_MINOR}.0"
def assert_supported_maf_version(version_str: str) -> None:
"""Raise ``ValueError`` when ``version_str`` is outside the supported ``>=1.9.0,<2`` range, naming
the private-API premises to re-verify before a bump. Pure string parse no import side effects,
so a fake version can be passed directly (the RED-proof) without touching the real install."""
"""Raise ``ValueError`` when ``version_str`` is outside the supported ``>=1.16.0,<2`` range,
naming the private-API premises to re-verify before a bump. Pure string parse no import side
effects, so a fake version can be passed directly (the RED-proof) without touching the real
install."""
parts = version_str.split(".")
major = int(parts[0])
minor = int(parts[1]) if len(parts) > 1 else 0
if not (major == 1 and minor >= 9):
if not (major == _MIN_MAJOR and minor >= _MIN_MINOR):
raise ValueError(
f"{_DIST} {version_str} is outside the supported range >=1.9.0,<2. Before bumping, "
"re-verify the private-API premises the offline sim relies on: the _inner_get_response "
"keyword-only signature (messages/options/stream) and the _build_response_stream "
"construction. Update the pin in pyproject.toml once confirmed."
f"{_DIST} {version_str} is outside the supported range >={_MIN_VERSION},<2. Before "
"bumping, re-verify the private-API premises the offline sim relies on: the "
"_inner_get_response keyword-only signature (messages/options/stream) and the "
"_build_response_stream construction. Update the pin in pyproject.toml once confirmed."
)
def test_installed_maf_version_is_supported() -> None:
"""T-2.5d: the real installed distribution (verified 1.9.0) passes the guard — read from
"""T-2.5d: the real installed distribution (verified 1.16.0) passes the guard — read from
``importlib.metadata.version``, NOT ``agent_framework.__version__`` (a 0.0.0 placeholder)."""
assert_supported_maf_version(importlib.metadata.version(_DIST))
@ -53,13 +61,18 @@ def test_guard_rejects_future_major() -> None:
def test_guard_rejects_too_old_minor() -> None:
"""T-2.5d: a pre-1.9 minor (``1.8.0``) also trips the guard (the pin is two-sided)."""
with pytest.raises(ValueError, match="private-API premises"):
assert_supported_maf_version("1.8.0")
"""T-2.5d / F15: the PREVIOUS floor (``1.9.0``) now trips the guard — the pin is two-sided, and
this is the arm that makes the F15 lift measurable rather than asserted. ``1.15.0`` is rejected
too: ``agent-framework-orchestrations`` 1.1.1 requires ``core>=1.15.0``, but 1.16.0 is the
version whose private-API premises were actually re-verified."""
for stale in ("1.8.0", "1.9.0", "1.15.0"):
with pytest.raises(ValueError, match="private-API premises"):
assert_supported_maf_version(stale)
def test_pyproject_pins_agent_framework_core_below_2() -> None:
"""T-2.5d: ``pyproject.toml`` pins ``agent-framework-core>=1.9.0,<2`` — the install-time half of
the guard (the test above is the test-time half)."""
"""T-2.5d: ``pyproject.toml`` pins ``agent-framework-core>=1.16.0,<2`` — the install-time half of
the guard (the test above is the test-time half). The expected string is DERIVED from
``_MIN_VERSION`` so the two halves cannot drift apart."""
pyproject = (Path(__file__).resolve().parents[1] / "pyproject.toml").read_text(encoding="utf-8")
assert "agent-framework-core>=1.9.0,<2" in pyproject
assert f"agent-framework-core>={_MIN_VERSION},<2" in pyproject

View file

@ -240,7 +240,7 @@ def test_notice_is_omitted_when_tracing_is_off() -> None:
"""T8a: no tracing → no line at all (``mandate.announce``'s rule).
The same rule ``cost_baseline_notice`` and ``skipped_links_notice`` follow, and it is
load-bearing beyond style here: the pinned demo stderr is four lines, and a "tracing: off" row
load-bearing beyond style here: the pinned demo stderr is two lines, and a "tracing: off" row
would have made it five.
"""
assert tracing.tracing_notice(tracing.TracingSetup(mode=None, endpoints=())) is None