docs(mandate): how a domain expert commissions a run — and one honesty fix the run itself exposed

`docs/bestille-en-kjoring.md` is the commissioning half of the expert-facing pair
(`ekspert-svar.md` is the judging half): the mandate file field by field, how to
run it, and — separated deliberately — what a commission does NOT do. It directs
what is evaluated, never what is approved.

Registered in _LIVE_DOCS, so it cannot silently fall behind the code.

The example output in it is COPIED FROM A REAL RUN, not composed, and running
that run is what found the defect fixed here: three approaches against the same
cost line each validated at 30000 NOK, and the settlement printed
"Validated total: 90000 NOK". Commissioned approaches are ALTERNATIVES — they
usually attack the same line — so summing them reports money the project cannot
realise. A domain expert reading that total would reasonably believe the run
found 90k.

The settlement now reports how many approaches held and which one the run
carries: a selection, not an arithmetic claim. That also removes the last money
addition from this module, which is the right place for it not to be.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01ULCqjLF61rehj5cZmdUoR3
This commit is contained in:
Kjell Tore Guttormsen 2026-08-05 16:31:27 +02:00
commit 30bcdd3544
5 changed files with 174 additions and 10 deletions

132
docs/bestille-en-kjoring.md Normal file
View file

@ -0,0 +1,132 @@
# Bestille en kjøring
Dette dokumentet er for deg som **bestiller** en kjøring — fagpersonen som vet hvilke tilnærminger
det er verdt å prøve på et prosjekt, og som vil vite nøyaktig hva systemet skal gjøre før det gjør
det. Søsterdokumentet [ekspert-svar.md](ekspert-svar.md) handler om det motsatte enden: å avgi dommen
etterpå.
> **Ærlighetsmerking.** Bestillingen styrer hva som **vurderes**. Den styrer aldri hva som
> **godkjennes**. Den deterministiske validatoren gjelder uendret for en tilnærming du selv har bedt
> om — ber du om noe tallene ikke bærer, blir det avvist, og avvisningen kommer tilbake til deg med
> begrunnelsen.
## Oppdragsfila
En bestilling er én JSON-fil. Du gir den til kjøringen med `--mandate`.
```json
{
"objective": "Kutt energikostnad i BYGG-KONTOR-NORD uten ombygging, med tiltak som kan gjennomføres i 2026.",
"approaches": [
{
"id": "led-retrofit",
"label": "LED-retrofit av kontorbelysning",
"description": "Drift mener armaturene er originale og har gått langt forbi oppgitt levetid."
},
{
"id": "driftsavtale",
"label": "Reforhandling av driftsavtale",
"description": "Avtalen er ikke justert siden 2019 og dekker areal vi ikke lenger disponerer."
}
],
"allow_own_proposals": true,
"success_criteria": "Minst ett tiltak som passerer validatoren og som drift kan stå inne for."
}
```
| Felt | Hva det gjør |
|---|---|
| `objective` | Hva kjøringen er til for, i klartekst. **Påkrevd** — en kjøring uten uttalt hensikt er nettopp det denne fila finnes for å hindre. |
| `approaches` | Tilnærmingene du vil ha vurdert. Hver får sin egen vurdering og sin egen linje i oppgjøret. |
| `allow_own_proposals` | `true` (standard): systemet foreslår også sitt eget, i tillegg til dine. `false`: kun dine. |
| `success_criteria` | Hva du vil regne som vellykket. Gjengis ordrett i kunngjøringen; ingenting tolker den maskinelt. |
**Om `id`:** den er nøkkelen tilnærmingen rapporteres under. To tilnærminger kan ikke dele `id`
(da ville de kollapset til én linje i oppgjøret), og `own-proposal` er reservert for systemets eget
forslag.
**Om `description`:** den mates ordrett inn til modellen. Det er her fagkunnskapen din faktisk
gjør en forskjell — *hvorfor* du tror tilnærmingen er verdt å prøve er nøyaktig det systemet ikke
kan lese seg til av kostnadstallene.
**Enten tilnærminger eller egne forslag.** En fil med tom `approaches` **og**
`allow_own_proposals: false` blir avvist: da har kjøringen ingenting å gjøre.
## Å kjøre den
```bash
uv run python -m portfolio_optimiser.run BYGG-KONTOR-NORD \
--docs-dir shared/examples/bygg-energi-mikro \
--bundle-dir shared/examples/bygg-energi-mikro \
--mandate oppdrag.json \
--scripted-replies replies.json
```
`--mandate` virker i begge moduser. I porteføljemodus (`--portfolio`) gjelder bestillingen **hvert**
prosjekt i passet, og hvert prosjekt gjør opp for seg.
**Vil du se hva kjøringen ville gjort, uten å bruke penger?** Legg til `--live-dry-run`. Da skrives
kunngjøringen, og kjøringen stopper før første modellkall.
**Fila leses strengt.** Mangler den, eller er den feilformet, blir kjøringen **nektet** (rc 1) — den
går aldri videre på en halvlest bestilling. Det er med vilje: et oppgjør som beskriver arbeid ingen
har bestilt, er verre enn ingen bestilling.
## Hva du får: kunngjøringen
Før noe koster penger, skriver kjøringen hva den skal gjøre:
```
Run mandate for BYGG-KONTOR-NORD
Objective: Kutt energikostnad i BYGG-KONTOR-NORD uten ombygging, ...
Evaluates: 2 expert-proposed approach(es) + the system's own proposals
1. led-retrofit — LED-retrofit av kontorbelysning
2. driftsavtale — Reforhandling av driftsavtale
Stops at: 3 rounds / 100000 tokens
Contacts: no external services
Success: Minst ett tiltak som passerer validatoren ...
```
Kjører du med `--dimension-config`, kommer det en `Scoped to:`-linje til. Linjer for ting kjøringen
ikke har — avgrensning, tallmål — utelates i stedet for å stå tomme: en tom linje ville lest som en
beslutning ingen har tatt.
`Contacts` er egress-erklæringen: en kjøring når aldri en ekstern tjeneste den ikke har navngitt
her. `Stops at` er takene — systemet kjører aldri en ubegrenset løkke.
## Hva du får: oppgjøret
Etter kjøringen svarer den for bestillingen, én linje per tilnærming:
```
Mandate outcome
led-retrofit VALIDATED 30000 NOK LED-retrofit av kontorbelysning
driftsavtale REJECTED claimed 240000 exceeds feasible 90000
own-proposal NOT EVALUATED budget exhausted before this approach was evaluated
Validated: 1 of 3 approaches — best 30000 NOK (the outcome this run carries)
```
De tre statusene betyr forskjellige ting, og forskjellen er poenget:
- **VALIDATED** — tallene holdt. Beløpet er det validatoren slapp gjennom.
- **REJECTED** — tilnærmingen ble vurdert, og falt på tallene. Grunnen står der; det er den du kan
gjøre noe med (ofte er den «regnet mot feil grunnlag», ikke «dårlig idé»).
- **NOT EVALUATED** — kjøringen rakk den aldri. Den er **ikke** avvist, og den er ikke glemt.
En tilnærming du bestilte forsvinner aldri i stillhet. Det er hele grunnen til at oppgjøret finnes.
**Tilnærmingene summeres ikke.** Flere av dem angriper som regel samme kostnadslinje, så de er
alternativer — ikke besparelser som legges oppå hverandre. Derfor står det *hvor mange som holdt*
og *hvilken kjøringen bærer videre*, aldri en sum. (Dette ble oppdaget ved å faktisk kjøre den:
tre tilnærminger mot samme linje ga «totalt 90 000», som ingen av dem kunne innfri.)
## Hva bestillingen ikke gjør
- **Den overstyrer ikke validatoren.** Se ærlighetsmerkingen øverst.
- **Den avgjør ikke om et tiltak skal gjennomføres.** Det gjør et menneske, etterpå — se
[ekspert-svar.md](ekspert-svar.md).
- **Den setter ikke et tallmål.** Sparemålet har ett hjem, målkonfigurasjonen (`--goals`, som gjelder
porteføljemodus). To kopier av samme tall drifter fra hverandre, så oppdraget bærer hensikten i ord
og gjengir tallet hvis det finnes.
- **Den lagrer seg ikke som kunnskap.** Bestillingen er en instruks for én kjøring. Det som lærer
systemet noe, er dommen du avgir etterpå.