portfolio-optimiser/docs/plan/2026-08-23-magentic-utforskningssloeyfe.md
Kjell Tore Guttormsen 4a19d39e63 feat(tracing): U14 - sporing er opt-in, og "av" betyr at MAF aldri kalles (ORDRE 20260823T165757Z)
PORTFOLIO_OTEL er eneste bryter, lest paa truthiness. Uten den kalles
configure_otel_providers ikke i det hele tatt: spans lages fortsatt
(ENABLE_INSTRUMENTATION defaulter True) og kastes, saa ingenting KAN forlate
prosessen. `console` skriver spans til stderr - demoens stdout er byte-identisk
med fasiten, maalt. `otlp` eksporterer over nett, og kun mot et endepunkt
operatoeren selv har navngitt.

To regler er MAALT, ikke valgt (observability.py:849 bygger exporter-lista i
fast rekkefoelge):

- enable_console_exporters sendes EKSPLISITT False i begge moduser. Overlatt til
  miljoeet faller den tilbake paa ENABLE_CONSOLE_EXPORTERS, hvis
  ConsoleSpanExporter skriver til STDOUT - nettopp det S6 maalte som oedeleggende
  for goldenen.
- `console` NEKTER naar en OTEL_EXPORTER_OTLP_*_ENDPOINT finnes: env-avledede
  exportere bygges UBETINGET og FOER vaare, saa ordet "console" ville vaert en
  usann paastand om hvor kjoeringens innhold tok veien. Validering, ALDRI
  reparasjon - vi fjerner ikke operatoerens variabel bak ryggen paa dem.

Tre kallsteder (run.main, simulation.main, hosting.main): demoen er et skriptet
bevis, ikke produktet, og en soem bare demoen naar ville latt de to inngangene en
virksomhet faktisk kjoerer vaere usporbare. tracing_notice er ENESTE renderer og
returnerer None naar sporing er av - omisjon, aldri tom rad.

IKKE bygget, med grunn: PLAN_CREATED/REPLANNED/PROGRESS_LEDGER_UPDATED hoerer til
sloeyfa U4 bygger; en emitter uten kallsted er en form gjettet i stedet for maalt.
OTLP-exporter-PAKKENE er bevisst ikke deklarert (egress + grpc/protobuf-vekt i et
publisert wheel); uttalt i README/DEPLOY/env.template.

Ny dep: opentelemetry-sdk>=1.42,<2 (operatoerbeslutning 2, 23.08). EN pakke, ikke
to - ConsoleSpanExporter bor inne i sdk-en. opentelemetry-api fulgte med
1.42.1 -> 1.44.0, maalt uskadelig.

Load-bearing MAALT (tests/test_tracing_loadbearing.py), ni mutasjoner alle roede
mot HELE suiten + groenn kontroll 943/5. Tre av de roede bor i tester som fantes
fra foer (golden-transkriptets fire-linjers stderr + portefoelje-CLI-ens stille
pass), altsaa er omisjons-regelen gatet av uavhengige vitner.

Golden ea8c534... uendret. mypy src + ruff rene.

Ordren tar ogsaa de fire operatoerbeslutningene inn i planens paragraf F.
Laasen paa orchestrations 1.0.1 er ENDELIG (operatoerbekreftelse 23.08), ikke
midlertidig: spike-ordrens "revert hvis E7 staar" er overstyrt av den senere
beslutningen, som betinget paa groenn suite - ikke paa E7.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-23 20:51:46 +02:00

56 KiB
Raw Blame History

MAF-utnyttelsesrevisjon U1U15 + plan for Magentic-basert utforskningssløyfe

Ordre 20260823T125528Z-1876024635-from-.claude · Økt 53, 2026-08-23 · Fable 5/xhigh uten advisor (operatør-overstyring). Planarbeid: ingen kode i src/, ingen tester. Hvert tall i dette dokumentet kommer fra en kommando som ble kjørt denne økten (vedlegg V1V3); alt som ikke ble målt er merket «ikke målt» eller «spike».

