☰ Contents

Agent Runtime

F verified factP decided planC open challenge

Purpose

Everything else in this wiki assumes a runtime that can do five things the health-AI branch does not: run a tool loop, spawn sub-agents, take a plan from a child and a decision from a human, be interrupted without losing the case, and resume in another worker (Platform Details PG-2). This page is the design of that runtime: the loop, how an agent graph is instantiated from profiles, how agents talk to each other, how a write gets approved and executed, and how the whole thing survives a dead worker. No third-party harness is adopted as the runtime (decision 02.09.2026); the patterns worth having are taken from the surveyed harnesses in § 9, each with its source.

F What the branch supplies and what it does not (verified 01.09.2026, Platform Details PG-2): per-activity cancellation with fencing, epochs and a dedicated cancel lane, and a hash-chained milestone timeline with transactional-outbox delivery exist and are reused; the loop, sub-agent spawning and plan submission are new work.

1. The objects

P Begin implementation with the developer guide, module declarations and model execution contract. The runtime storage additions are the proposed 010-runtime-execution.sql upgrade, not a live installation.

Object Is Persisted as
Case the durable unit of work (§ 11) CASES, papers
Task one agent instance's assignment inside a case: profile version, parent task, denied context, budget slice, grant subset, memory node, state TASKS
Turn one model call plus the tool executions it requested, ending in a persisted turn record AUDIT_LEDGER events + a turn checkpoint
Message a typed record between tasks — never free text (§ 3) ledger event, payload hash + artefact reference
Artefact anything larger than a message: a plan, a tool result, a compaction summary, a paper revision paper store, referenced by hash
Gate a pending human decision with its packet (Gating § 1) GATES, GATE_DECISIONS, GRANTS

P The agent graph is data: TASKS with parent pointers. There is no code object called "the graph" — the root task spawns children, children spawn sub-agents, and the tree the ledger shows is the graph. This is what lets the same runtime run the different module graphs — each drawn at the head of its module page (Configuration Module § 0, Support Module § 0, Data and Information Module § 0, Development Module § 0).

2. The loop — one turn

P One turn is one recorded model request/response and its resulting action list. Model Execution defines the exact request format, context order, output validation and provider behaviour. A deterministic task runs its registered handler without a model call. Every externally observable operation is journalled separately; a turn is not a distributed transaction.

