docs(truth): S1 truth maintenance — CHANGELOG, stack line, MCP claim, conflict semantics, 90% cut-list
Findings 4-7 from the 2026-07-02 status analysis, per the session plan (S1): - CHANGELOG rewritten truthfully (was: 'Plan phase - no framework code yet') - README stack line names the split GA packages, not the agent-framework meta-package - CLAUDE.md: MCP downgraded to extension point (in-process FunctionTool is the default seam) - Verdict conflict semantics documented as chosen (store first-write-wins per id, disk/wiki last-write-wins per file; full B10 taxonomy deliberately deferred) - docs/extending.md: explicit 90%-principle cut-list (B10, B11, U12, U14, concurrent fan-out) - .gitignore covers .trekexecute-progress-* (docs/.DS_Store was already untracked/ignored - the plan's git rm --cached assumption was stale; no-op) No code behavior changed (docstring only in verdicts.py). Suite 152/4 green, mypy clean, ruff format --check clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AaQCFnfsh3tfq1VfzdJpoi
This commit is contained in:
parent
705c5dd49a
commit
ae01127510
6 changed files with 54 additions and 5 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -28,3 +28,6 @@ STATE.md
|
|||
# er operatør-lokal annotering m/ localStorage, progress.json er execute-tracker)
|
||||
.claude/projects/**/*.html
|
||||
.claude/projects/**/progress.json
|
||||
|
||||
# Voyage execute-tracker (efemert per kjøring)
|
||||
.trekexecute-progress-*
|
||||
|
|
|
|||
19
CHANGELOG.md
19
CHANGELOG.md
|
|
@ -3,5 +3,20 @@
|
|||
All notable changes to this project will be documented in this file.
|
||||
|
||||
## [Unreleased]
|
||||
- Initial repository scaffold (Python, `uv`, MAF). Plan phase — no framework code yet.
|
||||
- License to be finalized at release prep (Fase 4).
|
||||
|
||||
### Added
|
||||
- Deterministic backbone: mandatory blocking validator (solver + Monte Carlo against the shared
|
||||
golden suite), budget meter with hard fail-fast caps, provenance stamping.
|
||||
- Agentic learning loop wired end to end, one load-bearing seam at a time (target-picture steps
|
||||
1, 3/4, 5, 7, 8): OKF-navigated bundle context with the gated ExpeL fold, maker-checker debate
|
||||
where the checker gates the reasoning, informed refinement (previous rejection reason fed into
|
||||
the next bounded attempt), async verdict file inbox, and gated wiki promotion (fail-closed).
|
||||
- Offline end-to-end simulation proving the learning loop closes with a scripted client
|
||||
(`uv run python -m portfolio_optimiser.simulation`) — plumbing proof, not live-model proof.
|
||||
- Framework-neutral shared core in `shared/`: OKF concept + example bundle, golden validator
|
||||
suite, and the expert-reviewer persona as an Agent Skill.
|
||||
- Test suite: 152 passing tests (4 skips are live-provider-only), every wired seam covered by a
|
||||
load-bearing test that goes red when the seam is detached.
|
||||
|
||||
### Notes
|
||||
- License to be finalized at release prep.
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ Python ≥3.10. MAF (`agent-framework-core` 1.9.0). Pakkehåndtering: `uv`. To b
|
|||
- `ruff` for lint+format. `pytest` for test.
|
||||
- Modell-valg som konfig (modell-map rolle→Foundry-deployment), ikke spredt i kode.
|
||||
- Metode kodifiseres som **Agent Skill** (`agentskills.io`: `SKILL.md` + `scripts/` + `references/`).
|
||||
- Datatilgang via **MCP**-servere; data-source-konfig JSON-Schema-validert, fail-fast.
|
||||
- Datatilgang: in-process `FunctionTool` er default-sømmen i kjørestien; **MCP er extension point**, demonstrert via `build_mcp_server` (`datasource.py`) men ikke wiret inn i kjørestien. Data-source-konfig JSON-Schema-validert, fail-fast.
|
||||
|
||||
## Kommandoer
|
||||
- Sync: `uv sync`
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ The mandatory deterministic backbone (validator + budget meter + provenance) is
|
|||
- **Step 7 — async file feedback loop (wired).** `run_project(..., verdict_dir=...)` adds the long feedback timescale (target picture §3/§7): an expert/persona drops a verdict file (plain JSON — the raw output layer, §10 R2) into an inbox folder *after* a run, and a separate, later run ingests it — merged into the store *before* the Step-1 fold — so a verdict that landed out of band reaches the next hypothesis. The loop is fully resumable across runs separated in time; no live session is assumed. The system *reads* the folder, the expert/persona *writes* it (§3 role split), so `run_project` deliberately does **not** persist its own captured verdict back (that is the outbox / Step-8 concern). Ingestion is tolerant (a missing folder, foreign or half-written files are skipped, not raised) and **merges** (never replaces), preserving `run_portfolio`'s cross-project store. Reachable from the CLI via `--bundle-dir --verdict-dir`. Load-bearing: `tests/test_step7_async_loop_loadbearing.py` — a verdict dropped after run A must reach run B's prompt (run B uses a *fresh* store, so the transfer is the file loop, not in-memory carryover), with an empty-inbox control proving causality.
|
||||
- **Step 8 — gated wiki promotion (wired).** When an expert/persona **approves** an outcome, `verdicts.promote_verdict` lifts it from the raw output layer into the context layer (the OKF bundle) as a `type: verdict` concept file, navigable by the next run's `seed_store_from_bundle` (target picture §3/§6/§7). The **gate** is fail-closed: a verdict whose decision is not an approval raises `PromotionRefused` and writes/links nothing — only human/persona-approved knowledge enters the wiki, never raw agent output (self-contamination). The promotion is provenance-stamped (who approved / which experiment / when — `timestamp` is a required keyword, no wall-clock default). The OKF writer lives in `okf.py` and stays pure stdlib (D7-portable, MAF-free). **R4 = optional + gated:** `promote_verdict` is a public opt-in primitive, deliberately **not** wired into `run_project` (mirrors `write_verdict` — the system reads context; the gate/persona promotes). Two honesty limits: the promoted file is *minimal* (it carries the learning signal only as `description`/body prose — it does not reproduce the hand-authored seed's structured `realization_rate` etc.), and because the verdict id is the learning key, two approvals about the same candidate share a filename (last-write-wins, like `write_verdict`) — the wiki grows one curated file per distinct candidate, not per verdict event. Load-bearing trio (`tests/test_step8_promotion_loadbearing.py`): the gate refuses a non-approved verdict (red if the gate is removed), the approved verdict is navigable (red if `link_in_index` is detached), and the promoted signal stays out of `bundle_context` — reaching a prompt only via the gated ExpeL fold (red if a descriptive index label leaks it into the read-context).
|
||||
|
||||
**Verdict conflict semantics (chosen, minimal).** The in-memory `VerdictStore` is **first-write-wins per verdict id** (a re-ingested duplicate is dropped, so repeated inbox merges are idempotent), while the disk layers — `write_verdict` (inbox authoring) and `promote_verdict` (wiki) — are **last-write-wins per file**. Ids are content-hashes of the candidate *features*, so "same id" means "same candidate measure", not "same verdict event". A full verdict-conflict taxonomy (B10: rejection categories + a rule for conflicting expert verdicts) is deliberately deferred until real domain experts produce conflicting verdicts.
|
||||
|
||||
## Offline simulation — the end-to-end proof
|
||||
|
||||
The loop is proven end to end **offline**, with no real model. `portfolio_optimiser.simulation`
|
||||
|
|
@ -68,7 +70,7 @@ marker follows the artifact file (red the moment the persona is re-inlined).
|
|||
|
||||
## Stack
|
||||
|
||||
Python ≥3.10 · MAF (`agent-framework`) · `uv`. Backend profiles: Azure/Foundry (full) + local (fallback).
|
||||
Python ≥3.10 · MAF via the split GA packages (`agent-framework-core`, `agent-framework-foundry`, `agent-framework-openai`, `agent-framework-orchestrations` — deliberately **not** the `agent-framework` meta-package; see `pyproject.toml`) · `uv`. Backend profiles: Azure/Foundry (full) + local (fallback).
|
||||
|
||||
## Develop
|
||||
|
||||
|
|
|
|||
|
|
@ -51,3 +51,24 @@ Model choice is **config, not code** (B12): `src/portfolio_optimiser/data/model_
|
|||
|
||||
The role keys (`proposer`, `checker`, `default`) let you assign a distinct model per debate role;
|
||||
`default` is the fallback when a role is unmapped.
|
||||
|
||||
## Bevisst ikke bygget (90 %-kuttlista)
|
||||
|
||||
Per the design philosophy (a ~90 % generic core with clear extension points — we do not chase
|
||||
the last 10 %), the following are **deliberate cuts**, not roadmap debt. Each is extension
|
||||
territory for a deployer, with the seam named:
|
||||
|
||||
- **B10 — full verdict-conflict taxonomy.** Chosen minimal semantics (documented in
|
||||
`verdicts.py` + README): the in-memory store is first-write-wins per verdict id; the disk
|
||||
layers (`write_verdict`, `promote_verdict`) are last-write-wins per file. The full taxonomy
|
||||
(rejection categories + a rule for conflicting expert verdicts) is deferred until real experts
|
||||
produce conflicting verdicts.
|
||||
- **B11 — expert notification.** `run_project(notify=...)` is a stub seam (`run.py`): pass any
|
||||
callable; no delivery mechanism (e-mail/Teams/webhook) ships with the core.
|
||||
- **U12 — checkpointing / crash-survival of a run.** A run either completes or is re-run; the
|
||||
async verdict inbox (step 7) is the resumable boundary, not intra-run state.
|
||||
- **U14 — OpenTelemetry / observability.** Provenance stamping is the audit trail the core
|
||||
ships; OTEL wiring is a deployer concern.
|
||||
- **Concurrent fan-out.** `run_portfolio` iterates projects sequentially by design (fresh
|
||||
per-project execution state; one threaded `VerdictStore`); parallel orchestration is left to
|
||||
the deployer and would need budget-cap coordination.
|
||||
|
|
|
|||
|
|
@ -223,7 +223,15 @@ class VerdictStore:
|
|||
return ranked[:k]
|
||||
|
||||
def add(self, verdict: Verdict) -> None:
|
||||
"""Persist a captured verdict in-memory (idempotent on content-hash id)."""
|
||||
"""Persist a captured verdict in-memory.
|
||||
|
||||
Conflict semantics (chosen, documented): the store is FIRST-write-wins per ``id`` — a
|
||||
later verdict with the same content-hash id is dropped, which makes repeated Step-7 inbox
|
||||
merges idempotent. The disk layers are the opposite: ``write_verdict`` (inbox) and
|
||||
``promote_verdict`` (wiki) are last-write-wins per file. Because ids hash the candidate
|
||||
FEATURES, "same id" means "same candidate measure", not "same verdict event". A full
|
||||
verdict-conflict taxonomy (B10: rejection categories + a rule for conflicting expert
|
||||
verdicts) is deliberately deferred until real experts produce conflicting verdicts."""
|
||||
if all(v.id != verdict.id for v in self.verdicts):
|
||||
self.verdicts.append(verdict)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue