OpenProgram · Provider / Runtime design · implemented and reviewed · 2026-08-12

JSON Schema Structured Output

Design baseline: main@708bfbab. Implementation evidence: Tasks 1–6; final review commit 2fb471b3. Sections 2–15 retain the approved design and implementation checklist; §16 separates implemented contracts, verification evidence and unmet complete gates.

Design conclusion: Continue the existing Runtime.exec(..., response_format=...) as the sole public entry. Add normalized schema configuration to public provider types. Capability negotiation selects native provider JSON Schema or a constrained hidden-tool fallback. Regardless of provider strict claims, final results require strict JSON parsing and local validation against the caller’s original schema. Failure permits at most one semantic repair request; return only validated Python JSON values.

The existing Agent loop now implements schema normalization, negotiation, strict local validation, one optional repair, typed CLI/Web entries, record/replay v2 and the session-title consumer. Unverified provider combinations fail closed; ordinary text calls retain return semantics.

1. Scope, goals and boundaries

Function-tool arguments use the canonical tool schema for local validation. Strict provider export represents optional fields as required nullable fields; shared argument normalization restores those added nulls to omitted object properties before canonical validation. Required fields and originally nullable values retain their semantics. Only nulls accepted by the actual strict property schema are eligible, so enum, conditional and combinator constraints remain enforced. Normalization copies inputs, preserves array positions and applies the same optional-default semantics to non-strict callers. It does not modify function signatures or weaken canonical schemas.

Unified semantics

Python Runtime, Agent, CLI and WebSocket/API share one schema, negotiation result, validator and error classification.

Streaming compatibility

Preserve incremental provider text/tool events while explicitly representing candidate attempts, discard, retry and final structured results.

Reproducibility

Record/replay captures normalized requests and every provider call; offline replay reruns identical parsing, validation and bounded retries.

Excluded: lossy schema-to-provider-subset rewriting, automatic Pydantic types, cross-language code generation, tokenwise incremental JSON AST, Web schema editor, or changed ordinary-text returns/events.

2. Baseline audit (main@708bfbab)

This section records project-start facts and gaps as the basis for design decisions; it does not describe the current state of 2fb471b3 .

LayerCurrent factsGap
Public Runtimeagentic_programming/runtime.py Already exposes response_format: dict and forwards it on the callable-model path._call_via_providers() does not use it; default provider calls silently fall back to ordinary text.
Provider typesStreamOptions has sampling, tool, transport and header fields; Model has reasoning/thinking/compat; AssistantMessage stores only text/tools/errors.No output schema, model capability or parsed JSON fields.
Model catalogmodels_dev.py Already normalizes and caches models.dev structured_output .Model has no corresponding field; Pydantic construction discards it and the model list does not show the capability.
Agent loopAgentLoopConfig Inherits shared stream options; each round may execute tools until an assistant message contains no tool call.Reconstructing SimpleStreamOptions has no schema to forward, nor final-candidate validation/repair state.
Stream eventsProvider events cover text/thinking/tool/done/error; dispatcher converts them to Web/CLI stream_event.Cannot represent “candidate 1 invalid; clear and enter attempt 2” or “final structured value validated”.
CLI / Web / API--print Finally prints strings; WS chat_response.result.content is a string; POST /api/function/{name} returns asynchronous acknowledgements.Requests cannot carry schemas; successful results lack typed JSON and errors lack stable machine-readable codes.
Session titleDesign documentation specifies {title:string} schema.agent/dispatcher/titles.py Actually prompts “ONLY title text”, then cleans lines, quotes, prefixes and length.
Record/replayFormat v1 records model, context, options and raw provider events; replay compares fields strictly.New default fields change shapes between old recordings and new requests; validation retries must preserve call-index order.

Call and return chain

Runtime.exec
_call_via_providers
AgentSession
Agent loop
API provider stream
event parsing
CLI / WS / API

