portfolio-optimiser/README.md
Kjell Tore Guttormsen c0d3ba4459 docs(s53): README two-mode CLI matrix + honesty-scoped learning-loop claims + CHANGELOG catch-up
README: the stale one-line CLI mention replaced by the two-mode flag matrix (single-project vs
--portfolio), runnable 'uv run python -m portfolio_optimiser.run ...' examples, the --outbox-dir
!= --verdict-dir self-contamination warning (documented, not enforced), and the --decision/
--rationale inert-in-portfolio note. Honesty scoping: the prior-verdict fold (the learning step)
is stated to happen ONLY on the --bundle-dir path; a --docs-dir-only run is single-shot (no fold).
CHANGELOG [Unreleased]/Added: S5.3 CLI-parity entry (six flags + portfolio mode + load_dimension +
recipe doc) plus catch-up for the shipped-but-undocumented S4.1 (preflight), S4.2 (--live-dry-run),
S5.1 (hitl CLI), S5.2 (notify); stale test count 237 -> 431. extending.md verified accurate (B11
notifier note stands verbatim — main() auto-wires no notifier; no CLI section to sync), left
unchanged.

SC4 honesty grep clean (each hit in bundle-path context, none on a fold-less path):
  grep -rniE 'learning loop|learns from|self-improv' README.md docs/extending.md CHANGELOG.md
  README:11 (system-level) :56 (wiki substrate) :67 (8-step bundle loop);
  CHANGELOG:13 (gated ExpeL fold) :17 (offline simulation). Full suite 431 passed (no code touched).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KNNiJRk1sSwxgVLS5AobT1
2026-07-23 21:51:00 +02:00

159 lines
9.6 KiB
Markdown

