fix: findings from the Cowork test, released as v0.1.1

The operator tested v0.1.0 in Cowork on 2026-11-11. README now says what
was measured there and what was not.

- miljo: no longer claims that kontroll compares "with tolerance, not bit
  for bit" when numpy or scipy differ from the note's versions. kontroll
  always compares byte for byte first, and numpy 2.5.3 gave bit for bit.
- kontroll --alt: show every differing line. The cut-off line now names
  the option instead of only counting the lines left out.
- skill simuler: the question before a run must be answerable with yes or
  no. In the test it became a choice between variants and the run stalled.
- skill forbedre: offer the working copy as a zip when the user cannot
  open the folder it was made in, as in Cowork.

263 tests pass; full kontroll: bit for bit with the note.

Co-Authored-By: Claude Fable 5.2 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-11-11 20:24:58 +01:00
commit 13f6c28dc0
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
11 changed files with 126 additions and 29 deletions

View file

@ -62,7 +62,7 @@ Kommandoer:
forlop <fil> ["ledd"] stabilitetssjekk og forløp måned for måned (numpy og scipy)
scenario <fil> [--ut <mappe>] kjør en scenariofil (numpy og scipy)
notatet [--ut <mappe>] kjør notatets simuleringer uendret, om lag 1-2 min (numpy og scipy)
kontroll [--rask] kontroller at modellen regner som notatet
kontroll [--rask] [--alt] kontroller at modellen regner som notatet (--alt: vis alle ulike linjer)
miljo [--opprett] vis Python, numpy og scipy; --opprett installerer dem i en egen mappe
arbeidskopi <mappe> lag en kopi av modellen som du kan endre
@ -568,8 +568,8 @@ def rapport_miljo(valg: Valg) -> None:
if samme_som_notatet(regner, laast):
print("Versjonene av numpy og scipy er de samme som notatets, så kontroll kan gi bit for bit.")
else:
print("Versjonene av numpy og scipy er ikke de samme som notatets: kontroll sammenligner da "
"med toleranse, ikke bit for bit.")
print("Versjonene av numpy og scipy er ikke de samme som notatets. Tallene kan da avvike i siste "
"siffer; kontroll sier om de er like bit for bit eller innenfor toleranse.")
def kjor_prosess(kommando: list[str]) -> int:
@ -608,8 +608,8 @@ def opprett_miljo(valg: Valg) -> int:
print()
rapport_miljo(valg)
if toleranse:
print("\nNumpy og scipy er ikke notatets låste versjoner. Resultatene kontrolleres da med toleranse, "
"ikke bit for bit.")
print("\nNotatets låste versjoner av numpy og scipy lot seg ikke installere, så nyere versjoner er "
"installert. Kjør kontroll for å se om tallene er like bit for bit eller innenfor toleranse.")
return OK

View file

@ -2,8 +2,11 @@
"""Gjenskapingskontroll: sjekk at modellen i denne mappa regner som notatet.
Bruk:
python3 kjor.py kontroll [--rask]
python3 kontroll.py [--rask] (med en Python som har numpy og scipy)
python3 kjor.py kontroll [--rask] [--alt]
python3 kontroll.py [--rask] [--alt] (med en Python som har numpy og scipy)
--alt viser alle linjene som er ulike. Uten den vises de 40 første per sammenligning, og
resten telles.
Kontrollene, hver med én linje dom og detaljer bare ved avvik:
1. Kjernen og fasit: sha256 for filene i kjerne/ og fasit/ mot notatets verdier, som står som
@ -118,7 +121,8 @@ SCENARIO_TREGE = ("mpc11",)
LIK, TOLERANSE, ENDRET, IKKE_KJORT = "lik", "toleranse", "endret", "ikke kjørt"
RANG = {LIK: 0, TOLERANSE: 1, IKKE_KJORT: 2, ENDRET: 3}
MAKS_LINJER = 40 # linjer med forskjeller som vises per sammenligning
MAKS_LINJER = 40 # linjer med forskjeller som vises per sammenligning (alle med --alt)
VISNING = {"alt": False} # settes av --alt i main()
SLINGRING = 1e-9 # relativ slingring når avviket sammenlignes med én enhet i siste siffer
UTVIDELSER = "Utvidelser" # merkelappen for kontroll 5
@ -220,8 +224,9 @@ def vis_tall(x: float) -> str:
def forskjeller(fasit: list[str], naa: list[str]) -> list[str]:
"""Linjene som er ulike, som en kort diff (notatet / nå)."""
linjer = list(difflib.unified_diff(fasit, naa, "notatet", "nå", lineterm="", n=0))
if len(linjer) > MAKS_LINJER:
linjer = [*linjer[:MAKS_LINJER], f"... ({len(linjer) - MAKS_LINJER} linjer til)"]
if len(linjer) > MAKS_LINJER and not VISNING["alt"]:
linjer = [*linjer[:MAKS_LINJER],
f"... ({len(linjer) - MAKS_LINJER} linjer til; «kontroll --alt» viser alle)"]
return linjer
@ -594,10 +599,11 @@ def niva(dommer: list[Dom], *, rask: bool, arbeidskopi: bool) -> list[str]:
def main(argv: list[str]) -> int:
"""Kjør kontrollene og skriv dommen. Returnerer exit-koden."""
if argv not in ([], ["--rask"]):
print("Bruk: kjor.py kontroll [--rask]", file=sys.stderr)
if len(set(argv)) != len(argv) or not set(argv) <= {"--rask", "--alt"}:
print("Bruk: kjor.py kontroll [--rask] [--alt]", file=sys.stderr)
return 2
rask = argv == ["--rask"]
rask = "--rask" in argv
VISNING["alt"] = "--alt" in argv
arbeidskopi = (ROT / ARBEIDSKOPI_MERKE).is_file()
regnemiljo = har_numpy_scipy()
print(f"Kontroll av modellen i {MODELL}" + (" (arbeidskopi)" if arbeidskopi else "") + "\n", flush=True)