Schema must travel through this existing chain. Do not add a structured provider client bypassing the Agent loop, which would create separate tool-calling, deadline, record/replay, DAG and persistence semantics.

3. Public API and normalized types

Retain the parameter name response_format because it is already public and documented. Support bare JSON Schema and an envelope containing OpenProgram controls. Normalize exactly once.

JsonValue = None | bool | int | float | str | list["JsonValue"] | dict[str, "JsonValue"]

class JsonSchemaOutput(BaseModel):
    type: Literal["json_schema"] = "json_schema"
    schema: dict[str, Any]                  # Original caller schema; never mutate in place
    name: str = "response"                 # ^[A-Za-z_][A-Za-z0-9_-]{0,63}$
    description: str | None = None
    strict: bool = True
    fallback: Literal["auto", "none", "prompt"] = "auto"
    max_validation_retries: Literal[0, 1] = 1

# Existing-call compatibility: a bare dict is the schema, not a provider payload.
Runtime.exec(content=..., response_format={"type":"object", ...}) -> JsonValue
Runtime.exec(content=..., response_format=JsonSchemaOutput(...)) -> JsonValue
Runtime.exec(content=..., response_format=None) -> str

Public provider fields

class Model(BaseModel):
    structured_output: bool | None = None   # models.dev: true / false / unknown

class StreamOptions(BaseModel):
    ...
    response_format: JsonSchemaOutput | None = None

class AssistantMessage(BaseModel):
    ...
    structured_output: JsonValue | None = None
    structured_output_mode: Literal["native", "tool", "prompt"] | None = None
    structured_output_attempt: int | None = None

None means unknown capability, not False. Raw JSON text remains in assistant text content for history, DAG, audit and replay; program calls return structured_output.

4. Capability negotiation

The model catalog says only whether the model claims structured-output support. API adapters must also specify payload dialect, streaming and tool-combination constraints; negotiation checks both.

class StructuredOutputCapabilities(BaseModel):
    native: Literal["supported", "unsupported", "unknown"]
    dialect: Literal["openai_chat", "openai_responses", "anthropic",
                     "google", "bedrock"] | None
    streaming: bool
    with_tools: bool
    schema_profile: str                 # Provider-subset checker name

negotiate(model, api_provider, options, tools) -> StructuredOutputPlan
# plan.mode: native | tool | prompt
# plan.provider_schema: Semantically equivalent projected copy
# plan.original_schema: Used for final local validation
  1. Invalid schema: fail immediately.
  2. If the adapter explicitly supports the API, the model field is not False, schema projects losslessly and current tools are supported, choose native.
  3. fallback="auto" and the provider supports strict tool schemas, schema projects losslessly, and tool_choice does not prohibit/force another tool: choose a hidden tool.
  4. fallback="prompt": explicit prompt fallback guarantees only local validation, not provider-level strictness.
  5. Otherwise raise StructuredOutputUnsupportedError before sending, reporting provider, model, conflicting capabilities and fallback reasons.
Do not optimistically interpret unknown capability. openai-codex and gemini-subscription may reuse official model entries but use different backends. Without API-adapter contract tests, Model.structured_output=True alone must not enable native fields.

Provider mappings and initial boundaries

APINative payloadInitial strategyCombination constraints
openai-completionsresponse_format={type:"json_schema", json_schema:{name,description,strict,schema}}nativeCheck tools/parallel against compatibility profiles; do not assume complete OpenAI support for community endpoints.
openai-responsestext.format={type:"json_schema", name,description,strict,schema}nativeRetain refusal/incomplete termination; fall back to hidden tools when capabilities are insufficient.
azure-openai-responsesResponses text.formatnativeDeployment model and API version must pass adapter probes/contracts, not model-name assumptions.
anthropic-messagesoutput_config.format={type:"json_schema", schema}nativeMerge with existing output_config.effort without overwriting; refusal/max_tokens still require local failure classification.
google-generative-airesponse_mime_type="application/json" + response_json_schemanativeAccept only the officially supported subset; mark tool combinations with_tools=False.
bedrock-converse-streamoutputConfig.textFormat.structure.jsonSchema until contract tests pass; schema is a JSON stringnativeThe adapter decides supported models and citation conflicts; local validation still uses the original schema.
openai-codexNo verified ChatGPT-backend contracttool fallbackNative capability unknown; do not send private OpenAI Responses fields.
gemini-subscriptionCloud Code Assist requests internally contain generationConfig, but compatibility is unverifiedtool fallbackEnable native capability only after independent contract tests.

