flywheel

List Flywheel Runs With Group Route

Flywheel V1.4 — per-agent runs including group-subject runs that touched this agent.

Powers the per-agent Runs tab in the cockpit. Returns the UNION of (a) agent-subject runs and (b) group-subject runs where a VoiceAgentVersion was written for this agent. Ordered by created_at DESC. The frontend distinguishes group-sourced rows via voice_agent_group_id IS NOT NULL on each FlywheelRunRead.

get/api/v1/voice-agents/{voice_agent_id}/flywheel-runs-with-group

Path parameters

voice_agent_idstring uuid required

Query parameters

limitinteger

Max rows returned, ordered by created_at DESC. Includes group-subject runs that touched this agent.

Max rows returned, ordered by created_at DESC. Includes group-subject runs that touched this agent.

Response

Successful Response

idstring uuid required
tenant_idstring uuid required
voice_agent_idstring uuid nullable required
voice_agent_group_idstring uuid nullable required
target_agent_typestring required
triggered_by_user_idstring uuid nullable required
candidate_voice_agent_idstring uuid nullable required
candidate_version_idstring uuid nullable required
source_version_idstring uuid nullable required
status'draft' | 'aggregating' | 'plan_ready' | 'applied' | 'sim_running' | 'sim_passed' | 'sim_failed' | 'promoted' | 'rolled_back' | 'dismissed' required

Cycle state machine for FlywheelRun (Flywheel V1.0 cockpit).

Single source of truth for cockpit mode (readonly ↔ editing), promote-button gating, and rail-card vocabulary. Per V1 invariant #3 — never derived from aggregator_outcome; the column is the canonical signal. aggregator_outcome remains as the V0.6/V0.7 audit detail (per V1 invariant #7 — additive over renaming); V1.9 deprecates it at the Pydantic layer.

Allowed transitions (enforced in tests, not in DB):

DRAFT → AGGREGATING → PLAN_READY → APPLIED → SIM_RUNNING
      → (SIM_PASSED | SIM_FAILED) → PROMOTED → ROLLED_BACK
DRAFT | PLAN_READY | APPLIED | SIM_RUNNING
      | SIM_PASSED | SIM_FAILED → DISMISSED

Lifecycle:

  • DRAFT: row created at intent-confirm; no plan yet.
  • AGGREGATING: aggregator running.
  • PLAN_READY: FDE produced a PromptEditPlan; not applied.
  • APPLIED: candidate VoiceAgentVersion written.
  • SIM_RUNNING: simulator kicked off, awaiting webhook.
  • SIM_PASSED / SIM_FAILED: webhook reported verdict.
  • PROMOTED: candidate flipped to prod active_version_id.
  • ROLLED_BACK: a prior promote was reverted in a later cycle.
  • DISMISSED: operator closed the cycle without promoting.

Column-width note: the underlying VARCHAR is VARCHAR(32) (set in the V1.0 migration via explicit ALTER COLUMN ... TYPE VARCHAR(32)), even though SQLAlchemy's Enum(native_enum=False) auto-derives VARCHAR(11) from the longest member name ("aggregating"). Same drift pattern as AggregatorOutcome after V0.7.1 (see migration d4a8b3c5e7f1). The next engineer who runs alembic revision --autogenerate will see SQLAlchemy propose narrowing back to 11 — the column comment on FlywheelRun.status explains why the override is intentional.

intentstring nullable required
bypass_reasonstring nullable required
dismissed_reasonstring nullable required
aggregator_outcome'skipped_no_findings' | 'skipped_target_agent_missing' | 'skipped_empty_plan' | 'validation_failed' | 'candidate_written' | 'llm_failed' | 'entry_staging_agent_missing' | 'bluejay_dev_agent_not_configured' | 'bluejay_simulation_not_configured' | 'candidate_not_active' | 'bluejay_api_failed' | 'apply_validation_failed' required

Terminal state of a FlywheelRun (Flywheel V0.6 + V0.7).

V0.6 defined six mutually exclusive values covering every way the aggregator itself can end. V0.7 adds four more that follow the CANDIDATE_WRITTEN branch when sim kickoff fires and either fails preflight or fails the Bluejay HTTP call. The column on flywheel_runs is nullable — the row is inserted BEFORE the aggregator runs (so a mid-pipeline crash leaves an inspectable row with aggregator_outcome IS NULL), and the outcome is set at each exit path.

V0.7 contract: when any of the four new codes lands, the run's candidate_version_id is still populated (V0.6's aggregator wrote the candidate before sim kickoff ran). The row is self-documenting as "V0.6 wrote the candidate, V0.7 couldn't sim-test it, see the outcome code for why." V0.8's promote gate separately blocks on sim_passed IS NOT True.

