[0.2.0] - 2026-09-06
Breaking
- Saga authoring no longer inlines reason/commit capability blobs. Deploy
kind: stepmanifests first, then compose withuse:/version/with/when(workers → steps → sagas). Migration011_step_definitions.sqladdsstep_definitionsand pinsstep_definition_name/step_definition_versionon step instances. - Saga definitions store the authoring AST only. Deploy link-checks catalog refs (ports, tighten, artifacts) but does not persist an expanded reason/commit photocopy. At saga start (and child spawn), the engine hydrates
use:refs into instancefrozen_steps. Redeploy sagas after upgrading — expanded definition bodies are not accepted. - Step and worker
(namespace, name, version)pins are append-only. Redeploying the same version fails with an immutable-version error; bump the version to change capability. - Worker
tool_sourcesno longer support legacytransport: sse. Usestreamable_http(the default whentransportis omitted) with a Streamable HTTP endpoint URL (commonly/mcp). Deploy rejectsssewith a clear validation error. - Compensation YAML must declare exactly one tool in
tools.allow(same as commit). Reason and commit steps share that single-tool undo path. Worker-manifest / DBcompensation_promptis removed (migration008_drop_compensation_prompt.sql). - Removed
WARDEN_REACT_TOOL_MESSAGE_LIMITand all client-side MCP tool-output clipping. Tool payloads flow unchanged to the LLM transcript andtool_results/ facts extraction. Tier-1 memory redaction (coupled to the clip limit) is removed; golden-ratio compression still digest/drops historical turns. - Worker MCP client upgraded to MCP Python SDK 2.x (
mcp>=2.0,<3on the worker extra). Rebuild worker images / re-runuv sync --extra workerafter pulling. Client code uses v2 pagination (PaginatedRequestParams), streamable HTTP 2-tuple transports, and SDK snake_case protocol fields; the stdio mock fixture uses the v2 lowlevelServerhandler API. - Soft-disable catalog definitions moved from
PATCH /v1/definitions/{kind}/{uuid}toPATCH /v1/definitions/{kind}?id=or?namespace=&name=&version=(CLI flags match).
Added
-
First-class step manifests (
kind: step): catalog capabilities with input ports,step_kindreason|commit, and capability fields; sagas compose viause:+version+with+when(tighten-only overrides). Saga deploy link-checks refs; saga start freezes hydrated steps onto the instance. Input portschemafragments are enforced at step deploy (valid Draft-7), saga deploy (value:literals), and schedule (resolved bindings). List withwarden list definitions --type step/GET /v1/definitions/steps. Docs: Step manifests. -
Worker definitions store the full validated manifest in
bodyJSONB (same pattern as sagas/steps). Migration012_worker_definition_body.sqlbackfills and drops denormalizedmodel_provider/model_name/system_prompt/tool_sources/adaptercolumns.GET .../workers/{id}?include_body=truereturnsrow.body. Workertemperatureis persisted and applied at runtime. -
Catalog definition list/get items are uniform:
id,namespace,name,version,is_active, timestamps (+ optionalbody). Workers gainedis_active(013_worker_is_active.sql); top-level listadapterwas removed (still inbody). Soft-disable viaPATCH /v1/definitions/{workers|steps|sagas}with query identity (idor triple) orwarden definitions set-active. Inactive workers/steps/sagas are rejected at deploy link-check and runtime load with structured HTTP codes. -
Catalog vocabulary: executable saga graph type is
HydratedSagaBlueprint(authoring remainsSagaAuthoringBlueprint). Worker timing buckethydration_msrenamed toworker_init_ms. Reserve hydrate for catalog→runtime graph resolution; Jinja uses render; worker command load uses prepare/worker_init_ms. -
Registry deploy stores definition bodies via one shared
_definition_body_payloadhelper. Removed unusedget_saga_definition_by_id, unreachable worker version-mismatch branch, and dead CLI list params; tests shareworker_definition_body/step_definition_bodyfactories. -
MCP and tools → Tool payload hygiene — pagination, field projection, and summary-tool patterns for bounded MCP returns.
-
Reason-step
tools.bind(⊆with): pin saga-resolved values onto ReAct MCP tool args (saga wins, ∩ toolinputSchema), strip those keys from the LLM-facing schema, and persisttools_bindon step rows (migration009_tools_bind.sql). Rejected on commit, compensation, andagent-adapter: simple. -
provider: azure— Azure OpenAI / Microsoft Foundry via LangChainChatOpenAIand the OpenAI-compatible/openai/v1/path (AZURE_OPENAI_API_KEY,AZURE_OPENAI_ENDPOINT; workermodel_nameis the deployment name). Defaults to Chat Completions for prompt-cache friendliness; Responses API is opt-in viaWARDEN_AZURE_USE_RESPONSES_API. -
Submit-mode ReAct soft-feeds recoverable tool mismatches (e.g.
search_replaceold_text not found/ non-unique match, missing path, patch-apply text failures) into the transcript with a one-line recovery hint instead of failing the step withTOOL_OUTPUT_ERROR.apply_patch_sandboxJSON rejects get the same hint. Transport/MCP error/ invalid-argument failures remain hard. Override viaToolLifecycleHooks.tool_output_is_recoverable. -
Submit-mode ReAct softens recoverable tool invoke exceptions (e.g.
IsADirectoryError, Pydantic arg validation) by normalizing them toError:tool payloads and reusing the same recoverability classifier; MCPCallToolResult.isErroris honored at the tool wrapper. Infrastructure failures (MCPError, connection/timeout, mixedExceptionGroupwith transport leaves) still raiseTOOL_INVOKE_FAILED.
Changed
- Definition list/get/patch routes share a generic CRUD helper; PATCH identity moved from path UUID to query
idornamespace+name+versiononPATCH /v1/definitions/{workers|steps|sagas}. - Catalog inactive/missing errors use a shared exception hierarchy (
InactiveCatalogDefinitionError/CatalogDefinitionNotFoundError) with structured HTTP detail payloads.
Fixed
-
Parent saga deploy rejects inactive child saga definitions referenced by
spawn_sagas(must exist andis_active); inactive catalog refs map to HTTP 409 with structuredINACTIVE_CATALOG_DEFINITION(missing → 404CATALOG_DEFINITION_NOT_FOUND). -
Soft-disable catalog definitions via
PATCH /v1/definitions/{workers|steps|sagas}?id=or?namespace=&name=&version=(CLI:--idXOR--namespace/--name/--version). -
Schedule-time input validation logs a warning when resolved arguments are present but
input_portsis empty (no hard-fail for engine-native steps). -
Child spawn maps missing/inactive child definitions to clean
STEP_FAILEDcodes (SPAWN_CHILD_DEFINITION_NOT_FOUND/SPAWN_CHILD_DEFINITION_INACTIVE) instead of unhandled exceptions. -
Policy CEL is frozen into
frozen_steps/policy_definitionat saga start (parity with compensation); runtime gates evaluate the embed strictly — noPOLICIES_ROOTfallback (migration014_policy_definition.sql). -
output_schemaJSON is frozen ontofrozen_stepsasoutput_schema_definitionat start/spawn hydrate; materialize and loop mint read the embed only (no runtime disk reload). -
Reason-step Jinja prompts and skill payloads are frozen at saga start as
prompt_definition/skills_definition(static bare{% include %}inlined via Jinja lexer/AST; migration015_prompt_skills_definition.sql). Workers render andload_skillfrom those embeds — no livePROMPTS_ROOT/SKILLS_ROOTreads during task execution. Incomplete skill embeds fail at start freeze; schedule also requires fullskills.allowcoverage. -
Child spawn maps hydrate/asset freeze failures to
SPAWN_CHILD_HYDRATE_FAILED. Child creates use a nested transaction (savepoint) so a mid-loop failure rolls back partial children instead of committing orphans beside a failed spawn. -
Worker command setup catches missing/inactive worker catalog errors (
CATALOG_DEFINITION_NOT_FOUND/INACTIVE_CATALOG_DEFINITION) after the idempotency claim is taken: emitSTEP_FAILED/COMPENSATION_FAILEDand release the claim instead of leaving the step stuckIN_PROGRESS. -
Policy refs must be exact paths under
POLICIES_ROOT(e.g.gate.yaml); the stem-only.yamlfallback and deploy-time legacy warning are removed. -
Startup schema sentinels now require
worker_definitions, definitionbody/is_activecolumns, andtools_bind/policy_definition/prompt_definition/skills_definitionon step instances. -
Prompt Jinja rendering uses
SandboxedEnvironment(file + inline paths), strips introspection helpers (cycler/joiner/namespace/lipsum), and fails closed on unsafe attribute access. -
ReAct
_submitis accepted only as a singleton tool batch. Mixed batches run the other tools, feed a_submit must be alonetool error, and continue the loop (no silent short-circuit that drops sibling calls). -
Reason / simple human messages no longer
json.dumpsstring prompts (Jinja and inline templates stay plain text). Structured dict/list prompt inputs are still JSON-encoded. -
Worker-scoped skills (
SKILLS_ROOT/<worker>/<id>.md): stepskills.allowdrives a staticallowed_skillsindex + virtualload_skill; skill frontmatterallowed_toolsunions withtools.allowextras for react reason steps (007_skills_allow.sql). -
Coerce sloppy LLM tool arguments against MCP
inputSchemabefore ReAct validation (common Ollama/vLLM stringified array/object fields) -
Admit LLM JSON against reason-step
output_schemabefore validation (_submitandsimplestructured output) -
LLM JSON admission no longer crashes on nullable union
typearrays; coerce string"null"/"none"to JSONnullwhen the schema allows it -
no_submit_callno longer lists successful plain-text MCP tool output inlast_tool_errors; expose final assistant text aslast_assistant_contentonmodel_text_exit