External contract references: OpenAI Structured Outputs, Anthropic structured outputs, Google GenerateContent API, Amazon Bedrock structured outputs, Azure OpenAI structured outputs. These interfaces change; adapter contracts are required before enabling capabilities.

5. Native mode and fallbacks

Native schema

Adapters only map normalized configuration to provider payloads. Existing providers/_schema may supply traversal/dialect detection, but transformations must preserve semantics, such as key naming or JSON-string serialization. Dropping constraints, relaxing additionalProperties, rewriting unions or any other lossy transform reports the exact path and disables native mode. Final validation always uses the caller’s original schema.

Hidden-tool fallback

{
  "name": "__openprogram_submit_json",
  "description": "Submit the final response matching the required schema.",
  "parameters": <provider_schema>,
  "strict": true
}

Prompt fallback

Enable only when the caller explicitly sets fallback="prompt" : add a compact schema and JSON-only requirement to the system layer, then parse/validate final text identically. This is not strict and is never silently used in auto . This distinguishes provider constraints from purely local detection.

6. Streaming events and incremental text

Structured output does not incrementally validate schemas. Forward raw text deltas per attempt so consumers may display generated JSON; parse only after final text_end . Add two framework events and optional attempt metadata on text events to prevent invalid attempts becoming final messages.

EventTextStart/TextDelta/TextEnd.output_attempt: int | None = None

EventStructuredOutputRetry {
  type: "structured_output_retry",
  attempt: 1,
  next_attempt: 2,
  issues: [{code, path, schema_path, message}],
}

EventStructuredOutputEnd {
  type: "structured_output_end",
  attempt: 1 | 2,
  mode: "native" | "tool" | "prompt",
  value: JsonValue,
}
1
Forward attempt=1 text/tool deltas into a client candidate buffer without committing persistent messages.
2
After provider raw done , strictly parse and validate. On success emit structured_output_end, followed by exactly one done; stream EOF without raw done/error fails as incomplete rather than synthesizing success from a partial message.
3
For repairable failure emit structured_output_retry. CLI/Web clear candidate buffers; the Agent loop starts another provider stream.
4
Commit if attempt 2 succeeds; otherwise emit error without done and do not persist an invalid final assistant message.

Provider raw done is consumed by the Agent loop; expose only the structured transaction’s final done. Ordinary text retains its event sequence. CLI/TUI/Web safely ignore unknown events for rolling upgrades.

7. Strict parsing, validation and bounded retries

Parsing rules

Retry states

FailureConsumes validation retry?Handling
Invalid schema definitionNoBefore request StructuredOutputSchemaError.
Unsupported provider/model or conflicting configurationNoBefore request StructuredOutputUnsupportedError.
JSON syntax, schema violation, missing hidden submissionYes, at most onceAppend the invalid assistant candidate and deterministic issue list to temporary repair context; request once more.
Refusal, content filter or safety blockNoStructuredOutputGenerationError(code="refusal").
max_tokens/incomplete, context lengthNoRetain provider reason; the caller adjusts budgets. The framework does not enlarge token limits itself.
transport/rate limit/provider internalNoExisting stream/exec retry still handles these; they do not consume validation retries, but every Runtime provider call consumes shared max_retries budget.
Cancellation or deadlineNoPropagate immediately without repair.

Shared call budget