V0.6 codes:

  • SKIPPED_NO_FINDINGS: zero open reviewer findings for the target agent at run time. No LLM call, no version write.
  • SKIPPED_TARGET_AGENT_MISSING: the target agent could not be resolved — missing, wrong tenant, or non-production. No LLM call, no version write. (Named for a clone until that concept was removed; the aggregator now targets the production agent.)
  • SKIPPED_EMPTY_PLAN: LLM ran cleanly and returned PromptEditPlan(edits=[]) — its verdict was "none of these findings merit an edit." Distinct from LLM_FAILED (normal no-op, not a Sentry-worthy error; breadcrumb only). Findings stay open for the next run.
  • VALIDATION_FAILED: the non-empty plan failed pre-validation (unknown finding_id / scenario_id / preset key) or post-apply validation (pydantic ScenarioBase.model_validate / prompt delimiter check / tool-name collision). All-or-nothing — no version written. Cited findings transition to dismissed with reason "ai_suggestion_invalid"; uncited stay open. Sentry captures because this signals AI drift or validator drift.
  • CANDIDATE_WRITTEN: success. New VoiceAgentVersion on the target agent with source=AI_SUGGESTION + flywheel_run_id=<run.id> + suggestion_rationale=<plan>. Cited findings transition to batched; uncited stay open (cited-only batching — uncited findings aren't addressed and deserve another run). V0.7 additionally populates sim_started_at and bluejay_sim_external_id on this outcome — the sim is waiting for the webhook to flip sim_passed.
  • LLM_FAILED: pydantic-ai agent raised (network error, malformed response, timeout). Findings stay open (retryable next run). Sentry captures.

V0.7 sim-kickoff codes (all follow a successful candidate write, all leave sim_started_at and bluejay_sim_external_id NULL so the webhook handler can't match a stale sim):

  • ENTRY_STAGING_AGENT_MISSING: the source call's entry agent could not be resolved when the sim was kicked off (e.g. deleted after V0.6's aggregator ran).
  • BLUEJAY_DEV_AGENT_NOT_CONFIGURED: the agent exists but its bluejay_dev_agent_external_id is NULL. Operator pastes the id via PATCH /voice-agents/{id}/bluejay-dev-agent-id (V0.3 endpoint), re-runs flywheel.
  • BLUEJAY_SIMULATION_NOT_CONFIGURED (V0.7.1): the agent exists and has a Bluejay Dev Agent id, but its bluejay_simulation_external_id is NULL. Bluejay's real API has a two-level shape (Agent + Simulation template), so the operator creates a Simulation in Bluejay's UI targeting that Dev Agent once and pastes the simulation id via PATCH /voice-agents/{id}/bluejay-simulation-id. Mirrors the V0.3 Dev-Agent-id paste pattern.
  • CANDIDATE_NOT_ACTIVE: defensive. V0.6's aggregator writes the candidate and flips the agent's active_version_id atomically in the same transaction, so this firing indicates a real bug — Sentry captures. Never expected in steady state.
  • BLUEJAY_API_FAILED: Bluejay HTTP timeout / 5xx / network error on either create_digital_human_from_transcript or queue_simulation_run. Distinct from LLM_FAILED (V0.6's LLM engineer path) so the FlywheelRun audit trail stays crisp.

V1.1a apply-time code:

  • APPLY_VALIDATION_FAILED: apply-plan re-validated the stored plan against the prod agent's current metadata_json and apply_and_validate returned validation errors — the plan was built against stale agent state (manual edit landed between preview and apply, or the mutator now hits a state the validator rejects). The route returns 422; the run transitions to DISMISSED with this outcome and validation_errors populated. Distinct from VALIDATION_FAILED (V0.6 — validation failed at preview time, before the candidate could be written) because the operator's remediation differs: re-run preview to get a fresh plan against the current state. Findings stay open.
finding_idsstring[] required
prompt_edit_planobject nullable required
llm_errorstring nullable required
validation_errorsobject[] nullable required
sim_passedboolean nullable required
sim_started_atstring date-time nullable required
sim_completed_atstring date-time nullable required
bluejay_sim_external_idstring nullable required
sim_providerstring nullable required
sim_coverage_notestring nullable required
sim_scenariosobject nullable required
promoted_atstring date-time nullable required
promoted_version_idstring uuid nullable required
candidate_version_numberinteger nullable
open_findings_countinteger nullable
created_atstring date-time required
updated_atstring date-time required

Changes

Changed in 1 of the 28 revisions of this API.2

    • ○

      added the optional property // to the response with the status

      response-optional-property-added

    • ○

      added the optional property // to the response with the status

      response-optional-property-added

    This revision also has 8 changes that name no endpoint, such as unreferenced schemas being removed. See the revision's changelog

Of the 28 revisions, 1 has no diff computed.