run_project KREVDE verdict_input og kjorte capture_verdict ubetinget; CLI-en
defaultet det til {"approved", "reviewed by expert"} og hosting listet det som
PAAKREVD. Netto: hver flaggloes kjoering 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 -- paa flaten som ble overlevert
14.08. Non-goal 3, brutt i en soem.
RunResult.verdict er naa Verdict | None, og None er hva stillhet produserer:
ingenting myntes, ingenting lagres, ingenting varsles. Prinsippet sto allerede i
repoet -- RunFailure sin docstring: aa fylle et felt med en dummy legger
FABRIKKERT proveniens inn i aggregatet.
Traceability koster ingenting: RunResult.verdict_key (property, derivert fra
kandidaten) er verdicts.verdict_key sitt alt dokumenterte formaal -- identisk
med verdict.id naar en dom BLE gitt, og fortsatt meningsfull naar ingen ble det.
Det er den outboxen og den hostede responsen stempler.
Halv dom NEKTES paa begge doerer (FeedbackContract er eneste sted formen
valideres; CLI-en nekter ved navn FOER enhver mode-dispatch). Validering, aldri
reparasjon. De to mode-partisjonene fikk --decision/--rationale inn: kommentarene
sa ordrett at en aerlig nekt var uimplementerbar fordi de non-None
argparse-defaultene gjorde en eksplisitt verdi uskillbar fra defaulten -- med
defaultene borte er den implementerbar.
Hosting er WIDENING, ikke bryting: verdict_input flyttet fra _REQUIRED_FIELDS
til _OPTIONAL_FIELDS. Ingen ekstern kaller brekker.
AERLIGHETS-GRENSE: referanse-fixturens SYNTETISKE verdict_input-rader staar
uroert -- de er merket SYNTETISK paa fire steder og er reviewens F5 (maaling av
misjonspaastanden), ikke F2. Project.verdict_input er naa valgfri.
Load-bearing MAALT (tests/test_ungiven_verdict_loadbearing.py, 15 armer), aatte
mutasjoner alle roede mot HELE suiten + gronn kontroll 1080/5 og golden
demo-transcript.stdout BYTE-UENDRET (ea8c534773acdbe41ae68f2c55724d69aaf8be4f).
En mutasjon falsifiserte testen foerst (vakuoes-gate-klassen, ellevte gang):
--report-armen brukte et bart --report, som nekter rc 1 uansett fordi --ledger
mangler.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1121 lines
107 KiB
Markdown
1121 lines
107 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.9.0). 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 fire linjer. **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.
|
||
- **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 FIRE linjer. **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
|
||
fire-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
|
||
(`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 (`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
|
||
(`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
|
||
(`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`.
|
||
- **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
|
||
(`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`.
|
||
- **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.)
|