max_validation_retries and max_retries are two limits, not multiplicative loops. The former caps semantic repairs after initial-candidate failure; the latter caps total provider calls initiated at the Runtime boundary by one synchronous/asynchronous exec , including the initial call, semantic repair and calls following transport failures. Repair requires both an available validation retry and remaining shared call budget.

Therefore Runtime-boundary provider calls never exceed max_retries. For example, max_retries=1 causes an invalid first candidate to immediately return StructuredOutputValidationError; max_retries=2 with one default validation retry permits only initial call plus one repair. A repair transport failure already consumes budget; the outer layer cannot regain a full budget. Adapter/SDK transport retries remain governed by existing deadlines/transport settings, not validation retry counts.

structured_output_retry.attempt and next_attempt continues identifying structured candidate attempts, not transport attempts. Emit only after Runtime confirms remaining shared budget and is about to repair; never emit false retry events on exhaustion.

Repair prompts contain only failure type and bounded issues, without reinterpreting schemas or calling another LLM. Repair context lasts only for this exec; session history/final DAG keep only the valid final assistant, while model-call records retain both provider calls for audit.

8. Coexistence with tool calling

CaseBehavior
Native schema without user toolsApply native output format directly.
Native schema + user tools; adapter with_tools=TrueEach Agent round carries schema. User tools execute normally; validate terminal text from the round without tools.
Native schema + tools, combination unverifiedUse hidden-tool fallback if auto permits it; otherwise fail preflight.
Tool fallback + user toolsCombine hidden submit and user tools. Ordinary tools may run multiple rounds; submit ends the transaction.
One response contains ordinary tools and submitReject the candidate and enter bounded repair; do not execute ordinary tools, avoiding side effects after finalization.
Caller forces a specific tool or disables toolsDo not override intent; use native if available, otherwise unsupported.

Explicitly copy structured configuration during agent_loop._stream_assistant_response() Reconstructing SimpleStreamOptions . Final validation belongs at the Agent-loop no-tool terminal boundary, not duplicated inside provider parsers.

9. CLI, WebSocket/API and error semantics

Python API

value = runtime.exec(content=..., response_format=schema)  # Python JsonValue

except StructuredOutputError as e:
    e.code       # invalid_schema | unsupported | invalid_json | validation_failed |
                 # missing_submission | refusal | incomplete
    e.provider
    e.model
    e.attempts
    e.issues     # bounded machine-readable list

StructuredOutputError uses the existing LLM-error boundary without pretending to be a transport error.retryable means whether a new exec/budget could succeed; validation-retry exhaustion is indicated by attempts .

CLI

Add to existing one-shot openprogram --print the option --json-schema PATH; initially leave interactive REPL/TUI input unchanged. Success writes canonical UTF-8 compact JSON plus newline to stdout and exits 0. Errors go to stderr: usage/invalid schema exits 2, unsupported 3, generation/final-validation failure 4. Existing provider/auth/cancel semantics remain. Persist compact JSON in session assistant content with parsed value/mode in metadata.

WebSocket and asynchronous HTTP API

// request: existing chat action and POST /api/function/{name} body
response_format?: JsonSchemaOutput | JSONSchema

// stream
{type:"chat_response", data:{type:"stream_event", event:{
  type:"structured_output_retry", attempt:1, next_attempt:2, issues:[...]}}}

// success
{type:"chat_response", data:{type:"result", content:"{...}",
  structured_output:<JsonValue>, structured_output_mode:"native", attempt:1}}

// terminal error
{type:"chat_response", data:{type:"error", code:"validation_failed",
  content:"Structured output failed validation", attempts:2, issues:[...]}}

10. Record/replay and persistence

11. Session-title migration

After core capabilities are ready, migrate agent/dispatcher/titles.py as the first internal consumer:

TITLE_SCHEMA = {
  "type": "object",
  "properties": {"title": {"type": "string", "minLength": 1, "maxLength": 80}},
  "required": ["title"],
  "additionalProperties": false,
}

