Commit graph

44 commits

Author SHA1 Message Date
392f8493da chore(repo): planning artifacts become local-only; fixture builders become code
Operator ruling 2026-08-05, which settles decision (g): planning documents are
generally never public, and what OUR OWN sessions generate does not go out on
the forge at all. The example itself stays public so others can run the
process.

`.claude/projects/` is the Voyage session workbench -- 25 briefs/plans/reviews
this project's own sessions produced. Untracked and gitignored, exactly as
STATE.md already is, and for the same stated reason: this repo has a public
mirror, so that class of material is local-only rather than tracked.

The line is drawn at who wrote the document, and it is drawn deliberately:
`docs/plan/`, `docs/research/` and `docs/rapport/` stay tracked. Those are
curated, dated documents written for the repo's readers, three of them linked
from the README as the decision record. Move that line if it was meant wider.

Two files were NOT process artifacts and are not deleted. Both
`build_fixture.py` scripts are cited by tracked tests
(`test_ingest_golden_sql.py`, `test_ingest_golden_http.py`) as the documented
rebuild path for byte-exact goldens -- reproduction code that had landed in the
wrong directory. Moved next to the goldens they build; both docstrings updated,
so no tracked file is left pointing into an untracked tree (verified: the only
remaining `.claude/projects` string in a tracked file is the .gitignore rule
itself). One prose reference in the dated Foundry auth recipe was dropped for
the same reason.

652 tests still pass.

Does NOT address the 27 of these already readable on open/ since the S12
release -- untracking stops future publication only. That retraction is a
separate operator decision and is deliberately not taken here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWsexbQjPo9rsV3aUE54ZS
2026-08-05 10:08:17 +02:00
a3b238307c docs(repo): meet the org repo-standard gate — 0 ERROR
Ran `repo-standard` (v0.1.1, class `standalone`) and fixed everything it
flagged as ERROR, plus the WARN links that were genuinely dead.

README first screen:
- opening line is now byte-identical to the forge description, so
  description == catalog == README is machine-checkable (badges moved below).
- `## Install` (required for class `standalone`): clone + `uv sync`, stated as
  clone-only because the shared spec, persona skill and example bundles under
  `shared/` are read from the working tree at run time. `uv run pytest` named as
  the verification, with the fact that no CI runner exists said out loud rather
  than implied by a badge.
- `## Non-goals` (required): the five limits already binding in CLAUDE.md —
  not a compliance product, not a portfolio-level reallocator, not autonomous
  decision-making, not turnkey, not a model benchmark.

Dead relative links (measured, not guessed):
- `docs/plan/2026-07-10-sesjonsplan-fase2-6.md` pointed at
  `../2026-07-14-revisjonspakke-DF-DI.md` six times; the file sits in
  `docs/plan/`, not `docs/`. (The sibling `../review-2026-07.md` links are
  correct and untouched.)
- the Fase-1 spike brief linked repo-root-relative from
  `.claude/projects/…/`; re-anchored with `../../../`.

The one remaining README ERROR was a gate false positive: `checkInternalLinks`
resolves targets against `git ls-files`, which lists files only, so a link to a
directory can never resolve. `[shared/](shared/)` now points at
`shared/README.md` — a better target anyway, since that file carries the
pull-only subtree rule. Not fixed here: the classifier lives in another repo.

Remaining WARNs are all inside `shared/`, deliberately untouched: it is a
pull-only commons subtree, and the nav-golden files are byte-level fixtures
that gate `test_nav_golden_*` — four of them are OKF bundle-internal links,
and the `/etc/passwd` ones are the negative escape fixture doing its job.

Suite green: 630 passed, 4 skipped (markdown-only diff; no test touched).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ri3aVJPfynCZtHRhesCzUH
2026-08-03 21:56:19 +02:00
c02c1addba fix(semretrieval): refuse a non-finite embedding instead of scoring it (kø-(l)/S3.1 MINOR)
`cosine`'s docstring claimed its guard was load-bearing because "a NaN reaching the
ranking sort key would corrupt ordering silently rather than failing loudly" — but the
guard tested `norm == 0.0` only, which a NaN or inf norm passes straight through. The
claim was prose, not behaviour.

Measured, not assumed: `cosine(unit, nan_vector)` AND `cosine(unit, inf_vector)` both
returned `nan`, and a NaN sort key made ranking INPUT-ORDER-DEPENDENT — six permutations
of the same three candidates produced four distinct orderings. That defeats the total
order `HybridRanker` documents ("`id` makes the result independent of input order").

