feat(cli): console entry points + the demo's stderr damping (P4 pkt. 5 og 2)

Two commands are now part of the install surface a fresh clone gets from `uv sync`:
`portfolio-optimiser` (run:main) and `portfolio-optimiser-demo` (simulation:main).
Deliberately two of five main()s — costsim/hitl/preflight stay module-invoked; every
name here is a name the freeze has to carry. Pinned against the INSTALLED distribution's
metadata, not the TOML: a [project.scripts] line that has never been synced is a claim,
not a command. Measured: stdout is byte-identical across both invocation forms.

stderr (P4 pkt. 2), the session's open decision, resolved by measurement rather than by
preference. Damped: the round-cap notice only, via a filter on the emitting logger, keyed
on the message and installed by main() — never at import, so a library consumer keeps its
own logging config. NOT damped: the two ExperimentalWarnings. They fire while the package
__init__ imports run -> agent_framework, always before simulation's own imports and under
both invocation forms, so silencing them would mean filtering warnings inside the library
package on every consumer's behalf; they are pinned in pkt. 3 instead. A console-script
wrapper was rejected for a second reason: the two forms would then write different stderr,
and a byte-fasit would pin the command rather than the program. stderr 6 -> 4 lines.

A first implementation wrapped simulation's own agent_framework import in a scoped mute.
Measurement showed it can never fire — the package __init__ has already imported
agent_framework by then — so it was removed rather than left as a green-but-dead seam.

Load-bearing MEASURED against the whole suite, five mutations all red + green control:
remove [project.scripts] · typo the target · detach the main() call · make the filter drop
everything · install the filter at import time. The typo mutation also felled a test: the
resolve-assert re-checked the expected constant against itself, and now resolves what the
distribution actually installs. 775 -> 785 passed / 4 skipped.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01C2bxLcCRguxXzpM4priTMn
This commit is contained in:
Kjell Tore Guttormsen 2026-08-09 15:13:09 +02:00
commit ab7f45aa95
6 changed files with 298 additions and 2 deletions

View file

@ -26,6 +26,11 @@ uv sync
Clone rather than install into an existing environment: the shared spec, the persona skill and the
example bundles under [`shared/`](shared/README.md) are read from the working tree at run time.
`uv sync` installs two commands: `portfolio-optimiser` (the CLI) and `portfolio-optimiser-demo`
(the offline end-to-end proof). They are equivalent to the `python -m portfolio_optimiser.run` and
`python -m portfolio_optimiser.simulation` forms used throughout this README, which keep working —
the module form is spelled out below so a reader can see which module answers a given command.
Verify the install by running the whole suite from the clean clone:
```bash
@ -54,7 +59,7 @@ ls shared/examples/bygg-energi-mikro/
demonstrably informed by the first:
```bash
uv run python -m portfolio_optimiser.simulation
uv run python -m portfolio_optimiser.simulation # or: uv run portfolio-optimiser-demo
```
Each run shows the refinement step: the proposer's first claim is falsified by the deterministic