portfolio-optimiser-claude/tests
Kjell Tore Guttormsen c267bfa255 feat(harness): the procedure rebuilt ten times gets the four controls it kept skipping
Point 7 decided: green-before/red-after becomes a standalone script, not a
pytest fixture and not prose. Ten hand-builds (2x oekt 23, 4x 31, 2x 32, 1x 33,
1x 37) is not the argument on its own -- typing was never the cost. The argument
is that four of its controls fail SILENTLY, and prose cannot enforce any:

  the anchor occurring exactly once, so the mutation lands on the seam alone;
  the node id having actually run -- MEASURED here, a mistyped id exits 4, which
    is non-zero and therefore MIMICS red to any harness asking "rc != 0?"
    (and `--collect-only -q` exits 0 on an id that does not exist, so that is
    not an existence check either);
  the restore read back FROM DISK and sha256-compared, because under .venv/
    nothing is tracked and `git status` never sees the mutation;
  the redness landing where the proof is about.

NOT a fixture. The suite runs every session, so a fixture that writes to disk
turns every interrupted run into a mutated tree -- and it would mutate modules
the running process already imported. The mutation class that CAN live in the
suite (mutate a copy in memory, call the guard directly) already does, as
test_guard_red_when_*, and needed no tool at all.

THE ORDERING QUESTION, ANSWERED RATHER THAN LEFT AN IMPLEMENTATION DETAIL.
"Positive controls before negatives" was never in tension with this procedure.
It only looked that way because "the control" was read as one thing when it is
three. --red must be green BEFORE (the measuring apparatus: the id resolves and
passes right now) and red AFTER (the measurement itself -- "can this go red at
all?" is not answerable until the mutation exists, so this is not a control
that ran late). --green must hold in BOTH runs; if it reds, the mutation landed
wider than the seam and the target's redness attributes to nothing. The order
follows from what each one measures.

VALUE-PROVED IN BOTH DIRECTIONS -- AND THE FIRST VERSION FAILED IT. Gating on
"is it an AssertionError?" rejected three of the four real proofs run against
the tool itself: a legitimately red test dies as AssertionError, as
`Failed: DID NOT RAISE`, or as a custom exception. That is modelling pytest
instead of reading it (oekt 28), and a gate that refuses real evidence gets
switched off, which is worse than none. Replaced with --red-at: the caller pins
the line, the tool checks the pin against pytest's real output, and an unpinned
red is reported with its type rather than silently blessed.

Five seams proved by the harness, each restored byte-identical: rc=4 read as
red · the anchor check disabled · the green-before check disabled · the restore
verification disabled · and the parity extension below. They died of
AssertionError, NotAValueProof, Failed, Failed and AssertionError -- the spread
that killed the first gate. Negative half: a docstring-only mutation stays green
and is refused as NOT a value proof; a mistyped id raises; a 16-occurrence
anchor is refused before a byte is written.

Two honesty guards caught this commit on the way in, both correctly. The README
anchor guard reddened on a new heading -- its ids are ground truth measured
against the published surface and may not be re-derived, so the heading became
bold text instead. The README/CLI parity guard reddened because the harness is a
second command-line surface that is neither a portfolio_optimiser_claude module
nor third-party tooling: its help is now captured too, with a positive control,
so its documented flags stay MEASURED rather than exempted.

