portfolio-optimiser/docs/fase1-spikes
Kjell Tore Guttormsen 37547fe292
refactor(examples): replace sector-specific example material with generic, fictitious examples
The context sets, the packaged knowledge bases and the example bundles are
replaced by one fictitious example set about IT operations in an invented
organisation: three context sets (serverrom-2027, driftsavtale-2027 and the
two-base drift-og-avtale-2027), two synthetic knowledge bases under
src/portfolio_optimiser/data/kunnskapsbaser and two example bundles under
src/portfolio_optimiser/data/bundles. Numbers, codes and structural values in
tests and fixtures are kept; names, ids and wording change. Dated measurement
documents that only recorded runs on the replaced material are deleted.

Gate figures measured on the new set are not comparable with earlier ones.
The exclusion gate from the previous commit is green: 0 tracked files hit
outside the shared/ subtree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-23 15:04:21 +02:00
..
findings-a.md feat(fase1): spike A - group chat maker-checker vs single-agent [skip-docs] 2026-06-24 10:13:23 +02:00
findings-b.md refactor(examples): replace sector-specific example material with generic, fictitious examples 2026-09-23 15:04:21 +02:00
findings-c.md refactor(examples): replace sector-specific example material with generic, fictitious examples 2026-09-23 15:04:21 +02:00
findings-d.md feat(fase1): spike D - verdictstore + expel retrieval [skip-docs] 2026-06-24 10:32:39 +02:00
README.md fix(fase1): spike B fan-out measures real conversation bleed, not a counter 2026-06-24 11:09:55 +02:00

Fase 1 — De-risk spikes (A–D)

Throwaway spikes. Their only job is to turn the research §15 register's most dangerous documented-but-unverified assumptions into measured facts before the Fase 2 vertical slice. Code here is expected to be discarded once the findings are recorded — see Disposal.

Why these four

Before committing to the full architecture in Fase 2, we empirically de-risk the four assumptions that — if wrong — force a redesign:

Spike Assumption (register ref) What it measures
A Group Chat maker-checker beats a single-agent baseline by enough to justify its multiplicative token cost (U3 / G7) convergence rounds, stall frequency, token use — maker-checker vs single-agent, with a cheaper/better verdict
B The known MAF footguns behave as predicted and our guards hold: Magentic unbounded termination when limits=None (G1/B4); shared-builder / fan-out state corruption (G2/B7) guard fires on unbounded Magentic; a reused Workflow accumulates conversation across runs (project N contaminates N+1) while the fresh-instance helper gives each run a clean thread — measured by received-message content, not a call counter
C A blocking deterministic hybrid-validator (B1) can structurally block an out-of-range proposal structural rejection of an out-of-range proposal; P10/P50/P90 for a valid one; capped self-repair
D ExpeL retrieval (B2) surfaces a relevant prior verdict for a similar new proposal top-K retrieval returns the structurally-similar verdict over surface-text decoys

Each spike produces a short findings note (findings-{a,b,c,d}.md) with a confirmed/refuted verdict and a token-use line (including 0 — no live LLM where the spike runs without an endpoint).

Resolved version gate (premise-verified at planning time)

  • Installed agent-framework-core = 1.9.0 (introspection / uv pip show).
  • The orchestration builders (GroupChatBuilder, ConcurrentBuilder, MagenticBuilder, StandardMagenticManager, TerminationCondition) live in the separate package agent-framework-orchestrations, GA 1.0.0 (requires agent-framework-core<2,>=1.9.0 — exactly our installed core).
  • Because the spikes are throwaway, this package + pulp (Spike C solver) + pytest-asyncio (MAF orchestrations are async) are pinned in the dev optional-dependency group — not core. Promotion to core is a Fase 2 decision.

Gate-green contract

Each spike splits into:

  • a logic layer — pure functions/classes we author, always exercised by the quality gate (no live endpoint, no full MAF workflow run); and
  • an integration/live layer — drives the real MAF builders and/or a real LOCAL LLM; runs when available, otherwise skips (except Spike B, where driving the builders with a fake client IS the de-risk).

The headline empirical claims (Spike A's better/cheaper verdict; live token numbers) live in the integration/live layer and are honestly reported as endpoint-dependent. The gate stays green from the logic layer alone.

Status — all four de-risked (gate green)

Spike Assumption Result Verdict Token use Implication for Fase 2
A maker-checker > single-agent (U3/G7) verdict logic green; GroupChatBuilder drivable; cheaper/better is endpoint-dependent CONFIRMED (logic) — empirical arm gated word-count proxy; live arm not run (no endpoint) Keep the codified verdict (better ∧ ≤3× tokens); measure the empirical cost/benefit on a LOCAL endpoint before locking the debate default
B Magentic unbounded + fan-out bleed (G1/G2) unbounded max_round_count=None needs an external guard; a reused Workflow accumulates the conversation thread across runs (project N's prompts/replies leak into N+1), a fresh instance per run does not — measured by received-message content CONFIRMED 0 — no live LLM Require explicit round/stop caps for any Magentic loop (fail-fast); use a fresh-instance-per-run factory for fan-out (B7)
C blocking hybrid-validator (B1) typed IR + real CBC solve + Monte-Carlo P10/P50/P90; out-of-range → Rejection (distinct type, no percentiles) CONFIRMED 0 — deterministic; live gen gated Keep the Rejection/ValidatedProposal type split (structural block) + CBC-absent escalate; migrate to pulp[cbc]/COIN_CMD for PuLP 4.0
D ExpeL retrieval (B2) structural similarity (codes + measure + magnitude) returns the true match as top-1 over surface-text decoys; deterministic CONFIRMED 0 — deterministic retrieval Keep structured similarity as the baseline; add embeddings only if it proves insufficient on real data

Builder de-risk (Step 2): FakeChatClient drives the GA GroupChatBuilder, ConcurrentBuilder, and MagenticBuilder — confirmed by the builder smoke + probes, so Spikes A/B never needed the escalate fallback. Per-spike detail: findings-{a,b,c,d}.md.

Quality gate at Fase 1 close: ruff check . ✓, ruff format --check . ✓, mypy src ✓, pytest ✓ (48 passed, 1 skipped = the gated live arm). src/ untouched.

Disposal

These spikes are throwaway. To remove them completely after the findings are recorded:

rm -rf spikes tests/spikes docs/fase1-spikes

Then revert the three pyproject.toml edits that supported them:

  1. Remove the dev deps agent-framework-orchestrations, pulp, pytest-asyncio from [project.optional-dependencies].dev.
  2. Restore [tool.ruff] src = ["src", "tests"] (drop "spikes").
  3. Restore [tool.pytest.ini_options] pythonpath = ["src"] and remove asyncio_mode = "auto".

Then uv sync --extra dev to regenerate uv.lock. Nothing in src/ is touched by the spikes, so disposal leaves the framework core exactly as Fase 0 left it.