Examples
These walkthroughs trace concrete request paths through the documented APIs and surface the contract between the schema, services, CLI, and frontend. Each example labels which steps are working today vs. Proposed behavior gated on the changes in Future work.
For runnable local-first examples, see the repository examples index:
Example 1 — Create a lens, bind it to an agent, run it
Goal: An owner authors a Research lens, binds it as the default research lens for an Agent Lenser, and runs it once with a custom prompt.
CLI walkthrough (today):
# 1. Create the lens (interactive editor, citty wizard)
lenserfight lens version create
# 2. Publish the head version
lenserfight lens version publish --lens <lens-id>
# 3. Bind to the agent (Proposed: no direct CLI today; use API)
# In code:
agentsService.setMainLensBinding(aiLenserId, lensId)
# 4. Execute via workflow run
lenserfight run --workflow <workflow-id> --inputs '{"topic":"agent orchestration"}'What you can verify:
- The lens row appears in
lenses.lenseswithhead_version_idset. - The version row in
lenses.versionscarries theinput_contractandoutput_contract. - After binding,
agents.lens_bindingshas one row withis_default=true. - After running,
lenses.workflow_runs.status=completedandlenses.workflow_node_resultshas one row per node with timing and cost.
Example 2 — Connected workflow, team execution, approval gate
Goal: An owner builds a three-node ConnectedLens workflow (Research → Synthesize → Publish), assigns it to an agent team with autonomy level assisted, dispatches a run, and the publish step pauses for owner approval.
Setup
Author lenses (Example 1 covers this).
Build the workflow:
tsconst workflow = await workflowsService.createWorkflow({ title: 'Research → Publish', lenser_id, }) const nodes = await workflowsService.upsertNodes(workflow.id, [ { lens_version_id: researchV1, label: 'Research' }, { lens_version_id: synthV1, label: 'Synthesize' }, { lens_version_id: publishV1, label: 'Publish' }, ]) await workflowsService.upsertEdges(workflow.id, [ { source: nodes[0].id, target: nodes[1].id, merge: 'last_write_wins' }, { source: nodes[1].id, target: nodes[2].id, merge: 'last_write_wins' }, ])Build the team:
tsconst team = await agentWorkspaceService.createTeam({ ai_lenser_id, name: 'Research Squad' }) // (Proposed) addTeamMember calls — currently INSERT agents.team_members directly: await supabase.from('agents.team_members').insert([ { team_id: team.id, agent_id: researcherAgentId, role: 'researcher', lane: 0 }, { team_id: team.id, agent_id: synthesizerAgentId, role: 'synthesizer', lane: 1 }, { team_id: team.id, agent_id: publisherAgentId, role: 'publisher', lane: 2 }, ])Create assignment with assisted autonomy:
tsawait supabase.from('agents.workflow_assignments').insert({ ai_lenser_id, workflow_id: workflow.id, assignee_kind: 'team', assignee_team_id: team.id, approval_policy: { requiresApproval: true, mode: 'sensitive_actions', gates: ['publish_output'], }, retry_policy: { maxRetries: 2, retryOn: ['rate_limit', 'provider_error'] }, })
Dispatch and approve
What you can verify:
- After dispatch,
agents.team_runs.status='running'and the Research+Synthesize nodes complete. - The Publish node sits in
lenses.workflow_node_results.status='blocked'withwaiting_reason='human_input'. agents.team_runs.approval_statusflips to'pending'.- After approval,
approval_status='approved'and the run completes. agents.agent_run_eventscontainsapproval_granted(orapproval_rejected).
Example 3 — Autonomous scheduled research with publish gating
Goal: A weekly Monday-08:00-Europe/Istanbul schedule dispatches the same Research → Synthesize → Publish workflow under an autonomous-with-gates assignment. The CRON runs without owner intervention until the publish step, which always pauses.
# Create the assignment with autonomous-with-gates policy
# (Proposed CLI; today via SQL or REST INSERT)
INSERT INTO agents.workflow_assignments (
ai_lenser_id, workflow_id, assignee_kind, assignee_team_id,
approval_policy, retry_policy, failure_policy, queue_policy
) VALUES (
$1, $2, 'team', $3,
'{"requiresApproval":false,"gates":["publish_output","spend_threshold"]}',
'{"maxRetries":2}',
'{"mode":"isolate"}',
'{"mode":"serial","onMissed":"skip"}'
);# Create the schedule
SELECT public.fn_upsert_workflow_schedule(
p_workflow_id => $1,
p_cron_expr => '0 8 * * 1', -- Monday 08:00
p_timezone => 'Europe/Istanbul',
p_assignee_type => 'team',
p_assignee_id => $2,
p_workflow_assignment_id => $3,
p_approval_policy => '{"requiresApproval":false,"gates":["publish_output","spend_threshold"]}',
p_inputs_template => '{"topic":"weekly digest"}'
);Run-time flow
What you can verify:
- The schedule is in
lenses.workflow_scheduleswithis_active=trueandnext_run_atpopulated. - After Monday 08:00, a new
workflow_runsrow exists withstatusadvancing throughqueued → running → .... - Two of three nodes complete autonomously.
- The Publish node sits in
blockedwithwaiting_reason='human_input'. team_runs.approval_status='pending'withmetadata.gate_kind='publish_output'.schedule.last_dispatch_status='dispatched'(the dispatch itself succeeded; pending approval is run-level).- After a decision, the run terminates and
schedule.last_completed_atandlast_resultpopulate.
Common patterns
Idempotent dispatch
Pass an idempotency_key (Proposed envelope field) on dispatch. The engine refuses duplicate writes within a window. Today, prevent duplicate CRON dispatches by setting queue_policy.mode='serial'.
Failure isolation
Set failure_policy.mode='isolate' (the default). When one branch fails, sibling branches continue. Combine with retry_policy.maxRetries=2 for transient provider errors.
Per-environment policy split
Use one assignment per environment (dev / stage / prod) bound to the same workflow but different teams and policy bundles. Schedules can target the appropriate assignment via workflow_assignment_id.
Streaming the inspector
Open SSE on lenses.workflow_run_events filtered by run_id; render each event using WorkflowEventType. On reconnect, fetch all events with event_id > last_seen from workflowsService.listRunEvents.
Anti-patterns
- Don't call Supabase directly from the frontend or CLI for
agents.team_runsmutations. UseagentWorkspaceServiceand (when shipped)fn_decide_approvalso events and audit trails are written atomically. - Don't disable approval gates by setting
approval_status='approved'directly on a sensitiveteam_run. The proposed bypass-attempt audit will flag this. - Don't hard-code a
next_run_aton the schedule row. Let the tick fn compute it fromcron_expr+timezone. - Don't create parallel "agent profile" tables. Agents are
lensers.profilesrows withtype='ai'.