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

@ -0,0 +1,177 @@
# 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
**16 distinkte private/ugaranterte MAF-former leaner po på. 16 sjekket (19 mekaniske sjekker).
2 endret seg.**
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 (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. Den mest konsekvensrike endringen (§ 4) var
usynlig for den og ble fanget av testsuiten. En form-probe er gulvet, ikke beviset — nevneren over
skal leses som «16 former sjekket mekanisk», ikke «alt som kunne endre seg er sjekket».
## 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.
**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) |
| 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 |

View file

@ -45,10 +45,15 @@ Alt annet i dette dokumentet er målt mandag 10. august mot det pinnede transkri
uv run python -m portfolio_optimiser.simulation
```
Forventet: **exit 0**, **61 linjer stdout**, **4 linjer stderr**.
Forventet: **exit 0**, **61 linjer stdout**, **2 linjer stderr**.
Stderr er fire linjer og skal være der: to `ExperimentalWarning` fra MAF (Skills + MemoryStore),
en blanklinje, og `(arbeidskopi: <systemets temp-katalog>/po-sim-…)`.
Stderr er to linjer og skal være der: en blanklinje og
`(arbeidskopi: <systemets temp-katalog>/po-sim-…)`.
**Endret 02.09 (F15):** til og med MAF core 1.9.0 sto det også to `ExperimentalWarning`-linjer her
(Skills + MemoryStore), og runbooken sa fire. Core 1.16.0 flytter den advarselen fra importtid til
FØRSTE BRUK, og demoen bruker ingen av de to typene — så linjene er borte fordi MAF sluttet å sende
dem, ikke fordi noe ble dempet. Ser du dem likevel, kjører du en eldre kjerne enn pinnen.
**Katalogen varierer, prefikset gjør ikke det.** På macOS er temp-katalogen `/var/folders/…/T/`,
**ikke** `/tmp/` — så linja på skjermen blir lang og stygg, og det er riktig. `po-sim-` er den delen