result = runtime.exec(content=prompt, toolset="none", max_iterations=2,
  response_format=JsonSchemaOutput(
    schema=TITLE_SCHEMA, name="session_title", max_validation_retries=1,
))
title = result["title"]

Retain Unicode trimming and final database length protection; remove ONLY-title-text instructions and quote, Title: and first-line format cleanup. Title generation stays best-effort: structured errors retain the phase-1 placeholder without affecting the main turn. Update session/name.md and feature matrix to distinguish implemented from designed behavior.

12. References: adopt, modify, reject

Repository references/pi-ai already has shared stream options, provider-local payload builders, tool schemas and supportsStrictMode, but strict applies only to tool definitions. It lacks generic response schemas, final JSON validation and structured retries.

DecisionSource/current stateTreatment in this design
AdoptForward shared options/types through every provider adapter.JsonSchemaOutput belongs in public types; payload mapping stays provider-local.
AdoptTool strictness is provider/model compatibility, not a unilateral caller promise.Hidden-tool fallback is constrained by negotiation and schema profiles.
Modifypi-ai keeps strict capability in compat; models.dev already supplies structured_output.Use a tri-state model field plus independent API-adapter capability; decide jointly.
ModifyExisting _schema can downgrade tool schemas for providers.Structured output permits only lossless projection; always validate locally against the original schema.
RejectRely only on provider strict and return raw text.Rejected: refusal, truncation, compatible endpoints and provider bugs require local parse + validate.
RejectRepair final output using forgiving/partial JSON parsers.Rejected: accept only one complete JSON document; failures enter bounded semantic repair.
RejectParse every JSON delta into incremental objects and publish them.Rejected: schema validation is defined only for complete candidates; streaming carries candidate text and attempt only.
RejectCreate an independent structured-output provider path.Rejected: reuse Agent loop, tools, deadlines, DAG and record/replay.

13. Exact files to change

