portfolio-optimiser/src/portfolio_optimiser
Kjell Tore Guttormsen 7552c0ef73 fix(explore): a plan review never shows an expert the word None and calls it progress
[skip-docs]

PM addendum 4 to order 20260902T151931Z-250257273.

PlanReviewRequest.current_progress and ParkedExploration.current_progress were
both built with str(review.current_progress). The value is a
MagenticProgressLedger | None and is None in every run measured so far, so
str() produced the four characters None, the renderer's truthiness guard found
them non-empty, and the terminal printed a "progress so far" section whose only
content was None. The same four characters went into
{run_id}-plan-review.json -- the one thing that crosses the process boundary in
the asynchronous door, where nobody can ask what it meant.

_progress_text is the ONE conversion, beside _plan_text: absent becomes the
empty string, never "None". The data layer states absence by being absent, as
Bundle.skipped's empty tuple does. The TERMINAL states it in words -- this is
the one surface where omission is wrong, because silence at a gate somebody
signs is exactly what PlanReviewInputError already refuses for EOF.

Measured, and it changed the test: reverting both sites left ONLY a
source-inspection arm red, which is a lint and not a gate, because the first
arms construct a PlanReviewRequest themselves and never enter either site. Each
site now has a behavioural witness that drives the real door -- the terminal for
the synchronous one, the parked question FILE for the asynchronous one.

Load-bearing measured, five mutations, all red against the WHOLE suite, each
with its own signature: both sites reverted (3 red) - synchronous site alone
(2) - parked site alone (2) - the terminal goes silent again (2) - the
placeholder always fires, swallowing a real ledger (1, the control alone).
Green control 1202 passed / 5 skipped; golden demo-transcript.stdout unchanged
(ea8c534773acdbe41ae68f2c55724d69aaf8be4f).

The recorder wiring in resume_exploration is untouched, per the order.
2026-09-03 01:06:27 +02:00
..
data feat(validator): anchor the deterministic gate to the project's real cost baseline (S4.0) 2026-08-03 17:19:31 +02:00
__init__.py feat(3): flaten sier sant om seg selv, og to gater måler at den fortsetter å gjøre det 2026-08-14 06:57:25 +02:00
backends.py feat(4b): AZURE-profilen leser miljøet sitt, ikke operatørens laptop 2026-08-13 22:27:21 +02:00
budget.py feat(budget): enforce a global portfolio token cap before the call, not after it (S3.4/F10) 2026-07-31 21:34:48 +02:00
contracts.py feat(hitl): ekspertdommen kan ikke oppstaa av stillhet (F2, ORDRE 20260825T214801Z) 2026-08-27 01:22:07 +02:00
costsim.py docs(s54): --report CLI surface + truthful costsim kost_mot_verdi seam note 2026-07-24 01:38:10 +02:00
datasource.py feat(fase2b): OKF-navigated bundle context replaces chunk-stuffing 2026-06-30 06:42:19 +02:00
dimension.py feat(s53): load_dimension fail-fast loader (MAF-free, mirrors load_goal_config) 2026-07-23 21:31:32 +02:00
explore.py fix(explore): a plan review never shows an expert the word None and calls it progress 2026-09-03 01:06:27 +02:00
generate.py feat(1b): proposeren får en grammatikk — strict structured output [skip-docs] 2026-08-14 14:00:32 +02:00
hitl.py feat(explore): plan-reviewen kan besvares over DAGER (U12 + asynkron U13, rad 3) 2026-08-26 12:26:04 +02:00
hosting.py feat(hitl): ekspertdommen kan ikke oppstaa av stillhet (F2, ORDRE 20260825T214801Z) 2026-08-27 01:22:07 +02:00
ingest.py feat(ingest): P2/S1.b — innholdsgaten står, rundt materialize og ikke i den 2026-08-09 22:58:00 +02:00
ingest_mcp.py chore(deps): move the ingest library pin to v0.3.2 — as far as latest goes today 2026-08-05 12:11:12 +02:00
ir.py feat(validator): anchor the deterministic gate to the project's real cost baseline (S4.0) 2026-08-03 17:19:31 +02:00
ledger.py fix(money): quantize NOK to øre in one order, from one source (kø-p) 2026-08-03 20:08:59 +02:00
mandate.py feat(okf): one bundle-id rule, reconciled against the mount and origin-marked 2026-09-02 21:04:04 +02:00
mcp_tools.py feat(provenance): a run records which external service it actually called 2026-08-05 21:37:29 +02:00
notify.py fix(s52): resolve ConsoleNotifier default stream at call time 2026-07-17 03:18:00 +02:00
okf.py feat(okf): one bundle-id rule, reconciled against the mount and origin-marked 2026-09-02 21:04:04 +02:00
outbox.py feat(explore): plan-reviewen kan besvares over DAGER (U12 + asynkron U13, rad 3) 2026-08-26 12:26:04 +02:00
persona.py feat(persona): load the falsification skill from commons at call time 2026-09-02 21:31:32 +02:00
preflight.py fix(5): preflight kjenner samme endepunkt-variabler som kjørestien [skip-docs] 2026-08-14 10:48:45 +02:00
provenance.py feat(visibility): en uforankret kjoering sier det - felt + linje (ORDRE 20260821T092039Z) 2026-08-21 12:21:48 +02:00
reference_domain.py feat(hitl): ekspertdommen kan ikke oppstaa av stillhet (F2, ORDRE 20260825T214801Z) 2026-08-27 01:22:07 +02:00
retrieval.py fix(retrieval): fail-closed on uncanonicalisable paths (embedded null byte) 2026-07-21 08:00:11 +02:00
run.py feat(verdicts): a cross-base candidate collision is reported, never dropped in silence 2026-09-02 21:15:50 +02:00
semretrieval.py fix(semretrieval): refuse a non-finite embedding instead of scoring it (kø-(l)/S3.1 MINOR) 2026-08-03 21:48:50 +02:00
shared_root.py feat(4a): wheelen bærer shared/ som pakkede data — arbeidstreet er overstyringen 2026-08-13 21:13:08 +02:00
simulation.py feat(explore): den offline generalproeven kan AAPNE en base (MAJOR-1 b, ORDRE 20260902T151931Z) 2026-09-02 18:45:53 +02:00
tools.py feat(okf): adjudication_for names the unknown state and a tool carries it 2026-09-02 20:36:54 +02:00
tracing.py fix(maf): en vakt som gikk inert i STILLHET, funnet ved aa loefte pinnen (F15, ORDRE 20260829T155150Z) 2026-09-02 19:35:49 +02:00
validator.py feat(validator): anchor the deterministic gate to the project's real cost baseline (S4.0) 2026-08-03 17:19:31 +02:00
value_report.py feat(s54): value_report text + deterministic JSON formatters 2026-07-24 01:29:31 +02:00
verdicts.py feat(verdicts): a cross-base candidate collision is reported, never dropped in silence 2026-09-02 21:15:50 +02:00
workflow.py feat(fase3): make the maker-checker checker actually gate the reasoning 2026-06-30 07:24:30 +02:00