Refuse rather than coerce, and deliberately NOT symmetric with the zero-norm branch: a
zero vector is a legitimate handled state (`FakeEmbedder` returns `np.zeros` by design),
whereas a non-finite component only ever means the INJECTED embedder is broken. Scoring
it `0.0` would launder that into "no semantic similarity" while ranking proceeded on a
forged signal — validation, never repair, mirroring `read_spend`.

Reachable via the documented `Embedder` extension point, not the shipped fake; scoped to
the norms (90% principle — a finite-normed dot-product overflow is not chased).

Also corrects `docs/extending.md`, which stated `SEMANTIC_WEIGHT_DEFAULT = 0.5` while the
code has said `0.25` since the weight was lowered.

625 -> 630 tests. Load-bearing MEASURED against the WHOLE suite, five mutations all red:
detach the guard entirely · coerce to 0.0 instead of raising · check only the first norm ·
drop "non-finite" from the message · (control) detach the zero-norm branch, which fails
ONLY the zero-norm test — the new guard does not mask the existing one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018V9vNBmxAmgJ2JMoHByiHS
2026-08-03 21:48:50 +02:00
9dc3722161 fix(ingest): run the MCP stdio transport against a real server, and repair its error contract (kø-x)
`stdio_call_tool` shipped never having been executed end to end — docs said so
explicitly. Running it found a real defect: `stdio_client` and `ClientSession` are
each an anyio task group, and anyio re-packages anything leaving one in a
`BaseExceptionGroup`. Both errors the transport raises from inside the session
(`mcp_tool_error`, `mcp_non_text_content`) therefore reached callers as exception
groups, never as the `IngestError` the whole Door A path catches and switches on by
`code`. No canned-tool test could see this: they never enter a task group.

`_unwrap_ingest_error` recovers the owned error and re-raises it; anything unowned is
re-raised untouched, so this narrows an exception group rather than blanket-catching.
Duck-typed on `.exceptions` because `except*`/`ExceptionGroup` are 3.11+ and this
project supports >=3.10.