923 -> 950 tests. ruff, mypy strict (src + scripts), full suite green, offline.
`git status` clean before and after every mutation run.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 08:57:06 +02:00
..
data feat(portfolio): K11 — per-run value report, pure projection over the three layers (parity row 25) [skip-docs] 2026-07-25 06:25:02 +02:00
_scripted.py feat(loop): S8 — D7 agentic loop: budget meter, maker-checker gate, informed refinement 2026-07-03 07:21:02 +02:00
test_budget.py feat(portfolio): C3.5 — pre-call run-total USD budget belt (parity row 16/31) [skip-docs] 2026-07-23 22:45:37 +02:00
test_bygg_energi_mikro.py feat(validator): S6 — deterministic backbone: typed IR, golden-frozen validator, provenance stamp 2026-07-03 06:27:40 +02:00
test_checker_gate_loadbearing.py feat(loop): S8 — D7 agentic loop: budget meter, maker-checker gate, informed refinement 2026-07-03 07:21:02 +02:00
test_cli_paritet_loadbearing.py feat(harness): the procedure rebuilt ten times gets the four controls it kept skipping 2026-08-25 08:57:06 +02:00
test_contracts.py feat(portfolio): K2 — sequential multi-project run (parity row 4) 2026-07-23 21:39:39 +02:00
test_costsim.py test(loadbearing): positive controls for the static-guard half of the sibling-vacuity class 2026-08-01 20:01:21 +02:00
test_dry_run_loadbearing.py feat(portfolio): K8 — live-run drill, pre-call artifact capture (parity row 21) [skip-docs] 2026-07-24 06:54:58 +02:00
test_goals.py feat(ledger): K1 — savings ledger + goal contract (parity rows 2-3) 2026-07-17 04:00:14 +02:00
test_hitl_loadbearing.py test(loadbearing): measure the last 8 all-negative tests instead of assuming them 2026-08-09 10:08:12 +02:00
test_ingest.py chore(deps): re-pin llm-ingestion-okf to v0.3.1 + migrate tests to stable error codes 2026-07-20 07:22:09 +02:00
test_ingest_adoption.py chore(deps): re-pin llm-ingestion-okf to v0.3.1 + migrate tests to stable error codes 2026-07-20 07:22:09 +02:00
test_ingest_golden.py feat(ingest): I3 — D7-speil av ingest (filkatalog/CSV), bygget fra commons-spec alene 2026-07-04 06:12:43 +02:00
test_ingest_loadbearing.py test(loadbearing): the spec moved to O2 and the emitter did not — ratchet the gap 2026-08-09 10:19:40 +02:00
test_ingest_spec_loadbearing.py test(ingest-spec): §11's two tables get the §12 row-anchor form 2026-08-20 23:18:23 +02:00
test_ingest_sql.py chore(deps): re-pin llm-ingestion-okf to v0.3.1 + migrate tests to stable error codes 2026-07-20 07:22:09 +02:00
test_ingest_sql_golden.py feat(ingest): I5 — SQL D7-speil, bygget fra commons-spec alene 2026-07-04 07:18:59 +02:00
test_ingest_sql_loadbearing.py test(loadbearing): the spec moved to O2 and the emitter did not — ratchet the gap 2026-08-09 10:19:40 +02:00
test_ingest_stamp_conformance_loadbearing.py test(loadbearing): the spec moved to O2 and the emitter did not — ratchet the gap 2026-08-09 10:19:40 +02:00
test_ir.py fix(validator): C2.6 — finiteness hardening, Infinity can no longer vacuously clear the gate (closes R-2) 2026-07-16 20:10:12 +02:00
test_ledger_loadbearing.py fix(ledger): normalize every load rejection to ValueError at the ledger's own entrance 2026-07-25 15:29:27 +02:00
test_loop.py test(loadbearing): close the vacuous-negative class across the whole suite 2026-07-31 21:39:28 +02:00
test_method_spec_loadbearing.py test(loadbearing): positive controls for the static-guard half of the sibling-vacuity class 2026-08-01 20:01:21 +02:00
test_method_spec_rule_phrases_loadbearing.py test(loadbearing): bind a rule phrase to every section the shape guard cannot see 2026-08-09 10:25:55 +02:00
test_mutation_harness.py feat(harness): the procedure rebuilt ten times gets the four controls it kept skipping 2026-08-25 08:57:06 +02:00
test_notify_loadbearing.py test(loadbearing): positive controls for the static-guard half of the sibling-vacuity class 2026-08-01 20:01:21 +02:00
test_notify_seam_loadbearing.py feat(portfolio): K10 — notification/notifier seam, opt-in webhook egress (parity row 23) [skip-docs] 2026-07-24 20:16:56 +02:00
test_okf.py test(loadbearing): positive controls for the static-guard half of the sibling-vacuity class 2026-08-01 20:01:21 +02:00
test_outbox_loadbearing.py feat(portfolio): K5 — outbox persistence, run_id-named pairs (parity row 7) [skip-docs] 2026-07-23 22:31:18 +02:00
test_persona_skill_loadbearing.py feat(learning): S9 — D7 læringssløyfe: verdict-inbox, fail-closed promoteringsgate, artefakt-sourced persona 2026-07-03 07:36:15 +02:00
test_portfolio.py feat(portfolio): K2 — sequential multi-project run (parity row 4) 2026-07-23 21:39:39 +02:00
test_portfolio_learning_loadbearing.py test(loadbearing): measure the last 8 all-negative tests instead of assuming them 2026-08-09 10:08:12 +02:00
test_preflight.py test(sdk): the pin was a permission, so give the premises a proof 2026-08-18 16:57:57 +02:00
test_provenance.py feat(validator): S6 — deterministic backbone: typed IR, golden-frozen validator, provenance stamp 2026-07-03 06:27:40 +02:00
test_provenance_sdk_version_loadbearing.py feat(run): stamp the drill's SDK build in the dry-run run-config [skip-docs] 2026-07-25 15:35:16 +02:00
test_readme_anchors_loadbearing.py fix(readme): the four dead TOC anchors, and a guard that reads the renderer instead of modelling it 2026-08-17 11:28:46 +02:00
test_run_entrance_loadbearing.py test(loadbearing): close the vacuous-negative class across the whole suite 2026-07-31 21:39:28 +02:00
test_s10_run_layer.py test(loadbearing): close the vacuous-negative class across the whole suite 2026-07-31 21:39:28 +02:00
test_sdk_isolation.py test(sdk): the pin was a permission, so give the premises a proof 2026-08-18 16:57:57 +02:00
test_sdk_tool_namespace_loadbearing.py test(sdk): the server knows its own name, and the tool never hears it 2026-08-25 08:33:07 +02:00
test_sdk_version_guard.py test(sdk): the pin was a permission, so give the premises a proof 2026-08-18 16:57:57 +02:00
test_simulation_loadbearing.py test(loadbearing): close the vacuous-negative class across the whole suite 2026-07-31 21:39:28 +02:00
test_step1_expel_loadbearing.py test(loadbearing): close the vacuous-negative class across the whole suite 2026-07-31 21:39:28 +02:00
test_step5_refine_loadbearing.py test(loadbearing): close the vacuous-negative class across the whole suite 2026-07-31 21:39:28 +02:00
test_step7_async_loop_loadbearing.py test(loadbearing): close the vacuous-negative class across the whole suite 2026-07-31 21:39:28 +02:00
test_step8_promotion_loadbearing.py test(loadbearing): close the vacuous-negative class across the whole suite 2026-07-31 21:39:28 +02:00
test_validator.py fix(validator): C2.6 — finiteness hardening, Infinity can no longer vacuously clear the gate (closes R-2) 2026-07-16 20:10:12 +02:00
test_valuereport_loadbearing.py test(loadbearing): close the vacuous-negative class across the whole suite 2026-07-31 21:39:28 +02:00
test_valuereport_seam_loadbearing.py feat(portfolio): K11 — per-run value report, pure projection over the three layers (parity row 25) [skip-docs] 2026-07-25 06:25:02 +02:00