FileModify
openprogram/providers/types.pyAdd JsonSchemaOutput, JsonValue, model tri-state, StreamOptions fields, AssistantMessage parsed fields and structured retry/end events.
openprogram/providers/api_registry.py
openprogram/providers/register.py
Register adapter structured capabilities; provide unknown-safe lookup without guessing from model names.
openprogram/providers/structured_output.py newInput normalization, preflight, negotiation, strict parsing, local validation, bounded issues, schema hash and error types.
openprogram/providers/_schema/*Add lossless/lossy reporting and provider schema profiles; retain original schemas.
openprogram/providers/sources/models_dev.py
openprogram/providers/enabled_models.py
apps/server/openprogram_server/_webui/_model_listing/listing.py
Retain, construct and expose structured_output tri-state.
openprogram/providers/openai_completions/openai_completions.py
openprogram/providers/openai_responses/openai_responses.py
openprogram/providers/azure_openai_responses/azure_openai_responses.py
Map Chat/Responses/Azure native payloads and handle refusal/incomplete.
openprogram/providers/anthropic/anthropic.pyMerge format into existing output_config while retaining adaptive-thinking effort.
openprogram/providers/google/google.py
openprogram/providers/google_gemini_cli/google_gemini_cli.py
Official Google adapter maps native fields; subscription adapter initially uses explicit unknown/tool fallback.
openprogram/providers/amazon_bedrock/amazon_bedrock.py
openprogram/providers/openai_codex/openai_codex.py
Bedrock maps outputConfig; Codex adapter initially uses explicit unknown/tool fallback.
openprogram/agent/types.py
openprogram/agent/agent_loop.py
openprogram/agent/internals/_event_parsing.py
Forward schemas; implement no-tool terminal validation, hidden tools, one repair, derived events and final parsed messages.
openprogram/agentic_programming/runtime.py
openprogram/providers/callable_model.py
Fix silent provider-path omission; add overloads/parsed returns and identical final validation for callable models.
openprogram/providers/recording.py
openprogram/providers/replay.py
Format v2, normalized option comparison and multi-call validation-retry tests; raw provider event set unchanged.
apps/cli/python/openprogram_cli/_impl/commands/chat.py
apps/cli/python/openprogram_cli/_impl/application.py
apps/cli/python/openprogram_cli/_impl/chat.py
apps/cli/python/openprogram_cli/_impl/repl/turn.py
One-shot schema argument, typed result, stdout/stderr/exit codes and session persistence.
apps/server/openprogram_server/_webui/ws_actions/chat.py
apps/server/openprogram_server/_webui/routes/chat.py
apps/server/openprogram_server/_webui/_execute/chat.py
apps/server/openprogram_server/server.py
Request validation, schema forwarding and structured stream/result/error envelopes.
apps/cli/src/ws/client.ts
apps/cli/src/screens/repl/wsHandlers/handleChatResponse.ts
apps/cli/src/screens/repl/wsHandlers/streamingHelpers.ts
TS request/event types, candidate clearing on retry and typed-result reception; no schema editor.
openprogram/agent/dispatcher/titles.pyMigrate title consumer, remove format cleanup and retain length/best-effort boundaries.
pyproject.toml
uv.lock
Direct dependency on jsonschema and update lockfile.
docs/reference/design/runtime/session/name.md
docs/reference/design/feature-matrix.html
After implementation, update documentation status and actual contracts.

14. Test matrix and acceptance

LayerRequired coverageSuggested files
Normalization/validatorBare schema, envelope, invalid meta-schema, object/array/scalar/null, non-finite values, fences/trailing text, stable issue paths; zero-network remote refs, local direct-reference cycles and global 1 MiB/4096 nodes/8192 edges/100 depth budgets.tests/component/providers/adapters/test_structured_output.py (new)
Negotiationtrue/false/unknown × adapter support; lossy schemas; fallback none/auto/prompt; tool_choice/parallel conflicts.Above plus test_provider_meta.py
Provider payloadPer-adapter payload snapshots; Anthropic effort+format merge; Bedrock schema JSON string; unknown adapters send no private fields.Existing tests/component/providers/adapters/test_openai.py, test_anthropic.py, test_gemini.py + new Bedrock cases
StreamingOne-attempt success; invalid delta→retry→clear→success; second failure without done; no retry on cancellation/deadline; no synthesized success for EOF without raw terminal.tests/providers/test_structured_output_streaming.py (new)
ToolsNative+tools; hidden tool alone; ordinary multiround tools then submit; reject ordinary+submit together; forced/none/parallel conflicts.tests/unit/programs/runtime/test_tools_runtime.py + new Agent loop cases
RuntimeNo schema returns str; schema returns JsonValue; default provider forwards it; callable models validate locally; sync/async prove initial, repair and transport-retry calls share max_retries budget.tests/component/programs/tools/test_runtime_structured_output.py (new)
Record/replayv2 single call, two-call validation retry, schema-mismatch paths, v1 rejection and identical offline derived events.tests/component/providers/registry/test_record_replay.py
CLISuccessful stdout is JSON only; exit 2/3/4; no full candidates on stderr; ordinary --print unchanged.tests/component/providers/adapters/test_cli.py + CLI integration
WS/APIRequest schema, retry event, typed result, machine error, foreign-session routing and old-client field ignoring.tests/unit/webui/test_webui_chat_dispatcher.py + TS tests
Session titleValid title, structured-failure placeholder, Unicode/80-character boundaries and no old regex dependency.tests/unit/context/test_dispatcher_compaction_title.py
RegressionAll provider streams, tool loops, ordinary chat, DAG, session persistence, typecheck and docs HTML.Existing suites + npm test/npm run typecheck
Acceptance threshold: Every native adapter needs payload contract tests and at least one controlled integration test; successful values pass the original local schema. Total Runtime provider calls do not exceed max_retries and semantic repairs do not exceed max_validation_retries; ordinary text recordings, events and return types remain unchanged.

15. Implementation order and completion

  1. Public types, direct dependency, schema preflight/validator/errors; establish pure unit tests first.
  2. Capability registry, models.dev tri-state retention and provider schema profiles.
  3. Provider payload mappings and adapter contracts; unverified adapters stay unknown.
  4. Agent-loop terminal validation, hidden tools, events and one repair; Runtime parsed returns.
  5. Record/replay v2, CLI and WS/API consumers; verify old text behavior unchanged.
  6. Migrate session titles; update feature matrix and naming documentation.

Provider capabilities, contracts and title consumer are implemented. Feature matrix retains the user-approved score 78.5 and existing status cells; complete regression gates and specific trust-boundary evidence remain separately recorded here. Adding payload fields alone is not feature completion.

16. Implementation and verification evidence

ScopeReviewed implementationBoundary
Public contract and providersJsonSchemaOutput Normalization/preflight; native/hidden-tool/explicit-prompt negotiation against a registry snapshot; verified OpenAI, Anthropic, Google and Bedrock mappings; final local validation against the caller schema.Unverified adapters, models without explicit opt-in, lossy schema projection or tool-control conflicts never send unverified native payloads; fallback="prompt" must be explicitly requested.
Agent / RuntimeThe Agent loop owns negotiation, typed retry/end events, at most one validation repair and parsed terminal; Runtime.exec/async_exec returns validated Python JSON values, while ordinary calls return text.Invalid candidates do not enter final assistant messages; cancellation, deadlines, refusal and incomplete do not become repairs.
Entries and reproductionCLI --json-schema PATH|-, Web typed lifecycle, recording v2 and strict offline replay share normalization; structured one-shot CLI stdout has exactly one JSON document.No Web schema editor, cross-language generation or incremental JSON AST; explicitly reject v1 structured recordings while retaining ordinary-v1 compatibility.
Session titleDefault Agent provider/model, toolset="none", at most two model iterations and one schema repair; accepts only {title:string}, with only Unicode trim and 80-code-point storage protection in the consumer.Setup, exec and close failures are best-effort and log only fixed lifecycle labels; provider exception bodies and tracebacks never enter logs.

Commit evidence: Task 1 00945c85..27a5aa8a; Task 2 da4ececd..8ca7b04c; Task 3 6f8c80b7..546c2bc7; Task 4 27e4764f..510f60ee; Task 5 9743f3e1..86ca66d5; Task 6 5bba3e64..2fb471b3. Final scoped SPEC and QUALITY reviews for Tasks 1–6 are PASS with remaining 0.

Verification state: Task 6 affected selection: 219 passed, 1 skipped; Task 7 focused selection: 226 passed, 1 skipped; documentation build: 453 pages, link check: 0 broken. The complete Python selection on the final Task 7 tree produced 3290 passed, 5 skipped, 1 xfailed, 2 failed; the two failures were the existing tool-cancellation ExecInterrupt assertion and memory-writer turn-order assertion, independently reproduced before implementation at baseline 86ca66d5 . They are not attributed to this structured-output diff, but do not satisfy complete-gate release requirements.

Subsequent quality fixes: Global preflight now enforces 1 MiB/4096 nodes/8192 edges/100 depth budgets and rejects external resources/direct cycles. Local validation uses a no-retrieval registry; structured streams commit only after a provider raw terminal. Scoped SPEC/QUALITY conclusions will be recorded after review.

Repository evidence: openprogram/providers/types.py, sources/models_dev.py, seven provider adapters, agent/agent_loop.py, agentic_programming/runtime.py, agent/internals/_event_parsing.py, CLI/WebSocket chain, recording.py/replay.py, agent/dispatcher/titles.py, references/pi-ai. Line numbers are not tied to source snapshots; locate implementation by symbols/tests.

External provider documentation only confirms current payloads/constraints; OpenProgram adapter contracts remain the prerequisite for enabling capabilities.