F 14 Anthropic Ai
Status: ๐ข ready to test Owner: Adilet Last updated: 2026-05-08 Last verified on production: โ
Goal
An admin stores the Anthropic API key in the database (no env var required), tests it from the admin UI, and three fire-and-forget AI agents (intake / run summary / cycle close) light up across the platform. Errors are categorised into 8 friendly modes with retry buttons.
Actors
- Primary โ Admin (configures + retries)
- Secondary โ Anthropic API; downstream agents that auto-run
Preconditions
- Admin is signed in.
- A working Anthropic API key (starts with
sk-ant-).
Trigger
Admin opens /app/admin/integrations and clicks the "Configure" CTA on
the Anthropic card.
Happy path โ configure key
| # | Actor | UI surface | Action | API call | DB writes | Side effects | Suggested event |
|---|---|---|---|---|---|---|---|
| 1 | Admin | /app/admin/integrations | Click "Configure" on Anthropic card | โ | โ | โ | โ |
| 2 | Admin | edit form | Paste API key (password input, data-1p-ignore) | โ | โ | โ | โ |
| 3 | Admin | edit form | Click "Test" | POST /api/admin/integrations/anthropic/test body { apiKey } | โ | calls client.models.list({ limit: 20 }) (free, no token spend) | โ |
| 4 | Anthropic | โ | Returns OK or 401/402 | โ | โ | โ | โ |
| 5 | Admin | edit form | "Save" | PUT /api/admin/integrations/anthropic body { apiKey } | upsert organization_integrations row with provider_slug='anthropic', config={ api_key }, status='active' | invalidate module-level Anthropic SDK client cache | integration.configured |
| 6 | Frontend | configured-state card | shows "Active" w/ masked key + Test/Edit/Disconnect | โ | โ | โ | โ |
Happy path โ three downstream agents fire
Once configured, three agents auto-run on respective triggers:
| Agent | Trigger | Output | Failure mode |
|---|---|---|---|
intake_issue | F-08 โ non-draft issue POST | issues.ai_suggestions (title / severity / bug_type / confidence) | Surfaced via aiLatestFailure on issue detail |
run_summary | F-07 โ run PATCH status=completed | test_runs.ai_summary | Stored in test_runs.ai_summary_failure_reason |
cycle_report | Cycle PATCH status=closed | New cycle_documents row, kind='report' | Logged in ai_runs |
All three call Anthropic with claude-sonnet-4-20250514. Vision uses
{ type: 'image', source: { type: 'url', url } } directly with public
DO Spaces URLs (no base64). Capped at 3 images per intake call.
Happy path โ retry intake
| # | Actor | UI surface | Action | API call | DB writes | Side effects | Suggested event |
|---|---|---|---|---|---|---|---|
| 1 | Admin/Lead | issue detail AiSuggestionsCard (failure state) | Click "Retry" | POST /api/test-cycles/:id/issues/:issueId/intake | new row in ai_runs; UPDATE issues SET ai_suggestions = โฆ if success | โ | ai.intake_retried |
(Retry is admin/lead only โ testers can't burn budget.)
Failure categorisation
src/lib/ai/intake.ts โ categoriseError() maps Anthropic errors to:
failure_reason | Trigger | UI copy |
|---|---|---|
out_of_credits | 402 / "credit balance" | "Top up at console.anthropic.com" |
auth_failed | 401 / 403 | "API key invalid or revoked" |
rate_limited | 429 | "Will retry on next issue" |
context_limit | input too large | "Issue + attachments too large" |
service_error | Anthropic 5xx | "Anthropic having issues right now" |
timeout | network | "Try again in a moment" |
parse_error | model didn't call tool | "Re-running may help" |
unknown | fallback | Generic |
Acceptance criteria
- AC-1 โ Given the admin saves an Anthropic key, when the next
issue is filed, then within 60s
issues.ai_suggestionsis populated AND theai_runsrow'sstatus='done'withcost_usd_centsset. - AC-2 โ Given a bad key, when "Test" is clicked, then the response surfaces "auth_failed" friendly copy and Save is blocked (or save anyway depending on UX โ verify on prod).
- AC-3 โ Given the key is removed (
DELETE /api/admin/integrations/anthropic), when the next issue is filed, thenisAiEnabled()returns false, no Anthropic call is made, and the issue still saves cleanly. - AC-4 โ Given a vision-bearing issue with 5 images, when intake runs, then only the first 3 images are sent to the model (cap).
- AC-5 โ Given a model fails to call the
submit_intake_analysistool, when the response is parsed, thenai_runs.failure_reason='parse_error'and the AiSuggestionsCard shows "Re-running may help" with a Retry button (admin/lead only). - AC-6 โ Given a non-admin views the AiSuggestionsCard's failure state, then there is no Retry button visible.
- AC-7 โ Given a cycle is set to
status='closed', when the cycle-close agent finishes, then a newcycle_documentsrow exists withkind='report'and the body is the AI's summary. - AC-8 โ Given the SDK client cache is in-memory and keyed on the resolved API key, when an admin rotates the key, then subsequent calls use the new key without a server restart.
Test data / fixtures
- Baseline seed
- A working Anthropic API key with at least $0.10 of credit
- An active cycle with an open run + a tester ready to file an issue
Negative paths
| # | Scenario | Expected behavior |
|---|---|---|
| N-1 | Save without testing | Allowed; first intake call would surface failure if invalid |
| N-2 | Out of credits | failure_reason='out_of_credits', friendly copy |
| N-3 | Multiple rapid issue filings | Some will hit 429; categorised as rate_limited; retry button on each |
| N-4 | Anthropic latency >30s | Categorised as timeout; upstream doesn't block (fire-and-forget) |
| N-5 | Tester clicks Retry | UI hides the button; API would 403 if hit directly |
Manual QA checklist
- As admin, open
/app/admin/integrations - Confirm Anthropic card shows "Not configured"
- Click Configure โ paste valid key โ click Test โ green
- Click Save โ card flips to "Active" with masked key
- As tester, file a new issue with 1 screenshot (F-08)
- Wait ~30s, refresh issue detail โ AiSuggestionsCard shows suggested title / severity / bug_type
- Verify
ai_runstable has a row withkind='intake_issue',status='done',cost_usd_cents > 0 - Edit the Anthropic config โ paste an invalid key โ save โ next
issue shows the failure card with
auth_failedfriendly copy - Restore valid key
- As admin, click Retry on a failed AiSuggestionsCard โ new
ai_runsrow, suggestions populate - As tester, sign in and view a failed AiSuggestionsCard โ confirm Retry button is not visible (admin/lead only)
- Close a cycle (status='closed') โ wait ~60s โ new
cycle_documentsrow appears withkind='report' - Disconnect the integration โ confirm subsequent issues file with
no AI call (no
ai_runsrow created)
Automated test outline
test.describe("F-14 anthropic ai", () => {
test("admin configure โ test โ save", async ({ page }) => { โฆ });
test("intake populates suggestions", async ({ request }) => { โฆ });
test("retry intake (admin only)", async ({ request }) => { โฆ });
test("disconnect short-circuits agents", async ({ request }) => { โฆ });
test("8 failure modes categorised", async ({ request }) => { /* one test per mock */ });
});Code references
- UI:
src/components/admin/integrations/AnthropicIntegrationCard.tsx,src/components/test-cycles/AiSuggestionsCard.tsx - Pages:
src/app/app/admin/integrations/page.tsx - API:
src/app/api/admin/integrations/anthropic/route.ts(GET/PUT/DELETE)src/app/api/admin/integrations/anthropic/test/route.tssrc/app/api/test-cycles/[id]/issues/[issueId]/intake/route.ts(retry)
- Lib:
src/lib/ai/client.ts(getAnthropic,isAiEnabled,loadAnthropicConfig,invalidateAnthropicCache,readAnthropicIntegrationStatus)src/lib/ai/intake.ts(runIntakeForIssue,categoriseError,AI_FAILURE_REASONS)src/lib/ai/run-summary.tssrc/lib/ai/cycle-close.ts
- Schema:
organization_integrations(provider_slug='anthropic'),ai_runs,AI_RUN_KINDS,AI_RUN_STATUSES,issues.ai_suggestions+issues.ai_intake_run_id,test_runs.ai_summary
Events emitted (proposed)
integration.configuredโ{ provider: 'anthropic', configured_by }integration.testedโ{ provider, success, error_code }integration.disconnectedโ{ provider, disconnected_by }ai.intake_completedโ{ issue_id, latency_ms, cost_usd_cents, suggested_severity, confidence, image_count }ai.intake_failedโ{ issue_id, failure_reason }ai.intake_retriedโ{ issue_id, retried_by }ai.run_summary_completed/_failedai.cycle_report_completed/_failed
Open questions / known gaps
- Cost cap per cycle:
ai_runs.cost_usd_centsis logged but not enforced (no hard ceiling). Flagged infeature-matrix.mdยง5.2. - Run-summary has no retry button (only intake does).
- No "regenerate cycle report" button โ only fires once on close.
- Vision is image-only โ no video frame extraction.
- Encrypted credentials store deferred; key is in plain
organization_integrations.config.api_keyjsonb.