# F15 — MAF-pinnen løftet 1.9.0 → 1.16.0 > **Ordre:** `20260829T155150Z-30491335-from-.claude` (operatørbeslutning 29.08: operatøren betaler > for F15 nå; F3 og F16 TIER 2 er UTSATT og er **ikke** rørt her). > **Grunnlag:** [MAF-gjelden — omfang](2026-08-29-maf-gjelden-omfang.md) § 2. > Hvert tall under kommer fra en kommando kjørt i denne økten. ## 0. Resultat | | | |---|---| | `agent-framework-core` | **1.9.0 → 1.16.0** | | `agent-framework-orchestrations` | **1.0.1 → 1.1.1** (krever selv `core>=1.15.0` — de to gulvene kan ikke løftes hver for seg) | | `agent-framework-foundry` / `-openai` | 1.8.2 / 1.8.2, **uendret** (målt etter `uv sync`) | | Ny transitiv | `msgspec` 0.21.1 | | Testsuite | **1089 passed / 5 skipped / 0 failed** (før: 1087 passed + 2 failed = samme 1089 ikke-skippede; ingen test tapt eller lagt til) | | `uv run ruff check .` | All checks passed | | `uv run ruff format --check .` | 187 files already formatted | | `uv run mypy src` | Success, 34 source files | | Golden `demo-transcript.stdout` | **BYTE-UENDRET**, `ea8c534773acdbe41ae68f2c55724d69aaf8be4f` | | Golden `demo-transcript.stderr` | **REGENERERT** — fire linjer → to. Beslutning, ikke rydding; se § 3 | ## 1. Iron Law — vakten var rød først `tests/test_maf_version_guard.py` er testen. Diffen som løfter gulvet til 1.16.0 lå uncommittet i treet fra en tidligere økt, og ble **kjørt rød mot 1.9.0 før pinnen ble rørt**: ``` 2 failed, 2 passed (test_installed_maf_version_is_supported, test_pyproject_pins_...) ``` Gulvet bor nå i **én** konstant (`_MIN_VERSION`), og `pyproject`-asserten deriverer sin forventede streng fra den — to literaler for ett faktum drifter (kø-(p)), og et driftet gulv er en vakt som slutter å vokte uten én lokal diff. Etter bumpen: 4 passed. ## 2. Nevner-disiplin — tre tall **17 distinkte private/ugaranterte MAF-former leaner po på · 16 sjekket MEKANISK av proben (19 sjekker), hvorav 1 endret seg · den 17. ble funnet av TESTSUITEN, og den endret seg også · 2 endret seg totalt.** **Den formen er stilt opp slik fordi den første formuleringen var usann.** Et tidligere utkast sa «16 former, 16 sjekket, 2 endret» — men den ene av de to (checkpoint-antallet per park) var aldri blant de seksten; proben så den ikke. «2 av 16» ville altså tilskrevet proben et funn den ikke gjorde, og skjult at nevneren for den mekaniske sjekken er 1. Nevner-disiplin gjelder også mitt eget instrument. Inventaret er **derivert fra repoets egne siteringer**, ikke oppfunnet: hver underscore-modul- referanse i `src/` (`grep -rnoE "_[a-z_]+\.py:[0-9-]+" src/`) pluss hver MAF-form en søm faktisk overstyrer eller konstruerer. Instrumentet (`probe.py`) kjørte mot BEGGE versjoner; det er efemert og bor ikke i repoet. | Form | 1.9.0 | 1.16.0 | |---|---|---| | P1 `BaseChatClient._inner_get_response` kw-only (messages/stream/options) | OK | OK | | P2 `BaseChatClient._build_response_stream` | OK | OK | | P3 `…_magentic:MagenticPlanReviewRequest` (allowlist-streng) | OK | OK | | P4 `…_magentic:MagenticPlanReviewResponse` (allowlist-streng) | OK | OK | | P5 strengt stall-sjekk `stall_count > max_stall_count` | OK | OK | | P6 reset-sjekk `reset_count >= max_reset_count` | OK | OK | | P7 `next_speaker not in _participant_registry.participants`-fallback | OK | OK | | P8 nøyaktig ÉN `ctx.request_info` i `_magentic.py` | OK (1) | OK (1) | | P9 `is_request_satisfied` på ledgeren | OK (8) | OK (8) | | P10 `list_checkpoints` svelger blokkert type til `logger.warning` | OK | OK (men se § 4) | | P11 `get_latest` velger på timestamp · P11b `allowed_checkpoint_types=` | OK | OK | | P12 runner matcher på graf-signatur | OK | OK | | P13 `…_base_group_chat_orchestrator` + `reached max_rounds`-meldingen | OK | OK | | P14 `extend_instructions` to-arg GA-signatur (`_sessions.py`) | OK | OK | | P15 console-exportere gatet på flagget · P15b `ENABLE_INSTRUMENTATION` | OK | OK | | **P16 `ExperimentalWarning`-emisjonen goldenen pinner** | 2 linjer | **0 linjer — ENDRET** | | **Checkpoint-antallet per park — form 17, IKKE i proben, fanget av SUITEN** | 1 lesbar | **2 — ENDRET** | **Kjent-positiv (L93): proben KAN oppdage en endret form.** `MiddlewareFailure` (ny i 1.15.0) er en delta målt uavhengig i § 2 av omfangsdokumentet. Kontrollen flippet **NO → YES** over bumpen. Den andre kandidaten, `_compaction.py`s `@experimental`-markører, er **forkastet som kjent-positiv**: den teller 0 i BEGGE versjoner, altså diskriminerer den ingenting — målt, ikke antatt. **Ærlighets-grense, uttalt:** proben sjekker FORMER — statiske egenskaper ved kilden. Den mest konsekvensrike endringen (§ 4) er en egenskap ved KJØRINGEN (hvor mange checkpoints en park skriver), og den er usynlig for enhver form-probe uansett hvor mange former den teller. Den ble fanget av testsuiten. En form-probe er gulvet, ikke beviset, og nevneren skal leses som «16 former sjekket mekanisk» — ikke «alt som kunne endre seg er sjekket». Det finnes ingen påstand her om at 17 er en uttømmende liste; 17 er det repoets egne siteringer og sømmer gjorde synlig. ## 3. Golden stderr — regenerert som beslutning **Målt:** null `ExperimentalWarning` fyrer på import under 1.16.0, mens `@experimental`-dekoratørene fortsatt FINNES i treet (3 i `_skills.py`, 5 i `_harness/_memory.py`). Verifisert mot exit-status (`import portfolio_optimiser` → rc 0, `IMPORT-OK`) før det negative resultatet ble lest som et faktum. **Rotårsak, lest ut av kilden:** `_feature_stage.py:411` bruker `_add_runtime_warning(...)` — 1.16.0 emitterer advarselen ved **første bruk**, ikke ved dekorering/import. Demoen instansierer verken `SkillResource` eller `MemoryStore`, så de to linjene sluttet å bli sendt **oppstrøms**. De ble aldri dempet av oss. Fasiten er derfor regenerert fra en faktisk kjøring gjennom den **uendrede** normalisereren: fire linjer → to (blanklinje + `arbeidskopi:`). `stdout` er byte-uendret, og `test_normalisation_does_not_mask_a_new_warning` er fortsatt grønn — P4 pkt. 2s garanti om at en NY advarsel når stderr står. Span 1 (`site-packages`-maskeringen) er **beholdt**: spannet er ubebodd, ikke pensjonert, og en advarsel som kommer tilbake skal fortsatt felle pinnen med stien maskert. Påstandene om «fire linjer» er rettet der de er LEVENDE (`tracing.py`, `test_tracing_loadbearing.py`, `test_explore_loadbearing.py`, `test_golden_transcript_loadbearing.py`, `CLAUDE.md` ×3, og operatørens `docs/plan/2026-08-12-demo-runbook.md`). De daterte plan-/reviewdokumentene er historikk og er IKKE omskrevet. ## 4. Den farlige endringen — en vakt som gikk inert i stillhet Ordren ba spesifikt om formen som «fortsatt IMPORTERER men har endret SEMANTIKK i stillhet». Det er denne, og den traff en load-bearing garanti: **Målt:** under 1.16.0 skriver én park **TO** checkpoint-filer. Bare den ene bærer plan-review-typen. Med en feildeklarert `_ALLOWED_CHECKPOINT_TYPES` blir derfor listingen **ikke lenger tom** — den taper nøyaktig den checkpointen som betyr noe, `get_latest` returnerer den ANDRE, og `explore._park`s vakt (`latest is None`) passerte. Kjøringen svarte `rc=0` og skrev et spørsmål som peker på en checkpoint som aldri kan bære svaret: **nøyaktig den døde spørsmålsfila laget finnes for å nekte**, nå stille. Per-fil-måling (samme park, samme katalog): | fil | FULL allowlist | TOM allowlist | |---|---|---| | `76ac52bf…` (bærer forespørselen) | ok | **BLOCKED** | | `b3fe53b7…` | ok | ok | **Fiksen tester egenskapen den alltid mente, ikke symptomet som pleide å innebære den:** vakten sjekker nå at checkpointen den navngir faktisk bærer DENNE forespørselen, mot det **deklarerte** feltet `WorkflowCheckpoint.pending_request_info_events` — ikke mot den private, emergente «blokkert ⇒ tom listing»-oppførselen. Det fjerner en privat avhengighet i stedet for å legge til en. ```python if latest is None or str(request.request_id) not in latest.pending_request_info_events: raise CheckpointUnreadable(...) ``` **Load-bearing MÅLT** mot HELE suiten, grønn kontroll 1089/5: **M1** — reverter vakten til `latest is None`: **1 rød** (`test_a_park_with_no_readable_checkpoint_refuses_instead_of_writing_a_dead_question`), restaurert og `shasum -c` OK. **Ærlighets-grense på selve fiksen, uttalt:** vakten hviler på at `get_latest` returnerer den checkpointen som BÆRER forespørselen. Det er målt sant i dag, og park → spørsmål → svar → resume → andre park er dekket av 19 grønne tester — men skulle MAF en gang skrive en checkpoint ETTER at forespørselen er reist, ville `_park` nekte en gyldig kjøring. Det er fail-closed-retningen (en nekt, aldri et dødt spørsmål), så det står som en grense å kjenne, ikke som en defekt å fikse. **Checkpoint-formen verifisert med FAKTISK KJØRING, ikke import:** `tests/test_async_plan_review_loadbearing.py` (19 tester) driver park → spørsmålsfil → svarfil → resume i en fersk interpreter, og er grønn. Det er den kjøringen ordren ba om. ## 5. Spiken traff den samme fella — og gaten er ÆRLIG svakere enn M1 `spikes/e_magentic.py` gjenopptok fra `checkpoint_ids[-1]` — SISTE element i en glob-ordnet listing, nøyaktig det `explore._park`s egen docstring advarer mot. Med to checkpoints døde resumen på `RuntimeError: No pending requests found in workflow context.` Spiken velger nå den checkpointen som bærer forespørselen, som produksjonen. **MÅLT, og det motsier det jeg antok:** mutasjonen som setter `[-1]` tilbake ble **IKKE rød** (1089 passed). Feilen er **rekkefølge-avhengig** (`Path.glob`) — den ble observert rød i den første fullkjøringen etter bumpen og grønn med samme effektive valg her. Spikens gamle form er altså **flaky, ikke deterministisk gal**, og fiksen står på den observerte feilen pluss fjerningen av et dokumentert anti-mønster — **ikke** på en gate. Sagt høyt fordi en mutasjon som ikke ble rød ikke er et bevis, og å presentere den som ett ville vært den vakuøse gaten dette repoet teller instanser av. ## 6. Fant IKKE, og bygde IKKE - **Ingen F3. Ingen F16 TIER 2.** Utsatt av operatøren; ikke rørt, ikke forberedt. - Ingen publisering, ingen push, ingen release. - **README-ens hosting-begrunnelse mistet sitt premiss og er RETTET, ikke gjenåpnet:** `agent-framework-foundry-hosting` krever `core>=1.13.0`, og treet låser nå 1.16.0 — så versjonsgrunnen til stdlib-serveren er borte. Om pakka skal adopteres er et ÅPENT spørsmål denne bumpen bevisst ikke gjenåpner; det som er målt og shippet er fortsatt stdlib-serveren. - **Ordrens steg 0 om STATE-linjen er en no-op:** `git stash list` er tom OG `STATE.md:20` sier allerede at den er tom. Linje 36 er en sann historisk merknad om økt 73. Det var ingenting å rette, og det sies her i stedet for å produsere en kosmetisk endring. ## 7. Verifiseringslogg | # | Påstand | Kommando | |---|---|---| | 1 | Vakten rød på 1.9.0 før pinnen | `uv run pytest tests/test_maf_version_guard.py -q` → 2 failed | | 2 | Installerte versjoner etter bump | `importlib.metadata.version(...)` → core 1.16.0, orch 1.1.1, foundry/openai 1.8.2 | | 3 | 16 former / 19 sjekker, begge versjoner | `probe.py` mot 1.9.0 (0 CHANGED) og 1.16.0 (1 CHANGED) | | 3b | Form 17 endret, funnet av suiten, ikke av proben | full suite → `test_a_park_with_no_readable_checkpoint…` failed | | 4 | Kjent-positiv virker | KP1 `MiddlewareFailure` NO → YES | | 5 | Kjent-positiv-kandidat forkastet | KP2 `_compaction.py` `@experimental` = 0 i begge | | 6 | Advarslene borte, ikke import-feil | `python -c "import portfolio_optimiser"` → rc 0 + `warnings.catch_warnings` → 0 | | 7 | Rotårsak | `_feature_stage.py:411` `_add_runtime_warning` | | 8 | To checkpoints, én bærer forespørselen | per-fil `decode_checkpoint_value` med FULL vs TOM allowlist | | 9 | M1 rød, restaurert | full suite → 1 failed; `shasum -c` OK | | 10 | M2 ikke rød | full suite → 1089 passed | | 11 | Grønn kontroll + de tre portene | `pytest` 1089/5 · `ruff check` · `ruff format --check` · `mypy src` | | 12 | stdout byte-uendret | `shasum` → `ea8c534773acdbe41ae68f2c55724d69aaf8be4f` | | 13 | Demoens stdout fortsatt 61 linjer | `python -m portfolio_optimiser.simulation \| wc -l` → 61 |