0. Sammendrag — de seks setningene som bærer resten

  1. Målingen: av .claudes ni «null i src/»-påstander holder 7 ordrett; U5 og U11 trenger presisering (artefaktet/sitatbæringen finnes, MAF-konstruktet gjør det ikke). Nevner: 32 filer.
  2. Magentic-status: hele Learn-siden hentet — ingen experimental-advarsel for Python (eneste «experimental» er et C#-pragma), og installert kode har ingen @experimental-markør på Magentic. F14-sitatet «untested … outside of the original Magentic-One design» står fortsatt. PyPI latest er 1.1.1 (Production/Stable), men bare 1.0.0/1.0.1 kjører på core 1.9.0.
  3. Designet: Magentic legges OVER den normative 8-stegs-sløyfa, ikke inni Steg 3. Utforskningen er en mandat-former: prompt + bundles (+ fagpersonens frø-hypoteser) → Mandaterun_project(mandate=…) uendret. Validatoren gater hvert tall i nøyaktig samme gate som i dag.
  4. Fire ting ble MÅLT på installert 1.0.0, ikke antatt: en Magentic-workflow er single-use (høylytt RuntimeError), men builder og manager-instans blør (4/4 kontaminering, worker hoppet over, fabrikkert sluttsvar); plan-review-rundturen virker, men har ingen revise-cap; max_round_count gir en kanonisk streng, aldri et typet signal; MagenticResetSignal nullstiller ikke deltakernes sesjon (orphan-attributt i 1.0.0).
  5. U14 er ikke «én funksjon»: setup_observability finnes ikke; inngangen er agent_framework.observability.configure_otel_providers, og opentelemetry-sdk er ikke installert — kallet krasjer med ModuleNotFoundError. U14 er en avhengighetsbeslutning.
  6. Rekkefølgen: S0S6-spikes (1 økt) → U14 (1) → U4+U13 synkron (2) → U12 + asynkron U13 (1) → U15 som egen sammenligning (1). U1/U2/U5/U11 avvises med begrunnelse. Seks økter.

A. U1U15-måling (nevner: 32 .py-filer i src/; kontroll: 117 i tests/, 6 i spikes/)

Metode: grep -rEn --include='*.py' "<regex>" src per kapabilitet, samme regex mot tests/ og spikes/ som kjent-positiv-kontroll, og — for hver regex som ga 0 i alle tre — en tredje kjøring mot installert MAF (.venv/lib/python3.12/site-packages/agent_framework{,_orchestrations}) som beviser at spørringen KAN finne (Verifiseringsloven ansikt 4). Skript og rå output: vedlegg V1. En uavhengig andre sveip (Opus-agent, 35 tokens, samme nevner) ga identiske nuller (V3).

ID Kapabilitet (§15.1) src tests spikes venv-kontroll Status Belegg / spørring
U1 ConcurrentBuilder fan-out 0 4 4 — (tests/spikes finner) IKKE — erstattet av håndrullet bølge run.py:1262 await asyncio.gather( (D-D). Regex ConcurrentBuilder|BuildConcurrent|FanIn
U2 WorkflowBuilder graf 0 0 0 21 filer IKKE Regex WorkflowBuilder|@executor|class .*\(Executor\)|WorkflowContext|SwitchCase|add_edge|add_fan_out
U3 GroupChatBuilder debatt 10 7 7 LANDET workflow.py:24 import · :104 GroupChatBuilder( · :108 termination_condition= · :113 .with_max_rounds(max_rounds)
U4 Magentic 0 4 9 4 filer IKKE tests-treff: tests/spikes/test_b_footguns.py, tests/spikes/test_imports.py, tests/test_method_spec_loadbearing.py:40 (framework-navn-svarteliste for spec-prosa — ikke bruk). .claudes «kun spikes/b_footguns.py» er riktig for BRUK
U5 Agent Skills (SkillsProvider) 0 0 0 3 filer IKKE som MAF-konstrukt; DELVIS som artefakt persona.py:3/:47 leser shared/skills/expert-reviewer/SKILL.md via EGEN load_persona_example, aldri SkillsProvider. Installert core: @experimental(feature_id=ExperimentalFeature.SKILLS)SkillsProvider (_skills.py:1718)
U6 MCP-tools 6 6 0 LANDET (opt-in) mcp_tools.py:42-43 import · :154 MCPStdioTool( · :170 MCPStreamableHTTPTool(. Ingest-sømmen bruker rå mcp.ClientSession (ingest_mcp.py:144-179)
U7 Function tools 4 10 0 LANDET datasource.py:18 from agent_framework import FunctionTool, tool · :65 · :69 @tool(
U8 Middleware 16 20 0 LANDET budget.py:27 · :228 class BudgetMiddleware(ChatMiddleware) · run.py:635/:645
U9 ContextProvider 8 11 4 LANDET verdicts.py:29 · :319 class ExpeLContextProvider(ContextProvider) · run.py:724/:843
U10 Vektorlager 2 19 0 DELVIS (egen, ikke MAF) semretrieval.py:355 save_vector_store · :393 load_vector_store — numpy brute-force (D-C). 0 på AzureAISearch|cosmos|qdrant|redis|VectorStore
U11 Citation-aware RAG 26 35 0 DELVIS (egen, med MAF-typer) provenance.py:18 from agent_framework import Annotation, TextSpanRegion · :78 to_annotations() («display only, NOT the source of truth»); datasource.py:1 «citation-bearing data source». 0 på AIContextProvider|TextSearchProvider|agentic_retrieval|KnowledgeAgent
U12 Checkpointing 3 (kun prosa) 0 0 14 filer FRAVALGT — §A.1 run.py:22, workflow.py:14, :115: «no checkpoint — research 01: durable resume is fragile». 0 kode-treff på checkpoint_storage|CheckpointStorage
U13 HITL-gates 3 0 0 DELVIS, og UØVD workflow.py:116 builder.with_request_info(agents=[agents[-1]]) bak enable_layer1_hitl=False (:78, run.py:524; ingen CLI-flagg). ENESTE test: tests/test_workflow.py:54 test_layer1_hitl_option_builds — bygger, kjører aldri gaten, sender aldri et svar. run.py awaiter debate.run til ferdig (:684-686) og leser aldri strømmen. 0 på RequestInfoEvent|approval_mode|MagenticPlanReview i src OG tests
U14 OpenTelemetry 0 0 1 4 filer IKKE Regex opentelemetry|setup_observability|OBSERVABILITY|get_tracer|start_as_current_span. uv pip list: KUN opentelemetry-api 1.42.1 (transitiv) — ingen sdk, ingen exporter
U15 Evaluering 0 0 0 2 filer IKKE Regex evaluate_workflow|LocalEvaluator|Evaluator|_evaluation. evaluate_workflow finnes (_evaluation.py:1833) under @experimental(feature_id=ExperimentalFeature.EVALS) (:1832); 20 @experimental i modulen

Utenfor de 15 (samme kjøring): U16 CompactionProvider 0/0/0 (venv 4) · U17 as_tool( 0/0/0 (venv 1) · U18 FoundryChatClient LANDET backends.py:150 · U19 Harness 0/0/0 (venv 1).

Mot .claudes ni null-påstander: 7 av 9 holder ordrett (U1, U2, U4, U12, U13, U14, U15). 2 av 9 trenger presisering: U5 og U11. De 6 landet-påstandene holder (U3, U6, U7, U8, U9, U10-delvis). Aliasing/re-eksport er utelukket: alle 14 agent_framework-importlinjer i src/ er listet i V3 og hver er et direkte navn; from agent_framework_orchestrations import → 0 treff (importstien i bruk er agent_framework.orchestrations, workflow.py:24).

A.1 U12 — fravalgt på et premiss som har råtnet, eller som holder?

Premisset i koden: «research 01: durable checkpoint resume is fragile» (workflow.py:14). «research 01» finnes ikke som dokument i repoet (grep -rn -i "research.01" docs/ STATE*.md → 0). Nærmeste dokumenterte grunnlag: kapabilitetskartet (docs/research/2026-06-24-maf-capability-map.md:54): «superstep-granular (not mid-LLM-call); resume needs identical graph. Defer past MVP unless needed»; egnethetsplanen 08-09 P9/P10 («pass-nivå-gjenopptakelse er levert via spend-fila; ingen checkpointing»); docs/extending.md:315 («A run either completes or is re-run; the async verdict inbox is the resumable boundary»).

Vurdering: begrunnelsen holder for dagens debatt (sekunderminutter; re-kjøring er billigere enn gjenopptakelse). Den holder ikke for målbildet: en utforskning som stopper i MagenticPlanReviewRequest og venter på en fagperson i dager kan ikke holde en prosess i live, og «re-kjør» kaster planen fagpersonen skulle revidere. Premisset er ikke råttent — det er utenfor sitt gyldighetsområde. Det som ble MÅLT i installert core 1.9.0 denne økten (V2 §7): mekanismen FINNES og dekker Magentic-tilstand — FileCheckpointStorage (_checkpoint.py:239), «restore then send» (run(responses=…, checkpoint_id=…) eksplisitt tillatt, _workflow.py:877), pending request_info re-emitteres ved resume (_runner_context.py:414-426), og MagenticOrchestrator. on_checkpoint_save skriver kontekst (runde/stall/reset-tellere), ledgere og managerens hele sesjon (_magentic.py:1266-1284, :746). «Skjør» er i dag konkret: base64-pickle av tilstand med allow-list (_checkpoint.py:242-250), graf-signatur må matche (_runner.py:275-279), og manager-state-feil ved lagring/gjenoppretting svelges med warning (_magentic.py:1281-1282, :1320-1321). Ingen ende-til-ende-resume med pending plan review er kjørt → spike S4 (§D).


B. Magentic-status mot primærkilden (hele siden hentet 2026-08-23)

Kilde: microsoft_docs_fetch mot learn.microsoft.com/en-us/agent-framework/workflows/orchestrations/magentic — hele siden.

  1. Ingen experimental-advarsel for Python. Eneste «experimental» på siden: C#-blokken #pragma warning disable MAAIW001 // Magentic types are experimental (.NET-diagnostikk). .claudes fravær er bekreftet, ikke bare ikke-motbevist.
  2. F14-sitatet står ordrett: «it is untested how well the Magentic orchestration will perform outside of the original Magentic-One design», og tipset «consider using the Group Chat pattern instead» for enklere koordinering. F14s VURDERING var riktig lest; operatøren har flyttet PREMISSET (målbildet krever planlegging), ikke sitatet. G8 i §15.3 er halvt riktig: funksjonelle workflows er fortsatt ExperimentalFeature.FUNCTIONAL_WORKFLOWS; Magentic er ikke markert.
  3. Installert kode: agent_framework/_feature_stage.py:43-65ExperimentalFeature har 13 medlemmer (DECLARATIVE_AGENTS, EVALS, FILE_HISTORY, FIDES, FOUNDRY_TOOLS, FOUNDRY_PREVIEW_TOOLS, FUNCTIONAL_WORKFLOWS, HARNESS, MCP_LONG_RUNNING_TASKS, MCP_SKILLS, PROGRESSIVE_TOOLS, SKILLS, TO_PROMPT_AGENT). Magentic er ikke der; grep -n -i "experimental\|feature_stage" agent_framework_orchestrations/*.py → 0.
  4. PyPI (curl https://pypi.org/pypi/agent-framework-orchestrations/json): latest 1.1.1 (2026-08-21), Development Status :: 5 - Production/Stable, 26 utgivelser. Krav per versjon: 1.0.0/1.0.1 → core ≥1.9.0; 1.0.2/1.1.0 → ≥1.13.0; 1.1.1 → ≥1.15.0. Core latest 1.15.0 (21.08).
  5. Installert = pinnet: uv pip list → orchestrations 1.0.0, core 1.9.0; uv.lock:61-63 låser 1.0.0; pyproject.toml:17 >=1.0.0, :12 core >=1.9.0,<2 (tests/test_maf_version_guard.py).
  6. Learn-siden beskriver LATEST API (manager_agent=, enable_plan_review=, intermediate_output_from=). Installert 1.0.0 har de samme konstruktør-navnene (V2 §2) — men builder-docstringen i 1.0.0 beskriver et HITL-API (MagenticHumanInterventionRequest, with_human_input_on_stall) som ikke finnes i koden (kun i docstrings, _magentic.py:1388-1559). Designet i §C er skrevet mot det som er målt installert, aldri mot docstring eller Learn.
  7. Core 1.12.0-notatet (21.07, samme minutt som orchestrations 1.0.1): «Prevent the Magentic manager from duplicating conversation history (#6297)». Om 1.0.1 bærer fiksen er ikke verifisert — det er spike S0.

C. Design: den utforskende sløyfa

C.0 Beslutningen — Magentic OVER sløyfa, ikke INNI Steg 3

Kjernespørsmålet: Magentic gir manageren frihet til å velge vei; validatoren gater utfallet. Er de forenlige? Ja — på én betingelse: utforskningen produserer KANDIDATER, aldri FORSLAG.

Tre fakta avgjør formen:

  1. Metodespesifikasjonen er normativ og commons-eid (shared/method-spec.md, pull-only). §3 Steg 3 KREVER en to-rolle maker-checker-debatt, rundetak og fersk tilstand per kjøring; Steg 2 krever «exactly one candidate measure» per proposer-kall; §8 «Never an unbounded loop, anywhere». En Magentic-sløyfe som ERSTATTER Steg 3 bryter spec-en og krever commons-amendment FØR bygging — og den ville flyttet validatoren INN i grafen. Sømkartet (V3) er entydig: alt som gjør et svar etterprøvbart ligger UTENFOR workflow-grafen og ETTER debate.run — generering run.py:748-759, validator generate.py:491, provenance run.py:793, checker-override :816, dimensjonsgate :826, domfangst :849, outbox :857.
  2. Mandate finnes og ER «en prompt om hva som skal gjøres» (mandate.py:55-88): objective + approaches[{id, label, description}] + allow_own_proposals + success_criteria. En Approach.description er «the expert's own prose reason … fed to the proposer VERBATIM» (mandate.py:44-49, generate.py:308-317) — altså «hypoteser fagpersoner kommer med». _evaluate_mandate (run.py:292-344) kjører hver approach gjennom generate_via_llmvalidate_proposal under SAMME meter, og rapporterer not_evaluated for det budsjettet ikke rakk (aldri utelatt). announce (mandate.py:130-183) sier høyt hva kjøringen skal gjøre FØR første betalte kall — repoets eksisterende før-kjøring-menneskesjekk.
  3. Validatoren er ren og offline (validate_proposal, validator.py:194-260; CBC via PuLP, _MC_SAMPLES = 512, ingen agent_framework-import) og kan eksponeres som FunctionTool (U7) uten å endre hva den er.

Derfor: utforskningen er en MANDAT-FORMER. Inn: operatørens prompt + tilgjengelige bundles (+ valgfrie frø-approaches fra fagpersoner). Ut: en Mandate hvis approaches er hypotesene utforskningen fant verdt å teste, med managerens begrunnelse som description, og bundle_id per approach. Deretter run_project(bundle_dir=…, mandate=…) uendret: debatt (Steg 3) → generering (Steg 2) → deterministisk validator (Steg 4, blokkerende, stempler validator_decision) → checker-gate → outbox/HITL (Steg 68). Ingen linje i den kjeden endres. Manageren velger VEI i et rom som ikke kan skrive til outbox eller wiki; validatoren gater hvert TALL som forlater rommet, i samme gate som i dag.

Tre nivåer av garanti (uttalt, så ingen leser mer inn i det enn det er):

Nivå Hva Hvor Stempel?
1 quick_validate-verktøyet i utforskningen samme validate_proposal, samme baseline, kalt av hypothesiser-agenten NEI — rådgivende tall i en utforskningslogg, aldri provenance
2 Pipelinen per approach run.py:748 _evaluatevalidate_proposal JAProvenanceStamp.validator_decision
3 Skriverettigheter kun pipelinen når outbox (outbox.write_outbox) og wiki (promote_verdict, gated) utforskningens sluttsvar er RÅ ({run_id}-exploration.json), aldri et forslag

Ærlighetsregelen (§1) følger direkte: managerens «final answer» presenteres ALDRI som et resultat. Resultatet er det _evaluate_mandate returnerer.

C.1 Manager + deltakersett (mot installert 1.0.0-API, V2 §23)

Rolle Type Gjør Verktøy / kanal
manager MagenticBuilder(manager_agent_factory=…)StandardMagenticManager(agent=…) med EGNE ledger-prompter (konstruktør-parametre task_ledger_*_prompt, progress_ledger_prompt, final_answer_prompt) planlegger, velger neste taler, fører progress ledger, replanlegger ingen — men BudgetMiddleware PÅ manager-agenten (C.2)
navigator Agent leser bundles PROGRESSIVT (§3 Steg 1) og svarer med sitater list_bundles() · read_bundle(id)okf.navigate_bundle + Bundle.index_summary()/bundle_context · read_file(id, path) innenfor retrieval.safe_resolve
hypothesiser Agent former én kandidat om gangen som IR-JSON (gjenbruker generate._build_messages + proposal_response_format()) quick_validate(ir_json){decision, reason, p10, p50, p90} (nivå 1)
expert_liaison menneske/persona svarer på spørsmål, reviderer retning plan review (MagenticPlanReviewRequest.approve()/.revise(feedback)) — og, etter spike S3b, AgentApprovalExecutor som deltaker for svar MIDT i kjøringen

Hvorfor StandardMagenticManager, ikke egen subklasse: egen manager = egen ledger-parsing = egen kopi av Magentic-One-logikken (kø-(p): den andre kopien drifter). Prompt-overstyring dekker behovet (norsk domene, IR-disiplin, «aldri oppgi et tall uten quick_validate»). Målt grunn til å overvåke: manageren parser ledgeren med en håndrullet JSON-skraper (_extract_json, _magentic.py:406-449, ast.literal_eval-fallback), 3 forsøk (:588-590, :710-723) → RuntimeError; MagenticProgressLedgerItem.answer er ikke typesjekket per felt (:300-302). Egen manager FØRST når en målt mangel krever det.

Hvorfor manager_agent_factory= og ikke manager_agent=: målt E4 (V2 §6) — manager_agent= konstruerer manageren eagerly (:1683) og gir SAMME instans til hvert build() (:1729-1730); factory-varianten kalles per build (:1732-1738).

C.2 Progress ledger som styringssignal, koblet mot budget.py

Ledgeren (_magentic.py:310-314, navn verifisert): is_request_satisfied / is_in_loop / is_progress_being_made / next_speaker / instruction_or_question. Stall-regelen (:1109-1118): not is_progress_being_made or is_in_loopstall_count += 1, ellers dekrementer (ikke nullstill); stall_count > max_stall_count (strengt >) → _reset_and_replan (:1149): kontekst nullstilles, reset_count += 1, replan, plan review på nytt med is_stalled=True hvis aktivert.

To signaler, to vakter, aldri slått sammen (S3.4-invarianten):

Signal Kilde Vakt Utfall
Semantisk — stall ledger max_stall_count → reset+replan → max_reset_count exploration_stop="stalled" — strukturert; IKKE krasj, IKKE suksess
Ressurs — tokens BudgetMiddleware (budget.py:228) på HVER agent inkl. manager pre-call-guard :242-244 BudgetExceeded(kind="tokens") → 429-kanalen
Ressurs — runder Magentic max_round_count (= Budget.max_rounds) intern (:1238) oversettes av vårt lag til BudgetExceeded(kind="exploration_rounds", limit, observed)

Målt grunn til oversettelsen (E5, V2 §4): max_round_count/max_reset_count gir ingen exception — en kanonisk assistent-melding «Workflow terminated due to reaching maximum round count.» + _terminated=True (:1222-1256), uten prepare_final_answer. Ved transporten er det uskillbart fra suksess. Vårt lag leser derfor round_count fra MagenticContext (eksponert via PROGRESS_LEDGER_UPDATED-eventene, ett per runde) og orchestrator._terminated, og produserer det typede stoppet SELV — slik BudgetExceeded/429 alt gjør for debatten. Strengen pinnes mot den installerte konstanten (ROUND_CAP_LOGGER-presedensen), aldri fri-tekst-matchet.

tick_round er IKKE koblet til orkestreringsrunder i dag (sømkart: to kallere, begge i generate.py:390/:455; run.py:632 gir meteret max(max_rounds*4, 4) for GENERERINGS-forsøk). Utforskningen får derfor sin egen Budget (tokens + runder) i en egen TokenMeter, og run_project sitt meter er urørt. Stall-telleren er IKKE en budsjettvakt og kobles ikke inn i TokenMeter.

Hver runde logges (ledger + next_speaker + instruction_or_question) til {run_id}-exploration.json, så en stoppet utforskning er lesbar uansett hvilken vakt som fyrte.

⚠ Spike S2: manageren er et Agent (StandardMagenticManager(agent=…), :528; ett create_session() i __init__, :572; alle kall via agent.run(…, session=…), :601), så agent-nivå ChatMiddleware SKAL fyre — men det er ikke målt for manager-stien, og en feil her gjør «harde token-tak» til en løgn for den mest pratsomme deltakeren. Kontroll: bar BaseChatClient no-op-er middleware (simulation.py:373-375) — spiken bruker ScriptedChatClient.

C.3 Grensene — B4 bygges ikke bort

Dagens Magentic (installert) Regel
with_max_rounds(max_rounds) (workflow.py:113) max_round_count (ctor-kwarg; default None = ubegrenset, :542) PÅKREVD, positiv, fail-fast (G1/B4/A4)
max_stall_count (default 3, :540) PÅKREVD, positiv — ingen arv av defaulten
max_reset_count (default None, :541) PÅKREVD, ≥0
termination-safety-net (:108) manageren avslutter ved is_request_satisfied (:1104-1107) eller ved ukjent next_speaker (:1128-1131 — stille sluttsvar!) rundetaket ER nettet; ukjent taler logges som avvik
plan review: ingen revise-cap, revises telles ikke som runder (målt E6: 2 manager-kall per revise, 0 ledger-kall) NY max_plan_revisions i kontrakten; hver revise tick_round()-es

ExplorationContract (pydantic, fail-fast, speiler contracts.TerminationContract): max_rounds, max_tokens, max_stall_count, max_reset_count, max_plan_revisions, enable_plan_review. Ingen defaults: en utforskning uten uttalte grenser nekter å starte.

C.4 B7 — tilstandsisolasjon. MÅLT (V2 §6; skript i V1)

Eksperiment Resultat Konsekvens
E1 én bygget workflow, to .run() run 2 → RuntimeError: This Magentic workflow has already been completed… (_magentic.py:914-918), 0 modellkall single-use — STERKERE enn GroupChat 1.9.0s stille tomme kjøringer [2,0,0]; kan ikke korrumpere, for den kan ikke kjøre
E2 to workflows, ÉN delt StandardMagenticManager run 2s manager-kall ser run 1s oppgave 4/4; meldingstall vokser 2→…→23→26→39 over kjøringsgrensen; run 2s første ledger svarte satisfied=True og workeren ble aldri kalt (0) — et fabrikkert sluttsvar ikke bare kost: en plausibel fasit produsert av null arbeid
E3 kontroll, fersk manager per build 0/5 kontaminering, 5 kall inkl. ledger_UNSAT (workeren KALT) 5-vs-4-kallformen er diskriminatoren
E4 én MagenticBuilder, to .build() samme 4/4-blødning som E2 (:1683, :1729-1730) «ny workflow» er ikke nok — ny BUILDER per oppgave, eller manager_agent_factory=
E7 MagenticResetSignal _magentic.py:1366 skriver self._agent_thread — lest av INGENTING (1 treff repo-vidt; den levende er AgentExecutor._session, 7 treff). Målt: cache/full_conversation tømt, sesjonsidentitet uendret stall-replan gir fersk manager-ledger men gamle deltakere; docstringens løfte (:1341-1344) holder ikke i 1.0.0

Regel: per utforskning ny builder → ny build → én kjøring, fersk klient per agent (som fresh_workflow/_default_factory alt gjør, run.py:436-440). Stol aldri på stall-reset for å tømme deltakerminne. fresh_exploration_workflow() speiler fresh_workflow() (workflow.py:74).

C.5 HITL-spranget: fra JA/NEI til «be om svar, bruke svarene»

Installert flyt (målt E6, V2 §5): with_plan_review()/enable_plan_review=Truectx.request_info(MagenticPlanReviewRequest{plan, current_progress, is_stalled}, MagenticPlanReviewResponse) (:1041-1048) → event type="request_info" med request_id → kjøringen STOPPER uten output → workflow.run(responses={request_id: req.approve()}) eller req.revise("…") (_workflow.py:899-908) → ved revise: feedback inn i historikken, manager.replan, REPLANNED-event, ny review-forespørsel (:1014-1031) → ved approve: løkka kjører. Dette er målbildets «be om svar, bruke svarene og jobbe videre» ordrett — og det virker på 1.0.0.

Ingen landingsplass i dag (sømkart §4): with_request_info bygges men ingenting konsumerer strømmen; hitl.py er et offline inspeksjonsverktøy (id-join outbox inbox, hitl.py:159), ikke en forespørsel/svar-mekanisme. Koblingen mot den asynkrone køa gjenbruker formen, ikke filene:

Eksisterende Utforskningen
outbox {run_id}-outcome.json med verdict_id NY artefaktklasse {run_id}-plan-review.json = {request_id, plan, current_progress, is_stalled, checkpoint_id} — samme RAW-lag, samme mappe, byte-deterministisk via outbox._dump
inbox {id}.json (verdicts.write_verdict) {request_id}.json = {decision: approve|revise, feedback} — eksperten SKRIVER, systemet LESER (§3 Steg 7-rolledelingen)
hitl.py pending (id-join) samme join over den nye klassen; python -m portfolio_optimiser.hitl pending viser begge
FeedbackContract.decision ∈ {approved, rejected} IKKE gjenbrukt — en plan-revisjon er ikke en dom; eget lite pydantic-skjema
simulation-personaen (load_persona_example) spiller eksperten SYNKRONT i demoen: revise("Test også …") én gang, så approve()

To tidsskalaer, som Steg 7: synkron (persona/operatør i samme prosess — demoen og første leveranse) og asynkron (fil-innboks over dager → krever U12: checkpoint_id i artefaktet, run(responses=…, checkpoint_id=…, checkpoint_storage=…)). Det er DÉT som flytter U12 fra «NEI» til «nå» — og spike S4 avgjør om 1.9.0 bærer det.

Svar MIDT i kjøringen (ikke bare ved plan): plan review er det ENESTE request_info orkestratoren selv reiser (4 treff i _magentic.py, 0 i basen). Men AgentApprovalExecutor (_orchestration_request_info.py:168, propagate_request=True :201) kan stå som DELTAKER: når manageren velger expert_liaison, blir dens output en request_info, og mennesket svarer med AgentRequestInfoResponse.approve()/.from_strings(…) (:55-79) — svaret blir deltakerens output i historikken. Ikke målt → spike S3b. Lander den, er «stille spørsmål … be om svar» dekket både ved plan og underveis.

C.6 «Hypoteser fagpersoner kommer med» — tre inngangsdører, i kostnadsrekkefølge

  1. I mandatet (finnes): Approach{label, description} → proposeren verbatim. Utforskningen tar frø-approaches som INPUT og MÅ bevare dem i output-mandatet (en hypotese fagpersonen ba om forsvinner aldri stille — not_evaluated-regelen anvendt på utforskning).
  2. I plan review (U13): revise("Test hypotesen at …") → replan med teksten i task-ledgeren.
  3. Underveis (S3b): expert_liaison-deltakeren — manageren SPØR, fagpersonen svarer, svaret blir kontekst for neste runde.
  4. I kunnskapsbasen (senere, commons-sak): type: hypothesis-konseptfil navigatoren finner. Krever OKF-vokabular-beslutning hos commons — ikke i denne planen.

C.7 OKF-bundles som inngang — hva manageren må vite

Manageren planlegger mot en katalog, aldri innholdet (§3 Steg 1). Per bundle: id · Bundle.index_summary() (okf.py:154) · kandidat-features (verdicts.bundle_candidate_features, :482) · om cost-baseline.json finnes (okf.load_optional_cost_baseline, :411) — avgjørende: en uforankret bundle kan ikke quick-valideres mot stage 0, og manageren skal vite hvilke hypoteser som KAN testes deterministisk · antall type: verdict-filer (Bundle.verdicts). Navigatoren åpner én bundle om gangen på managerens instruks. Multi-bundle er NYTT: run_project tar ÉN bundle_dir (run.py:514; bundle_dirs → 0 treff); utforskningen tar bundle_dirs, hver approach bærer bundle_id, og pipelinen kjøres per bundle som i dag (run_portfolio-formen).

C.8 Modulplassering (for byggeøktene, ikke for denne)

src/portfolio_optimiser/explore.py (NY): ExplorationContract, ExplorationResult{mandate, ledger_log, stop, plan_reviews}, fresh_exploration_workflow(...), explore(...). Importerer agent_framework.orchestrationsaldri fra okf.py/mandate.py/hitl.py (MAF-fri-guarden test_okf_is_maf_free). Verktøyene (quick_validate, navigator-tools) bor i explore.py og wrapper de rene modulene. run.py får ett nytt kallsted (--explore "<prompt>"explore()run_project(mandate=…)), hosting.py ett nytt whitelistet felt (explore_prompt), og simulation.py ett nytt scenario. Demo-transkriptet (tests/golden/demo-transcript.stdout) er byte-uendret fordi utforskningen er opt-in.


D. Prioritert vei gjennom de ni gapene

D.0 Versjonsbeslutningen (FØR noe bygges — én avgjørelse, ikke en glidning)

Anbefaling: bygg mot 1.0.x (installert) i denne planen. (1) core-bumpen 1.9→1.15 er seks minor-versjoner over privat-API-premisser test_maf_version_guard vokter, og tre målte 1.9-egenskaper (ExperimentalWarning-paret, single-use GroupChat [2,0,0], middleware-no-op på bar klient) må re-måles; (2) en bump er en egen beslutning, ikke et biprodukt av sløyfa; (3) 1.0.1 er et patch-hopp på samme core som kan tas gratis hvis uv lock --upgrade-package agent-framework-orchestrations gir 1.0.1 og suiten er grønn — og som KAN bære #6297 (§B pkt. 7) og/eller E7-fiksen. Det er spike S0. Bumpen til 1.1.x/core 1.15 er en EGEN økt etter denne planen (bonus: InvocationsHostServer krever core ≥1.13) — ikke en forutsetning.

D.1 Spikes (økt 54 — alle i spikes/e_magentic.py + tests/spikes/, aldri src/)

# Spørsmål Måling Felles hvis
S0 Gir 1.0.1 samme API og fjerner den E2/E4/E7? uv lock --upgrade-package agent-framework-orchestrationsuv pip list; re-kjør V1-skriptene magentic_b7*.py + magentic_reset.py; uv run pytest -q E7 står → bli på 1.0.0, noter
S1 B7 i repoets egen form E1E4 + E7 portert til spikes/ med ScriptedChatClient (ikke agentens FakeClient) 4/4 på delt builder, 0/5 på fersk
S2 Fyrer BudgetMiddleware på MANAGERENS kall? manager-Agent(middleware=[BudgetMiddleware(meter)]), Budget(max_tokens=1, …); assert BudgetExceeded propagerer ut av workflow.run OG at meter.tokens > 0 etter ett kall middleware fyrer ikke → egen klient-wrapper; plan-endring
S3 Plan-review-rundtur i repoets form E6 portert: request_info-event → run(responses=…) med reviseapprove; tell manager-kall per revise (målt 2) rundturen feiler på 1.0.0
S3b AgentApprovalExecutor som deltaker svar injiseres via from_strings("svar"), assert svaret står i neste manager-prompt ikke nåbar → kun plan review (dør 1+2)
S4 Resume med pending plan review with_checkpointing(FileCheckpointStorage(tmp)), kjør til review, ny prosess (subprosess), run(responses=…, checkpoint_id=…, checkpoint_storage=…); assert request_info re-emittert + sluttsvar feiler → U12 synkron-først, asynkron etter planen
S5 quick_validate-latens validate_proposal ×20 på bygg-energi-baseline-mikro med time.perf_counter > 2 s/kall → budsjettér kall i kontrakten
S6 OTEL gratis? uv add opentelemetry-sdk i scratch-venv; configure_otel_providers(enable_console_exporters=True); kjør demoen; tell spans ingen workflow.run-span → U14 er større enn én økt

D.2 Rekkefølgen

# Hva Gap Load-bearing for målbildet? Økt Modell
0 Spikes S0S6 U4/U13/U12/U8/U14 JA — hver er en antakelse planen ellers bygger blindt på 54 Opus 5/xhigh
1 U14 OpenTelemetryopentelemetry-sdk som deklarert dep, configure_otel_providers bak PORTFOLIO_OTEL=… (console default; OTLP KUN med eksplisitt OTEL_EXPORTER_OTLP_ENDPOINT — «ingen stille egress»), spans på DAGENS pipeline først; egne events for PLAN_CREATED/REPLANNED/PROGRESS_LEDGER_UPDATED (målt: _magentic.py emitterer null spans selv) U14 JA for «stole på» — og baselinen U15 måler mot 55 Opus 5/high
2 U4 + U13 synkronexplore.py (C.8), ExplorationContract, mandat-former, quick_validate, navigator-tools, plan review med persona; simulation-scenario; --explore i CLI U4, U13 JA — dette ER målbildet 5657 Opus 5/xhigh
3 U12 + asynkron U13FileCheckpointStorage, {run_id}-plan-review.json + innboks-svar, hitl.py pending utvidet, --resume <run_id> U12, U13 JA for svar over dager — betinget av S4 58 Opus 5/xhigh
4 U15 — EGEN sammenligning på golden-bundelen: utforskning vs. debatt på tre tall (validerte kandidater, tokens, runder), skrevet til docs/ som måling U15 JA for «finner den bedre tiltak» — men MAF-evals AVVISES (eksperimentell i kode, «wrong shape», kapabilitetskart :58) 59 Opus 5/high
U1 ConcurrentBuilder U1 NEI — bølgemodellen er målt, bærer budsjett-reservasjon (S3.4); Concurrent har ingen runder å reservere mot etter planen, om noensinne
U2 WorkflowBuilder U2 NEI — Magentic ER graf-laget; direkte adopsjon gir null ny kapabilitet nei
U5 SkillsProvider U5 NEI — ExperimentalFeature.SKILLS; egen loader virker; commons eier innholdet etter planen
U11 MAF RAG-providere U11 NEI — «navigate, never stuff» forbyr query-time retrieval; sitatbæring finnes avvist med begrunnelse

Seks økter (5459); faller S4, sparer pkt. 3 én økt.

D.3 For og mot de fire kandidatene ordren navngir

U4 + U13 tidlig — FOR: selve målbildet; alt annet er instrumentering av status quo. MOT: hviler på umålte antakelser (S2, S3b, S4) og på en request_info-sti repoet ALDRI har kjørt. Derfor spikes først — ikke omvendt. Plass #2 og #3.

U14 tidlig — FOR: (1) repoets deploy-gate; (2) uten sporing er en utforskning en svart boks — og Magentic-koden gir selv null spans, så ledger-eventene må vi logge; (3) LITE i kode: én funksjon + env; (4) instrumentert FØR utforskningen gir U15 sin baseline. MOT (målt): det er ikke gratis i avhengigheteropentelemetry-sdk + exporter må deklareres (wheel-vekt, uv sync, handover- pakka), og ENABLE_INSTRUMENTATION er default True (observability.py:697) så spans LAGES alt i dag og kastes. OTLP-exporter er egress og må være opt-in bak eksplisitt endepunkt. Plass #1, betinget av S6.

U15 — FOR: uten måling kan vi ikke påstå at sløyfa er bedre; ærlighetsregelen forbyr påstanden. MOT evaluate_workflow: eksperimentell i kode (20 dekoratorer) og feil form (kvalitetsscore offline, ikke domeneobjekt). Konklusjon: evaluering JA, som egen sammenligning; MAF-flaten NEI inntil EVALS forlater ExperimentalFeature. Plass #4 — den trenger noe å måle.

U12 — FOR: asynkron plan review over dager er umulig uten; mekanismen dekker Magentic-tilstand (målt i kode, §A.1). MOT: ende-til-ende-resume på 1.9.0 er umålt, restore-feil svelges, og en synkron persona-variant leverer målbildet i demoen uten den. Konklusjon: S4 avgjør; bygg i #3 hvis grønn, ellers synkron-først og U12 etter planen, sammen med core-bumpen.

D.4 Det som IKKE gjøres (uttalt)

  • Ingen endring i shared/method-spec.md. Utforskningen er et lag OVER §3; en amendment («Step 0 — Explore, optional») foreslås til commons via coord (operatørbeslutning, §F) — planen avhenger ikke av svaret.
  • Ingen D7-speiling nå: po-claude er PARKERT (STATE); utforskningslaget meldes dit når parkeringen oppheves, på linje med Steg-5-returtypen og demo-innboksen som også venter.
  • Ingen erstatning av GroupChat-debatten (Steg 3 står). Ingen open/-push, versjonsbump, Azure.
  • Ingen egen MagenticManagerBase-subklasse før en målt mangel krever det.

E. Verifisering — testbare kriterier (kommandoer, ikke «sjekk at det virker»)

Denne økten (planen selv):

# Kriterium Kommando → forventet
E1 Nevneren er 32 find src -name '*.py' | wc -l32
E2 Hver null-spørring kan finne bash scratchpad/u_measure.sh (V1) → venv_files ≥ 1 for hver av de 9 null-regexene
E3 Magentic ikke kode-markert grep -c -i "experimental" .venv/lib/python3.12/site-packages/agent_framework_orchestrations/_magentic.py0; grep -n "SKILLS|EVALS" …/agent_framework/_feature_stage.py:54, :64
E4 Installert = lås uv pip list | grep orchestrations1.0.0; sed -n '61,63p' uv.lockversion = "1.0.0"
E5 Single-use + builder-blødning reproduserbar uv run python scratchpad/magentic_b7.pyE1 run2 … RuntimeError, E2 VERDICT … 4/4, E3 VERDICT … 0/5
E6 Plan-review-rundtur reproduserbar uv run python scratchpad/magentic_b7b.pyE6 after APPROVE outputs: ['FINAL: …'], E5 outputs: ['Workflow terminated due to reaching maximum round count.']
E7 OTEL-sdk mangler .venv/bin/python -c "import opentelemetry.sdk"ModuleNotFoundError
E8 Planen er dok-gate-klassifisert uv run pytest -q tests/test_doc_constant_sync_loadbearing.py → grønn (datert sti = arkiv-klasse)
E9 Treet urørt utenom planen git status --porcelain → kun docs/plan/2026-08-23-…md + den fremmede presentasjon-HTML-en

Byggeøktene (kriterier som FELLES når sømmen detaches — load-bearing-metoden):

Leveranse Rødt-først-kriterium
S1/B7 test som deler builder mellom to utforskninger er RØD (4/4); fresh_exploration_workflow gjør den grønn (0/N)
S2/budsjett Budget(max_tokens=1) på manager-agenten → BudgetExceeded ut av explore(); detach middleware på manageren → testen rød
C.2/runder max_rounds=1BudgetExceeded(kind="exploration_rounds", limit=1, observed=1); detach oversettelsen → kjøringen «lykkes» med den kanoniske strengen → rød
C.3/kontrakt ExplorationContract(max_stall_count=None)ValidationError ved konstruksjon
C.5/plan review persona-revise én gang → REPLANNED i loggen og plan_reviews == 2; max_plan_revisions=1 + persona som alltid reviderer → typet stopp, aldri evig løkke
C.6/frø frø-approach id="fagperson-1" finnes i output-Mandate.approaches uansett hva manageren fant; detach bevaringen → rød
C.0/nivå 3 explore() skriver ingenting til outbox/wiki: ls outbox før/etter → kun {run_id}-exploration.json; git status shared/ ren
U12/S4 subprosess-resume: pending request_info re-emittert + sluttsvar; detach checkpoint_storage → rød
U14 demoen under PORTFOLIO_OTEL=console → ≥1 workflow.run-span på stderr; uten variabelen → stdout byte-identisk med tests/golden/demo-transcript.stdout
Golden uv run pytest -q grønn; shasum tests/golden/demo-transcript.stdout uendret

F. Nøkkelantakelser, risiko og åpne beslutninger

Alle åtte radene er MÅLT i økt 54 (ordre 20260823T162224Z, spikes S0S6). Måleapparatet er spikes/e_magentic.py + tests/spikes/test_e_magentic.py (16 tester), kjørt mot orchestrations 1.0.1 på core 1.9.0; hele suiten 920 passed / 5 skipped, golden-transkriptet uendret (ea8c534773acdbe41ae68f2c55724d69aaf8be4f).

# Antakelse Test Status
A1 Agent-nivå ChatMiddleware fyrer på managerens kall S2 MÅLT GRØNN. BudgetMiddleware på manager-agenten krediterer meteret (meter.tokens == 8 etter ett kall) og stopper kjøringen. Detach-kontroll: uten middleware fullfører SAMME 1-token-budsjett. Planens budsjettgaranti står.
A2 BudgetExceeded reist inni en Magentic-deltaker propagerer ut av workflow.run S2 MÅLT GRØNN, og som repoets EGEN type — ikke pakket i en ExceptionGroup: isinstance(exc, BudgetExceeded), kind="tokens", limit=1, observed=8. Trippelen kø-(y) leser er intakt, så 429-kanalen kan brukes uendret.
A3 request_info-rundtur S3 MÅLT GRØNN på 1.0.1. Review stopper kjøringen uten output (manageren har da kun kalt facts+plan); revise koster nøyaktig 2 manager-kall (facts_update, plan_update), null ledger-kall og null runder, og spør på nytt; approve kjører løkka til sluttsvar. Bekrefter at max_plan_revisions MÅ inn i kontrakten — ellers er en alltid-reviderende ekspert et ubundet forbruk.
A4 Resume med pending plan review i NY prosess S4 MÅLT GRØNN. Foreldreprosessen stopper på review og etterlater checkpoints; en subprosess (spikes/e_magentic_resume.py) som aldri så kjøringen svarer fra checkpointen alene og driver workflowen til sluttsvar. Pris, ikke forutsett: FileCheckpointStorage NEKTER å deserialisere MagenticPlanReviewRequest/…Response uten at begge navngis i allowed_checkpoint_types — uten det er checkpoint-fila uleselig og listingen TOM, altså en resume som feiler som et FRAVÆR. Begge prosesser må deklarere dem.
A5 AgentApprovalExecutor som deltaker gir svar midt i kjøringen S3b MÅLT GRØNN — dør 3 i C.6 står. Ekspertens ord når både liaisonen og en senere manager-prompt. To rundturer per menneskesvar: from_strings([svar]) mater svaret tilbake INN i liaisonen og gjenopptar IKKE manageren (målt: null manager-kall mellom de to forespørslene); først approve() sender liaisonens output videre. Kostnad: AgentApprovalExecutor er IKKE re-eksportert fra agent_framework.orchestrations (svartypen ER det) — døra koster i dag en privat-API-import.
A6 Persona-manuset kan drive en Magentic-manager (ledger-JSON) offline S1/S3 MÅLT GRØNN med repoets ScriptedChatClient (ikke lenger en ad-hoc FakeClient). Ett forbehold funnet: selectoren får den SAMMENSLÅTTE prompten, så ett av fem manager-kall bærer to markører — rutingen må teste senere-stadium-markøren først. Og en next_speaker som ikke matcher en deltaker gir stille sluttsvar uten at noen ble spurt (_magentic.py:1128-1131), målt da liaison-spiken først het worker.
A7 Utforskning som opt-in holder demo-transkriptet byte-uendret shasum MÅLT uendret gjennom hele økten (ea8c534…).
A8 1.0.1 er API-identisk med 1.0.0 S0 MÅLT: API-identisk, ATFERD ikke. Diffen er upstream-regresjonsfiksen #4371: StandardMagenticManager.__init__ mistet self._session = agent.create_session(), og hvert manager-kall mynter nå en engangs-sesjon. Konsekvens: E2 OG E4 er BORTE (4/4 → 0/5 begge). E1 (single-use RuntimeError, null kall) og E7 (MagenticResetSignal skriver til orphan-attributtet _agent_thread, :1369, sesjonsidentitet uendret) står. Manager-sesjonen fjernet også fra checkpoint-state, konsistent med at manageren nå er tilstandsløs per kall.

Målt korreksjon til E-tabellen (U12/S4): kriteriet «detach checkpoint_storage → rød» er FEIL — å fjerne checkpoint_storage= fra run() lar HELE suiten stå grønn (920 passed), fordi .with_checkpointing(...) på builderen allerede ga workflowen lageret. De to bærende punktene er checkpoint_id= (fjernes → rød) og builderens .with_checkpointing(...) (fjernes → rød).

Load-bearing MÅLT (mot HELE suiten, grønn kontroll 920/5): manager_keeps_persistent_session konstant True (2 røde — E2+E4 alene) · aldri fest BudgetMiddleware på manageren (3 røde, mens detach-armen forblir grønn) · detach markør-registreringen (1 rød — S3b-positiven alene, kontrollen grønn) · flipp _route-rekkefølgen så pre-survey testes først (4 røde) · resume uten checkpoint_id= (1 rød) · builder uten .with_checkpointing() (1 rød) · tom _ALLOWED_CHECKPOINT_TYPES (1 rød) · og den falsifiserte: resume uten checkpoint_storage= (0 røde — funnet over).

S5 — quick_validate-latens: median 13,6 ms over 20 kall på bygg-energi-baseline-mikro (forankret baseline + assumption-bånd, så stage 0 + CBC + 512-sample Monte Carlo er alle med). Langt under 2 s-terskelen: verktøyet kan kalles fritt i løkka, og kontrakten trenger ingen egen latens-post. Båndet er med med vilje — uten det faller _monte_carlo tilbake på item.unit_cost, alle draw blir identiske, og tallet ville underrapportert den ekte kostnaden.

S6 — OTEL er gratis, men IKKE via enable_console_exporters. I et scratch-venv pinnet til samme stack (core 1.9.0 / orch 1.0.1 / opentelemetry-sdk 1.44.0; ingenting lagt til pyproject.toml) gir configure_otel_providers(enable_console_exporters=True) 2 workflow.run-spans — pluss workflow.build, executor.process, edge_group.process, message.send, invoke_agent, chat synthetic — men de skrives til stdout, som ville ødelagt golden-transkriptet. Med exporters=[ConsoleSpanExporter(out=sys.stderr)] kommer de 2 workflow.run-spanene på stderr og demoens stdout er byte-identisk med fasiten (samme shasum). U14 er altså én økts arbeid — forutsatt at exporteren konstrueres eksplisitt mot stderr, aldri via flagget.

U14 er LANDET (økt 55, ordre 20260823T165757Z-6762213199). Operatøren tok alle fire beslutningene som anbefalt 23.08, og bekreftet samme dag at låsen på orchestrations 1.0.1 er ENDELIG, ikke midlertidig — spike-ordrens «revert hvis E7 står» er overstyrt av den senere beslutningen, som betinget på grønn suite (920/5, målt) og ikke på E7. Levert:

  • opentelemetry-sdk>=1.42,<2 som DEKLARERT dependency (beslutning 2). Én ny pakke, ikke to: ConsoleSpanExporter bor inne i sdk-en. opentelemetry-api fulgte med fra 1.42.1 til 1.44.0 — målt uskadelig (golden ea8c534… uendret før og etter).
  • src/portfolio_optimiser/tracing.pyPORTFOLIO_OTEL på truthiness, modusene console (spans → stderr) og otlp (kun mot et navngitt endepunkt), tracing_notice som eneste renderer.
  • Wiret i TRE inngangspunkter: run.main, simulation.main, hosting.main.
  • S6-forbeholdet er implementert som skrevet: ConsoleSpanExporter(out=…), aldri enable_console_exporters=True. Målt: demoen under PORTFOLIO_OTEL=console gir workflow.run- spans på stderr og stdout BYTE-IDENTISK med fasiten; uten variabelen null spans.
  • To ting E-tabellen ikke forutså, begge lukket: (i) MAF bygger env-avledede OTLP-exportere UBETINGET, så console NEKTER når en OTEL_EXPORTER_OTLP_*_ENDPOINT er satt — ellers var «console» en usann påstand om hvor kjøringens innhold tok veien; (ii) enable_console_exporters faller tilbake på ENABLE_CONSOLE_EXPORTERS i miljøet, så den pinnes eksplisitt False i begge moduser. Begge er MÅLT som defektklasser, ikke antatt.
  • IKKE bygget, med grunn: PLAN_CREATED/REPLANNED/PROGRESS_LEDGER_UPDATED — de hører til sløyfa U4 bygger, og en emitter uten kallsted er en form gjettet i stedet for målt. De landes i økt 5657, på sømmen U14 nettopp la. OTLP-exporter-PAKKENE er bevisst ikke deklarert (egress + grpc/protobuf-vekt i et publisert wheel); uttalt ærlighets-grense i README/DEPLOY/env.template.

Suite etter U14: 943 passed / 5 skipped, golden ea8c534… uendret, mypy src + ruff rene.

Beslutninger som trenger operatøren (speiles i avslutningsblokken):

  1. Versjon — AVGJORT 23.08: låsen står på 1.0.1, ENDELIG. Bakgrunnen: ordrens «felles hvis» sa at hvis E7 står i 1.0.1, revert til 1.0.0. E7 STÅR — men 1.0.1 fikset noe større enn det som ble håpet (E2/E4, «en plausibel fasit produsert av null arbeid»). Operatøren aksepterte 23.08 at gevinsten veier tyngre enn ordrens bokstav, og at betingelsen som gjelder er «grønn suite» (målt 920/5), ikke E7. Ikke midlertidig, og ikke å gjenåpne uten en ny måling. Bumpen til 1.1.x / core 1.15 er fortsatt en EGEN økt etter denne planen.
  2. U14 som deklarert avhengighet — AVGJORT 23.08: ja. Landet i økt 55; se «U14 er LANDET» over. Kostnaden ble ÉN pakke (opentelemetry-sdk), ikke to.
  3. HITL — AVGJORT 23.08: synkron-først. Merk at ordrens BEGRUNNELSE er utdatert mens konklusjonen står: den sier «A4 er umålt og kun kode-lest», men A4 ble MÅLT GRØNN i økt 54, så asynkron er teknisk mulig. Valget er dermed ren rekkefølge, ikke risiko — asynkron U13 forblir pkt. 3 i rekkefølgen, betinget av S4 som nå er grønn.
  4. Commons-amendment «Step 0 — Explore» — AVGJORT 23.08: sendt. .claude sendte forslaget til portfolio-optimiser-commons via coord. Vi venter IKKE: planen slår selv fast at ingen amendment er påkrevd, og «Step 0» er foreslått som VALGFRI. Kommer den, er den en tilføyelse.

Vedlegg V1 — målingsskript og rå output (U1U19)

Skript (scratchpad/u_measure.sh, kjørt 2026-08-23; kopiert hit ordrett så målingen kan gjentas):

#!/bin/bash
cd "$(git rev-parse --show-toplevel)"
SRC_N=$(find src -name '*.py' | wc -l | tr -d ' ')
TST_N=$(find tests -name '*.py' | wc -l | tr -d ' ')
SPK_N=$(find spikes -name '*.py' | wc -l | tr -d ' ')
echo "DENOMINATOR src=$SRC_N tests=$TST_N spikes=$SPK_N"
m() { s=$(grep -rEn --include='*.py' "$2" src | wc -l | tr -d ' ')
      t=$(grep -rEn --include='*.py' "$2" tests | wc -l | tr -d ' ')
      k=$(grep -rEn --include='*.py' "$2" spikes | wc -l | tr -d ' ')
      printf "%-4s %-60s src=%-4s tests=%-4s spikes=%s\n" "$1" "$2" "$s" "$t" "$k"
      [ "$s" != "0" ] && grep -rEn --include='*.py' "$2" src | head -12; }
m U1  'ConcurrentBuilder|BuildConcurrent|FanIn'
m U1b 'asyncio\.gather|create_task\('
m U2  'WorkflowBuilder|@executor|class .*\(Executor\)|WorkflowContext|SwitchCase|add_edge|add_fan_out'
m U3  'GroupChatBuilder|with_max_rounds|termination_condition|RoundRobinGroupChatManager'
m U4  'Magentic'
m U5  'SkillsProvider|McpSkillsSource|AgentSkillsProvider|agent_framework.*skills'
m U6  'MCPStdioTool|MCPStreamableHTTPTool|MCPWebsocketTool|as_mcp_server'
m U7  'FunctionTool|ai_function|@tool|tools=\['
m U8  'Middleware|middleware='
m U9  'ContextProvider|HistoryProvider|context_providers='
m U10 'AzureAISearch|azure\.search|cosmos|qdrant|redis|VectorStore|vector_store|Qdrant|Chroma|chromadb'
m U11 'AIContextProvider|TextSearchProvider|citation|SourceName|SourceLink|agentic_retrieval|KnowledgeAgent'
m U12 'checkpoint|Checkpoint'
m U13 'approval_mode|request_info|RequestInfoEvent|RequestPort|MagenticPlanReview|ApprovalRequired'
m U14 'opentelemetry|setup_observability|OBSERVABILITY|get_tracer|start_as_current_span'
m U15 'evaluate_workflow|LocalEvaluator|Evaluator|_evaluation|agent_framework.*evaluation'
m U16 'CompactionProvider|SlidingWindow'
m U17 'as_tool\(|AsAIFunction'
m U18 'FoundryChatClient'
m U19 'HarnessAgent|ShellExecutor|FileMemoryStore|ToolApprovalAgent'

Rå tellinger: DENOMINATOR src=32 tests=117 spikes=6 · U1 0/4/4 · U1b 1/5/0 · U2 0/0/0 · U3 10/7/7 · U4 0/4/9 · U5 0/0/0 · U5b(SKILL\.md) 1/5/0 · U6 6/6/0 · U6b(rå mcp) 7/6/0 · U7 4/10/0 · U8 16/20/0 · U9 8/11/4 · U10 2/19/0 · U10b(numpy/cosine) 49/102/1 · U11 26/35/0 · U12 3/0/0 · U13 3/0/0 · U14 0/0/1 · U15 0/0/0 · U16 0/0/0 · U17 0/0/0 · U18 7/8/0 · U19 0/0/0.

Kjent-positiv-kontroll mot installert MAF (grep -rEl <regex> agent_framework agent_framework_orchestrations | wc -l): U2 → 21 · U5 → 3 · U14 → 4 · U15 → 2 · U16 → 4 · U17 → 1 · U19 → 1 · checkpoint_storage|CheckpointStorage → 14 · MagenticBuilder|StandardMagenticManager → 4.

B7-/HITL-målingsskriptene (magentic_b7.py, magentic_b7b.py, magentic_reset.py) ligger i øktens scratchpad og porteres til spikes/e_magentic.py i økt 54 (S1/S3) — de committes ikke herfra, fordi de bruker en ad-hoc FakeClient og ikke repoets ScriptedChatClient.

Vedlegg V2 — installert Magentic-API (orchestrations 1.0.0, _magentic.py, 1802 linjer)

  • Eksport (agent_framework_orchestrations/__init__.py:43-62): MagenticBuilder, MagenticContext, MagenticManagerBase, MagenticOrchestrator, MagenticOrchestratorEvent/ …EventType, MagenticPlanReviewRequest/…Response, MagenticProgressLedger/…Item, MagenticResetSignal, StandardMagenticManager, MagenticAgentExecutor. Ikke i koden: MagenticHumanIntervention*, with_human_input_on_stall (kun docstrings).
  • MagenticBuilder(*, participants, manager=|manager_factory=|manager_agent=|manager_agent_factory= (nøyaktig én), task_ledger_*_prompt…, progress_ledger_prompt, final_answer_prompt, max_stall_count=UNSET→3, max_reset_count=None, max_round_count=None, enable_plan_review=False, checkpoint_storage=None, output_from, intermediate_output_from) (:1396-1422). Metoder: with_plan_review(enable=True) (:1516), with_checkpointing(storage) (:1564), build() (:1772; bidireksjonale kanter orkestrator↔deltaker :1796-1797). Grensene bor på MANAGEREN (:1693-1695, lest tilbake :905/:1115/:1240); manager= + prompt/limit-argumenter → warning og stille ignorert (:1661-1677). Ikke-agent-Executor som deltaker aksepteres (:1498-1499) men får placeholder-beskrivelse (_base_group_chat_orchestrator.py:115).
  • StandardMagenticManager(agent, …, max_stall_count=3, max_reset_count=None, max_round_count=None, progress_ledger_retry_count=None→3) (:528-544). Én AgentSession i __init__ (:572), alle kall agent.run(…, session=…) (:601). Ledger-parsing: fri tekst + _extract_json (:406-449), dataklasser ikke pydantic (:288-333).
  • Løkke: runde-teller ×2 (:1075-1076), grense-sjekk FØR inkrement på _round_index (:1238); stall :1109-1118; reset :1149-1185; grense → kanonisk streng + _terminated, ingen exception (:1222-1256); sluttsvar :1206-1220; ukjent next_speaker → stille sluttsvar (:1128-1131); single-use-guard :914-918.
  • Plan review: request :829-853 (plan, current_progress, is_stalled; .approve(), .revise(feedback)), response :800-826 (review: list[Message], tom = approve :1010), reises :1041-1048, svares workflow.run(responses={id: …}) (_workflow.py:899-908; ukjent id → ValueError :946), revise-håndtering :1014-1031 (ingen cap, ingen runde-telling). Per-tur-HITL via AgentApprovalExecutor (_orchestration_request_info.py:168).
  • Checkpoint: CheckpointStorage-protokoll _checkpoint.py:119, InMemoryCheckpointStorage :192, FileCheckpointStorage(storage_path, *, allowed_checkpoint_types) :239; WorkflowCheckpoint.pending_request_info_events :81; run(checkpoint_id=, checkpoint_storage=) _workflow.py:701-712, responses+checkpoint_id tillatt :877; superstep-granularitet _runner.py:143-144; re-emittering _runner_context.py:414-426; Magentic-state _magentic.py:1266-1284 + manager-sesjon :746; feil svelges :1281-1282/:1320-1321.
  • Observability: _magentic.py → 0 spans. Runtime-spans workflow.build/workflow.run/ executor.process/edge_group.process/message.send (observability.py:253-276). Inngang configure_otel_providers(*, enable_sensitive_data, enable_console_exporters, exporters, views, vs_code_extension_port, env_file_path, env_file_encoding) (:1168), enable_instrumentation (:1132); env ENABLE_INSTRUMENTATION (default True, :697), ENABLE_SENSITIVE_DATA, ENABLE_CONSOLE_EXPORTERS, OTEL_EXPORTER_OTLP_ENDPOINT/_PROTOCOL/_HEADERS, OTEL_SERVICE_NAME. Ikke re-eksportert fra agent_framework (hasattr → False). APPLICATIONINSIGHTS_CONNECTION_STRING → ikke funnet. opentelemetry-sdk: ikke installert.
  • Evals/Skills: evaluate_workflow(*, workflow, workflow_result|queries, evaluators, …) (_evaluation.py:1833, @experimental(EVALS) :1832); SkillsProvider(ContextProvider) (_skills.py:1719, @experimental(SKILLS) :1718; from_paths :1861).

Vedlegg V3 — kommandoer kjørt (utvalg; fullstendige lister i øktens agent-transkripter)

find src -name '*.py' | sort | wc -l                                    # 32
uv pip list | grep -i -E "agent.framework|^mcp |opentelemetry|azure-ai|anyio"
sed -n '61,75p' uv.lock ; grep -n "agent-framework" pyproject.toml
curl -s https://pypi.org/pypi/agent-framework-orchestrations/json       # + /1.0.0 … /1.1.1/json
sed -n '1,80p' .venv/.../agent_framework/_feature_stage.py
grep -rn "@experimental|@preview|feature_stage(" .venv/.../agent_framework/*.py .../_workflows/*.py
grep -n -i "experimental|feature_stage" .venv/.../agent_framework_orchestrations/*.py   # 0
bash scratchpad/u_measure.sh                                             # V1
grep -rEl <9 null-regexer> .venv/.../agent_framework .venv/.../agent_framework_orchestrations | wc -l
grep -rn -i "research.01" STATE.md STATE-arkiv-2026-08-21.local.md docs/   # 0
grep -rn "enable_layer1_hitl|layer1" src tests ; grep -rEn 'request_info|RequestInfoEvent' tests  # 0
grep -rn "agent_framework" src/portfolio_optimiser/*.py                  # 14 importlinjer
microsoft_docs_fetch learn.microsoft.com/en-us/agent-framework/workflows/orchestrations/magentic
uv run python scratchpad/magentic_b7.py ; … magentic_b7b.py ; … magentic_reset.py   # E1E7
.venv/bin/python -c "import opentelemetry.sdk"                            # ModuleNotFoundError