04 — agent_api Dispatcher / Runner
04 — agent_api Dispatcher / Runner (Workstream C)
Delivered LIVE: fn_process_agent_api_dispatch(p_dot_code, p_correlation_id, p_actor, p_mode, p_write_observation, p_idempotency_root) → jsonb. Generic, fail-closed, cannot execute a DOT.
Behaviour (in order, all fail-closed)
- Runtime gate — refuse unless
execute_enabled=false ∧ real_run_enabled=false ∧ dry_run_only=true(re-read each call). - Mode guard —
REAL_RUNrefused outright; mode must be PLAN_ONLY/VERIFY_ONLY/DRY_RUN. - Correlation required.
- Contract must exist for
dot_code. - DOT must exist in
dot_tools. - Fixture present + output namespace
DRYRUN-NS:%. DRY_RUNrefused ifendpoint_ref IS NULL— the structural block against faking a dry-run.- Optional prepared observation: writes via
fn_process_run_observe+fn_process_component_observe,evidence_type=SIMULATED_DRY_RUNonly,source_system='agent_api_dispatch_planonly',evidence_ref.preparedness='DRY_RUN_PREPARED_BLOCKED_NO_ENDPOINT'. Idempotent on the idempotency root. - Returns a JSON summary incl.
validated,endpoint_present,true_dry_run_possible,blocker.
Why SIMULATED_DRY_RUN and not a new DRY_RUN_PREPARED type
The evidence_type domain stays {REAL_RUN, DRY_RUN, SIMULATED_DRY_RUN, BACKFILLED_EVIDENCE}. Preparedness is recorded in evidence_ref — exactly the prior macro's no-inflation decision. The dispatcher therefore can never push a candidate past simulated/plan_only_tested.
Generality
Dispatch is by contract row, not a per-code switch. It refuses: missing contract, missing fixture, non-DRYRUN-NS: namespace, REAL_RUN, production runtime, and DRY_RUN-without-endpoint. It supports the macro's mode flags (--plan-only, --verify-only, --dry-run, --no-execute/--write-observation) as function arguments; the staged plan_only_runner.example.sh shape from the prior macro maps onto it, and all guarantees are enforced server-side so a CLI cannot bypass them.
What it is NOT
It is not an agent_api endpoint. It has no network/LLM call, no execution body. When endpoint_ref is bound in a future macro, the same dispatcher gains the ability to call it and (only then) record DRY_RUN.
Status
Dispatcher LIVE. Proven fail-closed (doc 06). Reversible via v5_rollback.sql.