Skip to content

The Closed Loop ​

How a report travels from a user's device, through a coding agent, into a build — and back to the same user to confirm the fix. Design rationale lives in DESIGN.md §7; this page is the contributor's map.


The flow ​

SDK report (+ reporter_id, notify_reporter)  →  dashboard / MCP  →  agent: claim · ask · fix · after-shot
        ↑                                                ↓   PR description "FeedbackKit: <id>"
reporter's device  ←  reporter-updates  ←  feedbackkit release  ←  merged (github-webhook)
  "is it fixed?"  →  verified   |   still broken (+ new screenshot) → reopened → re-dispatched
  (only if the reporter opted in; otherwise the team's "Mark verified")

Starting the agent ​

Five routes, all ending in the same linking/shipping/verifying steps. User-facing walkthrough: the dashboard's docs page Hand reports to an agent (web/src/pages/docs/DocsAgentsPage.tsx, mirrored as the agents topic in cli/src/docs.ts).

RouteTriggerCode
MCP (you + your agent)The agent calls get_prompt / claim_feedbackcli/src/mcp/server.ts, loopInstructions in cli/src/loop.ts
GitHub-hosted ActionsSend to agent → issue + projects.dispatch_labels / dispatch_commentcreate-github-issue, dispatchIssueToAgent in _shared/github.ts; the repo's own workflow (claude-code-action needs allowed_bots: feedbackkit-app)
Self-hosted Mac runnerSame labels, runs-on: [self-hosted, macOS, feedbackkit-agent]skills/setup/setup-agent-runner/ (Claude Code and Antigravity templates)
GitHub Copilotprojects.dispatch_copilot → assign copilot-swe-agent[bot] with a member's user token0018_copilot_dispatch.sql, github-user-auth, assignIssueToCopilot
feedbackkit watchRun on my machine inserts a dispatched event with data.target = "local"cli/src/commands/watch.ts (claims with data.queue_event)

On a reopen, reporter-updates re-applies the labels and re-assigns Copilot as the last member who dispatched it.

Delivery modes (0024) ​

projects.delivery_mode: batch (merge → a beta ships every merged fix → verified → promote) or branch (PR → a preview build ships the PR's fixes via release --channel preview --pr <n> → verified → the PR's "FeedbackKit" commit status passes → merge). The status is computed by prVerification() in supabase/functions/_shared/prStatus.ts (pending / success / failure from the linked reports' stages) and posted on the PR's current head by syncPrStatus, called from github-webhook, ci-release, reporter-updates and pr-status. Agent instructions switch on the mode in three places (see Keep in sync). User docs: /docs/delivery; setup: the setup-release-loop skill asks which mode first.

Reporter opt-in (0023) ​

feedback_items.notify_reporter is the composer's "Notify Me When It's Fixed" (off by default; null = an SDK from before the option, treated as opted in). reporter-updates skips reports with false; release_readiness counts their shipped fixes as unreachable instead of awaiting, so they don't hold a build back; the dashboard and Portal offer Mark verified (a verified event with actor_type = 'user', which 0022 keeps from being announced as the reporter's).

Push-to-main and betas ​

Agents commit to main with a FeedbackKit: <id> trailer. github-webhook's push handler moves the report to merged. beta.yml runs the tests, ships the iOS Portal to TestFlight and the macOS Portal as the beta-portal-mac prerelease, and scripts/feedbackkit_announce.sh records each build through ci-release with the project's release token. The owner promotes a verified beta (Releases tab, feedbackkit promote). See DESIGN.md §8.

PiecePath
Release tokens, channels, readiness viewsupabase/migrations/0015_push_to_main_releases.sql
Access tokens (scopes, run tokens), after-fix previews, retentionsupabase/migrations/0025_access_tokens_and_previews.sql, attach-preview, cleanup-previews
Token-authenticated release APIsupabase/functions/ci-release/
Push trailer linkingsupabase/functions/github-webhook/ (handlePush)
Beta pipeline.github/workflows/beta.yml, scripts/feedbackkit_announce.sh, .github/actions/setup-feedbackkit-cli
Readiness UIweb/src/components/ReleasesPanel.tsx, ReleaseTokensCard.tsx

Fix stages ​

feedback_items.fix_stage (checked text, 0014_closed_loop.sql) — kept separate from status because shipped Portal builds decode status strictly.

fix_stageSet bystatus becomes
agent_workingMCP claim_feedback, dispatch in create-github-issuein_progress
pr_opengithub-webhook (PR opened), MCP/CLI link_fixin_progress
mergedgithub-webhook (PR merged, or a trailer on a push to main), link_fix with a commitin_progress
shippedrecord_release RPC via feedbackkit releasein_progress
verifiedreporter-updates (reporter taps "Yes, it's fixed"), or Mark verified in FixLoopPanelresolved
reopenedreporter-updates (reporter taps "Still broken")in_progress

wont_fix is never overridden.

Where each piece lives ​

PiecePath
Schema, RLS, compare_builds, record_releasesupabase/migrations/0014_closed_loop.sql
Reporter-facing API (anonymous, project_key + reporter_id)supabase/functions/reporter-updates/
PR tracking, signature verificationsupabase/functions/github-webhook/
Agent dispatch (labels / trigger comment)supabase/functions/_shared/github.ts dispatchIssueToAgent
Agent MCP tools, release/link/timelinecli/src/loop.ts, cli/src/mcp/server.ts, cli/src/commands/
Swift SDK: identity, transport, cardModel/FeedbackReporterIdentity.swift, Networking/FixUpdatesClient.swift, UI/FixVerification*.swift
Web SDK: identity, transport, cardweb-sdk/src/fixes.ts, web-sdk/src/ui/fixCard.ts
Dashboardweb/src/components/FixLoopPanel.tsx, AgentDispatchCard.tsx, LoopChecklist.tsx, WatchButton.tsx; checklist and stuck-report rules in web/src/lib/loopHealth.ts
Watchlistfeedback_watchers + notify_watchers() (0023); Portal toolbar in FeedbackDetailView.swift
PortalDeveloperApp/Sources/Views/Detail/FixLoopSectionView.swift
Agent Skillsskills/workflow/fix-feedback/, skills/setup/setup-release-loop/, skills/setup/setup-agent-runner/

Keep in sync by hand ​

  • Build ordering — compare_builds in SQL, _shared/builds.ts, cli/src/loop.ts, FeedbackBuild.compare (Swift), compareBuilds (web SDK).
  • reporter-updates wire format — the Edge Function, FixUpdatesClient.swift, web-sdk/src/fixes.ts.
  • Event kinds — the feedback_events.kind check constraint, FeedbackEventKind in web/src/lib/types.ts and cli/src/types.ts, labels in FixLoopPanel.tsx and PortalFeedbackEvent.title, and the watcher titles in notify_watchers().
  • Notification kinds — the notifications.kind check, NotificationKind and its label/hint/dot maps in web/src/, and PortalNotification.iconName / the Portal's row tint.
  • Reporter reachability — reporterReachable() in web/src/lib/loopHealth.ts, PortalFeedbackItem.canReachReporter, ask_reporter's message, and the release_readiness view.
  • The agents page — DocsAgentsPage.tsx and the agents topic in cli/src/docs.ts.
  • Delivery-mode agent instructions — closingTheLoopSection (web), loopInstructions (CLI/MCP), and the create-github-issue body; and the delivery docs page vs. the delivery topic in cli/src/docs.ts.

Security notes ​

  • feedback_events insert policy: members only, actor_type ∈ {user, agent}, actor_user_id = auth.uid(). Reporter/GitHub/system events are written only by Edge Functions with the service role. No update/delete policy — it's an audit trail.
  • reporter-updates only ever touches rows matching both the project key and the reporter id, rate-limits reporter writes per project, and honours allowed_origins.
  • github-webhook rejects every delivery unless GITHUB_WEBHOOK_SECRET is set and the HMAC matches, and scopes matches to the delivery's repository.full_name.

Testing ​

bash
supabase start && supabase db reset
echo GITHUB_WEBHOOK_SECRET=testsecret > /tmp/fk.env
supabase functions serve --env-file /tmp/fk.env     # another terminal
cd cli && npm run build && node --test test/closed-loop.integration.mjs

Plus unit tests in each package (swift test --filter FixVerificationTests, web-sdk npm test + npm run test:e2e, cli npm test, Portal PortalTests).

Built with VitePress · FeedbackKit is open source