# portfolio-optimiser
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Python](https://img.shields.io/badge/python-%E2%89%A53.10-blue.svg)](pyproject.toml)
[![Built on Microsoft Agent Framework](https://img.shields.io/badge/built%20on-Microsoft%20Agent%20Framework-0078D4.svg)](https://github.com/microsoft/agent-framework)
A generic, open framework — built on **Microsoft Agent Framework (MAF)** — that finds cost
savings *inside* each project of a portfolio of independent projects. A swarm of agents
generates candidate measures; a **mandatory deterministic validator** (solver + Monte Carlo)
decides the numbers; domain experts judge the outcomes (human-in-the-loop); and the system
**learns from their verdicts** across runs.
> **Status:** the full 8-step agentic loop is wired and proven with load-bearing tests, and the
> end-to-end proof is an **offline simulation** with a scripted stand-in client — no live-model
> run yet. The **ingest layer** (real data sources) is implemented — file/CSV and SQL on both
> stacks with bit-identical golden extractions from the shared spec, plus HTTP as a MAF-only
> demonstrated extension point against a local mock — but exercised only against committed
> fixtures: no bundle has yet been materialized from a live source. A sibling implementation of
> the same method on the **Claude Agents SDK** is built in parallel from the same shared spec.
> **Disclaimer — technical framework only.** Deploying organizations own their processing
> purposes and assessments (DPIA, risk/ROS, security review). The framework ships the technical
> prerequisites — local-only mode, provenance, no silent data egress — but makes no compliance
> guarantees.
## Built on an LLM wiki: Karpathy's idea, Google's format
The knowledge architecture is the heart of the project, and it is deliberately not ours:
- **The idea** is Andrej Karpathy's **"LLM wiki"**: instead of pointing a model at documents
written for people, you curate a small, versioned body of knowledge written *for the model
to read* — concept files, explicit structure, explicit links.
- **The format** is Google Cloud's **[Open Knowledge Format (OKF)](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md)**
(open spec, v0.1), which formalizes that pattern: a knowledge **bundle** is a directory of
markdown files with YAML frontmatter (one required field, `type`), a reserved `index.md`
entry point, and intra-bundle cross-links forming an emergent graph. Custom frontmatter
fields are allowed and must be preserved — which is exactly where this project's own layers
(expert verdicts, ingest provenance) live.
Because OKF is open and vendor-neutral, the *same* bundles are consumed unchanged by both
reference implementations (MAF and the Claude Agents SDK sibling) — the knowledge outlives any
particular agent stack.
**Not RAG.** Agents read a bundle by **navigating** it — `index.md` first, then its
cross-links, with progressive disclosure — never by keyword retrieval or stuffing the whole
bundle into a prompt. Query-time retrieval against the bundle is explicitly forbidden by the
method spec: it would leak the verdict layer around the learning gate.
## AI-first, humans on top
A traditional wiki is built for *people* — optimized for humans finding and reading
information, with machine access bolted on afterwards. This project inverts that order, and is
a concrete example of what that looks like:
- The wiki (the OKF bundle) is written **for the model**: it is the agent's working memory and
the substrate the learning loop reads from and promotes into.
- The **human affordances are layers on top**: experts judge outcomes by dropping a plain JSON
verdict file in an inbox folder; an explicit, fail-closed **promotion gate** is the only path
by which an approved verdict becomes wiki knowledge; reports and reviews are rendered *from*
the machine-readable layers.
Humans stay decisive — nothing enters the wiki without an approval — but the primary reader of
every file is the model, not a person browsing.
## How it works
One run, one project, eight steps — with the learning loop closing across runs:
1. **Understand** — navigate the project's OKF bundle; fold the candidate's *prior expert
verdicts* into the hypothesis prompt (ExpeL-style, retrieved structurally, never by text).
2. **Hypothesise** — one typed candidate measure (strict IR, fail-fast schema).
3. **Debate** — a maker-checker pair argues the reasoning (round-capped).
4. **Validate** — two falsifiers on the same candidate: the **deterministic validator** gates
the numbers (blocking, never optional) and the **checker** gates the reasoning.
5. **Refine** — a rejected attempt retries *informed* by the rejection reason, under hard
attempt and token caps. Unbounded loops are forbidden everywhere.
6. **Propose or discard** — a validated proposal with risk percentiles, or a typed rejection.
7. **Expert feedback** — days later, an expert drops a verdict file in an inbox folder; a
later run picks it up. Fully resumable; no live session assumed.
8. **Promote** — an *approved* verdict is lifted into the wiki as a `type: verdict` concept
file, navigable by the next run. The gate is fail-closed: raw agent output never
self-promotes.
Every proposal carries provenance (citations into the bundle, model, validator decision, token
usage). Every seam above is protected by a **load-bearing test** — a test designed to *fail*
when the seam is detached, so the loop cannot silently degrade into theater.
## How it is set up
- **One shared, framework-neutral core** ([`shared/`](shared/), a git subtree of
[`portfolio-optimiser-commons`](https://git.fromaitochitta.com/ktg/portfolio-optimiser-commons)):
the business concept, the normative [method spec](shared/method-spec.md) and
[ingest spec](shared/ingest-spec.md), the expert-reviewer persona as an Agent Skill, and an
example bundle with a golden suite as the only ground truth. Both stacks implement from the
spec alone.
- **Per project: one OKF bundle** — the bundled examples are hand-curated; the ingest layer that
materializes a bundle from a source (file catalogues/CSV + SQL, HTTP as a MAF-only demonstrated
extension point) via a deterministic, schema-validated manifest that runs *before* the loop is
implemented and exercised against committed fixtures — no bundle has yet been materialized from
a live source.
- **Run:** the `run.py` CLI has **two modes** — a documented partition, since one invocation
cannot exercise every flag:
- **Single-project** — `PROJECT_ID --docs-dir <dir>`, plus optional `--bundle-dir`,
`--verdict-dir`, `--outbox-dir` (which requires `--run-id`), `--dimension-config`,
`--decision`/`--rationale`, and `--live-dry-run`.
- **Portfolio** — `--portfolio`, plus optional `--goals`, `--ledger`, `--dimension-config`;
it stops early and prints a `goal reached: …` line when the accumulated ledger meets a goal.
```bash
# Single-project, offline drill (builds contracts + clients, stops before the first model call):
uv run python -m portfolio_optimiser.run FV42-GSV-E1 --docs-dir <docs> --bundle-dir <bundle> --live-dry-run
# Portfolio run with a savings goal checked against an accumulated ledger:
uv run python -m portfolio_optimiser.run --portfolio --goals goals.json --ledger ledger.json
```
The **prior-verdict fold — the learning step — happens only on the `--bundle-dir` path**; a
plain `--docs-dir`-only run is single-shot (no fold). `--decision`/`--rationale` apply to the
single-project path only and are inert in portfolio mode. **`--outbox-dir` must differ from
`--verdict-dir`**: writing the raw outbox into a folder later read as an inbox would re-ingest
raw agent output past the promotion gate (self-contamination) — documented here, deliberately
not CLI-enforced. Stop criteria and budget caps are required at startup. Try the offline
end-to-end proof (no model, no network): `uv run python -m portfolio_optimiser.simulation`.
## What this enables
The reference case is portfolio cost review (the example bundle is a building-energy measure),
but the architecture is designed to generalize to any setting with the same shape — candidate
measures inside independent projects, numbers a deterministic tool can check, and judgement
only an expert has:
- **Portfolio reviews** — cost savings, energy efficiency, maintenance and procurement
measures, proposed per project and validated against the project's own data.
- **Compounding organizational memory** — approved expert verdicts become navigable knowledge;
the next run's hypotheses start from what experts actually decided, including realization
gaps no solver can compute.
- **Auditable AI** — an unbroken provenance chain from expert decision back through proposal,
bundle file and text span, and (with ingest) to the source system, query, and timestamp.
- **Vendor-neutral knowledge** — the same bundles drive two different agent stacks; switching
frameworks does not orphan the organization's curated knowledge.
## Docs
- [Target picture](docs/plan/2026-06-26-maalbilde-agentic-loop.md) — the agentic loop + OKF
knowledge architecture (north star).
- [Prior-art & platform research](docs/research/2026-06-23-prior-art-platform.md) (incl.
implementation register §15).
- [Ingest target picture](docs/plan/2026-07-03-maalbilde-ingest-lag.md) — connectors and the
ingest layer (frozen 2026-07-03).
## Stack & develop
Python ≥3.10 · MAF via the split GA packages (see `pyproject.toml`) · `uv`. Backend profiles:
Azure/Foundry (full) + local (fallback).
```bash
uv sync
uv run pytest
uv run ruff check .
```