# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ### Added - Deterministic backbone: mandatory blocking validator (solver + Monte Carlo against the shared golden suite), budget meter with hard fail-fast caps, provenance stamping. - Agentic learning loop wired end to end, one load-bearing seam at a time (target-picture steps 1, 3/4, 5, 7, 8): OKF-navigated bundle context with the gated ExpeL fold, maker-checker debate where the checker gates the reasoning, informed refinement (previous rejection reason fed into the next bounded attempt), async verdict file inbox, and gated wiki promotion (fail-closed). - Offline end-to-end simulation proving the learning loop closes with a scripted client (`uv run python -m portfolio_optimiser.simulation`) — plumbing proof, not live-model proof. - Framework-neutral shared core in `shared/`: OKF concept + example bundle, golden validator suite, and the expert-reviewer persona as an Agent Skill. - CLI parity (S5.3): `run.py` drives the whole method from the command line in **two modes** — single-project (`--dimension-config`, `--outbox-dir`/`--run-id`, plus the already-wired `--bundle-dir`/`--verdict-dir`) and portfolio (`--portfolio` with `--goals`/`--ledger`), the latter printing an observable `goal reached: …` line when a savings goal is met. Adds a fail-fast `load_dimension` loader and structured refusals (rc 1, no traceback) for misuse and mode-exclusivity violations. The prior-verdict fold is on the `--bundle-dir` path only; a `--docs-dir`-only run is single-shot. - Value report (S5.4): a read-only `--report [--json] --ledger ` surface on `run.py` that rolls up the accumulated `SavingsLedger` — per-project + portfolio totals (dimension-free-deduped integer øre), flagged cross-dimension overlaps (each counted once), and per-entry provenance — as a human table or deterministic JSON. Makes no model calls; mode-exclusive (only `--ledger`/`--json` permitted with `--report`, which requires `--ledger`). Honest scope boundary: the report core (`value_report.py`) now **exists** but is deliberately **not** wired into `costsim`'s `kost_mot_verdi` placeholder — that cost-vs-value integration is a separate, deferred step. The `costsim` seam note was reworded from the stale "fylles av S5.4 verdirapport" to a truthful forward reference so `costsim`'s own output no longer claims the wiring is done. - Semantic retrieval seam (S3.1): a new MAF-free `semretrieval.py` adds an `Embedder`/`Retriever` pair and a `HybridRanker` blending a numpy cosine term over the embedded feature triple (sorted cost codes, measure type, magnitude bucket) with the existing structural score, exposed as `--semantic-retrieval`. **What ships is the seam, not better retrieval quality**: the bundled `FakeEmbedder` is a deterministic sha256 projection with no semantics, so over a structural tie the order is deterministic but arbitrary. A real embedder is selected from a CLOSED registry via `--embedder-config` / `build_embedder` — deliberately never an import path, so a config file can never name arbitrary code to load. **Off by default and additive**: with no retriever passed the store delegates to `StructuralRetriever`, which reproduces the pre-seam ranking exactly, so the text-excluded default and every existing test are unchanged. The embedding excludes `description`, matching `similarity` ("text is ignored by design") and the verdict-id hash — so a flag-on run reads no surface text either, and a genuine expert verdict can no longer be outranked by the framework's own echo of the query. The ranker is passed PER CALL, never assigned to the caller's store, so the opt-in cannot outlive the run that asked for it. Accepted in both run modes; in single-project mode it requires `--bundle-dir` and `--verdict-dir` and is refused — never silently ignored — without them. Determinism, precisely: ranking order rests on the total order `(-round(score, 9), id)`; the BLAS thread pins set before numpy is imported (`VECLIB_MAXIMUM_THREADS` for Accelerate, `OPENBLAS`/`MKL`/`OMP` for other backends) defend the narrower claim that vector artifacts are byte-identical across environments. Ships an optional, rebuildable `vectors.npy` + `vectors.jsonl` store (byte-identical regardless of insertion order; fail-fast on a row/line mismatch; missing loads as `None`) — an authoring primitive with no caller in `src/`, offered to extenders like `write_verdict` and `promote_verdict`. numpy is confined to `semretrieval.py` and never enters `okf.py`, `retrieval.py` or `shared/`. - Azure/Foundry offline preflight config gate (`preflight.py`, S4.1). - Offline live-dry-run drill (`--live-dry-run`, S4.2): walks the whole path up to the eager client build and stops before the first model call — zero chat calls. - Out-of-band HITL verdict routing CLI (`hitl.py`, S5.1): code-prefix routing on the candidate id. - Expert-notification contract (`notify.py`, S5.2): the declared `Notifier` with `ConsoleNotifier`, `FileNotifier` (byte-deterministic JSONL), and `WebhookNotifier`, plus a fail-fast `build_notifier`; the webhook is the only egress point, fail-closed behind an explicit per-run `allow_egress` opt-in. Not auto-wired into `run.py`. - `docs/knowledge-base-recipe.md` (S5.3, D-H item 1): the documented team process (technical + domain expert) for building a knowledge base, with the honest 1–2 week expectation. - Test suite: 512 passing tests (4 skips are live-provider-only). Every wired seam is covered by a load-bearing test that goes red when the seam is detached — including, since the S3.1 review remediation, the `--semantic-retrieval` wiring at `main()` level. That claim did not hold for S3.1 as first shipped: the flag was covered only below the CLI, so hardcoding it off left the suite green. Each remediated seam now carries a recorded detach point, verified by mutation. ### Notes - Licensed under the MIT License (see `LICENSE`).