Verified against a REAL server subprocess (a local process costs no model tokens, so
the repo's cost discipline is untouched; the contract tests still spawn nothing):
`examples/ingest-golden-mcp/` + `tests/test_ingest_golden_mcp.py` — byte-identical
golden extraction mirroring the http/sql goldens, plus the tool-error and
missing-`server_ref` branches.

Also recorded: a server on the ingest path must expose a NULL-ARGUMENT tool, so
`datasource.build_mcp_server` cannot serve it (`retrieve_cost_docs(query)` has a
required parameter, verified to return an error result). The two are separate seams
by design.

Load-bearing MEASURED, five mutations all RED: detach the unwrap · detach
`initialize()` · make the error code generic · detach the `isError` branch · change
one byte of the served body.

612 -> 615 tests. ruff + format + mypy clean.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WiY53sm8JFqk7NN75g5wRS
2026-08-03 17:56:19 +02:00
8910a673ea feat(ingest): bound the default http transport in time, in front of the pinned library (S2.4)
The hole: `read_http`'s default transport is the library's `urllib_get`, which invokes the
stdlib opener with no `timeout=`. urllib's documented fallback is then the process-wide default
socket timeout — `None` out of the box — so an http source that accepts a connection and never
answers hangs a run indefinitely. That contradicts the invariant that nothing runs unbounded.

The spec text for S2.4 ("a timeout parameter on `_urllib_get`") could NOT be followed literally:
that function is UPSTREAM library code (`llm_ingestion_okf.connectors`, pinned v0.3.1, pull-only),
signature `(url, credential) -> str` — measured, not assumed. Same failure class as S2.2's
"implement it in `ingest.py`": spec text that says "change X" has to be checked against whether
X is ours at all.

So the fix goes in FRONT of the library: `timeout_get` scopes `socket.setdefaulttimeout` around
a delegate call to the library's own `urllib_get`, and `materialize` now hands the library that
wrapped transport instead of letting it resolve its own untimed default. This meets S2.4's own
verification criterion — a bound WITHOUT a second socket path — and avoids duplicating the
credential-header logic. An explicitly injected `http_get` is passed through UNWRAPPED: a
caller-owned transport (MCP fronts a subprocess with its own `timeout_seconds`) keeps its own
policy, and a process-global side effect is not ours to impose on it.

Honest limit, carried in the code comment, the test docstring and `docs/extending.md`, not just
in the commit: the default socket timeout is PROCESS-global. Under `concurrency=k` the runner is
asyncio on one thread, so the scoping holds; driving `read_http` from a thread-pool executor
would make it unsafe.

Half of S2.4's scope was already delivered upstream — transport failures are categorised as
`SourceError(code="http_transport")`. Coarser than the plan envisaged, but not ours to rewrite.

Two pre-existing guards went red on the first pass, both on PROSE only: `ingest.py` must not
contain "urlopen" (no forked connector) or "ingest_mcp" (AST-guarded mcp-free). No code violated
either — my docstrings merely named them. The guards were left exactly as strict as they were and
the prose was reworded; weakening a real guard to save a comment is the trade this repo refuses.

578 -> 583 tests. Five mutations MEASURED red (restored from scratchpad + `shasum -c` each time,
never `git checkout`):
  1. remove the timeout scoping entirely            -> RED
  2. apply the bound AFTER the delegate call        -> RED
  3. set the bound but never restore it (no finally)-> RED  (the unconditional control)
  4. hand the library a bare None again (pre-S2.4)  -> RED  (the wiring)
  5. make the wrapping unconditional                -> RED  (the conditional control)

Mutations 1 and 2 take ~10s to fail rather than failing instantly: that is the loopback test's
join deadline expiring. It is the measurement that the bound actually BITES — a black-hole
listener on 127.0.0.1 that completes the handshake and never answers, run on a daemon thread so
a detached seam fails an assertion instead of hanging the suite forever. Every other assertion
here only proves we set a global; that one proves the global does something.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TdLGwd33vqhkToh98Ym34P
2026-08-03 14:54:04 +02:00
ddd6338f02 feat(ingest): add the MCP connector as a transport inside the http family (S2.2)
Commons settled on 2026-08-01 that MCP is an extension of the `http` source
family, not a fourth family (`shared/ingest-spec.md` §4). This implements it
with ZERO schema change and zero spec amendment.

The transport discriminator lives in `base_url`, not in new manifest fields:
the shared library rejects unknown manifest keys fail-fast, and we consume it
pull-only at a pinned v0.3.1, so `server_ref`/`tool` as fields would have meant
a spec amendment plus a library release. It buys nothing — the library already
joins `base_url` + `/` + `query`, so `mcp+stdio://<server_ref>` + `<tool>`
reproduces exactly the two-part structure the (now stale) reference plan wanted.

Staying inside the family INHERITS what a fourth family would have had to write
and could have forgotten: the §8 network grant (measured to fire before any tool
call), the `max_rows` cap, §5 verbatim fenced rendering, and the §7 provenance
stamp. The discriminator gates rather than labels — `mcp_get` refuses a URL it
does not own, so an MCP transport can never quietly serve an `https://` manifest
and leave the bundle's provenance claiming a transport that was never used.

Parsing is string-based, not `urlsplit`-based: `urlsplit().hostname` lowercases
the host, which would silently break the case-sensitive env lookup `server_ref`
depends on.

`ingest.py` is untouched — it is AST-guarded mcp-free, so the transport lives in
its own module and is opt-in at the call site. `ingest_mcp.py` imports the open
`mcp` protocol client but never `agent_framework`, keeping the seam D7-portable.

Load-bearing, six mutations all measured RED: detach the scheme guard · make the
refusal unconditional · swap parsing to `urlsplit().hostname` · skip non-text
content instead of raising · force `allow_network=True` · smuggle in a MAF
import. Both source files restored byte-identical (`shasum -c`) after each.

Honesty: `stdio_call_tool` (the real stdio path) is written but never executed
end to end — every test injects a canned tool call, so the suite spawns no
subprocess and opens no socket. No golden fixture, and MCP stays unwired in the
optimiser run path. Stated in docs/extending.md rather than implied away.

555 -> 578 tests; ruff + mypy green.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0112FPR5TX6pDLiNicBPzE8i
2026-08-02 21:14:57 +02:00
9e149c6847 docs(s31): close the review's honesty gap — narrow semantic claims to the shipped mechanism 2026-07-25 13:00:13 +02:00
da8779fc7f docs(s31): close 1 review MAJOR — vector store recorded as an unwired authoring primitive 2026-07-25 12:54:28 +02:00
921a8daf71 docs(s31): --semantic-retrieval CLI surface + Embedder/Retriever extension points 2026-07-25 06:31:44 +02:00
d44305cda9 docs(s53): knowledge-base recipe (D-H item 1) — team process, honest 1-2 week expectation, no wizard
New English docs/knowledge-base-recipe.md grounded strictly in the D-H decision record
(revisjonspakke-DF-DI.md §3): setup is always a small team (technical + domain expert), the
deliverable is a recipe NOT a wizard (B9 onboarding interview + guided verdict command rejected),
domain expert delivers files in their own formats never schema/JSON, phased process (inventory ->
skeleton -> seed verdicts -> iterate), reading via Obsidian/VS Code. The honest 1-2 week
expectation is stated early and SOURCED verbatim to the record. Factory-dependent parts
(free-format verdict translation, clone-to-demo) are explicitly marked future/blocked-on-toolkit
so the doc never claims above the evidence level. Linked from README's Docs section with the
1-2 week expectation in context. SC5 (ASCII-only greps): file exists, '1-2 weeks' x2,
'knowledge-base-recipe' in README. src/ untouched; 431 passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KNNiJRk1sSwxgVLS5AobT1
2026-07-23 21:53:53 +02:00
0a11af74a4 refactor(ingest): adopt shared llm-ingestion-okf v0.3.1 behind a thin adapter
Door A (manifest -> connector -> deterministic materialization -> index) is no
longer implemented here. src/portfolio_optimiser/ingest.py becomes a thin
consumer seam over the shared library, git-pinned to v0.3.1 on the same Forgejo
channel portfolio-optimiser-claude uses. Net -626/+385; ingest.py 599 -> 145 lines.

shared/ingest-spec.md remains the normative spec: the library implements it, it
does not replace it. Spec changes continue to go via commons.

Acceptance criterion met and proven: all three golden bundles (file/sql/http)
are byte-exact before and after, including the idempotence re-run. examples/ and
shared/ carry ZERO modifications -- the fasit was not adjusted to fit.

The rejection set was verified equivalent, not assumed: all 22 malformations the
repo's pydantic models refused are refused by the library, with typed codes
(okf_type_reserved, credential_embedded, extraction_id_duplicate, ...).

Test rebinding (invariants preserved, vehicle changed): the library has zero
runtime dependencies by design, so pydantic is unavailable to it.
ManifestV1.model_validate(dict) -> load_manifest_bytes(bytes); ValidationError ->
ManifestError; model_fields -> dataclasses.fields; PathSecurityError ->
SourceError(path_escape); ValueError -> MaterializationError(ingested_at_invalid).
Tests now also pin the refusal `code`, the library's documented stability
contract -- a sharper assertion than "some validation error was raised".

Two accepted behavioural deltas, recorded rather than silently dropped:
- Title whitespace is stored verbatim instead of collapsed at validation, so the
  frontmatter title and the index label are no longer guaranteed identical for
  irregular whitespace. Both behaviours are spec-conformant (the spec is SILENT;
  the old one was a repo-local pinned decision). Queued as a commons-amendment
  candidate so both stacks pin the same answer. Goldens unaffected.
- The section 8 audit log moves to logger llm_ingestion_okf.materialize. Nothing
  in the repo consumed the old channel.
Also: the `type` discriminator is no longer a dataclass field, so the spec
cross-check asserts it explicitly -- without that line the swap would have
silently narrowed the test.

New tests/test_ingest_library_seam.py pins the seam itself: the restated section 5
stamp formula against the stamp the library actually writes (the one place the
adapter does not purely delegate, since v0.3.1 exposes no stamp helper), the
local-only allow_network default, the list[Path] unwrapping, and a guard that the
adapter never regrows local Door A machinery. All four verified RED when detached,
as were both golden regressions under a byte-level render mutation.

Door A is UNGATED: it calls no guard before writing to disk. Gating untrusted
content remains the caller's responsibility (guard wiring still planned).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01B4jNN186eVqfe1x5DnTU6r
2026-07-20 07:47:55 +02:00
8c252c1064 fix(s52): reject scheme-less webhook URL fail-fast in NotifierConfig 2026-07-17 03:14:36 +02:00
b84f4d46bb feat(s52): export Notifier public contract + sync extending.md B11 2026-07-16 19:53:57 +02:00
4ecd571961 docs(security): plan llm-ingestion-guard inclusion at ingest/inbox persist-gates
Map untrusted-ingest surface (ingest.materialize http/I6, verdict-inbox
load, promote_verdict, future received-bundle) vs first-party paths;
verdict = planned, wire scan/sanitize before M3 as S2.4/S2.5 extension.
Plan only — guard not wired. shared/ hardening owned by commons session.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145ZKPLMVeqM47z2jxxokym
2026-07-16 07:43:30 +02:00
123f71f547 docs(s41): verified Foundry auth recipe + necessary-but-not-sufficient note 2026-07-15 11:24:18 +02:00
12e7f6aabc docs(plan): utrulling D-F–D-I — sesjonsplan (D-F–D-I i §2, S3.5/S3.6/S5.4, graf+T0), roadmap-revisjonsblokk, commons-amendment-utkast (Step-1-analyse: NEI → minimal amendment), toolkit-repo-brief
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145ZKPLMVeqM47z2jxxokym
2026-07-15 05:29:41 +02:00
f133bd4a65 docs(plan): revisjonspakke D-F–D-I — intensjonsanalyse 2026-07-14 (innholdsmodell, felles OKF-modul/toolkit-repo, team-oppskrift, verdibevis + kostnadssimulering)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0145ZKPLMVeqM47z2jxxokym
2026-07-14 22:11:06 +02:00
e8cc86a84b docs(review): kryssmodell-review 2026-07 (14 funn, 11 detach-bevis) + revidert roadmap + sesjonsplan Fase 2-6 2026-07-10 06:28:11 +02:00
bdba6f66c6 docs(i7): reproduserbar grep i statusrapport rad #10/#11 (--exclude selv-referanse) 2026-07-04 23:34:26 +02:00
c1af510fba docs(i7): program status report — bevist/ikke-bevist + verifiseringslogg (I7) 2026-07-04 22:54:06 +02:00
329bcda67d docs(i7): sharpen D7 boundary in extending.md — CSV+SQL built, HTTP/MCP MAF-only 2026-07-04 22:48:01 +02:00
696f19af1e docs(i6): http source extension-point + D7 create_sdk_mcp_server pointer 2026-07-04 17:17:12 +02:00
fe4ae69a1e docs(plan): ingest-rammer — Opus 4.8 xhigh alle økter, ingen Fable 5
Operatørkorreksjon 2026-07-04: §Rammer sa «Fable 5 med xhigh — global default»
(scoped bort fra 2026-07-02-planens Opus-direktiv). Reverseres: Opus 4.8 xhigh
for ALLE økter og alle subagenter, ingen Fable 5 — matcher den globale regelen
(~/.claude/CLAUDE.md, «Modellvalg for subagenter»). Kun plandokumentet endret;
I6-arbeidets untracked-filer urørt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017MM6BWb1hWmJZuXFZ7rjxT
2026-07-04 17:17:09 +02:00
a3965fb372 docs(plan): målbilde + gated sesjonsplan for ingest-programmet
Program-planleggingssesjon per brief 2026-07-03: alle brief-premisser
verifisert mot ground truth (retrieval-forbudet sitert ordrett), planen
adversarial-reviewet x2 (2 blockere + 8 majors innarbeidet: verdict-lag-
reservasjon, lag-separasjon ved re-ingest, deterministisk timestamp,
guard-dekning, D7/HTTP-ærlighet, gatede spec-endringer). I1 er GATET på
operatør-godkjenning av målbildet. Kun dokumenter — ingen kode-, shared-
eller søskenrepo-endring; suite 157/4 grønn.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AaQCFnfsh3tfq1VfzdJpoi
2026-07-03 13:45:14 +02:00
234e84c8a9 docs(rapport): S11 — sammenligningsrapport begge stacker på pinned db86e15 2026-07-03 12:50:01 +02:00
aec95eb0da docs(plan): program-brief for ingest-lag — tools/konnektorer mot reelle datakilder
Brief (ikke plan) for nytt program etter S11: ingest-steg som materialiserer
kildeuttrekk til OKF-bundles (metode-spec forbyr query-time retrieval i løkka),
delt manifest-kontrakt i commons, referanseimplementasjon per stack.
Inkluderer oppstartsprompt for planleggingssesjonen.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QdSfQdND84oeq2mbjueLTS
2026-07-03 11:24:24 +02:00
b7f78ecf7d docs(plan): S3 — comparison protocol (pinned ref, metrics, liveness asymmetry)
Defines the S11 yardstick BEFORE either stack exists: pinned commons-ref
as identical input, metrics M1-M4, the verbatim liveness-asymmetry
declaration, five binding LLM non-determinism rules for S10/S11, and a
ban on comparing offline numbers with live numbers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AaQCFnfsh3tfq1VfzdJpoi
2026-07-03 01:10:05 +02:00
ae01127510 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
2026-07-03 00:34:56 +02:00
84d19c97d6 docs(plan): session-by-session execution plan from the 2026-07-02 status analysis
Sequences the decided arc (R1 extraction -> D7 sibling -> comparison) into
12 single-session increments (S1-S12), folding in the analysis findings:
truth maintenance (S1), the missing shared method spec (S2), extraction
de-risking + comparison protocol (S3), and per-session verification
criteria + key-assumption tests. Target picture stays the frozen north star.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015PAnzFPa9KXqjQEkw9q5Zs
2026-07-02 17:11:10 +02:00
7df6a66712 docs(maalbilde): konsolidert agentisk-loop baseline + OKF-kontekstarkitektur
Nordstjerne fra design-samtale 2026-06-26. Konsoliderer: 8-stegs sverm-loop,
trelagsmodell (OKF-kontekst/output-inbox/promoteringsgate), to feedback-
tidsskalaer (kort synkron + lang fil-basert/gjenopptakbar), OKF/LLM-Wiki
datagrunnlag (web-verifisert mot Google knowledge-catalog), den samlende
diagnosen (tilbakemelding-inn-i-prompt-dataflyt mangler 3 steder), invarianter,
testbar "ferdig", delt eksempel for begge repo, fase-nedbrytning. 2 Mermaid-
diagrammer. STATE peker hit. R1 besluttet (shared/-dir nå). Domene lener mot
energieffektivisering (lærings-overflate > FinOps' for-deterministiske kjerne).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019any9zfGNNwWJPX5Zq2QRz
2026-06-26 20:56:14 +02:00
129a40baea docs(fot-i-bakken): ground-truth-verifisert levert-vs-lovet — agentiske lag inerte
Fot i bakken før fase-valg (Fase 4 vs D7). Explore-agent kartla; hovedkontekst
selv-verifiserte de konsekvensrike funnene mot kildekoden:

- ExpeL-laeringssloyfa ER APEN: retrieval naar aldri modellen (generering run.py:194
  for retrieval run.py:216-221; sctx run.py:219 forkastes; 0 context_providers i src/).
- README 'Not yet usable' (README.md:5) motsier 'Fase 1-3 lukket' — release-blokkerende.
- Maker-checker-checker gater ingenting (modulo-selector workflow.py:93-96).
- Ingen ekte-modell-kjoring (alle 4 skip = live-provider). Ingen SKILL.md. Ingen entry-point.

Deterministisk ryggrad (validator/budsjett/provenance/sti-sikkerhet/onboarding) holder.
Neste: operatorbeslutning — lukk kjerne-gjeld (A) vs aerlig nedgrader+release (B).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019any9zfGNNwWJPX5Zq2QRz
2026-06-26 15:25:03 +02:00
207f057075 docs(fase3): extension-point guide (add project / data source / model-map) + SC6 test 2026-06-26 12:16:01 +02:00
2613a5183e docs(research): MAF 1.9.0 capability map — feature-utilization for Fase 2 [skip-docs]
Two independent grounded passes (installed-source introspection + official MS
Learn via MCP) produce a per-need adopt/keep decision table for using MAF
features well in Fase 2, instead of reinventing them.

Headline: Microsoft's Workflows "State Isolation" page documents verbatim the
exact footgun Spike B(b) found today — a reused Workflow accumulates agent
threads across runs; the fix is a fresh-instance-per-run factory. Our
fresh_workflow() IS the official pattern.

Key verdicts: ADOPT real UsageDetails token counts + a budget ChatMiddleware +
native builder round caps + GA @tool/MCP + observability; KEEP the hand-rolled
structural VerdictStore and inline validator (MAF memory/eval are the wrong
shape); ROLL a tiny role->deployment map (declarative is preview/not installed).
Corrections recorded: CLAUDE.md "Magentic experimental" stands at doc-level (no
code gate); Spike D extend_instructions is two-arg (source_id, instructions).

Skills answer: method-as-Skill yes (MAF consumes SKILL.md natively, experimental);
MAF-docs-mirror Skill no (rots vs live MCP); the digest lives in this map.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fif1r1En5W542HbZV88yMH
2026-06-24 11:36:26 +02:00
a2dff210ce fix(fase1): spike B fan-out measures real conversation bleed, not a counter
/trekreview flagged the Spike B(b) fan-out experiment as BROKEN_SUCCESS_CRITERION
(BLOCKER): it asserted a per-client call_count reached 3 on a reused instance vs
1 on a fresh one — a tautology true for any un-reset mutable counter, independent
of MAF, that never exercised the real G2/B7 shared-Workflow state-corruption
footgun. It was a false-confirm of a de-risk assumption.

Rebuilt to observe genuine MAF thread state via the messages each participant
RECEIVES (new FakeChatClient.received_texts seam):
- shared_instance_conversation_bleed: a reused built ConcurrentBuilder Workflow
  accumulates the conversation across .run() calls — run N's participants receive
  runs 0..N-1's prompts/replies (measured [[p0],[p0,p1],[p0,p1,p2]], strictly
  monotonic) => genuine cross-run contamination.
- fresh_instance_conversation_isolation: a fresh instance per run gives each a
  clean thread => each participant sees only its own project ([[p0],[p1],[p2]]).

Assumption now CONFIRMED with a meaningful observable. findings-b.md gains a
Method note recording why it was rebuilt; README rows updated.

Also fixes the MINOR: a_groupchat.run_live now mkdirs the findings dir before
write_text so a post-disposal run does not lose the measured result.

Gate green: ruff check + format, mypy src, pytest 48 passed / 1 skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fif1r1En5W542HbZV88yMH
2026-06-24 11:09:55 +02:00
b81e22b637 docs(fase1): consolidate spike findings + confirm green quality gate 2026-06-24 10:35:33 +02:00
f7a36b59ac feat(fase1): spike D - verdictstore + expel retrieval [skip-docs] 2026-06-24 10:32:39 +02:00
85439646ec feat(fase1): spike C - blocking hybrid validator (IR/solver/monte-carlo) [skip-docs] 2026-06-24 10:28:19 +02:00
44111113fb feat(fase1): spike B - magentic unbounded + concurrent state isolation [skip-docs] 2026-06-24 10:22:02 +02:00
9b9a17e2ed feat(fase1): spike A - group chat maker-checker vs single-agent [skip-docs] 2026-06-24 10:13:23 +02:00
ffbfe00317 build(fase1): add dev orchestration + solver + async deps, scaffold spikes 2026-06-24 09:57:57 +02:00
ffd3ad4dd7 docs: plain-text brief — goal + two approaches (MAF vs Claude Agent SDK) + learning goal
Framework-neutral narrative of what portfolio-optimiser aims to achieve and the
two hypothesised approaches to the same method. Claude Agent SDK paragraphs
corrected by the user: the SDK spans both emergent (one agent + subagents) and
explicit orchestration (hand-written or agent-authored workflow script with a
non-LLM validator gate). The real difference vs MAF is ready-made named
constructs vs building blocks — not emergent vs explicit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fif1r1En5W542HbZV88yMH
2026-06-24 09:21:31 +02:00
25bb07a46e docs(research): MAF vs Claude Agent SDK comparison + D7 sibling-impl decision
Verified comparison of Microsoft Agent Framework (ground-truth introspection of
installed agent-framework-core 1.9.0 + Microsoft Learn) and Claude Agent SDK
(Anthropic docs + npm/PyPI). Grounds decision D7: rebuild the same method on
Claude Agent SDK as a separate sibling repo, in sequence, sharing only the
spec + golden/conformance suite — not orchestration code.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Fif1r1En5W542HbZV88yMH
2026-06-24 06:51:24 +02:00
110c6e8446 docs: add cost-discipline + 90% principle as locked decisions (D5, D6)
Privat MS-tenant tilgjengelig men kostnadstak: lokal profil default i
utvikling, Foundry/Azure kun målrettet/minimal, ingen tunge test-kjøringer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9FyyENxebxVThjrn9et8C
2026-06-23 22:11:48 +02:00
ec9ac74976 feat: initial scaffold (Python framework on Microsoft Agent Framework)
Plan-fase: repo-skjelett, dokumentasjon (research + inkrementell plan),
Python/uv-oppsett, MAF-avhengighet. Ingen rammeverkskode ennå.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01H9FyyENxebxVThjrn9et8C
2026-06-23 22:01:22 +02:00