Forrige commit (2954216) listet tre rettelser; punkt 3 og K2s andre binding
naadde ALDRI CLAUDE.md - patch-skriptet feilet paa et anker etter at
docs/-filene alt var skrevet, og commiten gikk paa det som var staget. Meldingen
var altsaa foran endringen. Dette er endringen:
- bundle_id-raden sier naa at bundle_id_notice har TO kallsteder, og at
portefoelje-armen er BEVISST ikke wiret (bundle_id_source er None der ved
konstruksjon, saa en tredje utskrift ville vaert doed kode) - en ANNEN
avgjoerelse enn cost_baseline_anchoreds, som wiret nettopp den armen defensivt.
- navigasjons-raden baerer ordrens andre binding maalt med den shippede
funksjonen over alle 478 K2-nivaaer: verste nivaa 6 073 tegn / 2 094 tok =
4,9 % av den flate formen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1612 lines
154 KiB
Markdown
1612 lines
154 KiB
Markdown
# portfolio-optimiser
|
||
|
||
> **Note for visitors — what this file is.** This is the working agreement between the repository
|
||
> and the AI coding agent that builds it (the [Claude Code](https://claude.com/claude-code)
|
||
> convention), and it is written in Norwegian because that is the maintainer's working language.
|
||
> It doubles as the repository's **invariant ledger**: each block below records a design decision,
|
||
> the measurement that forced it, and the test that turns red when the decision is undone.
|
||
>
|
||
> **You need none of it to use the framework** — start with the [README](README.md). It is
|
||
> published anyway, because the reasoning behind a decision is worth more than the decision, and
|
||
> because a rule kept out of sight is a rule that drifts without anyone noticing.
|
||
|
||
## Kontekst
|
||
Generisk, åpent Python-rammeverk på **Microsoft Agent Framework (MAF)** som finner kostnadsbesparelser INNI hvert prosjekt i en portefølje av uavhengige prosjekter. Multi-agent samarbeid genererer kandidat-tiltak; en obligatorisk deterministisk validator avgjør verdiene; fageksperter vurderer via HITL, og systemet lærer av dommene. Publiseres på Forgejo for alle som har MAF.
|
||
|
||
Bakgrunn og beslutninger: [research](docs/research/2026-06-23-prior-art-platform.md) (§15 implementeringsregister), [plan](docs/plan/2026-06-23-incremental-plan.md). Løpende state: `STATE.md` (local-only).
|
||
|
||
## Stack
|
||
Python ≥3.10. MAF (`agent-framework-core` 1.16.0, `-orchestrations` 1.1.1 — F15, 02.09). Pakkehåndtering: `uv`. To backend-profiler: Azure/Foundry (full) + lokal (fallback).
|
||
|
||
## Konvensjoner
|
||
- Type hints overalt (`mypy` der mulig). Pydantic for validering/IR.
|
||
- `ruff` for lint+format. `pytest` for test.
|
||
- Modell-valg som konfig (modell-map rolle→Foundry-deployment), ikke spredt i kode.
|
||
- Metode kodifiseres som **Agent Skill** (`agentskills.io`: `SKILL.md` + `scripts/` + `references/`).
|
||
- Datatilgang: in-process `FunctionTool` er default-sømmen i kjørestien. **MCP er wiret som opt-in i kjørestien** (`mcp_tools.py` + `--mcp-config`, Trekk B 2026-08-05): konkrete eksterne servere blir verktøy agentene kan kalle UNDER debatten. Uten konfig gjøres null nettverkskall og verktøylista er uendret. Tre regler er load-bearing: **allowlist er påkrevd** (tom liste ville latt motparten bestemme hva agentene får kalle), **hver server og hvert tillatte verktøy navngis i kunngjøringen før første kall** (også uten `--mandate` — ingen udeklarert egress), og `--live-dry-run` åpner **ingenting**. Egen søm fra `ingest_mcp.py` (kildedokumenter FØR kjøring, null-argument-tools) — samme protokoll, ulik jobb. `build_mcp_server` (`datasource.py`) er fortsatt kun demo. Data-source-konfig JSON-Schema-validert, fail-fast.
|
||
- `shared/` er en **git subtree** av [`portfolio-optimiser-commons`](https://git.fromaitochitta.com/open/portfolio-optimiser-commons) (source of truth, R1 realisert 2026-07-03; publisert i `open/` 2026-08-04 — `commons`-remoten peker fortsatt på den private namespacen og virker uendret). Synk er **pull-only**: endringer committes i commons og hentes med `git subtree pull --prefix=shared commons main --squash`. ALDRI `git subtree push` fra konsument — re-split lekker hele konsument-historikken inn i commons (observert + opprydd 2026-07-03). Se `shared/README.md`. Wheelen bærer treet som pakkede data siden Fase 4a — se invarianten under.
|
||
|
||
## Kommandoer
|
||
- Sync: `uv sync` — installerer to konsoll-kommandoer: `portfolio-optimiser` (CLI, `run:main`) og
|
||
`portfolio-optimiser-demo` (offline-beviset, `simulation:main`). `python -m`-formene virker
|
||
uendret og er byte-identiske på stdout (målt). Bevisst KUN to av fem `main()` —
|
||
`costsim`/`hitl`/`preflight` er operatørverktøy, ikke produktets inngang, og hvert navn her er et
|
||
navn frysen må bære. Pinnet av `tests/test_console_entry_points.py` mot den INSTALLERTE
|
||
distribusjonens metadata, ikke mot TOML-en: en `[project.scripts]`-linje som aldri er `uv sync`-et
|
||
er en påstand, ikke en kommando.
|
||
- Test: `uv run pytest`
|
||
- Lint: `uv run ruff check .` + `uv run ruff format .`
|
||
- Type: `uv run mypy src`
|
||
|
||
## Arbeidsflyt (invarianter)
|
||
- **Rent teknisk rammeverk:** deployer eier DPIA/ROS/behandlingsformål. Bygg IKKE compliance-funksjoner — kun tekniske forutsetninger (lokal-only, provenance, ingen stille egress) + disclaimer.
|
||
- **90%-prinsipp:** bygg den generiske kjernen + tydelige extension points; jakt IKKE de siste 10 %.
|
||
- **Deterministisk validator er obligatorisk og blokkerende** — aldri valgfri plugin.
|
||
- **Framework-nøytral kontekst-søm:** OKF-bundle-navigasjon (`okf.py`) og den delte
|
||
`shared/`-kjernen er ren stdlib — null `agent_framework`/`mcp`-import, så samme bundles
|
||
konsumeres uendret av begge stacker (D7-portabel). Håndhevet av
|
||
`tests/test_okf.py::test_okf_is_maf_free`; importér aldri MAF inn i kontekst-laget.
|
||
- **OKF-navigert bundle-kontekst (ikke stuffing):** på bundle-stien bygges agent-lese-konteksten
|
||
ved å NAVIGERE bundelen (`okf.bundle_context`: index + frontmatter + cross-links, progressiv
|
||
disclosure) — aldri keyword-chunk-stuffing (målbilde §2/§4). `type: verdict`-laget ekskluderes
|
||
fra denne konteksten: tidligere dommer når hypotese-prompten KUN via den gatede ExpeL-folden.
|
||
Load-bearing: `test_bundle_context_excludes_verdict_layer` + empty-store-kontrollen i
|
||
`test_step1_expel_loadbearing.py` (realiseringssignalet lekker aldri inn via kontekst).
|
||
- **Navigasjons-kontrakten er hierarkisk, og escape — ikke dybde — er forbudt** (`method-spec`
|
||
§3 Steg 1): `navigate_bundle` følger cross-links REKURSIVT, dybde-først i først-sett-rekkefølge;
|
||
ledende `/` betyr **bundle-rot** (aldri filsystem-absolutt), alt annet er relativt til den
|
||
LENKENDE filas katalog; dedup skjer på **resolvert** sti (så `./a.md` == `a.md`, og sykler
|
||
termineres). `safe_resolve` er den ENESTE inn-/ut-av-bundle-testen (fail-closed) — den erstattet
|
||
den pensjonerte «separator = utenfor bundelen»-heuristikken, som forvekslet dybde med escape.
|
||
Manglende `index.md` er feil KUN i bundle-rota (navigasjon følger lenker, aldri katalog-enumerering).
|
||
Rendering er FLAT uansett dybde; nestede `index.md` er navigasjon, ikke innhold. Gaten er
|
||
commons-eide nav-goldens (`shared/examples/nav-golden-*/expected-read-context.md`, byte-nivå
|
||
fasit): `test_nav_golden_hierarchy_*` (positiv) + `test_nav_golden_escape_*` (negativ — en gate
|
||
som bare kan bli grønn beviser ingenting).
|
||
**Et hopp er TOLERERT, men ikke lenger TAUST (21.08):** `_walk` registrerer hver lenke den ikke
|
||
fulgte på `Bundle.skipped` — hvilken fil lenken sto i, lenketeksten ORDRETT (operatøren redigerer
|
||
den teksten, ikke den resolverte stien), og hvilken av de TO grunnene som gjaldt: `outside-bundle`
|
||
(escape — ofte bevisst, en lenke til nabobasen) eller `missing` (inne i basen, ingen lesbar fil —
|
||
nesten alltid en skrivefeil). Den tredje grenen, `canonical in seen`, er DEDUP og registreres
|
||
ALDRI — den er korrekt navigasjon og dét som terminerer sykler; en implementasjon som logget hvert
|
||
`continue` ville rapportert en frisk base som halvlest. Toleransen er URØRT (§4 krever at det ikke
|
||
kastes) — dette er synlighet, ikke en ny nekt. **Feltet DEFAULTER til tom tuppel, og det er
|
||
MOTSATT av `cost_baseline_anchored`s «påkrevd uten default»:** en tom trace er et ærlig POSITIVT
|
||
utsagn («hver lenke ble fulgt», `external_calls`-presedensen), mens en manglende bool måtte påstå
|
||
noe om en hendelse og begge påstandene ville iblant vært usanne. Sporet forlater kjøringen på
|
||
`RunResult.skipped_links` (RUN-nivå — navigasjonen skjer ÉN gang per kjøring, før noe forslag
|
||
finnes) og `DryRunReport.skipped_links`, aldri på `ProvenanceStamp`, som beskriver gaten som dømte
|
||
ÉN kandidat. `run.skipped_links_notice` er ENESTE renderer, tar den alt oppløste tuppelen og
|
||
returnerer `None` når ingenting ble hoppet over (omisjon, aldri tom rad — `announce`-regelen);
|
||
reason-TOKENET printes rått, så det finnes ingen andre display-vokabular å drifte fra feltet.
|
||
Ingenting av dette når `bundle_context` (som bygges av `index_summary` + `context_files` alene) —
|
||
dét er hva som holder nav-goldenene byte-uendret, og `Bundle(` har fortsatt ÉN konstruksjons-sted
|
||
(`okf.py`, i `navigate_bundle`). Load-bearing MÅLT
|
||
(`tests/test_navigation_visibility_loadbearing.py`), åtte mutasjoner alle røde mot HELE suiten +
|
||
grønn kontroll 897/5: detach `missing`-registreringen (6 røde) · detach `outside-bundle` (2 røde) ·
|
||
kollaps de to grunnene til én (2 røde) · registrer dedup-grenen (1 rød) · renderer returnerer alltid
|
||
linja (3 røde — inkl. kontrollene, altså er omisjonen selv gatet) · detach dry-run-printen (1 rød) ·
|
||
detach full-run-printen (1 rød) · konstant tom trace ut av `run_project` (4 røde).
|
||
- **Kuraterte skrivere kan ikke forfalske ingest-stempelet** (`ingest-spec` §3): `write_concept_file`
|
||
er repoets ene authoring-primitiv som materialiserer en konseptfil fra CALLER-oppgitt frontmatter,
|
||
og avviser derfor det KOMPLETTE eierskaps-stempelet (`generated: true` + `ingest_manifest`) med
|
||
`IngestStampError` — mens hver halvdel alene er lovlig (kuratert innhold kan bære ett
|
||
provenance-felt). Validering, ALDRI reparasjon: ingenting skrives. Uten dette kunne en kuratert fil
|
||
bli stille slettet av en senere re-materialisering, som fjerner nøyaktig det som bærer stempelet.
|
||
**`generated`-verdien er FAIL-CLOSED på YAML-1.1-sannhetsformer, ikke bare literalen `"true"`**
|
||
(funn 21.08, økt 52): `_YAML_TRUE_LITERALS` (`{"true", "yes", "on"}`, case-insensitivt) er
|
||
ENESTE vokabular, målt mot PyYAML sin `safe_load`-resolver — bare `1`/bare `y`/`n` er BEVISST
|
||
UTELATT (resolves til int/streng, aldri bool, så en YAML-leser ville uansett ikke lest dem som
|
||
stempelet). Uten dette var sjekken inert kun i kraft av at pinnet `llm-ingestion-okf v0.3.2`
|
||
skriver strengen `"true"` — en fremtidig `uv sync` mot en skrivemåte som `yes`/`on` ville latt
|
||
vakten slutte å vokte uten én lokal diff. Load-bearing MÅLT
|
||
(`tests/test_ingest_stamp_fail_closed_loadbearing.py`), fire mutasjoner alle røde mot HELE
|
||
suiten: revert til literalen `"true"` (2 røde — de nye sannhetsformene alene) · over-widen til å
|
||
inkludere `1`/`y` (1 rød) · `and`→`or` (4 røde, halv-stempel-lovligheten brutt) · detach gaten
|
||
helt (4 røde).
|
||
- **`IngestError` må overleve anyio-task-gruppene (kø-(x), 2026-08-03):** `stdio_client` og
|
||
`ClientSession` er hver sin task group, og anyio pakker ALT som forlater en av dem i en
|
||
`BaseExceptionGroup`. Derfor nådde `stdio_call_tool`s egne feil (`mcp_tool_error`,
|
||
`mcp_non_text_content`) kalleren som en exception group — aldri som `IngestError`, som er typen
|
||
hele Door A fanger og switcher på via `code`. `_unwrap_ingest_error` pakker ut og re-raiser den
|
||
eide feilen; alt annet re-raises URØRT (innsnevring, aldri blanket re-raise). Duck-typet på
|
||
`.exceptions`, fordi `except*`/`ExceptionGroup` er 3.11+ og repoet er `>=3.10`. **Ingen
|
||
canned-tool-test kunne fanget dette** — de går aldri inn i en task group; defekten dukket opp
|
||
første gang koden faktisk ble kjørt. En MCP-server på ingest-stien må eksponere en
|
||
**null-argument-tool** (URL-en bærer begge koordinatene, tool kalles med `{}`), så
|
||
`datasource.build_mcp_server` kan IKKE serve den — `retrieve_cost_docs(query)` har et påkrevd
|
||
argument og returnerer et error-result. De to er separate sømmer med vilje. Load-bearing MÅLT
|
||
(`tests/test_ingest_golden_mcp.py`), fem mutasjoner alle røde: detach unwrappingen · detach
|
||
`initialize()` · gjør feilkoden generisk · detach `isError`-grenen · endre ett byte av bodyen.
|
||
- **MCP-timeouten må komponeres MED anyios eget cancel scope, ikke `asyncio.wait_for` utenfra
|
||
(kø-(z), 2026-08-03):** STATE-premisset ("en utypet `TimeoutError` re-raises urørt") var FEIL —
|
||
målt mot en EKTE hengende server ga `asyncio.wait_for(run(), timeout=...)` aldri en
|
||
`TimeoutError` i det hele tatt; den kansellerer `run()` UTENFRA strukturen anyio selv eier
|
||
(`stdio_client`/`ClientSession`), og de to kansellerings-mekanismene komponerer ikke — målt
|
||
utfall var en `anyio.BrokenResourceError` inni en `BaseExceptionGroup` (en bakgrunns-reader-task
|
||
mistet skrive-enden midt i nedrigging). Fiksen er `anyio.fail_after(timeout_seconds)` NESTET
|
||
INNI begge task-gruppene, der anyio rigger ned sin egen struktur rent og raiser en ren
|
||
`TimeoutError`. **Innsnevringen dekker IKKE bare typen:** builtin `TimeoutError` er også
|
||
`socket.timeout` (≥3.10) og `asyncio.TimeoutError` (≥3.11), så et ubetinget `except TimeoutError`
|
||
ville mislabelt en HVILKEN SOM HELST `TimeoutError` som `mcp_timeout` — reviewet FØR commit
|
||
(advisor) fant nøyaktig dette. Retteslen er `anyio.CancelScope.cancelled_caught`: kun scopet som
|
||
faktisk traff SIN EGEN deadline tjener `mcp_timeout`-koden, ellers re-raises urørt (speiler
|
||
`_unwrap_ingest_error`s eierskaps-regel). **Ingen levende utløser finnes i dag** for en
|
||
"fremmed" `TimeoutError` på denne stien — MÅLT: MCPs eget per-request read-timeout
|
||
(`ClientSession.send_request`) konverterer sin `anyio.fail_after` til `McpError` FØR den når oss,
|
||
og en tool som raiser `TimeoutError` server-side blir et ordinært `isError`-resultat (samme
|
||
som enhver annen tool-exception) — begge verifisert empirisk, ikke antatt. Diskriminatoren er
|
||
likevel pinned med en syntetisk test (raiser fra `StdioServerParameters`-konstruksjon, inni
|
||
fail_after-scopet men FØR noen task group), fordi defektklassen ellers ikke har en nåbar sti å
|
||
bevise den mot. Load-bearing MÅLT (`tests/test_ingest_golden_mcp.py`) mot HELE 625-suiten, fire
|
||
mutasjoner alle røde: detach hele oversettelsen · revert til `asyncio.wait_for` · relabel koden ·
|
||
detach `cancelled_caught`-gaten (behold kun scope-presence). `anyio` promotert fra transitiv
|
||
(via `mcp`) til deklarert direkte dep (`pyproject.toml`) — modulen importerer den nå direkte.
|
||
- **Door A-innholdsgaten kan IKKE bo i `materialize` — den bor rundt den (P2/S1.b, 2026-08-09):**
|
||
`materialize` er en REN delegasjon til det pinnede `llm_ingestion_okf` v0.3.2s
|
||
`materialize_bundle`, som stager i minnet og utfører sin EGEN disk-fase; **det finnes ingen
|
||
callback mellom de to**, så en gate plassert «i skrivepunktet» kunne bare kjørt ETTER at bytene
|
||
hadde landet — en opprydding, ikke en gate (planens premiss, felt ved måling FØR bygging).
|
||
`materialize_gated` er derfor: **kopier bundelen → materialiser inn i kopien → skann det som ble
|
||
generert → publiser eller forkast.** **Kopien er BÆRENDE:** bibliotekets §3 eierskaps-skann,
|
||
kollisjons-gaten mot kuratert innhold og §6 index-merge leser alle den EKSISTERENDE bundelen —
|
||
staging i en tom katalog mister alle tre og publiserer en bundle uten kuraterte naboer og deres
|
||
index-lenker (datatap forkledd som sikkerhetsfiks; MÅLT av kun ÉN test, 809 andre merket
|
||
ingenting). `materialize` forblir UGATET med vilje — fire golden-suiter pinner bytene, og en
|
||
kaller som vil ha gaten ber om den ved navn. **Utfall per BUNDLE, diagnostikk per DOKUMENT:**
|
||
delvis publisering ville etterlatt bundle + index som svarer til INTET manifest, men
|
||
`import_bundle` itererer forbi første avvisning så hvert funn rapporteres. **Trust følger ORIGIN,
|
||
aldri channel** (`Origin.EXTERNAL`/`Channel.AUTOMATIC` = UNTRUSTED) — ikke et av guardens to
|
||
`Policy`-preset: `PRESET_USER_UPLOAD` bærer `quarantine_default=True` som Door A ikke har.
|
||
**Den laveste dispositionen er `warn`, ikke `allow`** (`warn < quarantine_review < fail_secure`;
|
||
`allow` finnes ikke) — en gate skrevet mot `== allow` ville avvist hvert dokument noensinne.
|
||
**Funnene til `log.md` (OKF §7), ALDRI konsept-frontmatter** — der ville de brutt fire goldener.
|
||
Guarden shipper ingen `py.typed`: mypy-override ALENE gjør sømmen type-BLIND, så `_stamp_line` +
|
||
koersering stopper `Any` ved grensen. Load-bearing MÅLT
|
||
(`tests/test_ingest_content_gate_loadbearing.py`), fem mutasjoner alle røde + grønn kontroll:
|
||
detach gaten · la den fyre ETTER publisering · `Origin.INTERNAL` · tom staging-katalog ·
|
||
rapporter kun første avvisning. **Målingen felte en VAKUØS test først:** en hard injeksjon
|
||
scorer `fail_secure` under BEGGE tierene, så `Origin.INTERNAL`-mutasjonen lot alle tre
|
||
avvisningstestene stå grønne — beslutningen så dekket ut uten å være testet. Båndet der tieren
|
||
faktisk avgjør er høy-entropi-innhold (`quarantine_review` vs `warn`), og testen ble skrevet mot
|
||
nøyaktig det.
|
||
- **`shared/` leses som PAKKEDE DATA, med arbeidstreet som overstyring (Fase 4a):** wheelen bærer
|
||
en byte-identisk speiling av hele `shared/`-treet under `portfolio_optimiser/_shared/`
|
||
(hatchling force-include i `pyproject.toml`), og `shared_root()` løser ved KALL-tid i fast
|
||
rekkefølge: `PORTFOLIO_SHARED_ROOT` → arbeidstreets `shared/` når det finnes → pakket kopi.
|
||
**Arbeidstreet er autoritativt i en checkout** — det er dét som holder pull-only-subtree-
|
||
kontrakten og de byte-eksakte goldenene urørt (målt: goldens shasum-identiske før/etter, og
|
||
`shared/` selv urørt). Den pakkede kopien er dét som gjør wheel og container mulig uten klone
|
||
(målt før: 1.0.0-wheelen bar 58 filer, null under `shared/`; etter: 122, hvorav 64 under
|
||
`_shared/`, og sdist→wheel-kjeden bærer treet). Speilingen er ALDRI en redigert derivat —
|
||
byte-identitet er egenskapen som lar commons-goldenene fortsatt gate den pakkede kopien.
|
||
Load-bearing MÅLT (`tests/test_shared_packaged_data_loadbearing.py`, ekte `uv build` i
|
||
fixturen — pakkekonfigen er selv en søm), tre mutasjoner alle røde mot hele suiten: detach
|
||
fallbacken (1 rød) · detach force-include (3 røde) · snu rekkefølgen (1 rød — ordnings-testen
|
||
var grønn før fiksen; dens kontroll på at pakket kopi FINNES er det som gjør flippen målbar).
|
||
- **AZURE-profilen leser MILJØET sitt ved kall-tid, ikke operatørens laptop (Fase 4b):** endepunktet
|
||
løses som første IKKE-TOMME av `_ENDPOINT_ENVS` — vårt eget `PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT`
|
||
FØRST, deretter Foundrys injiserte `FOUNDRY_PROJECT_ENDPOINT`. **Vårt vinner** (det er dét enhver
|
||
doc, recipe og test setter, så en eksport av det er en bevisst handling; en plattformverdi som
|
||
stille overstyrte den ville vært uforklarlig utenfra), og fallbacken er dét som lar samme image
|
||
kjøre hostet uten ekstra wiring. **Presedensen gjelder VERDIER, ikke deklarasjoner** — et
|
||
eksportert-men-tomt eget navn faller igjennom i stedet for å skygge et ekte injisert inn i en
|
||
fail-fast. Feilmeldingen navngir BEGGE: operatøren i en container og operatøren på en laptop
|
||
leter etter hver sin variabel. Credential velges av samme miljø: `AzureCliCredential` lokalt
|
||
(konstruksjon henter INGEN token — `az login` er operatørens manuelle steg),
|
||
`ManagedIdentityCredential` når `FOUNDRY_HOSTING_ENVIRONMENT` er satt, fordi containeren ikke har
|
||
noen Azure CLI og plattformen mynter den en egen Entra-identitet ved deploy. **Ikke
|
||
`DefaultAzureCredential`:** Learns egen MAF-veiledning sier «prefer a specific credential such as
|
||
`ManagedIdentityCredential` to avoid unintended credential probing» — probing ville vandret en
|
||
kjede som ikke KAN lykkes der, og gjort en konfigfeil om til en treg en. Markøren leses på
|
||
**truthiness, ikke presence**: en eksportert tom verdi er et shell-uhell, ikke et hosting-signal.
|
||
Klienten eksponerer INGEN credential-attributt (målt), så testene observerer via en
|
||
`FoundryChatClient`-recorder — med én UPATCHET arm, ellers ville de kun bevist at vi sender
|
||
*noe* som heter `credential`. Load-bearing MÅLT
|
||
(`tests/test_hosted_backend_loadbearing.py`), fire mutasjoner alle røde mot hele suiten + grønn
|
||
kontroll: detach credential-valget · presence i stedet for truthiness · detach fallbacken · snu
|
||
presedensen. **Fail-fast-testen ble skrevet VAKUØS først** (repoets 08-09-klasse):
|
||
`PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT` INNEHOLDER `FOUNDRY_PROJECT_ENDPOINT`, så asserten på det
|
||
injiserte navnet var oppfylt av vårt eget; den fjerner nå vårt navn før den sjekker.
|
||
- **Hostet inngang er en WRAPPER rundt `run_project` på ÉN asyncio-løkke (Fase 4d):** `main.py` →
|
||
`hosting.py` serverer hosting-kontrakten (port 8088/`PORT` på truthiness, `GET /readiness`,
|
||
`POST /invocations`, SIGTERM → exit 0) med stdlib asyncio — ALDRI `as_agent()` (validator,
|
||
baseline-forankring, checker-gate og ledger ligger UTENFOR grafen, spike §5) og ALDRI tråder
|
||
(NG1-guarden: `http.server`s trådvariant ville lagt samtidige kjøringer på OS-tråder der
|
||
S3.3-resonnementet ikke holder; samtidige invocations interleaver som koroutiner — samme modell
|
||
som `run_portfolio`s bølger, og `/readiness` svarer mens en kjøring venter på modell-I/O, målt).
|
||
**Formen er MÅLT, ikke valgt:** hosting-pakkas `InvocationsHostServer` finnes kun i bygg som
|
||
krever core>=1.13.0 (treet låser 1.9.0; eneste 1.9-kompatible bygg er en forlatt alfa med defekt
|
||
metadata — importerer `mcp` udeklarert), og et gjenbrukt bygget workflow er SINGLE-USE på 1.9.0
|
||
(målt kall-serie [2, 0, 0] — rundetaket persisterer i objektet, så gjenbruk gir TOMME kjøringer;
|
||
ferskt objekt per kall er ren kontroll). Payloaden whitelistes på `run_project`s signatur —
|
||
ukjente felt NEKTES ved navn (400), aldri stille droppet (valg-doc §0-fella anvendt på vår egen
|
||
flate); `profile` defaulter til `azure` KUN her (containeren har ingen lokal endpoint;
|
||
`run_project`s egen default forblir LOCAL). Feilmapping ærlig: `ValueError` (pydantic-kontrakter
|
||
subklasser den) → 400, `BudgetExceeded` → 429 (EGEN rad under, 14.08), alt annet → 500
|
||
`{error_type, error}` (speiler `RunFailure`), og en
|
||
`Rejection` er en VELLYKKET kjøring → 200 — det negative utfallet tilhører payloaden, aldri
|
||
transporten. `outbox.outcome_payload` er den ENE kopien av validated/rejected-forgreningen
|
||
(delt av fil-skriveren og HTTP-responsen — to kopier drifter, kø-(p)-regelen). **Container-innpakningen
|
||
(`Dockerfile`/`azure.yaml`) ER FJERNET 14.08** — se python-only-invarianten under; resten av
|
||
denne raden står, for `main.py` startes nå direkte (`python main.py`). Load-bearing MÅLT (`tests/test_hosting_loadbearing.py`), seks mutasjoner alle
|
||
røde mot hele suiten på riktig test: detach felt-mappingen · dropp ukjente felt stille · flipp
|
||
400/500 · detach azure-defaulten · detach SIGTERM-handleren · detach main.py-shimen (de to
|
||
siste fanges KUN av subprosess-testen — P4-presedensen). Chunked request-bodies støttes ikke, og under CPU-bundne strekk (CBC-solven)
|
||
står readiness — uttalt, ikke skjult.
|
||
- **Whitelisten må komponere med den EKTE `run_project`, og artefaktene gates som RÅ TEKST
|
||
(Fase 4e):** alle 4d-testene ga `invoke` en stand-in som sluker `**kwargs`, så whitelisten kunne
|
||
navngi et felt `run_project` ikke tar — eller sende samme argument to ganger — uten at én test
|
||
merket det, mens en levende container svarte 500. **Sømmen er `run._default_factory`, ikke
|
||
payloaden:** `client_factory` NEKTES av whitelisten med vilje (en kaller av en hostet agent skal
|
||
aldri velge serverens modellklient), så å patche factory-defaulten er eneste injeksjonspunkt
|
||
flaten etterlater (samme argument `test_run_cli_loadbearing` gjør for `main()`). Testen sender
|
||
HVERT whitelistet felt og asserterer dekningen mot `_ALLOWED_FIELDS`, så et felt lagt til senere
|
||
ikke kan gli forbi uøvet. Profilen er **LOCAL, ikke den hostede defaulten**: AZURE-armen slår opp
|
||
et Foundry-deployment-navn i modell-mappet FØR noen klient bygges (`run.py` stempler provenance
|
||
med det), så den kan ikke fullføre offline — **containeren trenger altså `PORTFOLIO_MODEL_MAP`
|
||
eller et utfylt `data/model_map.json`, ikke bare et endepunkt** (målt her, ikke antatt).
|
||
**Artefakt-halvdelen av denne raden er PENSJONERT 14.08** sammen med `Dockerfile`/`azure.yaml`
|
||
(rå-tekst-gaten pinnet `--platform linux/amd64` + ÉN kopi av startkommandoen; to av radens fem
|
||
mutasjoner traff nettopp den). Whitelist-halvdelen står URØRT. Load-bearing MÅLT
|
||
(`tests/test_hosting_loadbearing.py`), de tre gjenværende mutasjonene alle røde på riktig test og
|
||
på INGEN annen: send `project_id` to ganger · whitelist et felt `run_project` ikke tar · fjern
|
||
`bundle_dir` fra whitelisten.
|
||
- **Et tak som fyrer er IKKE en krasj — `BudgetExceeded` får sin EGEN kanal (429), og trippelen
|
||
bæres som STRUKTUR (1b-køen, 14.08):** prosjektets første levende kjøring døde på
|
||
`rounds limit=12 observed=13`, og den hostede flaten svarte `500 {error_type, error}` — altså
|
||
nøyaktig det samme den sier når modell-endepunktet faller. **Beslutningen er S3.4-invarianten
|
||
anvendt på transporten:** `budget_stop` ble holdt UTENFOR `stop_reason` fordi de to stoppene
|
||
betyr motsatte ting, og å svare ressurs-utmattelse på krasj-kanalen gjør «det gikk ikke»
|
||
uleselig på nøyaktig samme måte. **IKKE 200, og det er dét som skiller den fra `Rejection`:**
|
||
en `Rejection` er en kjøring som KONKLUDERTE (og hører derfor i payloaden), mens et uttømt
|
||
budsjett produserte ingen `proposal` i det hele tatt — en 2xx ville latt en automatisk kaller
|
||
bokføre «analysert» for en kjøring som analyserte ingenting. **429 fordi betingelsen oppstår av
|
||
en TILDELING** (`max_rounds`/`max_tokens` er whitelistede request-felt, og å heve dem er
|
||
kallerens egen botemiddel), aldri av en serverfeil — derfor 4xx, ikke 5xx.
|
||
`kind`/`limit`/`observed` legges ut som felt, ALDRI `str(exc)` (kø-(y): de beskriver ÉN ledger,
|
||
og «hvilket tak bandt, og hvor langt forbi» er hele det operative spørsmålet); `error_type`
|
||
holdes UTE — den nøkkelen tilhører feilkanalen, og en kaller som switcher på dens
|
||
tilstedeværelse skal ikke finne den her. `budget_exhausted` er IKKE foldet inn i `outcome_type`,
|
||
og kunne ikke vært det: `outbox.outcome_payload` er den ENE kopien av den forgreningen og tar
|
||
`ValidatedProposal | Rejection`, som en uttømt kjøring ikke har noen av. **Ærlighets-grense,
|
||
uttalt:** ingen `Retry-After` — å vente endrer ingenting, botemiddelet er et større tak eller å
|
||
akseptere stoppet, og en header som lover tid ville vært en løgn. Load-bearing MÅLT
|
||
(`tests/test_hosting_loadbearing.py`), fem mutasjoner alle røde mot HELE suiten, hver med sin
|
||
egen signatur + grønn kontroll 867/4: detach armen (2 røde) · flat streng i stedet for struktur
|
||
(1 rød — struktur-testen ALENE, altså rir den ikke på status-asserten) · ekko `limit` som
|
||
`observed` (1 rød) · utvid armen til `Exception` (6 røde, inkl. 400-armen) · stemple
|
||
`error_type` på budsjett-kroppen (1 rød). **500-armens vitne ble byttet, ikke slettet:** den
|
||
eksisterende testen brukte `BudgetExceeded` som sin 500-prøve, så å bare legge til en ny arm
|
||
ville etterlatt krasj-kanalen uten vitne — den bærer nå en ekte ikke-budsjett-`RuntimeError`,
|
||
og er dét som holder den nye armen SMAL.
|
||
- **Påstander flaten gjør om SEG SELV gates som rå tekst, linjeforankret (Fase 3, A5):** to påstander
|
||
bodde i prosa der ingen test kunne se dem, og begge drev. (1) `env.template` sa at credential
|
||
resolves via `DefaultAzureCredential` — den har ALDRI gjort det; gaten leser de klassene
|
||
`backends.py` faktisk konstruerer **fra selve tilordningslinja**, ikke fra modulen, fordi
|
||
kommentarene NAVNGIR `DefaultAzureCredential` fire ganger for å begrunne hvorfor den ikke brukes —
|
||
en fil-bred substring-gate ville vært rød på nøyaktig den prosaen den beskytter (repoets
|
||
08-09-klasse, fjerde gang). (2) README-ens wheel-filnavn bærer versjonen bygget stempler på fila,
|
||
så en versjonsbump ville stille etterlatt en publisert install-kommando som peker på en fil som
|
||
ikke finnes. **Hver positiv assert er paret med en KONTROLL** på at det søkes etter noe som finnes
|
||
— en ekstraktor som stille finner null lager en gate som bare kan bli grønn. Load-bearing MÅLT
|
||
(`tests/test_public_surface_claims_loadbearing.py`) mot HELE suiten, begge røde på riktig test og
|
||
på INGEN annen: gjeninnfør credential-påstanden (2 røde, 844 grønne) · la wheel-filnavnet drifte
|
||
(1 rød, 845 grønne). Bumpen selv var den tredje målingen — `pyproject` 1.0.0 → 1.1.0 gjorde
|
||
README-gaten rød alene, FØR README ble rettet. **`repo-standard`-gaten kan IKKE verifisere denne
|
||
fasen:** den var OK/20 sjekker før arbeidet startet, og `RELEASE-STALE` er strukturelt blind for
|
||
repo med null utgivelser (org-ops hovedbok #18). Bevisene er Forgejo-APIet, filinnholdet og
|
||
ren-klon-kjøringen.
|
||
- **GOVERNANCE er en LENKE, aldri en kopi (org-ops D11):** én kanonisk `GOVERNANCE.md` bor i
|
||
`repo-standard` og hvert repo lenker den fra README. Å skrive vår egen ville gjort oss til kopi
|
||
nr. 12 av en fil D11-bølgen holder på å rydde vekk. Bus-faktor 1 står uttalt i den kanoniske
|
||
teksten, ikke i vår.
|
||
- **Stoppkriterier + budsjett-tak påkrevd ved oppstart** (fail-fast, aldri ubegrenset loop).
|
||
- **Group Chat maker-checker** som debatt-default (IKKE Magentic, som er eksperimentell).
|
||
- **To falsifiserere, samme kandidat (Steg 3/4, målbilde §2/§6):** den deterministiske validatoren
|
||
gater *tallene* (blokkerende), checkeren gater *resonnementet*. Checkeren avslutter turen med en
|
||
`VERDICT: APPROVE` / `VERDICT: REJECT — <grunn>`-linje; et eksplisitt avslag blokkerer et ellers
|
||
validert forslag (`run_project` overflater begge debatt-deltakere via `output_from=agents` og
|
||
overstyrer utfallet til en checker-kilde-`Rejection`). Gaten er opt-in-reject (fail-open ved
|
||
manglende markør), og `provenance.validator_decision` forblir ærlig — den speiler KUN validatoren,
|
||
aldri checkeren (de to falsifisererne blandes aldri). Load-bearing:
|
||
`tests/test_checker_gate_loadbearing.py` blir rød ved BEGGE detach-punkt (revert `output_from`,
|
||
eller fjern override). Checkeren «må faktisk gate, ELLER vi slutter å kalle det maker-checker».
|
||
- **Informert forbedring, bundet (Steg 5, målbilde §5/§7):** `generate_via_llm`s ytre
|
||
`max_attempts`-løkke er ikke lenger blind — validatorens *forrige* `Rejection.reason` mates inn i
|
||
neste forsøks prompt (`_build_messages(prior_rejection=...)`), så proposeren korrigerer i stedet
|
||
for å gjenta. Kun den mest-nylige falsifiseringen (`last`, ikke akkumulert), kun grunnen (aldri
|
||
forrige proposal-JSON), under EKSISTERENDE tak (`meter.tick_round` + `max_attempts` — ingen ny
|
||
løkke; «forbedre til god nok» uten tak er forbudt). Eneste *per-forsøk*-falsifiserer her er
|
||
validatoren; å seede generering med checker-*kritikken* er run-nivå og separat scoped (IKKE bygget
|
||
her) — så koden påstår ikke mer enn den gjør. Load-bearing:
|
||
`tests/test_step5_refine_loadbearing.py` blir rød når reason-injeksjonen detaches (utfallet
|
||
flipper aldri + reason-verbatim-asserten faller); kontrollen beviser at løkka forblir bundet.
|
||
- **Falsifiserings-historikken FORLATER generate-løkka som typet returverdi (Steg 5, del 2):**
|
||
`generate_via_llm` returnerer `GenerationResult(outcome, refinements)` — ikke lenger bare
|
||
`ValidatedProposal | Rejection`. Før dette forbrukte løkka hver `Rejection` internt (`last`) og
|
||
DROPPET den, så Steg 5 var det ene av åtte steg uten observerbart utfall. **Returverdi, ikke
|
||
out-parameter/callback:** en returnert verdi kan ikke bli stille tapt av en kaller som glemmer å
|
||
sende en samler, og mypy tvinger hvert kallsted til å ta stilling. **`refinements` bærer KUN
|
||
avvisninger som faktisk ble matet tilbake** i et senere forsøks prompt — ved uttømt budsjett ER
|
||
den siste avvisningen `outcome`, den informerte ingenting, og å telle den med ville vært
|
||
dobbeltføring (en «samle alt»-implementasjon består den positive testen og faller på kontrollen).
|
||
Taket er URØRT: `max_attempts` + `meter.tick_round` står, og `last` driver fortsatt prompten alene
|
||
(prompt-veksten er uendret). `run.py` akkumulerer på tvers av `_evaluate`-kallene, så
|
||
`_evaluate_mandate` er urørt; `RunResult.refinements` er defaultet (`coverage`-presedensen), og
|
||
med mandat er den KONKATENERT på tvers av tiltak, ikke nøklet per tiltak (uttalt ærlighets-grense).
|
||
`scripted_factory` tar nå `str | reply_selector` per rolle, så simuleringens proposer korrigerer
|
||
seg innholds-nøklet uten en andre scriptet kropp. Load-bearing MÅLT
|
||
(`tests/test_step5_history_loadbearing.py`), fire mutasjoner: detach returneringen · samle-alt ·
|
||
detach run-wiringen · reverter simuleringens proposer til konstant svar.
|
||
- **Lang/async fil-løkke (Steg 7, målbilde §3/§7):** `run_project(verdict_dir=...)` er den lange
|
||
tilbakemeldings-tidsskalaen — en ekspert/persona dropper en verdict-fil (vanlig JSON, RAW-laget
|
||
per §10 R2) i en inbox-mappe ETTER en kjøring, og en separat, senere kjøring `load_verdicts_from_dir`
|
||
→ `store.add` **merger** den inn FØR Steg-1-folden (ingen endring i folden), så dommen når neste
|
||
hypotese. **Rolledeling (§3, ufravikelig):** systemet LESER mappa; eksperten/personaen SKRIVER den
|
||
— `run_project` persisterer ALDRI sin egen fangede dom tilbake (det er outbox/Steg 8). Merge, aldri
|
||
erstatt (`run_portfolio`-tråding intakt); tolerant last (manglende mappe / fremmede / halvskrevne
|
||
filer hoppes over, ikke raises — RAW-lag, kontrast `okf.load_ir_projection`s fail-fast); `id` leses
|
||
verbatim, re-mintes aldri. `write_verdict` er den offentlige authoring-primitiven (persona/test +
|
||
framtidig Steg 8), men wires IKKE inn i `run_project`. Load-bearing:
|
||
`tests/test_step7_async_loop_loadbearing.py` — en dom droppet etter Run A MÅ nå Run B's prompt
|
||
(Run B bruker FERSK store → overføringen er fil-løkka, ikke in-memory-carryover); tom-inbox-kontroll
|
||
beviser kausalitet. Markør = realiseringsverdi som finnes ingen steder i bundelen (ikke frøets 0.82).
|
||
- **Gated wiki-promotering (Steg 8, målbilde §3/§6/§7):** når en ekspert/persona GODKJENNER et
|
||
utfall, løfter `verdicts.promote_verdict` det fra RAW output-laget inn i kontekst-laget (OKF-bundelen)
|
||
som en `type: verdict`-konseptfil, navigerbar av neste kjørings `seed_store_from_bundle`. **Gaten er
|
||
fail-closed:** en ikke-godkjent dom (`decision ∉ {approved, approved_with_adjustment}`) raiser
|
||
`PromotionRefused` og skriver/linker INGENTING — kun menneske/persona-godkjent kunnskap når wikien,
|
||
aldri rå agent-output (selv-forurensning). Provenance-stemplet (hvem/eksperiment/når; `timestamp` er
|
||
påkrevd keyword, ingen wall-clock-default → deterministisk). **OKF-skriveren bor i `okf.py` og er ren
|
||
stdlib** (D7-portabel, MAF-fri — håndhevet av `test_okf_is_maf_free`); navigasjon følger KUN
|
||
index-cross-links, så `promote_verdict` linker filen i `index.md` via en NØYTRAL label (ellers lekker
|
||
signalet inn i `index_summary`→`bundle_context` utenom gaten). **R4 = valgfri+gated:** `promote_verdict`
|
||
er en offentlig opt-in-primitiv, wires IKKE inn i `run_project` (speiler `write_verdict` — systemet
|
||
leser; gaten/personaen promoterer). Ærlighets-grenser: promotert fil er MINIMAL (læringssignal kun som
|
||
`description`/body-prosa, reproduserer ikke seedens strukturerte `realization_rate` o.l.); id =
|
||
læringsnøkkel, så to godkjenninger om samme kandidat deler filnavn (last-write-wins, som `write_verdict`)
|
||
— wikien vokser én kuratert fil per distinkt kandidat, ikke per dom-hendelse. Load-bearing-trio
|
||
(`tests/test_step8_promotion_loadbearing.py`): gaten avviser ikke-godkjent dom (RØD uten gate); godkjent
|
||
dom er navigerbar (RØD når `link_in_index` detaches); promotert signal holdes ute av `bundle_context`
|
||
(RØD når en beskrivende index-label lekker det inn). Index-RMW er ikke-atomisk (enprosess-MVP).
|
||
- **En dom nøkles på SIN kandidat, ikke bundelens ene IR-projeksjon (S3.2):** `seed_store_from_bundle`
|
||
leser hver `type: verdict`-fils EGNE strukturelle felt fra frontmatter (`affected_codes` /
|
||
`measure_type` / `claimed_saving_nok`); mangler de, faller nøklingen tilbake til
|
||
`bundle_candidate_features` — så hver pre-S3.2-seed står uendret (fallbacken er BÆRENDE: fjernes
|
||
den, brekker step1-suiten ved collection). Uten dette kollapset en bundle med dommer om flere
|
||
kandidater dem på ÉN nøkkel, og en dom om kandidat B scoret perfekt strukturell match mot
|
||
kandidat As query. **ALLE TRE felt eller ingen:** en delvis erklæring raiser
|
||
`VerdictFrontmatterError` i stedet for å slås sammen med bundle-kandidaten — sammenslåingen ville
|
||
myntet en nøkkel som tilhører INGEN av kandidatene. Validering, ALDRI reparasjon (speiler
|
||
`write_concept_file`); den tolerante hopp-over-regelen hører til RAW-innboks-laget.
|
||
`claimed_saving_nok` parses med `json.loads` — SAMME literal-regel IR-projeksjonen gikk gjennom —
|
||
og skrives tilbake som `str()` av råverdien, fordi `_mint_id` hasher den (`30000` ≠ `30000.0`; en
|
||
normaliserende skriver ville splittet én kandidats signal på to id-er). `promote_verdict` skriver
|
||
de tre feltene, så en promotert dom ikke utgir seg for målbundelens kandidat; nøkkelen er
|
||
signal-fri, så Steg-8s no-leak-egenskap står. **Semantikken er bestemt HER** — commons' seeding-regel
|
||
(`method-spec` §3 Steg 1) hadde ikke kommet; D7-speiling forblir ÅPEN. Load-bearing MÅLT
|
||
(`tests/test_step32_multicandidate_loadbearing.py` + `test_step8_promotion_loadbearing.py`), fem
|
||
mutasjoner alle røde: detach per-dom-nøklingen · detach feltene `promote_verdict` skriver · gjør en
|
||
delvis/uparsebar nøkkel tolerant · normaliser magnituden ved skriving · fjern fallbacken (kontroll).
|
||
- **Den deterministiske gaten er FORANKRET i prosjektets faktiske kostbaseline (S4.0, F3/F8):** før
|
||
S4.0 resonnerte HVER stage kun om tall forslaget selv oppga, så en internt konsistent hallusinasjon
|
||
klarerte hele gaten. `validate_proposal(..., baseline=...)` avstemmer nå hvert `affected_item` mot
|
||
`CostBaseline` (`ir.py`) i en **stage 0 — FØR løseren** (billigst, og den eneste som skiller en
|
||
oppdiktet linje fra en ekte; å bruke en CBC-solve på tall som ikke tilhører prosjektet er arbeid på
|
||
et krav som uansett ikke kan valideres). To uavhengige avvisninger: ukjent kostkode, og ekte kode
|
||
med `quantity`/`unit_cost` utenfor `tolerance` (default 5 %, **konfig**) relativt til BASELINE-verdien.
|
||
Validering, ALDRI reparasjon — forslaget avvises, aldri stilltiende korrigert til baselinen.
|
||
**Argumentet er VALGFRITT** (`None` = pre-S4.0-oppførsel), men begge run-stier SETTER det: road-stien
|
||
fra `project.cost_items` (alltid — estimatet ER prosjektet), bundle-stien KUN når bundelen shipper
|
||
`cost-baseline.json` (`load_optional_cost_baseline`) — en pre-amendment-bundle er legitimt
|
||
uforankret, og det er dét som holder commons-goldenene byte-identiske. **Toleransen stopper ved
|
||
fravær:** en baseline som FINNES men er malformed raiser på BEGGE loaderne (å lese korrupt som
|
||
«ingen baseline» ville gitt en uforankret gate i forkledning — samme resonnement som `read_spend`).
|
||
**F8:** metode-cap-en slås opp i `METHOD_CAPS`-registeret (måletype→brøk, injiserbart), ikke mot
|
||
`energy_efficiency`-literalen — en andre metode er nå data, ikke en redigering av validatoren.
|
||
Format- og toleranse-semantikken er bestemt LOKALT (commons-amendmentet D-A pkt. 2 kom aldri, som i
|
||
S3.2); **D7-speiling ÅPEN.** Load-bearing MÅLT (`tests/test_s40_cost_baseline_loadbearing.py`), seks
|
||
mutasjoner alle røde: detach avstemmings-stagen · detach magnitude-toleransen · detach road-wiringen ·
|
||
detach bundle-wiringen · ignorer det injiserte cap-registeret · gjør den valgfrie loaderen tolerant.
|
||
**En UFORANKRET kjøring sier det nå — og BEGGE utsagn stammer fra kjøringens ENE oppslag, aldri
|
||
en andre lesing av bundelen (21.08):** `ProvenanceStamp.cost_baseline_anchored` er PÅKREVD uten
|
||
default (begge defaults lyver: `True` lar en glemsom konstruktør påstå en ankring som ikke skjedde,
|
||
`False` underrapporterer en ekte — en binær kjensgjerning om en falsifiserer har ingen ærlig
|
||
default), og `DryRunReport` bærer det samme fordi en dry-run stopper før noe stempel finnes.
|
||
`run.cost_baseline_notice(anchored)` er ENESTE renderer, tar den alt oppløste BOOLEANEN, og
|
||
returnerer `None` når kjøringen ER forankret — omisjon, aldri en tom rad (`announce`-regelen).
|
||
**IKKE foldet inn i `mandate.announce`, og det er en MÅLING:** den fyrer kun med `--mandate`, så
|
||
nettopp de bare bundle-dry-runsene defekten ble målt på ville fortsatt sagt ingenting — og den
|
||
renderes FØR `run_project`, altså før noen har oppløst baselinen. Utboksen trengte ingen endring
|
||
(`write_proposal` dumper hele stempelet). Ankeringen forblir VALGFRI: dette er synlighet, ikke en
|
||
ny nekt, og golden-transkriptet er byte-uendret fordi demoen kjører en base som HAR fila.
|
||
Portefølje-armen er DEFENSIV og uttalt (ingen referanse-prosjekt setter `bundle_dir`, så den er
|
||
unåbar i dag — `budget_stop`-presedensen; testen driver en crafted `PortfolioResult`). Load-bearing
|
||
MÅLT (`tests/test_baseline_visibility_loadbearing.py`), seks mutasjoner alle røde mot HELE suiten +
|
||
grønn kontroll 885/5: konstant stamp-wiring (3 røde) · konstant dry-run-wiring (1 rød) · detach
|
||
dry-run-printen (1 rød) · renderer returnerer alltid linja (2 røde — inkl. den forankrede
|
||
kontrollen, altså er omisjonen selv gatet) · detach full-run-printen (1 rød) · detach
|
||
portefølje-printen (1 rød). Det PÅKREVDE feltet tvang fem eksisterende test-konstruktører til å ta
|
||
stilling — det er egenskapen, ikke friksjonen.
|
||
- **Globalt token-tak håndheves FØR kall, aldri bare etterpå (S3.4, F10):** `PortfolioBudget` +
|
||
`PortfolioMeter` er ÉN ledger over hele porteføljepasset (og — seedet av `read_spend` — på tvers
|
||
av pass), mens per-run `Budget`/`TokenMeter` er uendret. Taket har tre tenner, med hver sin jobb:
|
||
(1) **oppstartsnekt** — en rest som ikke kan finansiere én kjøring raiser `BudgetRefused` FØR noe
|
||
lastes (et pass som har råd til null prosjekter er en caller-feil, ikke et resultat);
|
||
(2) **wave-assembly** — et prosjekt som ikke kan finansieres blir ALDRI STARTET, og passet stopper
|
||
strukturert (`budget_stop` + `stopped_early`, fullførte runs bevart). Aldri-startet er poenget:
|
||
et ufinansiert prosjekt som bare avbrytes har allerede kostet kall. Fordi hele bølgen sjekkes mot
|
||
SAMME før-bølge-rest, **reserverer** admission hver members krav — ellers overforplikter en bølge
|
||
av k taket med inntil k kjøringer; (3) **pre-call-guard** i `BudgetMiddleware` — et kall resten
|
||
ikke kan betale for NEKTES i stedet for å gjøres (post-charge-sjekken består: ekte usage kjennes
|
||
først etterpå, så guarden stopper NESTE kall, aldri det som er i lufta). `budget_stop` er et EGET
|
||
felt, aldri `stop_reason`: et mål-stopp er suksess, dette er ressurs-utmattelse — å slå dem sammen
|
||
ville gjort «vi stoppet» uleselig. `record`/`check` er SPLITTET i `PortfolioMeter` fordi tokens
|
||
leverandøren allerede har fakturert må nå ledgeren selv når samme charge bryter run-taket.
|
||
Spend-fila er vår EGEN regnskapstilstand: `read_spend` raiser på korrupt innhold (kontrast det
|
||
tolerante RAW-inbox-laget — å lese korrupt som null ville gitt tilbake et allerede brukt budsjett),
|
||
og `write_spend` tar et PÅKREVD `stamp` uten wall-clock-default (byte-determinisme, speiler
|
||
`promote_verdict`). `portfolio_meter` og `meter_factory` er gjensidig utelukkende — en
|
||
factory-meter er ubundet, så begge sammen ville gitt et pass som SER capped ut uten å være det.
|
||
Load-bearing MÅLT (`tests/test_portfolio_budget_loadbearing.py` + `tests/test_budget.py`), seks
|
||
mutasjoner alle røde: detach wave-sjekken · detach pre-call-guarden · detach bølge-reservasjonen ·
|
||
sjekk run-taket før global kreditering · detach oppstartsnekten · gjør `read_spend` tolerant.
|
||
- **`BudgetExceeded`s tre felt beskriver ÉN og samme ledger (kø-(y)):** `kind`/`limit`/`observed`
|
||
er ett strukturert stopp-event, og `observed` var det udefenderte tredje feltet — MÅLT: fire av
|
||
fem raise-steder (`TokenMeter.charge`, `tick_round`, og BEGGE armene i `exhausted()`) kunne
|
||
rapportere hvilken som helst verdi uten at 621 tester merket det; bare `PortfolioMeter.check`
|
||
var dekket. **Fellen som skjulte det:** `spikes/_harness.py:41` har sin EGEN kopi av
|
||
`BudgetExceeded`/`TokenMeter`, så `spikes/test_harness.py`s `observed`-assert dekker IKKE den
|
||
shippede modulen — produksjonens `tick_round` hadde null direkte test. `exhausted()` er det
|
||
ENESTE stedet som VELGER ledger (S3.4-guarden), så en refusal som sier `portfolio_tokens` mens
|
||
den rapporterer runets eget forbruk ville villedet enhver leser. **Testene bygges med
|
||
`observed != limit`**: ved nøyaktig-uttømt sammenfaller de to, og en test skrevet der kan ikke
|
||
skille dem — den ville passert på en implementasjon som ekkoet taket tilbake som forbruket.
|
||
Ingen defekt funnet i verdiene selv (kontrast (x)/(p)): trippelen var koherent alle fem steder,
|
||
gapet var rent dekning. Load-bearing MÅLT (`tests/test_budget.py`), ni mutasjoner alle røde:
|
||
fem `observed`-mutasjoner (inkl. kontrollen) + fire ekko-mutasjoner.
|
||
- **Pengetall kvantiseres i ÉN orden, fra ÉN kilde (kø-(p)):** `ledger.to_ore` er rammeverkets ENE
|
||
NOK→øre-konvertering (Decimal, ROUND_HALF_UP), og den brukes **per pengebeløp — deretter summeres
|
||
HELTALL**. `run.py` importerer den; aldri en egen kopi (to kopier av en penge-konvertering drifter,
|
||
og en driftet kopi setter de to sidene av en mål-sammenligning på hver sin skala — S4.0s
|
||
`REPLIES`-presedens). Før dette summerte `run.py`s mål-baseline `Project.total_cost`-FLOATS og
|
||
kvantiserte totalen ÉN gang, mens `SavingsLedger` summerte per-kandidat-heltall — og de to ordenene
|
||
møttes i nøyaktig ETT punkt: `_goal_limit_if_reached`, der et prosent-mål avgjør om et
|
||
porteføljepass stopper tidlig. **Målt divergens:** tre linjer à `60000.005` NOK er `18000003` øre
|
||
kvantisert først, men `18000001` summert først (float-drift til `180000.01499999998`) — nok til å
|
||
vippe et mål. **Kvantiser-først valgt fordi hver `CostItem` ER et beløp** (S4.0 gjorde per-linje
|
||
`quantity`/`unit_cost` til validatorens grunnsannhet), og fordi heltallsaddisjon er assosiativ →
|
||
rekkefølge-uavhengig under D-D-bølgemodellen, som float-folden ikke er. **To kallsteder, ikke ett:**
|
||
portefølje- og per-prosjekt-baselinen er separate, og en fiks på bare den ene OVERLEVDE hele suiten
|
||
(målt). Load-bearing MÅLT (`tests/test_money_quantization_loadbearing.py`), fem mutasjoner alle
|
||
røde: detach portefølje-baselinen · detach per-prosjekt-baselinen · gjeninnfør en privat kopi i
|
||
`run.py` · endre avrundingsmodus · la `realize` gå utenom `to_ore`. **Ærlighets-grense:**
|
||
`sum_claimed_saving_nok` (`run.py:_aggregate`) er BEVISST urørt — et float-NOK-rapportfelt som
|
||
aldri kvantiseres og aldri sammenlignes mot ledgeren, altså utenfor ordens-defekten.
|
||
- **En BETALT test får sin EGEN opt-in, og instrumentet bevises GRATIS (Fase 1b, siste trinn):**
|
||
`tests/test_full_run_live.py` kjører hele `run_project`-stien mot et ekte Foundry-deployment, og
|
||
gates på **fire** ting — de to Foundry-variablene, `PORTFOLIO_MODEL_MAP`, og et TREDJE, distinkt
|
||
`PORTFOLIO_LIVE_FULL_RUN` lest på **truthiness** (4b-invarianten). **Den tredje variabelen er
|
||
load-bearing, ikke pynt:** `test_foundry_profile_live.py` (klient-probe) og `test_portfolio_live.py`
|
||
(fan-out) gatet på nøyaktig SAMME to variabler, så å gjenbruke det paret ville betydd at en
|
||
operatør som eksporterer dem for den BILLIGE ett-ords-proben også fyrer den dyre fullkjøringen —
|
||
altså at måleprotokollens stige («bevis så mye som mulig før det dyre trinnet, så en feil er
|
||
attribuerbar») kollapser til ett trinn. **MÅLT:** med begge Foundry-variablene satt SKIPPET den
|
||
dyre, og den billige var grønn. **Regelen gjelder HVER betalt arm, ellers er den ingen regel:**
|
||
`test_portfolio_live.py` passerer ingen `client_factory` og er derfor selv en betalt kjøring — den
|
||
fyrte på to-variabel-paret fra et bart `uv run pytest`, og ble gatet på den TREDJE variabelen i
|
||
samme slengen. Å la den stå ville gjort denne raden halvt usann den dagen den ble skrevet; en
|
||
invariant som beskriver én av to armer er en påstand flaten gjør om seg selv uten dekning, som er
|
||
nøyaktig Fase 3-klassen. Den billige klient-proben beholder to-variabel-gaten med vilje — den ER
|
||
det billige trinnet. `PORTFOLIO_MODEL_MAP` er med av en annen grunn — attribusjon: uten
|
||
den feiler kjøringen av en KONFIGURASJONS-årsak som ser ut som en modell-feil.
|
||
**Asserten bor i ÉN kopi** (`conftest.assert_full_run_contract`, kø-(p)) og er smal med vilje:
|
||
fraværet av `{run_id}-parse-failures.json` (økt 35-invarianten «filens tilstedeværelse er
|
||
signalet») + at `validator_decision` avgjorde. **En `rejected` BESTÅR** — påstanden som felles er
|
||
at det strukturerte skjemaet ER akseptert av det levende endepunktet, ikke at modellen resonnerer
|
||
godt; å kreve `validated` ville vært en modell-dømmekraft-påstand ingen enkelt kjøring kan bære.
|
||
**Iron Law uten å betale to ganger:** et betalt kall kan ikke kjøres rødt-så-grønt, så
|
||
diskrimineringen bevises OFFLINE av `tests/test_live_full_run_contract.py` — to armer over samme
|
||
helper (én parse-feil → kontrakten MÅ feile; alle parser → MÅ passere). Load-bearing MÅLT, to
|
||
mutasjoner med hver sin distinkte signatur: detach artefakt-sjekken (T1 rød ALENE — kontrakten
|
||
degraderer da til `test_portfolio_live.py`s `len(runs)==1`-klasse) · raise ubetinget (T2 rød
|
||
ALENE — den motsatte vakuiteten, en live-test som bare kan bli rød). **Det betalte kallet er
|
||
MÅLINGEN, aldri beviset på at måleinstrumentet virker.**
|
||
- **Kostnadsdisiplin:** utvikle primært på lokal profil (gratis); Foundry/Azure (privat tenant finnes) kun til målrettet, minimal verifisering; billigste modeller + små syntetiske data + harde token-tak. Ingen tunge test-kjøringer.
|
||
- **Offline simulering = primært metode-bevis (kostnadsdrevet, erstatter §11.8):** operatøren kjører
|
||
IKKE MAF mot ekte modell (verken Azure/Foundry eller Ollama — API for begge repoene er for kostbart
|
||
privat). `portfolio_optimiser.simulation` driver `run_project` med en SKRIPTET syntetisk chat-klient
|
||
(`ScriptedChatClient` på `OpenAIChatCompletionClient` — IKKE bare `BaseChatClient`, ellers no-op-er
|
||
`BudgetMiddleware`) over to kjøringer adskilt av en promotering, og viser at læringssløyfa lukkes:
|
||
Run A's godkjente persona-dom (markør fraværende fra bundelen) → `promote_verdict` → re-seed → Run B's
|
||
hypotese-prompt bærer markøren (tom-wiki-kontroll på Run A beviser kausalitet). **Ærlighet (§1,
|
||
ufravikelig):** beviser plumbing + deterministisk ryggrad + at dataflyten lukkes — IKKE at en levende
|
||
LLM ville produsert forslaget/dommen (skriptede stand-ins). Den genuine modell-atferd-sammenligningen
|
||
lever på Claude-SDK-siden (minimal API-kjøring). Skriptet klient = MAF-side stillas, IKKE delt
|
||
(`shared/` forblir framework-nøytralt). Kjøres `uv run python -m portfolio_optimiser.simulation`.
|
||
Load-bearing: `tests/test_simulation_loadbearing.py` blir RØD når promoteringen detaches.
|
||
- **Demoen KJØRER begge tidsskalaer, og de bæres av HVER SIN markør (P1/S1.a):** Steg 7-linja sa
|
||
«lang fil-løkke», men `simulate_learning_loop` kalte `run_project` UTEN `verdict_dir` — dommen kom
|
||
som funksjonsargument (`verdict_input`, den KORTE i-kjøring-fangsten). Nå skriver en ekspert en
|
||
faktisk fil (`write_verdict`) i en innboks MELLOM kjøringene, og Run B får `verdict_dir=`.
|
||
**Hvorfor en ANDRE markør og ikke persona-dommen gjennom innboksen:** Steg 7 (innboks) og Steg 8
|
||
(promotering) er to ULIKE mekanismer som begge ender i Run B's hypotese-prompt — med én delt
|
||
markør kunne hver av dem båret den alene, og `test_simulation_loadbearing.py`s promoterings-assert
|
||
ville stått GRØNN med promoteringen detached, altså blitt vakuøs. `simulate_learning_loop` raiser
|
||
derfor `ValueError` når `inbox_marker == marker`. Innboksen ligger VED SIDEN AV bundle-kopien,
|
||
aldri inni: en dom-fil inne i bundelen når neste kjøring som navigerbar kontekst, som er en annen
|
||
mekanisme i denne sin forkledning. Sentinel-`id` (aldri myntet) — `_mint_id` hasher kandidat-
|
||
featurene, så en myntet id kolliderer med den promoterte dommens, og `VerdictStore.add` er
|
||
first-write-wins per id. Load-bearing MÅLT (`tests/test_step7_demo_inbox_loadbearing.py`), tre
|
||
mutasjoner røde + grønn kontroll: detach `verdict_dir=` · la Run B lese en TOM mappe · sett
|
||
innboks-markøren til en verdi som FINNES i bundelen.
|
||
- **Det skriptede manuset nøkles på PROSJEKT-ID-en, og det er MÅLT:** `scripted_proposer(candidates)`
|
||
bygger simuleringens proposer fra et `ScriptedCandidate`-register, så et nytt prosjekt er en
|
||
data-oppføring (demo-uke-plan §4 risiko 2) — ikke et andre håndskrevet manus. **Hvorfor ikke
|
||
kostkode/tiltaksnavn:** to prompt-former når selectoren — debatt-prompten bærer hele
|
||
bundle-konteksten, mens genererings-prompten (`generate._build_messages`) bærer
|
||
`Project: {id} - {name}` pluss *debatt-outputen* som kontekst, altså selectorens EGET tidligere
|
||
svar. Kostkode og tiltaksnavn står derfor i genererings-prompten kun fordi manuset selv la dem
|
||
der; å nøkle på dem ville nøklet manuset på sin egen output. Prosjekt-ID-en er den ene
|
||
identifikatoren BEGGE former bærer og som RAMMEVERKET stempler. **Validering, ALDRI reparasjon:**
|
||
null treff — eller mer enn ett — raiser `ScriptedCandidateError`; et default-svar ville besvart et
|
||
uregistrert prosjekt med et ANNET prosjekts tall, som på skjermen er umulig å skille fra en riktig
|
||
kjøring, og en tvetydig blob er et DATA-problem som skal falle på generalprøven, ikke avgjøres av
|
||
register-rekkefølgen. `flip_key` MÅ være fraværende fra bundelen (ellers bærer forsøk 1s prompt
|
||
den allerede). `simulate_learning_loop` tar `project_id` ved siden av `bundle_dir`. Load-bearing
|
||
MÅLT (`tests/test_content_keyed_script_loadbearing.py`), fem mutasjoner alle røde + grønn
|
||
kontroll: detach nøklingen · én global flip-key · fallback ved ukjent prosjekt · første-treff ved
|
||
tvetydighet · detach `project_id`-argumentet. **Flip-key-testen ble skrevet om under målingen** —
|
||
første form asserterte på FØRSTE register-oppføring, der «den matchede kandidatens nøkkel» og
|
||
«`candidates[0]`s nøkkel» sammenfaller; den kunne ikke skille de to implementasjonene.
|
||
- **Demoen kjører FORANKRET, og baselinen DERIVERES fra manuset (P4 pkt. 0):** før dette regnet
|
||
validatoren i demoen kun på tall forslaget selv oppga — S4.0-forankringen aktiveres bare når
|
||
kunnskapsbasen shipper `cost-baseline.json`, og ingen bundle under `shared/` har den. Reserven kan
|
||
aldri få fila DER (pull-only subtree + kriterium 8 krever goldenene byte-uendret), men det er en
|
||
*plasserings*-begrensning: `materialize_anchored_bundle` KOPIERER bundelen og legger fila til
|
||
utenfor `shared/`, og kjørestien (`run.py` → `load_optional_cost_baseline`) er da NØYAKTIG samme
|
||
søm en levert bundle ville brukt. **Retningen på avledningen er bærende:** reservens tall er
|
||
syntetiske, så manus-registeret er eneste grunnsannhet — `baseline_from_scripted_candidate`
|
||
avleder i KODE, aldri en andre håndskrevet kopi av de samme tallene (to kilder drifter, og drift
|
||
er nøyaktig det 10 %-prøven modellerer). På GO-dagen snus retningen (plan P3 b: registeret skrives
|
||
FRA levert fil). Begge skriptede svar må oppgi SAMME kostlinjer (`ValueError` ellers): var de
|
||
ulike, ville hypotese #1 blitt avvist av stage 0 istedenfor av P90 — samme REJECTED-linje på
|
||
skjermen, annen mekanisme. **Forankringen er usynlig i alt annet stdout** (målt: eneste diff mot
|
||
uforankret er KUNNSKAPSBASE-blokka), derfor printes den erklærte baselinen, og derfor er
|
||
`provenance` et PÅKREVD argument til `_baseline_lines` — kallstedet som velger bundelen er det
|
||
eneste som vet hvor tallene kom fra. Load-bearing MÅLT
|
||
(`tests/test_anchored_reserve_loadbearing.py`), fem mutasjoner røde + grønn kontroll: detach
|
||
main-wiringen · detach fil-skrivingen · la filnavnet drifte · detach to-svars-enigheten · returner
|
||
et literal i stedet for det avledede. **Målingen felte TESTEN først** (samme klasse som 08-06):
|
||
«ingen kostbaseline erklært» INNEHOLDER «kostbaseline erklært», og `ENERGI-TOTAL-EL` står allerede
|
||
i Steg 2-linja — begge assertene overlevde detach-mutasjonen. De to grenene deler nå ingen ordlyd.
|
||
- **Demoens stderr: rund-taket dempes, `ExperimentalWarning`-paret PINNES (P4 pkt. 2):** målt 08-09
|
||
var stderr seks linjer. `quiet_expected_round_cap_notice()` dropper KUN
|
||
«reached max_rounds=…; forcing completion» — en hendelse demoen selv provoserer (maker/checker
|
||
kjører til taket) — via et filter på den EMITTERENDE loggeren (`ROUND_CAP_LOGGER`, lest ut av MAFs
|
||
kilde). Logger-filtre gjelder kun loggeren posten ble logget GJENNOM; en forfars filtre konsulteres
|
||
aldri. Filteret installeres i `main()`, ALDRI ved import — en bibliotek-modul skal ikke
|
||
omkonfigurere loggingen til en konsument. **De to `ExperimentalWarning`-linjene dempes IKKE:** de
|
||
fyrer mens `portfolio_optimiser/__init__.py` importerer `run` → `agent_framework`, altså alltid FØR
|
||
`simulation` sin egen importblokk, under BEGGE kjøreformer — så å dempe dem ville krevd et
|
||
warnings-filter inne i bibliotekpakken, dvs. at rammeverket bestemmer hva MAF får si til enhver
|
||
konsument. En wrapper bak konsoll-kommandoen ble avvist av en andre grunn: da ville de to
|
||
kjøreformene skrevet ULIK stderr, og en byte-fasit ville pinnet kommandoen i stedet for programmet.
|
||
**Dempingen er smal ved konstruksjon** — nøklet på meldingen, ikke loggeren — nettopp så pkt. 3-pinnen
|
||
fortsatt kan felles av en NY advarsel. Load-bearing MÅLT
|
||
(`tests/test_demo_stderr_quiet_loadbearing.py`), fem mutasjoner røde + grønn kontroll: detach
|
||
`main()`-kallet (subprosess-testen er ENESTE som fanger det — de tre filter-testene installerer
|
||
filteret selv) · la filteret droppe alt · installer ved import · pluss de to entry-point-mutasjonene.
|
||
- **Demo-transkriptet er sjekket inn som fasit, og masken er SPANN-avgrenset (P4 pkt. 3):**
|
||
kriterium 6 er selv-identitet — to kjøringer av en REGREDERT demo er like enige som to kjøringer av
|
||
en riktig, så fasiten må forlate prosessen. `tests/golden/demo-transcript.stdout` er stdout ORDRETT
|
||
(målt byte-identisk over kjøringer OG i fersk klon), og er derfor også demoens abortsti: feiler
|
||
live-kjøringen, ER fila transkriptet. `….stderr` er normalisert på nøyaktig to MÅLTE miljø-spann —
|
||
`site-packages`-prefikset og temp-katalogen bak `(arbeidskopi: …)`, der `po-sim-`-prefikset holdes
|
||
SYNLIG fordi det er en egenskap ved programmet (`mkdtemp(prefix=…)`), ikke ved miljøet; pinnet
|
||
stderr er TO linjer (var fire til og med core 1.9.0 — se F15-raden). **Masken må ikke kunne vokse:** P4 pkt. 2 betalte for at en NY advarsel
|
||
fortsatt når stderr, og en normalisering som maskerte hele linjer ville opphevet det i ett trekk —
|
||
derfor er `test_normalisation_does_not_mask_a_new_warning` kontrollen som forbyr det (MÅLT: en
|
||
droppende normaliserer med fasiten regenerert under seg holder BEGGE likhets-testene grønne og
|
||
felles kun av kontrollen). `PYTHONIOENCODING` pinnes, ellers måler sammenligningen operatørens
|
||
locale i stedet for programmet. **Regenerering er en beslutning, aldri rydding** — fasiten kan ikke
|
||
bevise sin egen kjøring, den pinner outputen P3-kriteriene ble målt mot.
|
||
**Hashen radene under fører er `shasum -a 1` av INNHOLDET, aldri git-blob-id-en (S7a-3 pkt. 4).**
|
||
De to er ulike av konstruksjon — git hasher over `blob <len>\0` + innhold — så
|
||
`ea8c534773acdbe41ae68f2c55724d69aaf8be4f` er sha1 av fila, mens `git hash-object` på samme fil
|
||
gir `55bdea3ad20616480d81b9e7544604b248a541c9` (stderr-goldenen: `ede3e2f…` innhold /
|
||
`12893ec…` blob). MÅLT etter at en økt søkte uttømmende etter `ea8c534…` som git-objekt, ikke fant
|
||
det, og leste fraværet som at ni invariant-rader siterte en fantomverdi — Verifiseringsloven
|
||
ansikt 4 mot ens eget instrument: et negativt resultat fra feil spørring er ikke et faktum.
|
||
Radene var RIKTIGE hele veien; det som manglet var etiketten, og den står nå i hver av dem.
|
||
- **Frø-setningen AVLEDES fra kjøringen (P4 pkt. 4):** demoen sier høyt hvor Kjøring B's tidligere
|
||
dommer kommer fra, og splitten (`_verdict_origin_line`) regnes ut — linja rett over printer allerede
|
||
antallet, så en håndskrevet «én av tre» ville vært den andre kopien som drifter (samme regel som
|
||
pkt. 0-baselinen), og ville blitt sagt uendret etter at en framtidig bundle shipper en ANDRE frøsatt
|
||
dom. Klassifisereren er de to markørene demoen alt sporer; den hviler på at den frøsatte dommen
|
||
bærer INGEN av dem, som MÅLES på levert bundle. **Planens forhåndsskrevne ordlyd var FEIL mot
|
||
levert innhold** («én av de TO») — målt henter Kjøring B TRE: én fulgte med kunnskapsbasen, to er
|
||
demoens egne (én per tidsskala). Load-bearing MÅLT
|
||
(`tests/test_p4_honesty_sentences_loadbearing.py` + golden-transkriptet), fem mutasjoner alle røde
|
||
+ grønn kontroll: ett byte i en stdout-linje · detach dempingen · over-normaliser stderr · literal
|
||
splitt (fanget av INGENTING i 800 tester bortsett fra skille-testen) · detach frø-setningens print.
|
||
- **Delt ekspert-persona som Agent Skill (§8, framework-nøytral):** ekspert-reviewer-personaen bor i
|
||
`shared/skills/expert-reviewer/` (`SKILL.md` + `references/example-verdict.json`) og er den ENE
|
||
delte artefakten begge stacker instansierer reviewer-en fra. `shared/` forblir REN DATA — MAF-siden
|
||
leser den via `portfolio_optimiser.persona.load_persona_example` (call-time, fail-fast), Claude-SDK-
|
||
søskenet med sin egen loader mot samme JSON. Dette AV-STUBBER simuleringen: persona-dommen (decision
|
||
+ rationale + sporet markør) hentes nå fra artefaktet ved call-time, ikke en inline-literal — så
|
||
personaen er genuint konsumert og kan ikke råtne stille. **Decision er binær** (`approved`/`rejected`
|
||
— `FeedbackContract` run-stien tar; `approved_with_adjustment` avvises der, bor kun i bundle-seedens
|
||
frontmatter + promoterings-gaten); realiseringskorreksjonen lever i rationale-prosaen, ikke et tredje
|
||
enum. SKILL.md-prosaen nevner ALDRI en konkret framework (maf-guarden er import-formet). Load-bearing-
|
||
trio (`tests/test_persona_skill_loadbearing.py`): struktur+framework-nøytralitet (RØD på framework-
|
||
import), eksempelet er gyldig pipeline-input inkl. `FeedbackContract` (RØD på skjema-/kontrakt-drift,
|
||
på en throwaway-kopi — aldri den git-tracked fixturen), og sim-ens markør følger artefakt-fila (RØD i
|
||
det øyeblikk personaen re-inlines).
|
||
- **Den råe svarteksten fanges i en KALLER-EID SINK, ikke i en returverdi (Fase 1b, funn 1):**
|
||
`generate._fetch_parsed` kastet hvert uparsebart modellsvar i `except: continue`, så prosjektets
|
||
første levende kjøring brant tolv runder på formatfeil og etterlot **null tegn** av det modellen
|
||
faktisk sa — enhver videre betalt kjøring ville vært gjetning. **HVOR teksten overflates er avgjort
|
||
av en MÅLING, ikke av symmetri med Steg 5:** `meter.tick_round()` raiser `BudgetExceeded` INNE i
|
||
`_fetch_parsed`, og uten mandat fanger ingen den (`run.py`s ene `except BudgetExceeded` er
|
||
mandat-armen) — så på nøyaktig den stien fangsten finnes for, RETURNERER `generate_via_llm`
|
||
ingenting. Et felt på `GenerationResult` (Steg 5-formen) er derfor blindt for den, og et
|
||
outbox-artefakt skrevet ETTER kjøringen likeså. Sinken speiler i stedet `meter`: en kaller-eid
|
||
akkumulator løkka muterer, hvis innhold kalleren holder uansett hvordan løkka endte. Steg 5s
|
||
«returverdi, ikke out-parameter» gjelder en verdi som NÅR kalleren; her gjør den ikke det, og å
|
||
kopiere regelen blindt ville gjenoppbygd defekten ett lag opp. Artefaktet
|
||
`{run_id}-parse-failures.json` skrives fra en **`finally`**, ikke `except BudgetExceeded` — enhver
|
||
exception ut av genereringen ødelegger samme bevis, og en liste over exception-typer er en liste
|
||
som blir foreldet. **Teksten er VERBATIM** (en forkortelse gjør beviset om til en parafrase), og
|
||
fila skrives KUN når noe faktisk feilet, så dens tilstedeværelse ER signalet. Byte-determinisme
|
||
påstås IKKE for dette ene artefaktet — innholdet er en levende modells prosa. Load-bearing MÅLT
|
||
(`tests/test_parse_failure_capture_loadbearing.py`), seks mutasjoner alle røde mot HELE suiten +
|
||
grønn kontroll 859/4: detach fangsten (3 røde) · flytt skrivingen ut av `finally` (1 rød, KUN
|
||
budsjett-testen) · detach run-wiringen (2 røde, generate-testen grønn) · skriv artefaktet alltid
|
||
(kontrollen + den eksisterende `a5`-inerthetstesten) · trunker teksten til 40 tegn (3 røde) ·
|
||
trunker til 100 tegn slik at sentinelen OVERLEVER (1 rød — verbatim-asserten alene, den skarpe
|
||
diskriminatoren). Ærlighets-grense: `_charge_usage` kan raise FØR parse, og et svar tapt der er
|
||
ikke en parse-feil og fanges ikke.
|
||
- **Proposeren får en GRAMMATIKK, og skjemaet er DERIVERT + fail-closed (Fase 1b, funn 1b):**
|
||
`generate_via_llm` sender `options={"response_format": proposal_response_format()}` på hvert
|
||
genererings-kall. **Formen er MÅLT, ikke valgt:** `ChatOptions.response_format` tar
|
||
`type[BaseModel] | Mapping`, og BEGGE profiler ærer den — LOCAL
|
||
(`OpenAIChatCompletionClient`) sender en Mapping ordrett til Chat Completions, AZURE
|
||
(`FoundryChatClient` → `RawFoundryChatClient` → `RawOpenAIChatClient`) konverterer SAMME
|
||
envelope til Responses-APIets `text.format`. **Klassen er AVVIST på bevis:** gitt en klasse
|
||
konverterer klienten med `type_to_response_format_param`, som (målt) emitterer `minimum` /
|
||
`exclusiveMinimum` / `minItems` / `prefixItems` og et `assumptions`-node hvis
|
||
`additionalProperties` er et SKJEMA — fire ting Azures publiserte subset utelukker
|
||
(Learn: «Unsupported type-specific keywords» + `additionalProperties: false` i hvert objekt).
|
||
Vår egen mapping er eneste måte å styre hva som når tråden. **Å stripe beskrankningene koster
|
||
ingenting:** skjemaets jobb er FORM, validatorens jobb er VERDIER — `minItems`/`gt=0` gjenreises
|
||
av pydantic i `_parse_ir` og av `validate_proposal`. **Skjemaet DERIVERES fra `SavingsProposal`**
|
||
(`strict_json_schema`), aldri håndskrevet: en andre kopi av en form som alt bor i `ir.py` drifter
|
||
stille, og modellen ville fortsatt blitt bestilt for den gamle. **`assumptions` KAN IKKE bare
|
||
droppes, og det er en MÅLING:** feltet er det ene uttrykksløse (fri-form map av 2-tupler), men
|
||
`validator._monte_carlo` faller tilbake på `item.unit_cost` for hver kode uten bånd — uten bånd
|
||
i det hele tatt er alle 512 samples IDENTISKE og P10 == P50 == P90. Den stokastiske
|
||
falsifisereren ville gått inert mens den fortsatt rapporterte persentiler: repoets kardinalklasse
|
||
(en gate som bare kan bli grønn). Derfor bærer WIRE-en et array av navngitte entries og
|
||
`_parse_ir` folder det tilbake til IR-ens map — **additivt, aldri erstatning** (map-formen
|
||
parser uendret; alle scriptede svar i suiten og golden-transkriptet bruker den). Sanitiseren er
|
||
**fail-closed** (`StructuredOutputUnsupported`) på `prefixItems`/`oneOf`/`allOf`/fri-form map
|
||
uten deklarert override — validering, ALDRI reparasjon (speiler `write_concept_file`).
|
||
Prompt-linja «Respond with ONLY a JSON object» + parse-retry + funn-1-fangsten står URØRT: en
|
||
leverandør som ignorerer `response_format` må fortsatt få beskjed, og backstoppen er poenget.
|
||
Load-bearing MÅLT (`tests/test_structured_output_loadbearing.py`), seks mutasjoner alle røde +
|
||
grønn kontroll 864/4: detach wiringen (1 rød) · detach sanitiseren (3 røde) · dropp
|
||
`assumptions` fra skjemaet (1 rød) · fail-closed → stille reparasjon (1 rød) · detach
|
||
normaliseringen (3 røde) · erstatning i stedet for tillegg (2 røde — T5 PLUSS
|
||
golden-transkriptet, et uavhengig vitne). **T3 ble skrevet VAKUØS først** (repoets 08-09-klasse,
|
||
sjette gang): den påsto å bli rød når `assumptions` forsvant fra skjemaet, men den scriptede
|
||
klienten ignorerer skjemaet — påstanden ble bevist usann av M3 og testen fikk en DIREKTE
|
||
assert på skjemaet. **ÆRLIGHETS-GRENSE, UTTALT:** ingen betalt kjøring er gjort, så at det
|
||
emitterte skjemaet ER akseptert av det levende endepunktet er IKKE verifisert — testen beviser
|
||
konformitet med det DOKUMENTERTE subsettet, ikke aksept. Ollamas oppførsel på
|
||
`response_format` er likeledes uverifisert.
|
||
- **Overleverings-pakka ER `git archive HEAD`, aldri en kuratert kopi (Fase 5):**
|
||
`scripts/make-handover-package.sh` bygger én zip en ekstern organisasjon deployer uten å klone
|
||
repoet. **Tracked files only er hele eksponerings-kontrollen** — `STATE.md`, `*.local.md` og
|
||
`.env` er gitignorert, så de KAN ikke komme inn; et filter vedlikeholdt i skriptet ville vært den
|
||
andre kopien av den regelen, og den andre kopien er den som drifter (kø-(p)). Mottakeren får altså
|
||
HEAD selv. Versjonen LESES fra `pyproject.toml` — et hardkodet tall her ville råtnet ved neste bump
|
||
nøyaktig som README-ens wheel-filnavn gjorde (Fase 3). `DEPLOY.md` ligger i treet og blir dermed
|
||
med i arkivet av seg selv; den bærer mottakerens tre første spørsmål — hvem gjør hva
|
||
(plattform-operatør / bestiller / fagperson), prosessen ende-til-ende, og **hvorfor det ikke
|
||
finnes et chat-grensesnitt** (flaten er `POST /invocations`, og `as_agent()` er bevisst vraket
|
||
fordi validator, baseline-forankring, checker-gate og ledger ligger UTENFOR grafen — et chat-lag
|
||
ville rutet forespørsler rundt nøyaktig det som gjør svaret etterprøvbart). Den navngir også det
|
||
4e målte deploy-kravet som ingen rad hadde skrevet ned: pakket `model_map.json` bærer
|
||
`REPLACE-WITH-*`, så uten `PORTFOLIO_MODEL_MAP` starter tjenesten, svarer på `/readiness` og
|
||
feiler HVER invocation. Gaten er `tests/test_handover_package_loadbearing.py`, og
|
||
DEPLOY.md-asserten er LINJEFORANKRET: `PORTFOLIO_FOUNDRY_PROJECT_ENDPOINT` INNEHOLDER
|
||
`FOUNDRY_PROJECT_ENDPOINT`, så en delstreng-assert på det injiserte navnet ville vært oppfylt av
|
||
vårt eget (repoets 08-09-klasse, femte gang).
|
||
- **Overleveringen er KUN kjørbar Python, og fraværet er FJERNING — ikke filtrering (14.08,
|
||
operatørdirektiv etter ekstern test):** `Dockerfile` og `azure.yaml` er slettet fra TREET.
|
||
**Sømmen er valgt av den eksisterende invarianten, ikke av smak:** pakka ER `git archive HEAD`, så
|
||
å ekskludere filene fra arkivet ville krevd en kurerings-mekanisme (skript-filter eller
|
||
`export-ignore`) — den andre kopien av «hva mottakeren får», fri til å drifte fra HEAD, altså
|
||
nøyaktig kø-(p)-regelen raden over finnes for. Å beholde dem som «opt-in» ville ikke oppfylt
|
||
direktivet i det hele tatt. Fjerning holder arkivet ukurert OG gjør fraværet til en egenskap ved
|
||
HEAD, som er det eneste en gate kan måle. **De to gatene som pinnet flaten er håndtert BEVISST,
|
||
aldri stille svekket:** 4e-rå-tekst-gaten (`--platform linux/amd64` + ÉN kopi av startkommandoen)
|
||
er SLETTET med et notat der den sto — en gate som pinner en fjernet flate kan bare bli grønn — og
|
||
handover-gatens `_REQUIRED_MEMBERS` er ikke bare fratatt de to navnene, men erstattet av en
|
||
POSITIV fraværs-assert; å kun slutte å KREVE dem ville gitt en gate som ikke kan skille «fjernet»
|
||
fra «shippes fortsatt». Matchingen skjer på arkiv-MEDLEMSNAVN, ikke på prosa (dokumentene må
|
||
kunne forklare at ingen image shippes — repoets 08-09-klasse, sjette gang), og dokument-gaten
|
||
forbyr kommando-FRAGMENTER (`docker build`, `azd deploy`), ikke ordet. **Startkommandoen har nå
|
||
ÉN kopi igjen — DEPLOY.md-ens `python main.py`** — og den navngir inngangen subprosess-testen
|
||
faktisk kjører. **Ærlighets-grense, uttalt:** azd/hosted-agent-stien finnes ikke lenger i pakka;
|
||
hvordan prosessen driftes er mottakerens valg. `git archive HEAD` leser HEAD, ikke arbeidstreet,
|
||
så gaten er ekte men forsinket med én commit (funn 35). Load-bearing MÅLT
|
||
(`tests/test_handover_package_loadbearing.py`).
|
||
- **Sporing er OPT-IN, og «av» betyr at MAF ALDRI kalles (U14, økt 55):** `PORTFOLIO_OTEL` leses på
|
||
**truthiness** (4b-regelen) og er ENESTE bryter; uten den kalles `configure_otel_providers` ikke i
|
||
det hele tatt — spans LAGES fortsatt (`ENABLE_INSTRUMENTATION` defaulter `True`,
|
||
`observability.py:697`) og kastes, så ingenting KAN forlate prosessen. Et kall med tom
|
||
exporter-liste ville derimot installert providere og lest hver `OTEL_EXPORTER_OTLP_*` i det
|
||
omkringliggende miljøet — «av» må være fravær av kall, ikke kall uten innhold. **To regler er
|
||
MÅLT, ikke valgt** (`observability.py:849` bygger exporter-lista i fast rekkefølge: (1) env-avledede
|
||
OTLP-exportere UBETINGET, (2) de innsendte, (3) `ConsoleSpanExporter()` — default-sink **stdout** —
|
||
når `enable_console_exporters` er sann fra argument ELLER `ENABLE_CONSOLE_EXPORTERS`): (a)
|
||
`enable_console_exporters=False` sendes EKSPLISITT i BEGGE moduser, ellers gir en operatør med den
|
||
variabelen eksportert et span-dump på stdout — nøyaktig det S6 målte som ødeleggende for
|
||
golden-transkriptet; (b) `console` NEKTER når en OTLP-endepunkt-variabel finnes, fordi steg (1)
|
||
ville lagt til en nettverks-exporter ordet «console» lover ikke er der. **Validering, ALDRI
|
||
reparasjon** — vi fjerner ikke operatørens miljøvariabel bak ryggen på dem
|
||
(`write_concept_file`-regelen); nekten NAVNGIR variabelen. `otlp` uten deklarert endepunkt nektes
|
||
også: providere med ingenting å eksportere til er en kjøring som SER sporet ut og ikke er det. En
|
||
ukjent verdi nektes ved navn, aldri stille fallback til av. **`tracing_notice` er ENESTE renderer**,
|
||
tar den alt oppløste `TracingSetup` og returnerer `None` når sporing er av — omisjon, aldri tom rad
|
||
(`announce`-regelen), og her bærende utover stil: demoens pinnede stderr er TO linjer (FIRE før F15). **Tre
|
||
kallsteder, ikke ett** (`run.main`, `simulation.main`, `hosting.main`): demoen er et skriptet bevis,
|
||
ikke produktet, og en søm bare demoen når ville latt de to inngangene en virksomhet faktisk kjører
|
||
være usporbare. **OTLP-exporter-PAKKENE er BEVISST ikke deklarert** (egress + grpc/protobuf-vekt i
|
||
et publisert wheel; MAF raiser selv en `ImportError` som navngir pakka) — uttalt ærlighets-grense.
|
||
**`PLAN_CREATED`/`REPLANNED`/`PROGRESS_LEDGER_UPDATED`-eventene planen navngir er IKKE bygget:**
|
||
de hører til utforskningssløyfa (U4) som ikke finnes ennå, og en emitter skrevet før kallstedet er
|
||
en form gjettet i stedet for målt. Load-bearing MÅLT
|
||
(`tests/test_tracing_loadbearing.py`), ni mutasjoner alle røde mot HELE suiten + grønn kontroll
|
||
943/5: exporteren tar sin stdout-default (4 røde) · `enable_console_exporters` overlatt til miljøet
|
||
(3 røde — inkludert den ATFERDSMESSIGE, som kjører demoen med variabelen eksportert; uten den ville
|
||
raden bare vært en keyword-assert) · detach console-nekten (4 røde) · detach otlp-endepunkt-nekten
|
||
(1 rød) · ukjent modus faller stille til av (2 røde) · «av» kaller MAF likevel + renderer returnerer
|
||
alltid en linje (9 røde, hvorav TRE i tester som fantes fra før — `test_golden_transcript` sin
|
||
da fire-linjers (nå to-linjers) stderr og `test_portfolio_cli_offline`s stille-pass — altså er omisjonen gatet av
|
||
uavhengige vitner) · detach demo-wiringen + detach CLI-wiringen (5 røde) · detach hosting-wiringen
|
||
(1 rød, KUN subprosess-testen — P4-presedensen).
|
||
- **Utforskningssløyfa er en MANDAT-FORMER, og de tre garantinivåene er strukturelle (U4+U13
|
||
synkron, økt 56):** `explore.py` legger en Magentic-manager OVER den normative sløyfa — `prompt +
|
||
kunnskapsbaser → Mandate → run_project(mandate=…)` UENDRET, Steg 3s maker-checker urørt (commons-eid
|
||
og normativ). Manageren velger VEI; det som forlater friheten er `mandate.Mandate`, aldri et forslag.
|
||
**Nivå 1** = `quick_validate`-verktøyet (SAMME `validate_proposal`, SAMME baseline, men rådgivende —
|
||
når ALDRI provenance); **nivå 2** = pipelinen som stempler; **nivå 3** = skriverettigheter, som kun
|
||
pipelinen har. `explore()` skriver INGENTING. **Mandatet bygges fra hypotesiserens MERKEDE turer**
|
||
(`HYPOTHESIS: {"label","rationale"}`), aldri fra sluttsvaret — sluttsvaret er RÅTT per design, og en
|
||
parser på det ville gjort det til et forslag. Markøren er dét som gjør fail-closed mulig: en umerket
|
||
tur er ikke en påstand (ingen stillhet å lukke), mens en MERKET-men-uleselig linje raiser
|
||
(`write_concept_file`-regelen). **Frø-approaches bevares ALLTID og FØRST** — også når sløyfa fant
|
||
ingenting og også ved stopp (§ C.6 dør 1 er en bevaringsregel, ikke en belønning for å bli ferdig).
|
||
**Tre kanaler, aldri én:** tokens OG runder raiser `BudgetExceeded` (rundene som
|
||
`kind="exploration_rounds"`, oversatt av VÅRT lag fordi orkestreringen MÅLT ikke raiser ved sitt eget
|
||
rundetak — den returnerer en kanonisk assistent-melding som ved transporten er uskillbar fra suksess),
|
||
mens alt semantisk er en VERDI i `stop` (S3.4-splitten: utmattelse og utfall er ikke samme sak).
|
||
Diskriminatoren mellom «nådde taket» og «ble kappet av taket» er SISTE ledgers
|
||
`is_request_satisfied` — samme felt orkestratoren selv forgrener på (`:1106`) — aldri
|
||
termineringsmeldingen, som er en inline f-string (`:1253`) uten konstant å pinne mot og som en modells
|
||
eget sluttsvar kan inneholde. **`speaker_known` sjekkes FØRST og slår alt annet:** en `next_speaker`
|
||
uten treff gir stille sluttsvar med NULL deltakerarbeid (`:1128-1131`), altså et plausibelt svar
|
||
ingen jobbet for (E2-klassen) — sløyfas egne funn holdes da tilbake, frøene ikke.
|
||
**Kontrakten nekter tre ting ved konstruksjon:** hvert av seks felt er PÅKREVD uten default (MAF
|
||
defaulter `max_round_count`/`max_reset_count` til ubegrenset, så et utelatt felt faller ikke tilbake
|
||
til noe forsiktig, men til dét `method-spec` §8 forbyr); `max_reset_count=0` nektes — **MÅLT**, ikke
|
||
resonnert: `reset_count >= max_reset_count` mot en teller som starter på 0 gjør at kjøringen
|
||
terminerer FØR første runde med kun `facts`+`plan`, null ledger-events og «maximum reset count», altså
|
||
en utforskning som utforsket ingenting, forkledd som en stall som aldri skjedde (`max_stall_count=0`
|
||
er derimot LOVLIG — strengt `>` gjør 0 til «reset ved første stallede runde»); og
|
||
`max_plan_revisions>0` med `enable_plan_review=False` nektes (en cap på en hendelse som ikke kan skje).
|
||
`max_plan_revisions` finnes fordi A3 MÅLTE at en `revise` koster 2 manager-kall, **null** ledger-kall
|
||
og **null** runder og så spør PÅ NYTT — under rundetaket alene er en alltid-reviderende ekspert
|
||
ubundet forbruk under vakter som alle ser tilfredse ut. Ved cap: typet stopp, ALDRI en påtvunget
|
||
approve (repair av et menneskes beslutning er den verste sorten). **U14s tre utsatte events er
|
||
landet** (`plan_created`/`replanned`/`progress_ledger_updated` som span-events på ÉN
|
||
`exploration`-span) — emisjon er UBETINGET og «av» betyr at OTel kaster dem, samme form MAFs egen
|
||
instrumentering alt har; en flagget emitter ville vært en andre oppløsning av regelen `tracing.py`
|
||
eier. Uttalt ærlighets-grense: eventene registreres når event-strømmen foldes, så REKKEFØLGEN er
|
||
tro og tidsstemplene er ikke øyeblikkene manageren handlet.
|
||
**Load-bearing MÅLT** (`tests/test_explore_loadbearing.py`, 32 tester), tolv mutasjoner alle røde mot
|
||
HELE suiten + grønn kontroll 975/5: detach rundetak-oversettelsen (1 rød) · test rundetaket FØR
|
||
tilfredsstillelse (1 rød — den motsatte feilen, som gjør en fullført utforskning til en budsjettfeil) ·
|
||
detach ukjent-taler-sjekken (1) · `BudgetMiddleware` av manageren, deltakerne beholder den (1) ·
|
||
detach revisjons-capen (1) · dropp frø-bevaringen (3) · la en ukjent-taler-kjøring levere funnene
|
||
videre (1) · detach alle tre span-events (1) · tillat `max_reset_count=0` (1) · gjør en merket-men-
|
||
uleselig hypotese tolerant (1) · la `read_bundle` skrive i basen den leser (1) · send spans til OTels
|
||
default-sink (2). **TO av dem FALSIFISERTE testen først, og begge er repoets vakuøs-gate-klasse:**
|
||
(i) skrivefrihets-testen drev kun `explore()`, men en `ScriptedChatClient` returnerer TEKST og
|
||
emitterer aldri et verktøykall — så ingen scriptet kjøring når en verktøykropp, og hele lesesømmen
|
||
(eneste sted en skriving realistisk kan komme fra) lå utenfor gaten; testen kaller nå hvert verktøy
|
||
DIREKTE. (ii) stdout-testen brukte `capsys`, men `ConsoleSpanExporter`s `out`-default bindes når
|
||
`opentelemetry.sdk.trace.export` FØRST importeres — under pytest er det stdout ved COLLECTION, som
|
||
`capsys` aldri ser; spans lå faktisk på stdout mens asserten var grønn. Dét er ikke en test-quirk å
|
||
omgå, det er nøyaktig faktumet U14 finnes for, og arven er P4-presedensen: **subprosessen er
|
||
målingen**. Begge armene kjøres nå i et barn (av: null trace-data noe sted; `PORTFOLIO_OTEL=console`:
|
||
spanet + `progress_ledger_updated` på **stderr** og stdout tomt), med `EXPLORATION-OK` på stderr som
|
||
kontroll — uten den ville «stdout var tomt» vært like sant om et barn som krasjet ved import.
|
||
**Ærlighets-grenser, uttalt:** multi-base-dispatch (`Approach.bundle_id`, § C.7) venter til
|
||
`run_project` tar mer enn én `bundle_dir` — å shippe feltet før konsumenten er en form gjettet i
|
||
stedet for målt; `quick_validate`-dommene hypotesiseren så bor ikke i `ExplorationResult`, de er nivå
|
||
1 og hører hjemme i `{run_id}-exploration.json` som CLI-wiringen skriver; utforskningsrollene løses
|
||
via `resolve_model`s `default`-fallback til en operatør mapper dem eksplisitt; at en LEVENDE modell
|
||
kaller verktøyene er ikke bevist offline (samme klasse som structured-output-grensen).
|
||
- **Utforskningens KALLSTEDER: sporet er kaller-eid, og whitelisten ble en TREDELING (økt 57):**
|
||
`--explore "<prompt>" --explore-config FILE` i `run.py`, `explore_prompt` + `explore_contract` på
|
||
den hostede flaten, og `simulate_exploration` som et TREDJE sim-scenario — alle opt-in, alle over
|
||
den uendrede sløyfa. **`ExplorationTrace` er en KALLER-EID akkumulator (funn-1-sinken, ett lag
|
||
opp), og formen er tvunget av en måling, ikke valgt:** `explore()` raiser `BudgetExceeded` på
|
||
rundetaket og tokentaket fyrer fra middleware midt i løpet — på BEGGE stier konstrueres aldri et
|
||
`ExplorationResult`, mens § C.2 krever at artefaktet er lesbart «uansett hvilken vakt som fyrte».
|
||
Steg-5-regelen («returverdi, ALDRI en out-parameter») styrer en verdi som NÅR kalleren; her gjør
|
||
den ikke det, og å kopiere regelen blindt ville gjenoppbygd defekten den ble skrevet mot.
|
||
`ExplorationResult.ledger_log`/`.plan_reviews` BYGGES FRA akkumulatoren (`tuple(trace.ledger)`),
|
||
aldri ved siden av — to beholdere om ett faktum er kø-(p). `{run_id}-exploration.json` skrives fra
|
||
en **`finally`** (`write_parse_failures`-presedensen) via `explore.trace_payload` → `outbox`s
|
||
plain-mapping-skriver (RAW-laget forblir MAF-fritt); **`completed` er et EGET påkrevd felt**, fordi
|
||
en `stop: null` som betyr BÅDE «avsluttet normalt» og «vi fikk aldri vite» er stillheten
|
||
`cost_baseline_anchored` ble påkrevd for å lukke. **Åtte CLI-nekter, alle ved navn**, hvorav to
|
||
bærer en beslutning: (i) `--explore` + `--mandate` er TO KILDER TIL ETT MANDAT og NEKTES, aldri
|
||
slås sammen — `explore()` tar objective fra prompten og hardkoder `allow_own_proposals=True`, så
|
||
komposisjon ville stille overskrevet tre felt operatøren skrev selv; nekten NAVNGIR
|
||
biblioteksdøra (`seed_approaches`), fordi § C.6 dør 1 er et ekte behov flaten ikke betjener.
|
||
(ii) `enable_plan_review=true` nektes på BEGGE flater FØR `explore()` kalles, og det er en
|
||
TYPE-måling: `ExplorationError` er en `RuntimeError` og ligger utenfor `main()`s
|
||
`(ValueError, FileNotFoundError, ValidationError)`-tuppel og utenfor hostings 400-arm, så å
|
||
overlate den til sløyfa ville gitt traceback på CLI-en og 500 — krasj-kanalen — på HTTP.
|
||
(`TracingConfigError` er derimot en `ValueError`; 400-armen dekket den alt.) Etter nektene er hver
|
||
konfig-formet `ExplorationError` UNÅBAR fra begge inngangene ved konstruksjon; det som fortsatt kan
|
||
slippe ut (uleselig merket hypotese, uttømt budsjett) er RUN-en som feiler, ikke kalleren som tar
|
||
feil. **Hostings whitelist er nå `_REQUIRED` / `_OPTIONAL` / `_CONSUMED`:** utforskningsfeltene er
|
||
IKKE `run_project`-parametre, så Fase 4e-beviset fikk en NEGATIV halvdel — hvert videresendt felt
|
||
MÅ finnes i `inspect.signature(run_project)`, hvert konsumert felt MÅ ikke; uten den ville et felt
|
||
som glir fra konsumert til videresendt vært nøyaktig driften 4e finnes for. **Demo-scenarioet er
|
||
nåbart ved NAVN og bare der** (`main()` kaller det ikke, og at golden-transkriptet er byte-uendret
|
||
etter at det ble lagt til ER målingen av det), med en **vakuitets-vakt**: en label kunnskapsbasen
|
||
ALLEREDE oppgir refuseres, fordi den ville nådd hypotese-prompten som ordinær kontekst enten
|
||
utforskningen kjørte eller ei — `simulate_learning_loop`s to-markør-vakt i demo-form. Manager-
|
||
manuset nøkles på PROMPT-STADIET, ikke prosjekt-ID-en, og det er ikke et unntak fra
|
||
`scripted_proposer`-regelen: manageren får FEM ulike spørsmål og prosjekt-ID-en er konstant over
|
||
alle fem. **`--outbox-dir` uten `--run-id` NEKTES i utforskningsblokka, og det er en HOIST — ikke
|
||
en andre kopi av regelen:** `run_project` eier outbox-kontrakten og nekter på sin FØRSTE setning,
|
||
tidlig nok for enhver sti som fantes før U4, men utforskningen kjører FORAN det kallet — uten
|
||
hoisten brukes hele utforskningsbudsjettet på modellkall før nekten, og artefakt-skrivingen hoppes
|
||
over, så ikke engang regnskapet over hva som ble brukt overlever. Funnet i review FØR commit;
|
||
testen asserterer at INGEN modellkall skjedde, ikke bare at rc er 1 — ved exit-koden ser en nekt
|
||
etter forbruket identisk ut. **Scenarioet har BEVISST intet `label_in_bundle`-felt:** vakten
|
||
raiser før et resultat finnes, så feltet kunne kun vært `False`, og en assert på det ville vært
|
||
grønn mot enhver implementasjon — vakten ER kontrollen, og en alltid-sann gjentakelse av den ville
|
||
bare gjort den ekte lettere å avfeie. Load-bearing MÅLT
|
||
(`tests/test_explore_callsites_loadbearing.py`, 24 tester), sytten mutasjoner alle røde mot HELE
|
||
suiten, hver mot kontrollen som gjaldt da (990/5 for CLI-en + sporet, 996/5 for hosting, 998/5 for
|
||
sim-scenarioet, 999/5 for de to siste): detach sink-appenden (1) · andre liste
|
||
for rundene (4) · detach `--mandate`-nekten (1) · detach `--explore-config`-nekten (1) · skriv
|
||
artefaktet kun ved fullført kjøring (1) · detach CLI-ens `mandate=` (1) · slipp
|
||
`enable_plan_review` gjennom, CLI (1) · detach `--bundle-dir`-kravet, CLI (1) · detach
|
||
`--live-dry-run`-nekten (1) · fjern `--explore` fra portefølje-partisjonen (1) · videresend de
|
||
konsumerte feltene (2) · detach hostings `mandate=` (1) · detach `enable_plan_review`-nekten,
|
||
hosting (1) · detach `bundle_dir`-kravet, hosting (1) · detach sim-scenarioets `mandate=` (1) ·
|
||
detach vakuitets-vakten (1) · detach outbox/run-id-hoisten (1). **ÉN MUTASJON FALSIFISERTE TESTEN FØRST (repoets vakuøs-gate-klasse,
|
||
syvende gang):** portefølje-testen asserterte kun at meldingen nevnte `--explore`, og sto GRØNN
|
||
uten partisjonen — kjøringen falt da gjennom til «`--explore` requires `--bundle-dir`», som nevner
|
||
`--explore` også. To nekter som deler en delstreng er «assert aldri på ordlyd to grener deler»,
|
||
fanget av sin egen mutasjon; testen navngir nå `--portfolio`. **Ærlighets-grenser, uttalt:**
|
||
multi-base (`Approach.bundle_id`, § C.7) er FORTSATT ikke bygget — `run_project` tar én
|
||
`bundle_dir`; utforskningens egne modellkall er UANNONSERTE (annonseringens kontrakt er at en
|
||
KOMMISJON erklæres før arbeidet den bestiller, og før `explore()` returnerer finnes ingen —
|
||
`exploration_notice` dekker gapet i det sløyfa er ferdig); `BudgetExceeded` ut av `--explore`
|
||
tracebacker som den gjør for debatten i dag; og **den HOSTEDE flaten gir ingen innsyn i hva som
|
||
formet mandatet** — det er ingen outbox der og intet utforskningsfelt i `_response_payload`, så
|
||
ledgeren og de rådgivende dommene når kun CLI-ens artefakt. En bevisst scope-grense, men uttalt,
|
||
fordi flatens hele argument er at svaret er etterprøvbart.
|
||
- **Multi-base er en PARTISJON, aldri en videre `run_project`-signatur (U4+U13 del 3, § C.7, økt 58):**
|
||
planens § C.7 og økt 56s egen ærlighets-grense leste som om leveransen var «`run_project` tar mer
|
||
enn én `bundle_dir`». **Den kan ikke det, og nekten er STRUKTURELL:** på bundle-stien avleder
|
||
`run_project` FIRE enkeltverdier fra DEN basen — prosjektet (`_project_from_bundle`, som
|
||
fail-faster når basens egen `validator-input.json` ikke navngir det forespurte prosjektet),
|
||
validatorens stage-0-baseline (S4.0s hele poeng er at gaten er forankret i DETTE prosjektets
|
||
kostlinjer), agentenes lesekontekst og ExpeL-nøkkelen — og returnerer ETT stemplet `RunResult`.
|
||
En andre katalog på den signaturen ville tvunget et stille velg-en for alle fire, som er den
|
||
gjettede-form-klassen repoet nekter. **Planens egen setning sier det samme lest nært:**
|
||
«pipelinen kjøres per bundle som i dag (`run_portfolio`-formen)» = N kall, ikke ETT kall med N.
|
||
Premisset ble felt FØR bygging; ordren ba selv om nettopp den sjekken. **Konsekvensen er at INGEN
|
||
eksisterende kaller endrer signatur** — CLI, hosting og simulation sender fortsatt én base hver,
|
||
og kan fortsatt gjøre det. Tre sømmer: (1) `mandate.Approach.bundle_id`, default `""`, så hvert
|
||
mandat skrevet før i dag er fortsatt gyldig OG dispatchbart uendret; (2) `mandate.route_by_bundle`
|
||
— ren partisjon i `bundle_ids`-rekkefølge (aldri i approach-rekkefølge: spend-ordenen er en
|
||
egenskap ved hvordan kjøringen ble konfigurert, ikke ved hvordan en modell tilfeldigvis sekvenserte
|
||
hypotesene), **fail-fast på et mandat som ikke kan utføres som skrevet** (`load_mandate`-regelen —
|
||
en kjøring skal aldri gå videre på en stille degradert bestilling); (3)
|
||
`run.run_mandate_across_bundles` — dispatchen. **Den tar INGEN `project_id`-parameter, og det er
|
||
designet:** hver bases prosjekt leses fra DEN basens egen IR-projeksjon, altså nøyaktig verdien
|
||
`_project_from_bundle` allerede fail-faster mot, så en kaller-oppgitt konstant kunne uansett bare
|
||
vært riktig for én base av N — den eksisterende fail-fasten blir rutingsnøkkelen, og gjetningen
|
||
forsvinner. Ett `VerdictStore` trådes på tvers (kryss-base-læring, `run_portfolio`-formen), og
|
||
**delt INSTANS er påstanden — ikke lik verdi** (se vakuitets-funnet under). En base ingen approach
|
||
navngir kjøres IKKE (en kjøring koster penger, og bestillingen ba om ingenting der); med NØYAKTIG
|
||
én base absorberer den alt uten navn, som ikke er en gjetning men det eneste mulige svaret — og
|
||
det er dét som holder hvert pre-multi-base-mandat dispatchbart. `explore()` stempler `bundle_id`
|
||
på hver MYNTET approach, men **skriver ALDRI om et frø** (§ C.6 dør 1 er en bevaringsregel — å
|
||
fylle inn feltet på ekspertens vegne ville satt deres navn på en rutingsbeslutning de ikke tok);
|
||
frøene VALIDERES i stedet, **FØR første modellkall** (økt-57-hoisten: ved unntaket alene ser en
|
||
nekt etter forbruket identisk ut med en før). En umerket markør med flere baser NEKTES
|
||
(`HypothesisParseError`), med én base resolveres den. **Budsjett: de to S3.4-tennene som HAR
|
||
mening her** — oppstartsnekt (`BudgetRefused`) og aldri-startet + `budget_stop` +
|
||
`not_evaluated`-rader i `MultiBaseResult.unreached`; bølge-reservasjonen har ingen motpart, for
|
||
dispatchen er SEKVENSIELL. **Ærlighets-grenser, uttalt:** en base som RAISER propagerer
|
||
(collect-and-continue tilhører `run_portfolio`, der kalleren sendte inn en batch uavhengige
|
||
prosjekter); uten `portfolio_meter` er taket antall rutede baser × `max_tokens`, hver kjøring
|
||
bundet for seg; outboxen er IKKE wiret (N kjøringer trenger N `run_id`-er, og å mynte dem her
|
||
ville defaultet en nøkkel repoet krever at en kaller oppgir); og **CLI-en er BEVISST urørt** —
|
||
§ C.8 ber om ETT nytt kallsted i `run.py` (`--explore`, levert i 57), og et repeterbart
|
||
`--bundle-dir` er en NY operatørflate, altså en egen beslutning. Load-bearing MÅLT
|
||
(`tests/test_multibase_loadbearing.py`, 22 tester), tolv mutasjoner alle røde mot HELE suiten +
|
||
grønn kontroll 1020/5 og golden `demo-transcript.stdout` BYTE-UENDRET
|
||
(`shasum -a 1` = `ea8c534773acdbe41ae68f2c55724d69aaf8be4f`): detach myntet `bundle_id` (2 røde) · stille
|
||
gjennomfall ved >1 base (1) · ukjent id resolvert etter rekkefølge (1) · frø-sjekk etter forbruket
|
||
(2 — asserten er på at NULL modellkall skjedde, ikke på unntaket) · ruteren gjetter første base (1)
|
||
· uroutbar approach droppet (1) · dispatchen kollapser til én base (5) · `project_id` fra første
|
||
base (2) · detach aldri-startet-tannen (1) · `unreached` urapportert (1) · fersk store per base (1)
|
||
· detach oppstartsnekten (2). **ÉN MUTASJON FALSIFISERTE TESTEN FØRST (repoets vakuøs-gate-klasse,
|
||
åttende gang):** store-testen sammenlignet med `==`, og `VerdictStore` er en pydantic-modell med
|
||
VERDI-likhet — tre ulike TOMME stores er alle like, så «fersk store per base» lot HELE suiten stå
|
||
grønn. Delt instans er påstanden, så testen asserterer nå på `is`.
|
||
- **«Be om svar, BRUKE svarene» er nåbar fra CLI-en, og gaten er den ANDRE halvdelen (F4, økt 63):**
|
||
før dette nektet BEGGE operatørflatene `enable_plan_review` (`run.py`, `hosting.py`) og eneste dør
|
||
var `explore(..., plan_reviewer=...)` — MÅLT mot kilden, ikke lest ut av reviewens prosa.
|
||
`--plan-review` bygger en `terminal_plan_reviewer()` og gir den til den UENDREDE sløyfa: operatøren
|
||
vises planen og svarer `approve` eller `revise <hva>`; en revisjon går tilbake til manageren, som
|
||
replanlegger og spør IGJEN om den NYE planen. **Diskriminatoren er dét siste** — en dør som printer
|
||
planen, leser linja og kaster den består «operatøren ble spurt» og feiler målbildet (repoets
|
||
vakuøs-gate-klasse); T1 er derfor bygget som `test_explore_loadbearing`s T15 løftet til CLI-nivå og
|
||
er RØD mot en alltid-godkjenn-reviewer. **Vitnet er `{run_id}-exploration.json`, ikke skrapet
|
||
stdout:** `trace_payload` bærer alt tre (rekkefølge, beslutning, feedback verbatim) og skrives fra
|
||
en `finally`, så den ene kjøringen som mest trenger beviset — den et tak eller en ubesvart review
|
||
kappet — etterlater det. **Fail-closed på operatørens EGEN input:** alt utenfor det lukkede
|
||
vokabularet spørres på nytt (aldri lest som en beslutning), og **EOF raiser `PlanReviewInputError`**
|
||
— å lese stillhet som ja ville latt en autonom sløyfe kjøre på en plan ingen signerte, usynlig.
|
||
Strømmene resolveres ved KALL-tid (`shared_root()`-idiomet), ellers svarer reviewer-en fra strømmen
|
||
som fantes da den ble BYGGET. **Fire nekter, alle ved navn**, hvorav to lukker et stille dropp
|
||
ingen test dekket: `report_forbidden` (report-modus returnerer FØR hver utforsknings-nekt) og
|
||
portefølje-partisjonen. De to konfig-avhengige nektene DELER tokenet `enable_plan_review` og har
|
||
derfor bevisst ULIK særtekst («no reviewer was offered» / «no review is ever requested») — den
|
||
eksisterende testen asserterte på det delte tokenet og er rettet (økt-57-mutasjonen, niende gang).
|
||
**Hosting NEKTER fortsatt, og det er en beslutning:** reviewen er synkron, så den ville blokkert
|
||
HTTP-requesten på et menneske OG event-løkka som svarer `/readiness` — meldingen navngir nå
|
||
CLI-døra i stedet for å påstå at biblioteket er den eneste (Fase 3-klassen). **Mid-løp-spørsmål er
|
||
IKKE bygget, og fraværet er MÅLT:** `_magentic.py` har nøyaktig ETT `ctx.request_info` (`:1044`,
|
||
plan review) i hele modulen, så stacken kan ikke levere et spørsmål midt i løpet uten en ny
|
||
emitter. Reviewens «kun plan-review FØR løpet» er derimot upresist: samme forespørsel fyrer også
|
||
ved re-plan etter en stall (`is_stalled=True`), så døra ER nåbar midt i en kjøring på den ene
|
||
måten stacken støtter. Load-bearing MÅLT (`tests/test_plan_review_cli_door_loadbearing.py`, 12
|
||
tester), elleve mutasjoner alle røde mot HELE suiten + grønn kontroll 1040/5 og golden
|
||
`demo-transcript.stdout` BYTE-UENDRET (`shasum -a 1` = `ea8c534773acdbe41ae68f2c55724d69aaf8be4f`): detach
|
||
`plan_reviewer`-wiringen (5 røde) · EOF blir en godkjenning (1) · alltid-godkjenn (3) · alt som
|
||
ikke er en revisjon blir en signatur (1) · dropp `--plan-review` fra portefølje-partisjonen (1) ·
|
||
dropp den fra `report_forbidden` (1) · detach `--plan-review requires --explore` (1) · detach
|
||
review-uten-reviewer-nekten (2, hvorav én i en test som fantes fra før) · detach
|
||
reviewer-ingen-spør-nekten (1) · hostet nekt beholder påstanden fra før F4 (1) · strømmene fanget
|
||
ved bygge-tid (1).
|
||
- **Plan-reviewen kan besvares over DAGER, og det eneste som krysser prosessgrensen er DISK (U12 +
|
||
asynkron U13, planens § D.2 rad 3, økt 64):** F4 gjorde «be om svar, BRUKE svarene» nåbar, men
|
||
bare SYNKRONT — `terminal_plan_reviewer` blokkerer løkka på et menneske ved en terminal, så
|
||
svaret må komme mens prosessen lever. `--checkpoint-dir` PARKERER i stedet reviewen
|
||
(`FileCheckpointStorage` + `{run_id}-plan-review.json`), og `--resume <run_id>` leser svaret fra
|
||
`--review-inbox` i en prosess som ALDRI så kjøringen. **MÅLT FELLE (ansikt 4):**
|
||
`list_checkpoints` (`_checkpoint.py:386-388`) svelger en blokkert deserialisering til en
|
||
`logger.warning` og returnerer TOM liste — uten BEGGE
|
||
`MagenticPlanReviewRequest`/`…Response` i `allowed_checkpoint_types` feiler en resume som et
|
||
FRAVÆR, ikke som en feil, og en test som asserterte «listingen er tom, altså er det ingenting å
|
||
gjenoppta» ville vært GRØNN mot nøyaktig den defekten. `_ALLOWED_CHECKPOINT_TYPES` har derfor ÉN
|
||
kopi og `checkpoint_storage` er ENESTE konstruksjonssted (BEGGE prosesser må deklarere dem; en
|
||
andre kopi er kø-(p)-driften). **Vi er LOUDERE enn rammeverket der det tier:** en tom listing ved
|
||
park raiser `CheckpointUnreadable` i stedet for å skrive et spørsmål ingen kan besvare.
|
||
**Diskriminatoren er den ANDRE halvdelen:** en dør som skriver en spørsmålsfil og en resume som
|
||
leser en svarfil består begge «eksperten ble spurt» — så måltesten krever at et `revise` skrevet
|
||
dag 1 får manageren til å REPLANLEGGE og stille et NYTT spørsmål (indeks 1, nytt `request_id`) i
|
||
en fersk interpreter, med en approve-kontroll som beviser at døra også kan AVSLUTTE (en gate som
|
||
bare kunne parke igjen er en hengning i løkkeklær). **Budsjettet og revisjons-capen spenner over
|
||
suspensjonen:** `meter.charge(parked.tokens_spent)` (gjennom `charge`, ikke ved å sette `tokens`
|
||
— ladingen re-tester taket) og `trace.ledger.extend(parked.ledger)`, ellers får hver park et helt
|
||
budsjett på nytt: S3.4-klassen, ubundet forbruk under vakter som alle ser tilfredse ut. En
|
||
`revise` koster to manager-kall, emitterer null ledger og bruker null runde (§ F, A3), så
|
||
`max_plan_revisions` er det ENESTE båndet på den. **Fail-closed på ekspertens EGEN fil:**
|
||
`request_id`-mismatch, ord utenfor vokabularet og `revise` uten innhold refuseres alle ved navn.
|
||
`hitl.pending_plan_reviews` er registeret over hvem som fortsatt venter — tolerant på LESE-siden,
|
||
fail-closed på BESLUTTE-siden, og joinen er på `request_id` i BEGGE ender (to distinkte sømmer,
|
||
MÅLT: hver har sin egen mutasjon og sin egen røde test). Load-bearing MÅLT
|
||
(`tests/test_async_plan_review_loadbearing.py`, 17 tester), tretten mutasjoner alle røde mot HELE
|
||
suiten + grønn kontroll 1059/5 og golden `demo-transcript.stdout` BYTE-UENDRET
|
||
(`shasum -a 1` = `ea8c534773acdbe41ae68f2c55724d69aaf8be4f`): tom `_ALLOWED_CHECKPOINT_TYPES` (11 røde) · park
|
||
uten checkpoint (1) · resume alltid-approve (2) · tolerant `request_id` (1) · tolerant vokabular
|
||
(1) · `revise` uten feedback (1) · detach `meter.charge` (1) · detach
|
||
`trace.plan_reviews.extend` (1) · detach ledger/hypotese-carry-overen (1) ·
|
||
`pending_plan_reviews` ignorerer `request_id` (1) · detach to-dører-nekten (1) · detach
|
||
outbox/run-id-hoisten (1) · detach `--resume`-armen i `required_scripted_roles` (4 — MAJOR-2s
|
||
`KeyError: 'navigator'` på den andre flaten som bygger en utforskning).
|
||
**ÉN MUTASJON FALSIFISERTE SUITEN (repoets vakuøs-gate-klasse, TIENDE gang):** `trace.plan_reviews
|
||
.extend(parked.plan_reviews)` kunne detaches med HELE suiten grønn (1058/5) — capen leser
|
||
`parked.plan_reviews` DIREKTE, så den binder uansett, og de to første legene er identiske under
|
||
begge implementasjoner. Gaten måtte derfor bli det TREDJE leget, der artefaktet ellers taper dag
|
||
1s revisjon og to ULIKE planer deler indeks 1; den nye testen er rød mot mutasjonen og alene.
|
||
**Ærlighets-grenser, uttalt:** den hostede flaten NEKTER fortsatt (en synkron review ville
|
||
blokkert både requesten og event-løkka som svarer `/readiness`); en park MIDT i løpet (etter en
|
||
stall) har ingen nåbar sti under det skriptede manuset, så carry-overen som betjener den drives
|
||
gjennom en CRAFTED parkert tilstand (`budget_stop`-presedensen); og resume-legets
|
||
`PlanReviewParked` er et NORMALT utfall, ikke en feil.
|
||
- **Katalogkallet koster O(BASER), aldri O(KORPUS) — og det er stigens billigste trinn, ikke dens
|
||
dyreste (ordre `20260825T213645Z`, økt 65):** `list_bundles` returnerte hele rot-indeksens body
|
||
for HVER konfigurert base samtidig, pluss ett JSON-objekt per ufulgt kryss-lenke. Begge vokser med
|
||
korpuset, så prisen på å finne ut *hvilke baser som finnes* ble satt av hvor mye de *inneholder* —
|
||
progressiv disclosure snudd på hodet (målbilde §2/§4). **MÅLT med `o200k_base`, instrumentet først
|
||
validert mot commons' egne fasittall:** 112 116 tokens over tre flate Vegnormal-baser, og
|
||
**124 942 over de 171 grenbasene** som erstattet dem — grenformen (`vegnormal-okf` `8145c23`)
|
||
lukket bundle-siden (−82…92 % på `read_bundle`) og gjorde katalogsiden VERRE, nøyaktig som det
|
||
repoet forutså. Etter: **362** og **21 448** (per base 37 372 → 121 og 731 → 125).
|
||
**Et premiss ble felt FØR noe ble bygget på det:** «indeksbodyen forteller hva basen handler om»
|
||
er USANT for maskin-importerte baser — grenbasenes `index.md` har verken frontmatter eller prosa,
|
||
den er en ren lenkeliste (målt: 959 bytes, første tegn `-`), så feltet var dyrt OG innholdsløst
|
||
der. **Fast vindu, aldri en andel av basen** (`_CATALOGUE_EXCERPT_CHARS = 200`): en andel skalerer
|
||
med korpuset igjen, bare med mindre konstant. **Avkorting ANNONSERES som FELT**
|
||
(`index_truncated` ved siden av utdraget, aldri en markør limt inn i det — `BudgetExceeded`s
|
||
kø-(y)-regel), og en base som PASSER blir ikke merket avkortet og får hele bodyen: omisjon, aldri
|
||
en løgn i noen av retningene. **En ufulgt lenke overlever som ANTALL** — økt 51s «et hopp er
|
||
tolerert, men ikke lenger taust» står, mens per-lenke-detaljen blir liggende der den er
|
||
handlingsbar (`RunResult.skipped_links` / `DryRunReport.skipped_links`) og ikke rir med i et kall
|
||
hvis hele jobb er å være billig. Hele indeksen er fortsatt ETT `read_file(id, "index.md")` unna —
|
||
et disclosure-nivå, ikke datatap. **Taket (500 tegn/base) bor i TESTEN, ikke i `explore.py`:** en
|
||
test som importerte implementasjonens budsjett ville flyttet seg med det, og å heve budsjettet er
|
||
nøyaktig regresjonen gaten finnes for. Load-bearing MÅLT
|
||
(`tests/test_catalogue_cost_loadbearing.py`, 7 armer), **ni mutasjoner alle røde mot HELE suiten**
|
||
+ grønn kontroll 1066/5 og golden `demo-transcript.stdout` BYTE-UENDRET
|
||
(`shasum -a 1` = `ea8c534773acdbe41ae68f2c55724d69aaf8be4f`): ingen binding (4 røde) · bundet men vakuøst (2) ·
|
||
stille kutt (1) · over-annonsert (1) · per-lenke-lista rir med igjen (1) · det ufulgte faktumet
|
||
slettet (1) · suffiks i stedet for ordrett prefiks (2) · en andel i stedet for fast vindu (2) ·
|
||
`documents` gjort konstant (1). **M9 ble kjørt fordi `documents` var et felt uten gate** — et felt
|
||
ingen test kan se, råtner. **MAJOR-1 var IKKE nødvendig:** bindingen sitter i verktøykroppen bak
|
||
en uendret CLI-flate. Ærlighets-grenser, uttalt: `navigate_bundle` kalles fortsatt per base per
|
||
katalogkall (I/O og veggklokke, ikke tokens — ikke målt her); ingen LEVENDE modell har kalt det
|
||
nye verktøyet, så at en manager velger BEDRE med et utdrag enn med hele indeksen er ikke bevist
|
||
(structured-output-grensens klasse); og ordrens nevner for N100:2023 var 34 mens disken viser 40 —
|
||
tallene bruker den målte nevneren. Måling: `docs/2026-08-26-katalogkostnaden.md`.
|
||
- **Et fravær ved en signeringsport sies i ORD; i dataene sies det ved å være borte (PM-tillegg 4,
|
||
økt 77):** `PlanReviewRequest.current_progress` og `ParkedExploration.current_progress` ble begge
|
||
bygget med `str(review.current_progress)`. Verdien er en `MagenticProgressLedger | None` og er
|
||
`None` i hver kjøring målt så langt, så `str()` ga de fire tegnene `None`, rendererens
|
||
truthiness-vakt fant dem ikke-tomme, og eksperten som skulle SIGNERE en plan ble vist en
|
||
«progress so far»-seksjon hvis eneste innhold var ordet `None` — og de samme fire tegnene ble
|
||
skrevet inn i `{run_id}-plan-review.json`, det ENESTE som krysser prosessgrensen i U12s asynkrone
|
||
dør. `_progress_text` er den ENE konverteringen (ved siden av `_plan_text`): fravær blir tom
|
||
streng, aldri `"None"`. **Datalaget sier fravær ved å være tomt** (`Bundle.skipped`s
|
||
tom-tuppel-regel), mens **TERMINALEN sier det i ord** — det er den ene flaten der omisjon er
|
||
feil, fordi stillhet ved en port noen signerer er nøyaktig dét `PlanReviewInputError` alt nekter
|
||
for EOF. **Målingen endret testen:** å reversere BEGGE kallsteder lot KUN en kildeinspeksjons-arm
|
||
bli rød — en lint, ikke en gate, fordi de første armene konstruerer en `PlanReviewRequest` selv og
|
||
aldri går inn i noen av stedene. Hvert sted har nå et ATFERDSMESSIG vitne som driver den EKTE
|
||
døra: terminalen for den synkrone, den parkerte spørsmålsFILA for den asynkrone. Load-bearing
|
||
MÅLT (`tests/test_plan_review_progress_line_loadbearing.py`, 7 armer), fem mutasjoner alle røde
|
||
mot HELE suiten, hver med sin egen signatur + grønn kontroll 1202/5 og golden BYTE-UENDRET:
|
||
begge steder reversert (3 røde) · det synkrone alene (2) · det parkerte alene (2) · terminalen går
|
||
stille igjen (2) · plassholderen fyrer alltid og sluker en ekte ledger (1, kontrollen alene).
|
||
Recorder-wiringen i `resume_exploration` er URØRT (uvitnet, defensiv — S2b).
|
||
- **`evidence_for` avleder en tier KUN for nøkkelen SPEC §5.3 tierer (PM-tillegg 5, økt 77):**
|
||
`evidence_for(path, key="sources")` REISTE `ValueError`. Tieren ble avledet UBETINGET gjennom
|
||
`trust_tier`, som nekter en oppføring uten `by`-aktør — riktig, fordi en tillitsgrad avledet av
|
||
en oppføring som identifiserer ingen mynter den proveniensen den påstår å lese. Men `by` er
|
||
påkrevd av en VERIFIKASJONS-oppføring (SPEC §5.2), ikke av enhver provenienss-nøkkel: den avtalte
|
||
segmenterte formen bærer `segment_id`/`source_offset`, en `sources`-liste bærer `id`/`resource`.
|
||
Vakten tilhørende ÉN nøkkel ble anvendt på ALLE. **`trust_tier` er URØRT** — `_TIERED_KEY` navngir
|
||
den ene nøkkelen §5.3 tierer, og enhver annen nøkkel kommer tilbake med
|
||
`state`/`reason`/`items_seen`/`entries` og `tier=None`. **`admits_falsification` flyttet MED, og
|
||
det er samme faktum, ikke scope-krype:** den leste `tier != "unverified"`, og `None != "unverified"`
|
||
er SANT, så en `sources`-liste som FINNES ville klarert en terskel om verifisering; den navngir nå
|
||
tierene som klarerer. For hver verdi som var nåbar før endringen er de to skrivemåtene identiske —
|
||
derfor står de eksisterende K5-armene grønne, og derfor er dette en innstramming og ikke en ny
|
||
regel. **Målingen korrigerte testen:** en `verified`-verdi UTEN aktør når aldri `trust_tier`
|
||
gjennom `evidence_for` i det hele tatt — dekoderen nekter formen først, som
|
||
`unreadable`/`unsupported-flow`; begge vakter asserteres nå der hver av dem FAKTISK bor.
|
||
**Defekten fantes fordi parameteren aldri var ØVET:** alle 11 kallsteder brukte defaulten (målt
|
||
ved B4s slutt). Load-bearing MÅLT (`tests/test_evidence_key_parameter_loadbearing.py`, 6 armer),
|
||
fire mutasjoner alle røde mot HELE suiten + grønn kontroll 1208/5 og golden BYTE-UENDRET: avled
|
||
tieren ubetinget (3 røde) · reverter K5-terskelen (1, konsekvens-armen alene) · «fiks» det ved
|
||
aldri å tiere (3, inkludert to eksisterende falsifiserings-armer) · løsne `trust_tier` i stedet
|
||
(2, inkludert dens egen eksisterende gate).
|
||
- **`read_bundle` koster O(DOKUMENTER i én base), aldri O(bytes av den — S2c/MAJOR-3, økt 77):**
|
||
verktøyet returnerte `okf.bundle_context`, altså HELE den navigerte basen, og fordi utforskningens
|
||
deltakere deler ÉN samtalehistorikk red det ene `function_result`-et med i **hver senere prompt**
|
||
til full pris uten at noen ba om det igjen. **MÅLT FØRST, med nevner, og committet som docs før én
|
||
linje kode ble rørt** (`docs/2026-09-02-read-bundle-kontekstkostnad.md`): 3 861 / 10 406 / **12 595**
|
||
o200k-tokens for de tre eksempelbasene, **5 kopier** per CLI-`--explore`-kjøring (navigatør ×1,
|
||
manager ×3, hypotesiser ×1) = **54 % / 59 % / 59 %** av ALLE prompt-tokens. Instrumentet ble
|
||
validert mot en KJENT POSITIV før bruk (det reproduserte commons' egne publiserte
|
||
`bundle_context`-fasittall eksakt), og prompten måles som tekst + `function_call` +
|
||
`function_result` — `.text` alene måler en kontekstbærende prompt til noen få tegn.
|
||
**Formen er katalogens, ett trinn ned på stigen** (`list_bundles` = hvilke baser finnes,
|
||
`read_bundle` = hva er i DENNE, `read_file` = hva sier dokumentet): én oppføring per konseptfil med
|
||
`name`, `type`, `title`, `chars`. Etter: **259 tokens** på tunnelbasen, utforskningens
|
||
prompt-tokens −77 / −90 / **−91 %**. **Listen bygges av `Bundle.context_files`, ALDRI `files`** —
|
||
det er dén property som dropper `type: verdict`-laget (og nestede `index.md`) på hvert nivå, og en
|
||
liste bygget av `files` ville rutet tidligere dommer foran navigatøren UTENOM den gatede
|
||
ExpeL-folden mens hver kostnads-arm forble grønn. **Et premiss ble felt før noe ble bygget på det:**
|
||
«indeksbodyen er basens egen navigasjonsprosa, så den hører hjemme her» — tunnelbasens rot-indeks er
|
||
**alene 4 763 tegn ≈ 1 400 tokens**, altså nesten hele taket, for et felt katalogen alt gir et
|
||
bundet utdrag av og `read_file(id, "index.md")` fortsatt gir helt. **Taket bor i TESTEN**
|
||
(`_CEILING_CHARS = 1 500`), katalogtakets regel av katalogtakets grunn. **AVVIK fra ordren, uttalt:**
|
||
ordren ordlegger gaten i o200k-TOKENS; gaten bounder TEGN, fordi `tiktoken` ikke er en
|
||
prosjektavhengighet og en gate som skipper når en valgfri pakke mangler er en gate som kan være
|
||
stille fraværende — konverteringen er MÅLT (748 tegn / 259 tokens = 2,89 tegn/token), og ordrens
|
||
eget kriterium er verifisert direkte én gang av instrumentet. **«Ikke utløs» er bevist som en
|
||
MÅLING, ikke som en forsikring:** debattens tre `okf.bundle_context`-kopier er **byte-identiske før
|
||
og etter** i alle tre baser (12 047 / 32 567 / 39 104 prompt-tokens, hver prompt uendret) — sterkere
|
||
enn å påstå at `run.py` ikke ble rørt. **Verktøybeskrivelsen og navigatørens instruksjon er oppdatert
|
||
i samme trekk:** begge påsto «read its navigated context», og en beskrivelse som lyver om kroppen er
|
||
modellens instruks (Fase 3-klassen, påstander flaten gjør om SEG SELV). **To eksisterende asserts
|
||
ville blitt VAKUØSE i stillhet** (`read_bundle(...) != ""` er sant for enhver liste,
|
||
`test_explore_loadbearing.py` + `test_bundle_id_reconciliation_loadbearing.py`) og er styrket, ikke
|
||
latt stå. Load-bearing MÅLT (`tests/test_read_bundle_cost_loadbearing.py`, 6 armer), **sju mutasjoner
|
||
alle røde mot HELE suiten** + grønn kontroll **1 195/5** (fra 1 189/5, supersett, 0 fjernet) og golden
|
||
`demo-transcript.stdout` BYTE-UENDRET (`shasum -a 1` = `ea8c534773acdbe41ae68f2c55724d69aaf8be4f`): reverter sømmen
|
||
(7 røde) · bygg fra `files` (3) · bundet men VAKUØS tom liste (6) · dropp `chars` (2) · bær
|
||
indeksbodyen likevel (4) · `read_file` trunkerer (2 — inkludert katalog-gaten, et uavhengig vitne) ·
|
||
detach `reconcile_bundle_id` (1). **Ærlighets-grenser, uttalt:** dette er IKKE «−59 % kostnad» — en
|
||
navigatør som åpner *k* dokumenter betaler *k* `read_file`-resultater, og gevinsten er at den betaler
|
||
for det den VALGTE og at hvert resultat rir fra SITT kall og framover; at en LEVENDE modell velger
|
||
BEDRE med en liste enn med hele konteksten er IKKE bevist (structured-output-grensens klasse);
|
||
multiplikatoren 5 gjelder dette manuset (`misjonsreview-v2` § 4 målte 7× med et annet); debattens 3×
|
||
er PM-ens egen beslutning og er urørt; prefiks-caching (≈ 60 % debatt / ≈ 40 % utforskning cachebart
|
||
som koden står) er NOTERT, ikke bygget. **Et fravær som var instrumentfeil, ikke faktum:** første
|
||
ETTER-kjøring rapporterte 0 kopier på to baser — sonden var en midtskive som traff norske tegn
|
||
prompten serialiserer escaped; byttet til et ASCII-konseptfilnavn ga 5, som før.
|
||
- **Ekspertdommen kan ikke oppstå av STILLHET, og fraværet er en FØRSTEKLASSES tilstand (F2,
|
||
non-goal 3, økt 66):** `run_project` KREVDE `verdict_input` og kjørte `capture_verdict`
|
||
ubetinget, CLI-en defaultet det til `{"approved", "reviewed by expert"}`, og hosting listet det
|
||
som PÅKREVD. Netto: hver flaggløs kjøring myntet en ekspertgodkjenning ingen ga, den gikk inn i
|
||
den delte storen, og `run_portfolio` bar den inn i neste prosjekts hypotese-prompt som en
|
||
*prior expert verdict* — på flaten som ble overlevert 14.08. **`RunResult.verdict` er nå
|
||
`Verdict | None`**, og `None` er hva stillhet produserer: ingenting myntes, ingenting lagres,
|
||
ingenting varsles. Prinsippet sto allerede skrevet i repoet — `RunFailure`s docstring: å fylle et
|
||
felt med en dummy legger FABRIKKERT proveniens inn i aggregatet. **Traceability koster ingenting,
|
||
fordi nøkkelen DERIVERES fra kandidaten:** `RunResult.verdict_key` (property, ikke lagret felt —
|
||
en andre kopi av en nøklingsregel er kø-(p)) er `verdicts.verdict_key`s alt dokumenterte formål,
|
||
identisk med `verdict.id` når en dom BLE gitt, og fortsatt meningsfull når ingen ble det; det er
|
||
den outboxen og den hostede responsen stempler, så et artefakt fra en ukommentert kjøring er
|
||
fortsatt dømbart og joiner tilbake via Steg-7-innboksen. **Halv dom NEKTES på begge dører**
|
||
(`FeedbackContract` er ENESTE sted formen valideres, og CLI-en nekter ved navn FØR enhver
|
||
mode-dispatch): den manglende halvdelen er ekspertens å skrive, aldri vår å defaulte — validering,
|
||
ALDRI reparasjon (`write_concept_file`-presedensen). **Hosting er WIDENING, ikke bryting:**
|
||
`verdict_input` flyttet `_REQUIRED_FIELDS` → `_OPTIONAL_FIELDS`, så hvert kall som finnes ute
|
||
virker uendret; en kaller som utelot det fikk før 400 på et felt som ikke KUNNE fylles ærlig.
|
||
**De to mode-partisjonene fikk `--decision`/`--rationale` inn — og det er en KONSEKVENS, ikke
|
||
scope-krype:** kommentarene på begge stedene sa ordrett at en ærlig nekt var *uimplementerbar*
|
||
fordi de non-None argparse-defaultene gjorde en eksplisitt verdi uskillbar fra defaulten. Med
|
||
defaultene borte er den implementerbar, og «refused, never ignored» er partisjonens egen regel.
|
||
`run.verdict_notice` er ENESTE renderer og leser dommen av kjøringens EGET stempel, ikke av argv.
|
||
**Ærlighets-grense, uttalt:** referanse-fixturens SYNTETISKE `verdict_input`-rader står URØRT —
|
||
de er merket SYNTETISK på fire steder og er reviewens F5 (måling av misjonspåstanden), ikke F2;
|
||
`Project.verdict_input` er nå valgfri, så en rad UTEN dom er lovlig. Load-bearing MÅLT
|
||
(`tests/test_ungiven_verdict_loadbearing.py`, 15 armer), **åtte mutasjoner alle røde mot HELE
|
||
suiten** + grønn kontroll 1080/5 og golden `demo-transcript.stdout` BYTE-UENDRET
|
||
(`shasum -a 1` = `ea8c534773acdbe41ae68f2c55724d69aaf8be4f`): detach fangst-gaten (5 røde) · gjeninnfør
|
||
argparse-defaultene (22) · hosting krever fortsatt feltet (1) · CLI-en REPARERER en halv dom (2) ·
|
||
kontrakten reparerer en halv dom (1) · `verdict_key` lest av dommen i stedet for derivert (2) ·
|
||
begge partisjons-radene fjernet (2) · rendereren påstår en dom som aldri ble gitt (2). **ÉN
|
||
MUTASJON FALSIFISERTE TESTEN FØRST (repoets vakuøs-gate-klasse, ELLEVTE gang):** `--report`-armen
|
||
brukte et bart `--report`, som nekter med rc 1 uansett fordi `--ledger` mangler — testen sto
|
||
GRØNN med partisjons-raden fjernet. Den kjører nå mot en argv report-modus ellers ville AKSEPTERT
|
||
(gyldig `--ledger` + en kontroll som beviser rc 0 uten flaggene), så rc 1 er mutantens motsatte
|
||
utfall. **Migreringsnote:** ingen ekstern kaller brekker — hosting utvider, CLI-ens gamle
|
||
flaggform er uendret, og det som ENDRER seg er at en flaggløs kjøring nå SIER at ingen dømte i
|
||
stedet for å påstå `decision=approved`.
|
||
- **Planen et menneske signerer er TEKST, aldri en objekt-repr — og fraværet av repr er en EGEN,
|
||
bredere assert (BLOCKER-1, økt 67):** fire steder gjorde `str(review.plan)` på en MAF `Message`
|
||
som ikke har noen `__str__` (MÅLT: `type(Message).__str__ is object.__str__`), så BEGGE
|
||
HITL-dørene viste og lagret `<agent_framework._types.Message object at 0x…>`: terminalen F4
|
||
spør ved (`explore.py:1395`), de to opptakene i `plan_reviews` (`:1404` approve, `:1418` revise)
|
||
og den PARKERTE spørsmålsfila U12 (`:1478`) — det ENESTE som krysser prosessgrensen. En ekspert
|
||
som svarte `approve` signerte blindt. Fiksen er den eksisterende `_plan_text` (`:966`) på alle
|
||
fire; ingen ny hjelper. **Fire asserts var grønne mot defekten fordi de var TRUTHINESS eller
|
||
SELV-SAMMENLIGNING:** `reviews[1]["plan"] != ""`, `waiting[0].plan`, og
|
||
`reviews[0]["plan"][:40] in out` — den siste sammenlignet den samme repr-en med seg selv, så de
|
||
to flatene var enige mens begge var uleselige. Diskriminatoren er innhold som KUN kan komme av
|
||
`Message.text`: MAF komponerer plan-meldingen rundt managerens svar ORDRETT (målt), så en
|
||
sentinel i det skriptede svaret er til stede når teksten ble tatt og fraværende når repr-en ble
|
||
det. Sentinelen bor i et `reason`-felt fordi ingenting leser dem — å nøkle den til en ANSWER
|
||
ville endret kjøringen den måler. **`str(review.current_progress)` (`:1396`/`:1479`) er BEVISST
|
||
urørt:** den er en `MagenticProgressLedger | None`, ikke en `Message`, og pydantic renderer den
|
||
lesbart (målt) — men i disse kjøringene er den `None`, så terminalen skriver «progress so far:
|
||
None». Annen defekt, annen ordre. Load-bearing MÅLT
|
||
(`tests/test_plan_review_cli_door_loadbearing.py` + `tests/test_async_plan_review_loadbearing.py`),
|
||
fire mutasjoner — **ÉN PER LINJE**, fordi ordrens ene samlede revert undertestet og `:1418` er
|
||
revise-grenens egen kopi som ellers ikke hadde noe rødt vitne: `:1395` → 1 rød (stdout) ·
|
||
`:1404` → 2 · `:1418` → 1 (T1 alene) · `:1478` → 1 (async T9). Golden byte-uendret.
|
||
- **Generalprøven kan ÅPNE en base, og artefaktet sier hvilken (MAJOR-1, økt 67):** `--scripted-replies`
|
||
tok ÉN konstant streng per rolle, så ingen skriptet rolle kunne emittere et `function_call` —
|
||
MÅLT 0 verktøykall / 0 approaches / 1 runde på 4/4 baser, mens hvert annet felt i
|
||
`{run_id}-exploration.json` så ut som en kjøring som hadde virket. Måleprotokollens gratis trinn
|
||
(«bevis så mye som mulig før det dyre») kunne altså ikke bevise at navigatøren åpner noe, og
|
||
etter en BETALT kjøring kunne ingen lese om den gjorde det. To halvdeler, hver gatet så den ANDRE
|
||
ikke kan bære den. **(a) `ExplorationToolRecorder`** — en `FunctionMiddleware` på
|
||
utforskningsagentene som registrerer NAVN + `bundle_id`-argumentet i KALL-REKKEFØLGE på den
|
||
kaller-eide `ExplorationTrace`; `trace_payload` skriver det ved siden av `quick_validations`.
|
||
**SØSKEN av `mcp_tools.ToolCallRecorder`, aldri en gjenbruk:** den filtrerer til KONFIGURERTE
|
||
eksterne verktøy, dedupliserer per `(server, tool)` og returnerer SORTERT, fordi dens rad er en
|
||
egress-påstand; denne beholder rekkefølge uten dedup, fordi et sortert dedupet sett ikke kan
|
||
skille en generalprøve som LESTE en base fra en som bare listet dem. Å generalisere den ene til å
|
||
tjene begge ville brutt den andre. RESULTATET registreres ALDRI — det er basens innhold, altså
|
||
nettopp det MAJOR-3 målte til 73–93 % av alle prompt-tokens. Wiret i BEGGE workflow-byggene
|
||
(`explore()` OG `resume_exploration()`) — men **resume-armen er DEFENSIV og UVITNET, og det er
|
||
målt, ikke antatt** (`budget_stop`-presedensen): å detache KUN den andre forekomsten lot hele
|
||
suiten stå grønn (1087/5), fordi ingen park/resume-scenario under det skriptede manuset når en
|
||
verktøykropp. Den står fordi et gjenopptatt leg ellers ville registrert null verktøykall — samme
|
||
stille gap som `plan_reviews.extend` i økt 64 — ikke fordi en test holder den.
|
||
**(b) et trinn-MANUS** — en utforskningsrolle kan få en LISTE der et trinn er tekst eller ETT
|
||
`{"call": "<tool>", "args": {…}}`. Formen utvider den ENE kanoniske `_inner_get_response`-kroppen
|
||
(S2.5), aldri en andre klient: manuset ligger VED SIDEN AV selektoren (en selektor returnerer
|
||
`str` ved kontrakt, og et verktøykall er ikke tekst), og et utløpt manus faller tilbake på
|
||
selektoren — degradering til konstantformen, ikke til stillhet. Listeformen NEKTES for debattens
|
||
roller ved navn, og hvert malformet trinn nektes ved navn (validering, ALDRI reparasjon).
|
||
**Å skripte navigatøren er IKKE nok, og det er MÅLT:** med en KONSTANT manager konkluderer
|
||
kjøringen etter én runde uten å ha gitt noen deltaker en tur, så `tool_calls` forblir tomt
|
||
uansett hva navigatøren fikk. Manageren må selv få et trinn-manus — én tekst per stadium (fakta,
|
||
plan, ledger, ledger, sluttsvar), samme grunn `simulation._exploration_manager_reply` nøkler på
|
||
PROMPTEN og ikke prosjekt-ID-en: manageren får fem ULIKE spørsmål og én konstant svarer alle fem
|
||
med første rundes ledger. Kontroll-testen med konstant navigatør (og manager i stadier) er dét
|
||
som holder de to formene fra hverandre.
|
||
Load-bearing MÅLT (`tests/test_scripted_explore_door_loadbearing.py`, 12 armer), **ni mutasjoner
|
||
alle røde mot HELE suiten** + grønn kontroll 1087/5 og golden BYTE-UENDRET: detach recorderen
|
||
fra begge byggene (1) · dropp `tool_calls` fra `trace_payload` (3) · sorter+dedupliser sinken (2)
|
||
· registrer navnet uten basen (3) · nekt listeformen igjen (2) · aksepter lista men emitter tekst
|
||
(1) · toler et malformet trinn (1) · la en debattrolle ta listeformen (1) · dropp
|
||
ukjent-nøkkel-whitelisten (1). **ÉN MUTASJON FALSIFISERTE TESTEN FØRST (repoets
|
||
vakuøs-gate-klasse, TOLVTE gang):** nekt-testen brukte `{"invoke": …}` og sto GRØNN med
|
||
forms-sjekken detached — UKJENT-NØKKEL-grenen fanget den i stedet, så nekten under test hadde
|
||
intet vitne. To grener, to armer, to mutasjoner. Ærlighets-grense, uttalt: at en LEVENDE modell
|
||
kaller verktøyene er fortsatt ikke bevist (structured-output-grensens klasse) — dette gjør den
|
||
GRATIS halvdelen av stigen ekte, ikke den betalte.
|
||
- **MAF-pinnen er en TRIPWIRE, ikke en frys — og det som gjorde løftet farlig var IKKE en form som
|
||
forsvant (F15, 02.09, core 1.9.0 → 1.16.0, orchestrations 1.0.1 → 1.1.1):** de to kan ikke løftes
|
||
hver for seg (orchestrations 1.1.1 krever selv `core>=1.15.0`). Gulvet bor i ÉN konstant
|
||
(`_MIN_VERSION` i `tests/test_maf_version_guard.py`) 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. **17 private/ugaranterte former, derivert fra repoets
|
||
EGNE siteringer** (hver `_modul.py:linje`-referanse i `src/` + hver form en søm overstyrer eller
|
||
konstruerer); **16 sjekket MEKANISK av en probe mot begge versjoner (1 endret), den 17. funnet av
|
||
TESTSUITEN (endret) — 2 endret totalt.** Splitten er ikke pedanteri: «2 av 16» ville tilskrevet
|
||
proben et funn den ikke gjorde, og skjult at proben sjekker statiske egenskaper ved KILDEN mens den
|
||
farlige endringen var en egenskap ved KJØRINGEN — nevner-disiplin anvendt på mitt eget instrument. Kjent-positiv: `MiddlewareFailure`
|
||
(ny i 1.15.0) flippet NO → YES, altså KAN proben oppdage endring; `_compaction.py`s
|
||
`@experimental`-markører ble FORKASTET som kjent-positiv fordi de teller 0 i BEGGE versjoner og
|
||
derfor ikke diskriminerer noe. **Den farlige endringen var den ordren navnga — formen som fortsatt
|
||
importerer, men har flyttet semantikk i stillhet:** en park skriver nå **TO** checkpoints og bare
|
||
ÉN bærer plan-review-typen, så en feildeklarert `_ALLOWED_CHECKPOINT_TYPES` **tømmer ikke lenger
|
||
listingen** — den taper nøyaktig den checkpointen som betyr noe, `get_latest` returnerer den ANDRE,
|
||
og `_park`s `latest is None`-vakt passerte mens kjøringen svarte `rc=0` og skrev et spørsmål som
|
||
aldri kan bære svaret. Vakten sjekker nå EGENSKAPEN den alltid mente (`str(request.request_id) in
|
||
latest.pending_request_info_events` — et DEKLARERT felt) i stedet for symptomet som pleide å
|
||
innebære den, og fjerner dermed en privat avhengighet i stedet for å legge til en. **`ExperimentalWarning`-paret
|
||
P4 pkt. 2 betalte for å BEHOLDE er borte fordi MAF sluttet å sende det** — `_feature_stage.py`s
|
||
`_add_runtime_warning` emitterer ved FØRSTE BRUK, ikke ved import, og demoen instansierer ingen av
|
||
de to typene; goldenens stderr er derfor TO linjer, regenerert som en BESLUTNING, mens
|
||
`site-packages`-maskeringen er BEHOLDT (spannet er ubebodd, ikke pensjonert) og
|
||
`test_normalisation_does_not_mask_a_new_warning` fortsatt er grønn. Load-bearing MÅLT mot HELE
|
||
suiten, grønn kontroll **1089/5** og `demo-transcript.stdout` BYTE-UENDRET
|
||
(`shasum -a 1` = `ea8c534773acdbe41ae68f2c55724d69aaf8be4f`): M1 revert av vakten → 1 rød. **ÉN MUTASJON BLE IKKE
|
||
RØD, og det står som en ÆRLIGHETS-GRENSE, ikke som en gate:** spikens gjenopptak fra
|
||
`checkpoint_ids[-1]` (siste element i en glob-ordnet listing — nøyaktig anti-mønsteret `_park`s
|
||
docstring advarer mot) feilet i den første fullkjøringen etter bumpen og var GRØNN ved mutasjonen,
|
||
altså rekkefølge-avhengig og flaky. Fiksen står på den observerte feilen, ikke på et rødt vitne.
|
||
Rapport: `docs/2026-09-02-f15-maf-pinnen.md`.
|
||
- **Falsifisering MOT PROVENIENS: en dom hviler på det basen faktisk BÆRER, og det den ikke kunne
|
||
fortelle deg er en del av dommen (B4, Steg 1–13, økt 75+76):** `parse_frontmatter` er
|
||
linjeorientert og leser en flow-verdi som ÉN streng, så `verified: { by: …, at: … }` — SPEC §5.2s
|
||
kanoniske form — nådde konsumenten som ulesbar tekst, og en blokkform nådde den som **tom** streng.
|
||
Begge så ut som «ingen proveniens». **Den defekten er den samme fra begge sider, og produsenten
|
||
MÅLTE den selv:** `llm-ingestion-okf`s egen guard-parser avviser sin EGEN golden
|
||
(`docs/okf-nokkelinventar.md:219`, `OKFFrontmatterError … '['`) — bekreftet på nytt ved Steg 13 mot
|
||
`62b6192`. **Ett skann, to LESERE, aldri to parsere:** `_split_frontmatter` er modulens ENESTE sted
|
||
`---` sammenlignes (gatet av en skanner-tellende arm, M14 → 1 rød), og `decode_flow_value` er den
|
||
ene navngitte sømmen over de rå linjene. Dekoderen er **nøkkel-agnostisk** (aldri hardkodet
|
||
`{id, resource}` — den avtalte segmenterte formen legger til `segment_id`/`source_offset`, så en
|
||
to-nøkkels dekoder ville nektet nøyaktig de basene sømmen finnes for; M3 → 12 røde) og gjør
|
||
**ingen YAML-1.1-koersering** (`yes`/`on`/`1` forblir strenger — en bevisst divergens fra PyYAMLs
|
||
resolver, skrevet ned i stedet for arvet i stillhet; M4 → 1 rød, en ratchet grønn ved
|
||
konstruksjon).
|
||
**Tre tilstander, og den tredje ER poenget:** `evidence_for` svarer `present` / `absent` /
|
||
`unreadable`, og den ulesbare bærer trippelen `(state, reason, items_seen)` — «hvilken FORM» og
|
||
«hvor MANGE» er to ulike operative spørsmål, `BudgetExceeded`-kø-(y)-regelen anvendt på en
|
||
dokumentleser. Å kollapse `unreadable` inn i `absent` gjør en dom om MANGLENDE bevis til bevis for
|
||
FRAVÆR (M7 → 4 røde); å rapportere én fast form for alle (M8 → 9 røde). K5-terskelen bor ÉTT sted
|
||
(`admits_falsification`: `present` OG en tier over `unverified`) og har TO konjunkter som måles
|
||
hver for seg (M19a → 2 røde, M19b → 1 rød). **Nevneren er skrevet ned:** SPEC §5.1 navngir SEKS
|
||
oppføringsnøkler og produsenten skriver TO (`62b6192`), så terskelen krever kun det som faktisk
|
||
emitteres — å kreve `author`/`usage_count`/`last_modified` ville gjort den unåbar i praksis mens
|
||
den så streng ut på papiret. **`adjudication` har en TREDJE token som er VÅR:** vokabularet på
|
||
tråden er lukket (`proposed` | `adjudicated`), og `unknown` er hva FRAVÆR betyr — aldri noe et
|
||
dokument får erklære (M20a → 2 røde), og en bærer som gir fra seg en payload uten tilstanden er
|
||
vokabularet uten sømmen (M20b → 1 rød). **`evidence_for` KUNNE IKKE drive den** (D-e → 5 røde):
|
||
MÅLT er `adjudication` en SKALAR, og begge gyldige verdier kommer tilbake som ETT identisk
|
||
`unreadable`/`unsupported-flow`-record, så den ruten kan ikke skille dem i det hele tatt;
|
||
`parse_frontmatter` er skalarleseren over SAMME skann.
|
||
**Skriveren nekter nøyaktig det leseren ikke kan lese**, og det er strukturelt, ikke en konvensjon:
|
||
`verified_field` og `write_concept_file` reiser den SAMME navngitte `FlowDecodeError` (M5 → 5 røde,
|
||
M16 → 3 røde, M17 → 1 rød). Det usikre tegnsettet er TRANSKRIBERT fra produsentens eget
|
||
`_FLOW_UNSAFE_RE` (`materialize.py:176`, `[,\[\]{}]|:\s`) — ikke oppfunnet — og er BEVISST
|
||
smalere: vår parseparator er kolon-MELLOMROM, så `":\t"` kan ikke restrukturere en av våre par.
|
||
**Ko-drift er stengt av skrivesidens gate, ikke bare oppdagbar via en ekstern referent:** planen
|
||
spådde at M9 (skriver OG dekoder flyttes til blokkform sammen) ville la rundturen stå GRØNN.
|
||
**MÅLT er den spådommen ikke konstruerbar** — skriveren kan ikke flytte alene (M9a → 26 røde + 8
|
||
feil), fordi `render_frontmatter` enlinjer hver verdi OG `write_concept_file` nekter formen; M9
|
||
krevde derfor at BEGGE vaktene ble slått av samtidig, og ga 17 røde inkludert rundturen. Det er et
|
||
sterkere utfall enn planen ba om, og det er skrevet ned som målt, ikke som spådd.
|
||
**Navigasjonen forblir TOLERANT mens dekoderen nekter** (OKF SPEC §4), bevist med BYTES: M10 →
|
||
**1 rød, ett navngitt vitne**, mens begge nav-goldenene og demo-transkriptet står.
|
||
**Identitet er PARET `(bundle_id, concept_id)` (D6/B1), og `origin` har TRE verdier:**
|
||
`okf.reconcile_bundle_id` er den ENE avledningsregelen — konseptets egen frontmatter først, rot-
|
||
`index.md` som fallback, mountets basename til sist — og `ResolvedBundleId.origin` sier hvilken
|
||
(`declared-concept` / `declared-index` / `mount-derived`). **Påkrevd uten default**, av
|
||
`cost_baseline_anchored`s grunn: begge defaults ville løyet om en hendelse. **Ikke en boolean** —
|
||
en kaller som ikke kan skille «konseptet sa det» fra «vi falt tilbake to ganger» har fått et
|
||
stempel den ikke kan revidere (D-i → 3 røde). **MÅLT: null `^bundle_id`-treff** noe sted under
|
||
`shared/`, `src/`, `tests/` mot en kjent-positiv kontroll på 31 filer med `^type:`, så begge
|
||
erklærte grener er DEFENSIVE (`budget_stop`-presedensen) og drives fra CRAFTED baser; alle 27
|
||
`bundle_dirs=`-kallsteder står grønne. **⚠️ TO SETNINGER I DENNE RADEN ER ENDRET 03.09 — se
|
||
slakke-raden nederst (S7a-3 pkt. 1):** mount-avviket NEKTER ikke lenger (erklært vinner), og
|
||
«begge erklærte grener er DEFENSIVE» gjelder kun dette repoet — K2 erklærer nøkkelen i 619 filer. **`explore._bundle_index` forblir REN** — to av dens egne
|
||
armer konfigurerer kataloger som ikke finnes og forventer en id-feil, ikke en I/O-feil — så
|
||
avstemmingen skjer der en base faktisk ÅPNES: `explore.read_bundle` (M11a → 1 rød),
|
||
`run_project`s bundle-arm (M11b → 2 røde) og dispatcheren (M11c → 1 rød, som erstattet en privat
|
||
`Path(raw).name`-kopi). En base uten lesbar `index.md` er **ukjent, ikke uerklært** —
|
||
`navigate_bundle`s fail-fast propagerer. `BundleIdMismatch` subklasser `ValueError` så den lander
|
||
på CLI-ens nekt-tuppel og hostings 400-arm, aldri krasj-kanalen; nekten måles på **null
|
||
modellkall**, ikke på exit-koden (M12 → 2 røde. **Ærlighets-grense, MÅLT:** planens påstand
|
||
«exit-koden forblir 1 uansett» HOLDER IKKE for denne argv — mutanten reiser
|
||
`BudgetExceeded: rounds limit=12 observed=13` før noen nekt, så sink-asserten nås aldri. Asserten
|
||
på KALL er likevel riktig design; den er det som ville fanget en nekt-etter-forbruk som DID
|
||
returnerte rent).
|
||
**En kryss-base-kollisjon rapporteres, aldri droppet i stillhet (D2):** `VerdictStore.add` er
|
||
first-write-wins og `_mint_id` ekskluderer korpuset ved konstruksjon, så to baser som beskriver
|
||
SAMME kandidat kollapser på én dom. `_mint_id` er URØRT (normativ i `shared/method-spec.md`,
|
||
commons-eid) og `add` likeså — en drop DER kan like gjerne være mot en Steg-7-innboksdom eller et
|
||
bundle-frø. Regnskapet bor i dispatcheren, som er det ENE stedet som holder id→base-kartet
|
||
(M13 → 2 røde). **Id-en hentes fra `run_project`s EKSISTERENDE `notify`-søm, aldri fra
|
||
`RunResult.verdict`** (D-l): `notify` fyrer INNE i fangst-blokka, altså nøyaktig når en dom finnes
|
||
(F2), og båsen bygges FERSK hver iterasjon — hoistes den ut, leser en senere base forrige bases dom
|
||
og fabrikkerer en kollisjon som aldri skjedde (D-l → 1 rød). `MultiBaseResult.collisions`
|
||
DEFAULTER til tom tuppel (`skipped_links`-halvdelen: en tom trace er et ærlig POSITIVT utsagn), og
|
||
`collision_notice` returnerer `None` på den — omisjon, aldri tom rad.
|
||
**Metoden er en DELT Agent Skill, forfattet i commons (D4):** `shared/skills/falsification-
|
||
reviewer/` kommer KUN via `git subtree pull` — lokal forfatting ville gitt et wheel søskenet aldri
|
||
arver, og INGEN test i suiten oppdager det avviket. Terminologi-gaten er den som biter
|
||
(`OKF bundle` i kundevendt prosa → M18a, 1 rød), og den er paret med en KJENT-POSITIV kontroll på
|
||
at regexen KAN finne strengen. Rammeverks-nøytraliteten har ÉN kopi
|
||
(`test_method_spec_is_framework_neutral`), utvidet av Amendment 2 fra `expert-reviewer` alene til
|
||
`GUARDED_SKILL_DIRS` — den nye skillen hadde ingen vakt i det hele tatt (M18b → 1 rød, egen
|
||
signatur) — pluss en fail-closed arm som blir rød når et fremtidig subtree-pull bringer en TREDJE
|
||
skill som ikke er listet. Det verkede eksempelet materialiseres fra `frontmatter_verbatim`
|
||
ORDRETT (Amendment 3): den linjeorienterte `frontmatter`-projeksjonen kan ved konstruksjon ikke
|
||
bære blokkform, så å bygge fra den ville gjort det ulesbare tilfellet `absent` og armen ville stått
|
||
GRØNN mens den beviste det motsatte av sin egen docstring. Eksempelets egen dom er `undecided`, ikke
|
||
`survived` — en påstand hvis ene mulige gjendriver var ulesbar ble aldri angrepet.
|
||
**Load-bearing MÅLT: 30 mutasjoner, ALLE røde mot HELE suiten**, grønn kontroll **1189 passed /
|
||
5 skipped**, alle tre byte-fasitene OK og `demo-transcript.stdout` BYTE-UENDRET
|
||
(`shasum -a 1` = `ea8c534773acdbe41ae68f2c55724d69aaf8be4f`); node-ID-supersettet holder strengt (1094 → 1194,
|
||
**0 fjernet**). Per mutasjon: M1 27 · M2 2 · M3 12 · M4 1 · M5 5 · M6 1 · M7 4 · M8 9 · M9a 26+8E ·
|
||
M9 17 · M10 1 · M11a 1 · M11b 2 · M11c 1 · M12 2 · M13 2 · M14 1 · M15 1 · M16 3 · M17 1 ·
|
||
M18a 1 · M18b 1 · M19a 2 · M19b 1 · M20a 2 · M20b 1 · D-b 1 · D-e 5 · D-i 3 · D-l 1.
|
||
**TRE av planens spådde signaturer ble FALSIFISERT av målingen, og de står som målt:** M9s
|
||
«rundturen forblir grønn» (ikke konstruerbar), M12s «exit-koden forblir 1» (mutanten reiser i
|
||
stedet), og M17s spådde PAR (kun 1 rød — `verified_field("a: b", …)` runder faktisk trippen, fordi
|
||
`_find_pair_separator` tar FØRSTE kolon-mellomrom og verdien beholder sitt eget kolon).
|
||
**Operatørbeslutninger:** D1 én dekoder-søm · D2 dispatcher-side kollisjonsregnskap · D3 én
|
||
linjekilde bak begge lesere · D4 skillen forfattes i commons · D5 ingen ny avhengighet (ingen
|
||
PyYAML) · D6/B1 identitet = `(bundle_id, concept_id)`, konseptet først. **D7-speiling er ÅPEN** for
|
||
dekoder-semantikken, som S3.2 og S4.0. **Ærlighets-grenser, uttalt:** `evidence_for(path, key=…)`
|
||
med en ANNEN nøkkel enn `verified` REISER for enhver verdi som dekoder til oppføringer uten `by`,
|
||
fordi `tier` avledes ubetinget — MÅLT: alle 11 eksisterende kallsteder bruker defaulten, så
|
||
parameteren har aldri vært øvet, og det er RAPPORTERT, ikke fikset (utenfor denne ordren);
|
||
`verified_field` validerer ikke `at`; og at en LEVENDE modell kaller falsifiseringsskillen godt er
|
||
ikke bevist (structured-output-grensens klasse).
|
||
- **Den TREDJE projeksjonen inn i `CostBaseline` DERIVERES fra en tabell som alt er i basen — og
|
||
den nekter heller enn å oppfinne (MAJOR-4, økt 78):** `cost-baseline.json` er håndskrevet per
|
||
prosjekt og `baseline_from_project` tilhører veg-domenet, så et INGESTERT anbudskorpus kunne
|
||
navigeres og aldri forankres. `okf.derive_cost_baseline(bundle, *, project_id)` leser
|
||
prisskjemaet. **`project_id` er PÅKREVD keyword, ikke lest av basen:** `run._project_from_bundle`
|
||
fail-faster alt kjøringens id mot basens `validator-input.json`, så en andre lesing her ville vært
|
||
kø-(p) — og ville dratt en IR-projeksjon inn i en funksjon hvis hele input er en tabell.
|
||
**PREMISSET BLE MÅLT FØR NOE BLE BYGGET PÅ DET, og ordrens to pekere navnga TO ULIKE FORMER:**
|
||
`examples/*/expected-bundle/` bærer PIPE-tabeller, men hver eneste av dem er csv-/sql-kilt og
|
||
rendret av `render.render_table` — **xlsx-stien når ALDRI `render_table`.** Målt (pandoc 3.10.2,
|
||
produsentens egen `_PANDOC_WRITER = "markdown"` + `_PANDOC_ARGS = ("--eol=lf", "--wrap=none")`,
|
||
mot produsentens egen `tests/fixtures/two-line-krav.xlsx` og et håndlagt Prisskjema):
|
||
`extract._extract_office` konverterer, og `inbox.py` gir teksten videre til
|
||
`render_inbox_concept(sanitized_text, …)` URØRT — resultatet er en pandoc **SIMPLE table**, der
|
||
dash-linja definerer kolonne-spennene. En pipe-only leser ville altså vært INERT på nøyaktig det
|
||
korpuset MAJOR-4 finnes for, og en simple-only leser inert på eksemplene ordren pekte på. Begge
|
||
leses, av TO skannere over ÉN rollemapping og ÉN tallgrammatikk (`_split_frontmatter`-formen: ett
|
||
skann, to lesere), aldri to kopier av semantikken. **Span-slicing, ALDRI splitt på whitespace:**
|
||
K2s hele form er tomme priseceller, og en whitespace-splitt kollapser dem og skyver hver senere
|
||
verdi én kolonne til venstre — en feilmapping som leses som DATA (M16 → 3 røde).
|
||
**Ingen skjønn noe sted:** header-vokabularet er lukket og matches EKSAKT (en delstrengregel ville
|
||
latt `Enhetspris eks. mva` og `… inkl. mva` kreve samme rolle, og leseren ville valgt mellom to
|
||
priser), tallgrammatikken er lukket og **dot-desimal** (målt output er `1250.0`/`42.5`, så et
|
||
komma ville kjøpt ingenting og importert `1,250`s tusenskille-vs-desimal-tvetydighet gratis — et
|
||
håndskrevet norsk `1 250,50` er en NEKTET celle, uttalt), og hver tvetydighet er en nekt: ingen
|
||
kandidat-tabell, mer enn én, to kolonner om samme rolle, to rader med samme kostkode.
|
||
**Delvis priset skjema nekter i SIN HELHET** — ikke forsiktighet: en halvderivert base forankrer
|
||
noen koder mens `ProvenanceStamp.cost_baseline_anchored` rapporterer `True`, og den biten er
|
||
påkrevd-uten-default nettopp fordi begge defaults ville løyet. Skannet over `context_files`, aldri
|
||
`files` (MAJOR-3-regelen). **`CostBaselineDerivationError` subklasser `ValueError`**
|
||
(`BundleIdMismatch`-presedensen) — CLI-ens nekt-tuppel og hostings 400-arm, aldri krasj-kanalen.
|
||
Wiret bak `--derive-cost-baseline`, ALDRI stille: ÉN oppløsning i `run.py`s bundle-arm tjener både
|
||
full kjøring og `live_dry_run`, og **nekten PROPAGERER** i stedet for å degradere til fil-lasteren
|
||
(`load_mandate`-regelen — en kaller som ba om derivasjon og fikk en uforankret kjøring er besvart
|
||
av en stille nedgradert ordre). Tre CLI-nekter, alle ved NAVN: krever `--bundle-dir`, refusert i
|
||
`--portfolio` (ved navn, ikke ved gjennomfall til `--bundle-dir`-kravet — `--explore`-presedensen)
|
||
og i `report_forbidden` (report-modus returnerer FØR nekten, så en utelatelse er et stille DROPP,
|
||
ikke en nekt — F4-gapet). **FIXTUREN er MÅLT, ikke håndskrevet:** begge basene under
|
||
`tests/fixtures/` er pandocs output ORDRETT, og den uprisede er K2s faktiske pre-award-form —
|
||
**kolonnene OVERLEVER som blanke** (målt), så tabellen ER en kandidat og nekten blir den skarpe:
|
||
leseren identifiserer kolonnene, leser tomme celler og nekter ved navn. `validator-input.json`
|
||
skrives KUN i testen, aldri inn i den innsjekkede fixturen (`measure`/`claimed_saving_nok` er
|
||
menneskets/mandatets). **`pytest.raises(ValueError)` ville IKKE gatet arm (b), og det er MÅLT, ikke
|
||
resonnert:** M17 (drop positivitetsvakten) får mutanten til å reise
|
||
`pydantic_core.ValidationError` — `isinstance(e, ValueError) is True`,
|
||
`isinstance(e, CostBaselineDerivationError) is False` — så en løsere arm ville stått grønn mot
|
||
nøyaktig den mutasjonen den finnes for. Load-bearing MÅLT
|
||
(`tests/test_cost_baseline_derivation_loadbearing.py`, 22 armer), **18 mutasjoner alle røde mot
|
||
HELE suiten** + grønn kontroll **1230 passed / 5 skipped** (fra 1208/5, supersett, 0 fjernet) og
|
||
golden `demo-transcript.stdout` BYTE-UENDRET (`shasum -a 1` = `ea8c534773acdbe41ae68f2c55724d69aaf8be4f`):
|
||
M1 detach run-wiringen (2 røde) · M2 fallback ved nekt (1) · M3 hopp over uprisede rader (2) ·
|
||
M4 fabrikkér null (1) · M4b fabrikkér null uten tom-celle-nekten (1) · M17 drop
|
||
positivitetsvakten (1) · M5 transponer mengde/enhetspris (3) · M6 skann `files` (1) · M7 ta første
|
||
tabell ved tvetydighet (1) · M8 ta første kolonne ved duplisert rolle (1) · M9 last-write-wins på
|
||
duplisert kode (1) · M10 utvid tallgrammatikken (1) · M11 drop simple-skanneren (3) · M12 drop
|
||
pipe-skanneren (7) · M13 detach `--bundle-dir`-nekten (1) · M14 drop fra `report_forbidden` (1) ·
|
||
M15 drop fra portefølje-partisjonen (1) · M16 whitespace-splitt (3). **Ærlighets-grenser, uttalt:**
|
||
NS 3451 seksjonsrader (kode + overskrift, ingen mengde, inne i et ellers priset ark) er IKKE
|
||
klassifisert — K2 selv var ikke tilgjengelig å måle her, så et slikt ark NEKTER, og regelen skrives
|
||
når noen kan måle den ekte formen (en rad som er tom over alle tre kolonner er derimot en blank
|
||
spacer og hoppes over); stempelet sier AT en kjøring var forankret, aldri HVILKEN av de tre
|
||
projeksjonene forankret den; den hostede flaten er BEVISST urørt (feltet er ikke i whitelisten, så
|
||
4e-halvdelene står uendret); og ingen LEVENDE K2-fil er lest — fixturen er syntetisk, og at
|
||
produsenten faktisk emitterer denne formen for K2s Prisskjema er MÅLT på pandoc-stien, ikke på K2.
|
||
|
||
- **Den ERKLÆRTE `bundle_id`-en er identiteten; monteringsnavnet er en filsystem-tilfeldighet
|
||
(S7a-3 pkt. 1, økt 81):** til 03.09 NEKTET `reconcile_bundle_id` en base som erklærte en id
|
||
katalogen ikke bar. **Målt mot den første leverte basen som erklærer sin egen id** (K2: 618 av
|
||
630 konseptfiler + rot-`index.md`, alle `k2-trinn1-20260903`, levert som `K2-bundle-20260903`)
|
||
var følgen at basen **ikke kunne åpnes slik den var levert** — botemiddelet var å montere den på
|
||
nytt for hånd, én gang per leveranse, for alltid. Det er en konsument som avviser en produsents
|
||
legitime output over et katalognavn. **PM-beslutning: konsumenten slakker.** Erklært vinner
|
||
(konsept → rot-index → mount, B1s rekkefølge URØRT), avviket REGISTRERES —
|
||
`ResolvedBundleId.mount` bærer den overkjørte katalogen, `ProvenanceStamp.bundle_id_source` og
|
||
`DryRunReport.bundle_id_source` bærer hele oppløsningen inn i artefaktene, og `bundle_id_notice`
|
||
er ENESTE renderer, med TO kallsteder (returnerer `None` ved enighet — omisjon, aldri tom rad; omisjonen er selv
|
||
gatet, M5 → 2 røde inkludert et UAVHENGIG eksisterende vitne i
|
||
`test_navigation_visibility_loadbearing`). **Stempel-feltet er PÅKREVD uten default** av
|
||
`cost_baseline_anchored`s grunn, og her skjerpet: `None` er en VERDI (veg-stien har ingen base),
|
||
så et utelatt felt måtte bety det samme som «ingen base» — nøyaktig stillheten kravet lukker.
|
||
**Det som FORTSATT nekter er den ekte kollisjonen:** to KONSEPTER i ÉN base som erklærer ULIKE
|
||
id-er (`assert_declared_ids_agree`, kalt ved hver dør som ÅPNER en base). **Rot-`index.md` er
|
||
IKKE med i enighets-settet** — B1 anvendt en gang til: konsept-slår-index er en PRESEDENS-regel,
|
||
så en index i utakt med sine konsepter er fallbacken som taper, ikke to konsepter som kolliderer;
|
||
å folde indeksen inn ville nektet nøyaktig de K2-formede basene slakken finnes for (M3 → 1 rød).
|
||
Sjekken er en EGEN funksjon, ikke en gren i `reconcile_bundle_id`: den trenger en navigert
|
||
bundle, resolveren må forbli REN (`_bundle_index` løser id-er for kataloger som kanskje ikke
|
||
finnes), og separat gir den sin egen mutasjon per dør (M1/M2 → 1 rød hver).
|
||
**`explore._bundle_index` løser nå den erklærte id-en, og det er en KONSEKVENS — ikke
|
||
scope-krype:** den brukte `Path(raw).name` mens dispatcheren brukte `reconcile_bundle_id(raw).id`;
|
||
så lenge avviket ble nektet KUNNE de ikke divergere, men med erklært-vinner mynter `explore()`
|
||
approaches som navngir MOUNTET mens dispatcheren ruter på ERKLÆRINGEN — en utforskning hvis eget
|
||
mandat er uruterbart (M9 → 2 røde, M11 → 1 rød). Uleselig base faller tilbake til basenavnet
|
||
i stedet for å reise, fordi to av `_bundle_index`s egne armer konfigurerer kataloger som ikke
|
||
finnes og forventer en ID-feil, ikke en I/O-feil — ingenting utvides: den indeksen svarer på
|
||
hvilke id-er som kan NAVNGIS, og en base ingen kan lese nektes et øyeblikk senere av den døra som
|
||
faktisk åpner den. Load-bearing MÅLT (`tests/test_bundle_id_slack_loadbearing.py`, 13 armer),
|
||
**ti mutasjoner alle røde mot HELE suiten** + grønn kontroll **1243/5** og golden
|
||
`demo-transcript.stdout` BYTE-UENDRET (`shasum -a 1` =
|
||
`ea8c534773acdbe41ae68f2c55724d69aaf8be4f`): M1 detach gaten i `run_project` (1) · M2 detach den i
|
||
`read_bundle` (1) · M3 fold indeksen inn i enighets-settet (1) · M4 gjeninnfør mount-nekten (9) ·
|
||
M5 rendereren skriver alltid linja (2) · M6 detach CLI-utskriften (1) · M7 konstant stempel-wiring
|
||
(1) · M8 konstant dry-run-wiring (2) · M9 `_bundle_index` tilbake til mountet (2) · M11
|
||
dispatcheren tilbake til mountet (1). **TRE ARMER I `test_bundle_id_reconciliation_loadbearing`
|
||
ble SKREVET OM, ikke slettet** — (e)/(f)/(j) pinnet nekten beslutningen fjernet; (j) ble SKARPERE
|
||
enn den den erstattet (erklært id ruter, mountet nektes — to implementasjoner som skiller seg i
|
||
BEGGE halvdeler, ikke i en exit-kode). **Ærlighets-grenser, uttalt:** uten `concept_name` svarer
|
||
rot-`index.md`, så en base hvis index erklærer X mens konseptene erklærer Y løser til X (B1s
|
||
rekkefølge, urørt); **portefølje-armen er BEVISST IKKE wiret** — der er `bundle_id_source` `None`
|
||
ved konstruksjon (intet referanse-prosjekt setter `bundle_dir`), så en tredje utskrift ville vært
|
||
død kode; det er en ANNEN avgjørelse enn `cost_baseline_anchored`s, som wiret nettopp den armen
|
||
DEFENSIVT og drev den fra en crafted `PortfolioResult`, og den står her fordi stillhet om
|
||
asymmetrien er det eneste gale svaret. Og dispatcherens «to baser deler id»-nekt er NYLIG NÅBAR — den krevde før to
|
||
monteringer med samme basenavn, nå kolliderer to ULIKT navngitte kataloger som erklærer samme id.
|
||
Kontrakt: `docs/okf-konsum-kontrakter.md § 3.1`.
|
||
- **Stigen har tre trinn: `list_bundles` → `read_bundle` → `read_dir` → `read_file` (S7a-3 pkt. 2,
|
||
økt 81):** MAJOR-3 bygde `read_bundle` om fra HELE basen til én oppføring per konseptfil, og på de
|
||
tre eksempelbasene var det −77…91 %. Så kom det første EKTE korpuset: K2 navigerer til **629
|
||
konsepter bak 478 nestede indekser**, og en listing av 629 koster **42 761 o200k-tokens** som rir
|
||
i 7 av 12 prompter = **89 % av alle prompt-tokens**. Bindingen holdt asymptotisk og priset likevel
|
||
hele korpuset, fordi prisen på å finne ut HVA en base inneholder var satt av hvor mye den
|
||
inneholder. De 478 indeksene ble dessuten BYGGET av navigasjonen, KONSUMERT av den og så flatet
|
||
ut — agenten så 629 søsken og fikk aldri vite at korpuset hadde en form. **MÅLT ETTER:** rotnivået
|
||
er **3 954 tegn / 1 495 tok**, listing-tokens **307 573 → 12 595**, og utforskningens
|
||
prompt-tokens **343 826 → 49 225 (−86 %)**. BEFORE og AFTER er kjørt i SAMME økt med samme kode
|
||
(BEFORE er en mutasjon), og BEFORE reproduserer S7a-2s publiserte tall til 0,03 % — den
|
||
kjent-positive kontrollen på instrumentet. **`okf.directory_listing` er ENESTE renderer** og
|
||
begge verktøy ER den på hvert sitt nivå; to kopier av en listing-regel ville drevet til to svar om
|
||
én base (kø-(p)). **ET PREMISS I ORDREN BLE FELT FØR NOE BLE BYGGET PÅ DET:** `context_files` har
|
||
ALDRI holdt hierarkiet tilbake — hver `BundleFile.name` er allerede den fulle bundle-relative
|
||
stien, så treet var utledbart fra navnene; det var RENDERINGEN som flatet det ut. Dét er grunnen
|
||
til at `bundle_context` og begge nav-goldenene er BYTE-IDENTISKE etter endringen (ordrens eget
|
||
krav), gratis og ikke av forsiktighet. **Kataloger utledes av STIER, aldri av `index.md`** (en
|
||
nestet indeks er navigasjon, ikke innhold), og listingen bygges av `context_files`, ALDRI `files`
|
||
— en katalog-TELLING fra `files` ville reklamert for dokumenter navigatøren ikke får se (N2 → 7
|
||
røde). **Hver sti er BUNDLE-RELATIV, altså brukbar ordrett som neste kalls argument** — en
|
||
nivå-relativ form måtte komponeres av en modell, og en sti som aldri fantes er verre enn ingen
|
||
sti (`_index_excerpt`-regelen, ett trinn opp; N7 → 1 rød). `documents` på en katalog-oppføring er
|
||
antallet i hele SUBTREET — hva subtreet holder, ikke hva ett `read_dir` returnerer — og
|
||
beskrivelsen sier hvilken av de to det er. Ukjent sti NEKTES ved navn (`BundlePathNotFound`,
|
||
`ValueError`-subklasse som `BundleIdMismatch`): en tom listing er umulig å skille fra en katalog
|
||
som finnes og er tom (N5 → 1 rød). **Verktøybeskrivelsene og navigatørens instruksjon flyttet i
|
||
SAMME commit** — en beskrivelse som lyver om kroppen ER modellens instruks (Fase 3-klassen; N9 →
|
||
1 rød). **TO EKSISTERENDE ASSERTS VILLE BLITT VAKUØSE I STILLHET** og er styrket: `len(payload)`
|
||
er nå antallet NØKLER (tre, alltid), så `test_read_bundle_cost_loadbearing`s størrelses-arm talte
|
||
nøkler, og write-frihets-armen i `test_explore_loadbearing` kalte ikke det nye verktøyet i det
|
||
hele tatt. **AVVIK fra ordren, uttalt og målt:** ordren binder «K2 < 1 500 tegn» — K2 kan ikke
|
||
være en testavhengighet (utenfor repoet; MAJOR-3-gaten har samme begrensning), og tallet er ikke
|
||
oppnåelig: rota bærer 39 identifiserbare oppføringer og måler 3 954 tegn. Gaten binder derfor
|
||
1 500 tegn per listing over basene den KAN se, pluss egenskapen som ga fallet (kostnaden følger
|
||
oppføringer på ETT nivå, ikke dokumenter i basen), med en FLAT kontroll over 5× taket — uten den
|
||
ville en grønn binding like gjerne betydd at fixturen var liten. **Ordrens ANDRE binding
|
||
(«`read_dir` over største katalog bundet») er MÅLT med den SHIPPEDE funksjonen over alle 478
|
||
nivåer i K2:** verste nivå noe sted er **6 073 tegn / 2 094 tok** (65 underkataloger) = **4,9 %**
|
||
av den flate formens 42 761 — dyrere enn rota fordi hver sti er bundle-relativ, altså den målte
|
||
prisen på at en sti er brukbar ORDRETT i neste kall. Load-bearing MÅLT
|
||
(`tests/test_hierarchical_navigation_loadbearing.py`, 9 armer), **ni mutasjoner alle røde mot HELE
|
||
suiten** + grønn kontroll **1252/5** og golden BYTE-UENDRET (`shasum -a 1` =
|
||
`ea8c534773acdbe41ae68f2c55724d69aaf8be4f`): N1 reverter sømmen (4) · N2 bygg fra `files` (7) ·
|
||
N3 bundet men VAKUØST tom (12) · N4 dropp `chars` (5) · N5 ukjent sti blir en tom listing (1) ·
|
||
N6 `read_dir` ikke registrert (6) · N7 nivå-relative navn (1) · N8 katalog-tellingen konstant (2)
|
||
· N9 instruksjonen beskriver den gamle stigen (1). **Ærlighets-grenser, uttalt:** dette er ikke
|
||
«−86 % for enhver kjøring» (en navigatør som stiger ned *k* nivåer betaler *k* listinger); at en
|
||
LEVENDE modell navigerer bedre med en struktur enn med 629 flate dokumenter er IKKE bevist
|
||
(structured-output-grensens klasse); multiplikatoren gjelder dette manuset; og `navigate_bundle`
|
||
kalles fortsatt per verktøykall (I/O, ikke tokens — ikke målt). Måling:
|
||
`docs/2026-09-03-hierarkisk-navigasjon-k2.md`.
|
||
- **Sporet sier HVILKE dokumenter navigatøren åpnet, ikke bare hvilken base (S7a-3 pkt. 3, økt
|
||
81):** `ExplorationToolRecorder` registrerte NAVN + `bundle_id` i kall-rekkefølge (MAJOR-1). Over
|
||
de tre eksempelbasene holdt det — 39 konsepter, og en kjøring som åpnet to av dem etterlot et
|
||
spor en operatør kunne resonnere om. Over K2 gjør det ikke: **629 konsepter**, og `tool_calls`
|
||
leser `read_file, k2` to ganger, så **hvilke to av 629 kan ikke leses ut av leveransen i det hele
|
||
tatt** — økt 77 og 81 måtte begge instrumentere kjøringen for hånd for å svare. `ToolCall.path`
|
||
registreres for `read_file` OG `read_dir`; et spor som navnga dokumentene men ikke katalogene
|
||
ville sagt hvor en kjøring ENDTE uten å si hvordan den kom dit. **RESULTATET registreres fortsatt
|
||
ALDRI**, og det er MAJOR-1s beslutning gjentatt nettopp fordi pkt. 3 er øyeblikket den var lettest
|
||
å oppheve: resultatet ER basens innhold, målt til 89 % av hver prompt-token i en K2-kjøring.
|
||
`""` for et verktøy som ikke tar argumentet — `bundle_id`s egen regel anvendt på det andre feltet;
|
||
en verdi oppfunnet for et argument ingen sendte er falsk attribusjon. **ÉN argumentleser for begge
|
||
felt** (`_string_argument`, kø-(p)): to kopier av «les denne nøkkelen ut av begge formene
|
||
`FunctionInvocationContext` tillater» ville stått fritt til å være uenige om hva et fraværende
|
||
argument betyr. Load-bearing MÅLT (`tests/test_tool_call_path_loadbearing.py`, 5 armer), **fire
|
||
mutasjoner alle røde mot HELE suiten** + grønn kontroll **1257/5** og golden BYTE-UENDRET
|
||
(`shasum -a 1` = `ea8c534773acdbe41ae68f2c55724d69aaf8be4f`): P1 detach registreringen (3) ·
|
||
P2 dropp `path` fra `trace_payload` (3, inkludert et EKSISTERENDE vitne) · P3 koerser en
|
||
ikke-streng til en etikett (2) · P4 les kun Mapping-formen (2).
|
||
- **STATE.md er local-only** (gitignored). Voyage session-state er efemert; STATE.md er kanonisk kontinuitet.
|
||
- Prosess: Voyage-plugin (`/trekbrief → /trekplan → /trekexecute → /trekreview`) per større fase.
|
||
|
||
## Communication patterns
|
||
|
||
When linking to local files in responses, use named markdown links — `[Human-friendly name](file:///absolute/path)`, never bare `file://` URLs or autolinks `<file://...>`, always absolute paths (never `~/` or relative), one bullet per file when there are several. (Bare `file://` URLs render only the first as clickable across multiple lines; named links stay independently clickable.)
|