flowchart TB
  A["Claim task lease
and fence"]:::store B["Restore pinned state
reconcile calls"]:::store C["Persist request
invoke model"]:::work D["Persist and validate
the model response"]:::proof F["Read or control handler"]:::work G["Audit and gate
an effect proposal"]:::decision X["Execute or reconcile"]:::work P["Commit result,
checkpoint and outbox"]:::store A --> B B --> C C --> D D -->|"permitted local/read action"| F D -->|"effect proposed"| G G -->|"decisions satisfied"| X F --> P X --> P P -.->|"next eligible turn"| A classDef work fill:#eef4ff,stroke:#6889ba,color:#17365b,stroke-width:1.3px classDef decision fill:#fff4df,stroke:#b78c36,color:#65470d,stroke-width:1.5px classDef proof fill:#e9f5ef,stroke:#689b81,color:#224e39,stroke-width:1.3px classDef learn fill:#f1edf9,stroke:#9580b9,color:#534172,stroke-width:1.3px classDef store fill:#f5f7fa,stroke:#98a6b7,color:#34445a,stroke-width:1.2px classDef owner fill:#24486b,stroke:#24486b,color:#ffffff,stroke-width:1.4px click A href "agent-runtime.html#7-durability-interruption-resume" "Only the current worker may apply the next transition." click B href "agent-runtime.html#22-checkpoint-contents" "A journalled success is not replayed; unknown effects block dependent work." click C href "model-execution.html" "Exact sanitised bytes precede provider dispatch." click D href "model-execution.html" "One structured action document; no partial or unregistered action executes." click F href "model-execution.html#4-the-action-contract-and-control-tools" "Owned paper writes are allowed during read-only estate preparation." click G href "../gating.html" "Park without a transaction or worker while a decision waits." click X href "../contracts/Connectors.Abstractions.cs.html" "Current grant, packet hash, scope, fence and preflight are rechecked." click P href "../data-model.html" "Durable state, messages and next eligible work."

One turn is a durable state transition. The LLM proposes; software validates, schedules and executes. Waiting never holds an open target transaction.

2.1 The persisted sequence

P The host supplies one unit of work to stores, ledger and outbox. Those services do not commit independently. No unit of work remains open during a model call, an estate connector call or a human wait.

Step Software action Durable boundary
1 Scheduler claims an open task, or an expired worker lease requiring recovery compare-and-swap on task row version; increment fence epoch, assign worker and lease deadline
2 Load pinned manifest/case type/profile and last state paper; reconcile nonterminal calls refuse incompatible state or missing artefacts; unresolved effects park the affected work
3 Context assembler stages the exact sanitised request and records a model call intent immutable request paper + RUNTIME_CALLS prepared + ledger event, committed before contact
4 Mark call dispatched, then invoke IInferenceClient outside the transaction call identity, request hash, route and epoch cannot change
5 Store terminal provider result and measured usage response paper + journal result + ledger/usage records; only then may actions be interpreted
6 Validate the complete action document and every proposed call allocate stable call ids/ordinals and persist the pending action list; malformed output executes nothing
7 Run eligible reads/control handlers; prepare writes through § 5 each call intent precedes dispatch; results and typed messages are stored before consumption
8 Apply typed outputs to plan/task state and schedule eligible children message deduplication and state mutation share one transaction (§ 4)
9 Write an immutable state paper and checkpoint checkpoint FK + hash, task state, budget charge, ledger events and outbox records commit together
10 Release the lease or schedule the next turn parked tasks consume no worker; completed tasks return one terminal result

P RUNTIME_CALLS is the durable operation journal defined by Data Model. It stores model and tool calls using the same identity discipline, not the same retry semantics. Uniqueness is task + turn + call ordinal; the model call is ordinal 0. The runtime allocates ids before dispatch. A model-generated id or provider metadata field is not the deduplication authority.

P Read calls can run together when their capability says they are independent. A batch containing platform mutations, skills that write or estate mutations runs in its recorded order. Each result retains origin, trust/data class, content hash, size and truncation; large results are paper references. A no-op control action still records what was checked. Platform control tools obey task ownership and the manifest even though they need no estate write grant.

2.2 Checkpoint contents

P Mutation identity. A target mutation uses a logical key derived from case id + stable plan-step id + semantic packet hash. Model tool-call ids are correlation only. A repeated proposal after another model turn first looks up that logical key: matching completed work returns its recorded receipt, unknown work is reconciled, and different semantic content needs a new approved packet. The new control-tool invocation is still recorded, but it cannot create a second target operation merely by changing its tool-call id. Request-paper hashes verify exact bytes; replay matching uses the immutable semantic packet inside that paper.

P Read-only is a verified capability, not a SQL-prefix test. The Oracle reader uses reviewed query templates or a validated SELECT subset with an allowed-function set; SELECT can otherwise invoke a side-effecting function. Unsupported expressions fail closed or become a registered operation with known effects. Read accounts, statement validation and scope checks are separate controls.

P The runtime-state schema is the restorable state, stored as PAPERS.KIND=artefact with its schema id. TURN_CHECKPOINTS.STATE_PAPER_ID references it and STATE_HASH verifies its bytes. It carries selected versions, next turn number, phase, pending call/gate/message ids, paper heads, the inherited handle_scope_case_id and completed plan-step ids. The checkpoint's context hash identifies the exact model request; it is not a substitute for that request's stored bytes.

P The call journal is authoritative over effects; build state is a projection of confirmed completed plan steps. A completed journal call with no checkpoint is reconciled into the next checkpoint, not executed again. A checkpoint that references missing bytes is a restore failure and parks the case; the worker never fills the gap from a model's recollection.

3. Instantiating the graph

P A module manifest and case-type variant define the permitted stage/profile graph (Modules). The root proposes the actual subset as plan revision 1. The service validates edges, dependencies, data routes, ownership and required checkpoints, then creates TASKS. Parent pointers describe ancestry; plan dependencies describe ordering. A stage id is a stable label, not its sequence number.

P Every child receives a narrower execution context: task objective, permitted paper references, current grant subset, allocated minutes, own boot node and declared tools. A child cannot widen its parent's authority. Handover sub-cases receive read grants initially and obtain their own write decisions. One-depth consults use the owning domain profile with read-only capabilities and charge the asking case.

P Isolation is an explicit context/tool facade. A standard .NET DI scope does not remove registrations from its parent service provider. The host therefore builds AgentExecutionContext with allow-listed readers and tool descriptors; neither an agent nor its tool arguments receive IServiceProvider, a general filesystem reader, credentials or a raw connector. The interpreter authorises each dispatch again. A verifier gets the symptom and evidence with source/environment provenance, but not the author's transcript, conclusion or identity. The grader gets anonymised candidate outputs and its rubric; its blindness is to authorship, not to facts necessary to grade.

P Subtasks share the same durable scheduler even when run in-process. A remote peer uses the same message envelope over RabbitMQ. A task can be claimed by one worker only; its independent children can run elsewhere. Cross-process isolation is used for untrusted worktree/build execution: restricted workspace, scrubbed environment, no platform/Vault write credentials, and only the approved tool endpoints. Logical DI scoping is not an operating-system sandbox.

4. Inter-agent messages

P Authenticate the producer as well as the schema. In-process dispatch carries trusted execution context; peer messages use the estate's configured service identity, broker permissions and ServiceActivity authorisation. The receiver checks sender task/profile and ancestry against persisted assignments. A payload hash or a claimed sender field is not authentication.

P message-envelope.schema.json is the wire contract. The model proposes only the business payload; the runtime stamps message id, authenticated sender task/profile version, case lineage and trust label. The receiver validates the envelope, payload and referenced paper schema before applying it.

Kind Receiver action Stored or changed
Spawn validate declared profile, parent, scope and budget; allocate child TASKS, ledger, scheduling outbox
Plan validate steps/assertions and create immutable revision PLANS, PLAN_REVISIONS, PLAN_STEPS, plan paper
PlanConfirmation record parent coordination; do not grant estate authority new decision evidence; plan projection
Question / Answer park/resume named dependencies; human authority requires a real gate-decision reference question/answer papers, task state
Result check proof and reconciliation against expected stage output terminal task state and result paper
Verdict accept an isolated verifier/auditor result from its assigned task verdict paper; gate readiness
Finding file evidence for the domain owner case-local proposal; no foreign active-memory write
Consult / ConsultReply spawn one-depth read task; on deadline record no_answer consult paper, task state, budget
DeadEnd return checked facts and missing capability upward root may open H6; no recursive failure loop
Cancel advance fence, stop dispatch, reconcile in-flight calls cancellation evidence and task/session state

P Each delivery is inserted into INBOX_MESSAGES under a unique message id in the same transaction as its state transition and resulting outbox messages. The stored payload hash must equal that of a redelivery. Same id/same hash returns the prior disposition; same id/different hash is refused. Rabbit acknowledgement follows the commit. In-process messages use the same handler; they do not bypass deduplication.

P Reject an unknown type/version without guessing its payload. Park the target as a capability gap and return one DeadEnd to its parent where supported. Reject out-of-order plan revisions using expected revision and row version; do not silently reorder a Cancel behind a completion. Immutable plan revisions are never amended to attach approval: approvals live in gate/message records, and the current plan is a projection over them.

5. Write approval — end to end

P Workspace boundary. Owned paper and source-worktree edits are platform operations under the task's declared capability and approved source scope. They may run between gates. A filesystem path outside that worktree, Git publication, a deployment or any estate mutation is an effect operation and follows the protocol below. Worktree build processes receive neither platform/Vault write credentials nor unrestricted estate access.

P A model calls writes.propose, not a mutating connector. The stage prepares a draft/approved template instance, exact values, expected counts, assertions, preconditions and a revert descriptor. The write auditor combines mechanical checks with independent review of operation and revert semantics; it is not the writer. The module root asks the required checkpoint/effect decisions. The execution coordinator loads the persisted decisions and invokes the deterministic platform.write_executor.

sequenceDiagram
  autonumber
  participant A as Agent and auditor
  participant G as Gate and authority service
  actor H as Role or eligible policy
  participant X as Execution coordinator
  participant C as Connector
  A->>G: Exact proposal and audit verdict
  G->>H: Applicable decision packet
  H-->>G: Verdict bound to revision and hash
  G->>G: Commit decisions and pending work
  X->>G: Reload current authority
  X->>C: Read current preconditions
  X->>G: Claim impact and reserve exact grant
  X->>X: Persist call intent and fence
  X->>C: Execute the permitted operation
  C-->>X: Applied, not applied, partial or unknown
  X->>X: Persist outcome before checkpoint
  alt Outcome established
    X->>C: Permitted customer-surface check
    C-->>X: Evidence for required assertions
  else Partial or unknown
    X->>G: Park reconciliation or H7 compensation
  end

The auditor judges the proposal; the authority service grants; deterministic code executes. A failed or unknown outcome is reconciled before any retry or H7 compensation.

P Binding. The proposal's digest covers case/plan/shape versions, target traits, bound parameters, statements, assertions, preconditions and revert descriptor. It excludes the digest field itself, eventual grant ids, audit verdicts and timestamps. Decisions refer to that digest. The execution envelope adds grant ids and call identity without changing the proposal digest. This removes the circular dependency between a packet asking for a grant and a grant bound to that packet.

P Returned ids are resolved before the dependent packet is audited. Do not guess them at planning. For one atomic multi-statement operation, a reviewed template may explicitly bind a later statement to an earlier statement's returned id inside the connector; the binding expression and target bounds are part of the approved template, and no model call occurs in the transaction. Arbitrary runtime substitutions are refused.

P H2/H3 approve the plan/result at their declared event. H4 approves the release script set. Neither silently substitutes for effect authority. Gating derives all applicable decisions—such as HW-ddl plus H5 for customer-facing DDL—over the same packet. One UI action may answer several required decisions if the person holds every role; the service records each separately.

P Immediately before dispatch, the coordinator rechecks revocation, risk acceptance, shape/version state, expiry, target identity, packet hash and task fence. It serialises competing impact-key claims and reserves the grant for this journal call in one platform transaction. Reconciliation may reuse that same consumed grant identity; it cannot spend it on a second action. A changed preflight invalidates the old packet and returns to planning.

P Form A collapses apply/check/replay/commit inside one connector call. Form B is a connector-owned lease driven exclusively by deterministic executor code, within one executor operation; it admits no model call, human decision or agent hop while open. Irreversible and DDL operations use capability-specific execution paths, never rollback-capable Oracle transactions. Failure and Recovery defines the outcomes.

P Dry-run is non-mutating inspection/rendering only. A rehearsal that writes and rolls back is still an effectful simulation and needs its write gates. The connector advertises which kind it supports; a read credential never attempts DML.

6. Context management

P Model Execution § 3 owns assembly and sizing. Store the complete sanitised request and response as artefacts and only references/hashes in the ledger. Compaction writes a new derived paper with facts and sources, decisions by id, pending questions and retained result references. It never edits authoritative plans, grants, journal receipts or earlier evidence.

P The compactor has no connector or memory-publication tools. Check its output for preservation of pending work and decision references; on failure keep the previous summary and park if context no longer fits. A model swap must fit the required input/output contracts and pass the route's eval set; it is not a reason to discard history.

7. Durability, interruption, resume

P Claim the task using its row version and a new fence epoch. Renew the worker lease while active. All platform state changes check the current epoch; a stale worker may record late external evidence but cannot complete a task or dispatch another operation.

Journal state at recovery Required action
prepared, never dispatched execute the saved request only after fresh authority checks
dispatched with no terminal result mark uncertain and reconcile; never assume failure means no effect
succeeded with valid result paper reuse the result and apply missing local projections/checkpoint; do not repeat the target call
refused / conclusively not applied return the recorded refusal; a corrected proposal is a new revision/call
failed with proven absence of effect retry only under the capability's recorded rule and the same logical operation identity
unknown or partly applied hold conflicting impact keys, park dependent steps, reconcile or request compensation; no blind retry

P WhatLandedAsync returns applied / not_applied / partly_applied / unknown, plus how that was established. Row existence alone is insufficient if another actor could have created it. Use a target idempotency receipt, exact expected post-state with unique identity, request/pipeline id, or another capability-declared conclusive test. A connector unable to distinguish absence from uncertainty cannot automatically retry that mutation.

P A target commit and platform outcome commit are independent. If the latter fails, retry the local recording transaction, not the effect. If the effect's response was also lost, reconcile first. A ledger append conflict retries the local append from the current chain head using the same record id; it never replays a connector call. No-duplicate execution depends on conclusive connector evidence; otherwise the guarantee is explicit uncertainty and no blind retry.

P Cancellation first fences further dispatch. Roll back a connector transaction if still uncommitted; reconcile an already submitted external action even after cancellation. A cleanup opportunity persists known facts but cannot start another model-driven mutation. Hard stop refuses new writes/sends/pushes/DDL immediately at dispatch; reads and reconciliation remain available. Soft stop admits no new cases and parks existing work at the next declared checkpoint. Check authority changes at dispatch, not only at case open.

P Impact-key claims are persistent rows acquired in sorted order. Worker lease expiry does not release a key while its external outcome is unknown. Release follows conclusive absence/application/compensation. These locks coordinate platform cases; they do not lock external humans out of the estate. DDL's external-author boundary is Stage Planning § 3b.

8. Model routing, events, observability

P Model Execution defines provider selection and failures. Events reference profile/prompt/route/framework versions, exact request/response hashes, task ancestry and call id. Record measured usage; unavailable values stay unknown. Charge delivery-time minutes for active execution; count parked time separately as elapsed waiting. Provider/session/concurrency caps remain separate technical controls.

P State, ledger and OUTBOX_MESSAGES commit together; a confirmed publisher delivers at least once. SignalR sends reference notifications, not another state store. The UI reconnects from its ledger cursor through HTTP, deduplicates by sequence and refreshes the affected resource. Client queries enforce customer ownership server-side, including artefact reads; hiding a button is not authorisation.

P Generated prose is not a status transition. Derive progress from committed task, gate and call records. Logs/spans contain safe ids, durations and result categories, never raw prompts, identifiers or credentials. Diagnostics link to case evidence under its access rules.

9. What the surveyed harnesses settle

Survey of 02.09.2026 (primary sources cited inline; third-party-only claims marked). The runtime itself is not adopted from any of them (decision 02.09.2026); the patterns are.

9.1 The landscape, in one table

Runtime Language What it proves for us Source
DeepSeek Harness (dsh) TypeScript/Node, MIT, developer preview released 13.08.2026 with V4-Pro; Python SDK is a stdio wrapper the cleanest event-sourced loop: "the session log is the source of the context the model sees", model-visible means logged, turn/step vocabulary, staged tool pipeline, fail-closed approval, read-only sandbox default repo · persistence · tool pipeline · lifecycle
Microsoft Agent Framework (MAF) .NET and Python, GA 1.0 on 03.04.2026; successor of Semantic Kernel agents (now legacy) and AutoGen the only first-class .NET agent framework: IChatClient loop with middleware, ApprovalRequiredAIFunction (a run ends with an approval request, the answer arrives on the same session), superstep checkpoints that include pending approvals, OpenTelemetry GenAI conventions, A2A hosting, Durable Task runtime, a Harness agent with compaction and standing approvals GA · tool approval · checkpoints · observability · harness
Claude Agent SDK Python and TypeScript only; from .NET only by running the CLI as a subprocess (Anthropic's own guidance) the cleanest permission model: hooks → deny → ask → mode → allow → callback, deny beats bypass, pattern-scoped rules, plan mode never auto-approves writes, PreToolUse may defer a call; sub-agents get fresh context and return only a final message; child output scanned for control-tag imitation; read-only tools run in parallel, mutating ones sequentially overview · permissions · hooks · subagents · loop
OpenAI Agents SDK Python and TypeScript; no official .NET agents package found needs_approval per call, serialisable RunState for long-lived approvals, always_approve sticky decisions HITL
LangGraph · Google ADK Python/JS · Python/Go/TS both re-execute code before the pause on resume — ADK: "tools may execute multiple times during resumption" — the failure our invariant no turn spans an uncommitted write exists to avoid interrupts · ADK resume
Temporal .NET · Dapr Workflow .NET .NET SDKs exist durable execution done right: every model and tool call is an Activity whose result is recorded; replay skips completed work; workflows can wait days for a human signal Temporal · Temporal .NET · Dapr Workflow .NET

9.2 Patterns adopted P

Each pattern names where it lands in § 1–8 and its source.

# Pattern Lands in Source
1 Model-visible means logged. The model request is derived from surface events of the ledger, and dispatch asserts the request equals the projection; chunks, usage, approvals, hooks and compaction are log-only events § 2 rule 1, § 7 dsh persistence catalog
2 Turn/step vocabulary with a pre-step authorisation hook and a terminal turn-stopping checkpoint § 1, § 2 dsh lifecycle
3 Ordered permission pipeline — hooks → deny → ask → mode → allow → callback; deny beats everything; pattern-scoped rules (Sql(UPDATE *), Env(PROD)) § 2 rule 2, § 5 Claude Agent SDK permissions
4 Approval is a paused run, not a new turn. A tool call needing approval ends the run with an approval-request record kept in the checkpoint; the answer arrives on the same case; standing approvals per rule = our grants § 5 MAF tool approval; OpenAI RunState
5 Absent or unanswerable approval = deny, recorded with the reason § 5 dsh tool pipeline
6 Tool pipeline as middleware stages — pre-policy → guards → approval → execute (timeout, retry, metrics) → post-policy (accept / block / replace / add context) → normalise exceptions into isError results → one tool/result event § 2 rule 3 dsh tool pipeline; MAF function middleware
7 Read-only tools run in parallel, mutating tools sequentially with barriers; every connector operation is tagged § 2, Failure and Recovery § 4 Claude SDK readOnlyHint; dsh executionMode
8 Idempotency key on every mutating operation = {caseId}:{stablePlanStepId}:{semanticPacketHash}, stored with the result, short-circuited on replay; a compensation registered per step § 2 rule 4, Failure and Recovery § 2 Temporal activities; idempotency/saga guidance
9 Checkpoint after every turn, pending approvals included, so a restored case reloads the existing gate id rather than creating another gate § 7 MAF checkpoints
10 Sub-agents by denied context — task prompt plus own system prompt, restricted tools, own budget and turn cap; only a final artefact reference returns; depth, concurrency and spend capped § 3, Agents § 0.2 Claude SDK subagents; Anthropic multi-agent research
11 Artefacts, not transcripts between agents § 4 Anthropic multi-agent research
12 Orchestrator-workers only for decomposable read-heavy work; write-heavy, decision-coupled work stays single-threaded or a fixed graph — exactly the split between Data and information investigation (S2, fan-out) and any apply phase (one write executor) § 3 Anthropic building-effective-agents; Cognition "don't build multi-agents"; MAF agents-vs-workflows
13 Two-tier compaction — head/tail-prune old tool results first (dsh defaults: 8 k chars → 4 k head / 1 k tail), then summarise at ~80 % of the window keeping decisions, open items and ids; archive the full transcript before compacting § 6 dsh config catalog; Anthropic context engineering; Claude SDK PreCompact
14 Spill large outputs to artefacts; the prompt gets the tail and a reference § 6 dsh tool catalog; Anthropic writing-tools-for-agents
15 Quarantine untrusted content — spotlighting delimiters on ticket/email/DB free text; a no-tool reader step whose output is provenance-tagged data; policy checked on tagged data before any mutating tool; never one agent with private data + untrusted input + outbound send § 2 rule 3, Trust and Data § 1 Spotlighting (arXiv 2403.14720); CaMeL; lethal trifecta
16 Scan child-agent and tool outputs for control-tag and turn-marker imitation before the parent reads them § 3, § 4 Claude SDK subagents
17 OpenTelemetry GenAI conventions from day oneinvoke_agent, chat, execute_tool spans, token-usage metrics, sensitive capture off in PROD § 8 MAF observability
18 Replay-based regression tests — recorded sessions as fixture and oracle; a replay adapter feeds recorded assistant output so loop, permission pipeline and projections are tested without API keys § 10, Agent Framework § 6.5 dsh replay (third-party report)
19 Outcome-graded evals with isolated judges — grade the end state (rows, comment) before the text; one rubric dimension per judge call with an "Unknown" exit; pass@k and pass^k; start with 20–50 real ticket cases; an adversarial verifier against self-preference Agent Framework § 6.5 Anthropic demystifying evals; Claude Code dynamic workflows
20 Process hygiene — scrub *KEY*/*SECRET*/*TOKEN*/*PASSWORD* from child environments, resolve credentials per operation, await quiescence on cancel, report timeout / exit / signal orthogonally § 7, connectors dsh defensive patterns

9.3 The decision the survey raised — build on Microsoft Agent Framework, or from scratch P

"Native C# loop" does not have to mean "from scratch". MAF is GA in .NET and already provides four of the things § 2–8 specify — the IChatClient loop with middleware, approval-as-paused-run, superstep checkpoints with pending requests, and OpenTelemetry — plus provider adapters for Foundry, Azure OpenAI, OpenAI, Anthropic and others behind one interface.

Build on MAF primitives From scratch
Gets for free loop, middleware, tool approval, checkpoints, OTel, provider adapters, A2A nothing
We still build the case/ledger/grant model, connectors, the memory tree, gates, papers — none of which MAF has everything
Risk dependency on a Microsoft framework's release cadence; the checkpoint format is theirs; the estate's own AI branch chose its own provider abstractions, so two abstractions would coexist schedule: the loop is the largest unproven build item, and MAF exists precisely because loops are hard to get right
Fits the estate .NET, OpenTelemetry, DI-based — yes; Azure OpenAI EU deployments — yes

Decision (Vladimir, 02.09.2026 — D39): minimum dependencies. The provider abstraction is copied and ownedAI.Abstractions and the AI.AzureOpenAI adapter from the health branch, as the Software Architecture says — and the loop is written from scratch to the shape of § 1–8. Microsoft Agent Framework is not a dependency: one abstraction is not worth a framework. It is reconsidered only where a concrete piece of it — the checkpoint or the approval-as-paused-run machinery — would demonstrably save work on the T1 experiment; the burden is on MAF to prove the saving, not on the team to prove MAF can carry the design. Everything in § 9.2 is adopted as a pattern regardless.

9.4 The 80 % rule — scored (decision Vladimir, 02.09.2026 — D61)

The rule: the platform fully owns the runtime, on-premise — unless a free-to-use SDK demonstrably matches ≥ 80 % of the load-bearing requirements. Scored 02.09.2026 against the fourteen requirements § 1–8 imply (✓ = 1, ◐ = ½, ✗ = 0; sources: the § 9.1 table, web-verified 02.09.2026):

# Requirement MAF 1.0 (.NET) DeepSeek Harness (TS) Claude Agent SDK OpenAI Agents SDK Temporal / Dapr
R1 Native .NET, hosted in-estate (DI, Authority, RabbitMQ), on-prem on a customer VM ✗ (TS/Node, no C# client — a subprocess sidecar) ✗ (Python/TS; .NET via CLI subprocess) ✗ (no .NET)
R2 Pre-contact policy/grant check on every tool call (§ 2.2) ◐ middleware + tool approval; the grant/env model is ours ✓ permission policies, fail-closed, sandbox ✓ hooks → deny → ask → allow pipeline needs_approval per call
R3 Sub-agents with denied-context provisioning (§ 3) ◐ agents exist; DI-scope denial is ours ◐ subagent permission control ✓ fresh context, final-message-only
R4 Typed inter-agent messages; artefacts by hash; no transcript crossing (§ 4)
R5 Plan submission upward, parent confirmation, gates to a human inbox
R6 Approval as a paused run, resumed on the same case RunState ✓ signals
R7 Turn checkpoint incl. pending approvals; resume elsewhere without re-executing writes ◐ checkpoints exist, format theirs; idempotency/effect classes ours ✓ event-sourced log, resume/fork/replay ✓ activities recorded, replay skips done work
R8 Per-profile governed model route + EU data-zone enforcement (§ 8) ◐ adapters yes; the governed residency predicate is ours — two provider abstractions would coexist ✗ Anthropic-locked ✗ provider-locked
R9 Per-task/case budgets, soft/hard (§ 2.6)
R10 Ledger-native: model-visible-means-logged, hash-chained audit (§ 1–2) ✗ telemetry ≠ audit ✓ the session log is the context
R11 Two-tier compaction with summary artefacts (§ 6) ✓ documented defaults
R12 OpenTelemetry GenAI from day one (§ 8)
R13 Replay/eval testing without API keys ✓ native replay
R14 EU-resident, DPA-covered model path for Support (SG-9) ✓ Azure OpenAI EU ✓ self-hosted ✗ calls leave to Anthropic
Match ≈ 54 % ≈ 57 % ≈ 43 % ≈ 36 % ≈ 46 %

Verdict: none reaches 80 %. Temporal/Dapr ranks third after the two closest, but still fails exactly where the platform's point is: the grant/gate/ledger authority model (R2/R5/R9/R10), the data boundary (R8/R14), and — for dsh — the estate fit (R1: a Node sidecar runtime operated forever, in developer-preview stability). The owned, on-premise native loop stands (D39 confirmed). The exception is component-level, as decided: a concrete MAF piece (checkpoint or approval-as-paused-run machinery) may be adopted where it demonstrably saves work on the T1 experiment — the burden of proof is on the framework, and the internal contract of Architecture § 4 keeps the choice reversible. The scorecard is re-run when an SDK's surface changes materially.

10. The T1 experiment

The platform experiment named in Delivery § 2 is the acceptance test of this page. It passes when, on the Bulstrad QA environment: two isolated cases run concurrently with sub-agents; a child plan reaches its parent and a gate reaches the inbox; a holder of the role decides and the grant is bound to the artefact; a worker is killed mid-case and another resumes with the plan and evidence intact; a route swap changes the adapter without touching a session record; a tool call outside the grant is refused before contact; and every connector result has its ledger row.

11. Case control — the durable object model

The durable object model for work. Owned by the Support webservice; workers hold no durable state; the administration UI is its surface.

11.1 The model

P State table. Cases: opened (arrival stored) · running (at least one task active) · parked (question, gate, sub-case, connector/provider down, budget, capability gap) · at gate (human decision row open) · held (approved work waiting for execution) · resolved (requested work verified, customer told, precipitation queued) · closed (customer confirmation or channel closure) · cancelled (the customer withdrew, or a duplicate merged). Sessions: live · stopped · resumed · deleted.

11.2 Working papers

Every case owns a fixed paper set — the medium that makes a run resumable rather than repeatable [F: proven in the PC agent's 9951 run, source]:

Paper Holds Why
Plan steps with stable ids and a one-line assertion each (a count, a row that must exist) an unrun step leaves nothing behind that looks wrong — the log records only what ran; assertions make absence visible
Semantic contract the module's intermediate: whitelabel spec / fix packet / change packet the hard part is reviewed before anything is written
Write log every intended write and every statement actually sent — target, environment, mode, row count; never result rows reconstruction and teardown: the set removed is exactly the set written
Build state completed step ids + platform-returned ids (source→target maps) resume by skipping what is done; children re-parent onto returned ids, never predicted ones
MISSING deliberate omissions and unresolved questions: what, why needed, expected source, blocking or not missing information is a deliverable, not a failure
Report proof level reached, reconciliations, memory/skill changes made evidence for the decision-maker; settled facts go to memory, not here

The audit log indexes the papers; it does not replace them.

P Papers and case artefacts live behind a dedicated, token-gated file service owned by the Support module (decision 01.09.2026), with their revisions indexed in SRD_SUPPORT.PAPERS.

Not FileServer.V2. [F, verified 01.09.2026] its objects are immutable by schema with no version chain, a keyed re-save with changed bytes returns 409, scope codes are a closed Oracle CHECK set, tags are a closed vocabulary, listing is by scope and key-prefix only (never by owner), and text/markdown is rejected outright. Rewriting one named paper many times per case is exactly what it refuses.

The dedicated service is deliberately small. The user population is a handful of operators, so it is not designed for horizontal scale; it is designed for security and simplicity: every read and write carries a token scoped to the case and the role, papers are mutable with a revision history, listing is by case, and every access is an audit record. Nothing about how files are served needs to be inherited from a service built for a different problem.

11.3 The plan protocol

P The plan's execution obligations carry its full scope/assertion matrix, external owners and restoration dependencies (Case journeys). Service validation resolves each record in this case, compares the approved plan/scope revision and refuses missing/unknown proof. A read, delivered instruction or verified external action can complete a step without a platform write. PLAN_STEPS.STATUS=done is projected only after its required assertion passes; waiting/reported remain paper states projected to existing parked tasks.

  1. A child agent's plan goes to its immediate parent for confirmation; only the root agent asks the human (Agents Memory § 2 rule 8). Parent confirmation orders work — it never substitutes for a gate (Architecture CR-2).
  2. Plans persist with revisions; "show me the current plan" is a query.
  3. Plan reconciliation at close: every step id is matched against the write log and its assertion re-run. A plan that cannot be reconciled against what was written is a description, not a plan. [Why: v1's halted-checklist family, Baseline § 5.]
  4. A plan-only request ends at the plan (Non-Goals N-5).

11.4 Parking and resumption

P External human work parks as question with a step-scoped obligation and visible actor/age; registered external jobs use external_job. A reported result queues verification, never marks a platform effect applied. Timed restoration persists its due trigger before the temporary effect and wakes H7; drift or missing authority keeps it waiting. Post-result work is committed to the outbox with resolution and resumes independently of session/closure state. Dedup includes the verified result revision: a correction is a new learning event, not another independent case. Case journeys defines completion and successor obligations.

A question to the human parks the affected tasks only. The question carries: the gap, what was checked, what depends on it, the alternatives with consequences, the decision needed. The answer updates the affected decision/plan revision; verified work is never discarded. Resumption loads pinned state and reconciles the call journal (§ 7). Successful effects are not replayed; an unknown outcome blocks dependent work.

P The park taxonomy (D66) — a parked task names one reason class, and the class decides what wakes it: question (a human answer) · gate (a decision of a role holder) · sub-case (an Hd handover, Agents § 5) · connector-down / provider-down (the health signal, retried per the connector's capability description — Failure and Recovery § 4) · budget (a top-up or a profile decision) · capability-gap (a T-track build item). Parked state and age are case fields in Cases and in Control › Exceptions; per class the retry is mechanical (health-gated backoff for connector/provider, none for the human classes), and a parked case is never failed by the platform for waiting.

Example [F: the PC 9951 run, P-9]: an apply fails partway through a clone; the state file holds the completed ids and their platform-returned ids; the resumed run skips them and re-parents children onto the returned ids. Resumption may happen in a fresh worker on another machine, because papers and state live in the case.

11.5 Concurrency and limits

11.6 Intake

Cases open from: the administration UI, the plug-in's monitors (ticket system, mailboxes, help desk — intake.origins), a root agent's recruitment (module handover, gate Hd), or a schedule. platform.intake normalises the arrival, stamps its origin key, dedups, resolves the customer and checks the initiation right; precedent retrieval and the case type are the desk's S1 (Agents § 5c, D143). A case type is data, extensible per module, each type carrying its initial grants, budget, SLA class and stage set (Whitelabel Catalogue § 6).

Challenges