feat(console): interactive job actions — create, approve, run (Phases 1–2) - #10
Conversation
Phase 1 of making the Capella console interactive. The console was read-only; this wires the two write operations the API already exposes — create a job (POST /jobs) and approve a paused job (POST /jobs/:id/approve) — into the UI. Mutations go through Next.js Server Actions (lib/actions.ts, "use server"): a client component calls the action, which runs on the Next server and calls the API server-to-server with the same auth headers the reads use. No CORS, the API is never exposed to the browser, and the backend is unchanged. - lib/api.ts: export BASE + authHeaders (one source of truth for the actions) - lib/actions.ts: createJob (parses JSON spec, maps 400/403 to inline errors, revalidates) + approveJob; both fail-soft on an unreachable API - app/jobs/new + components/new-job-form.tsx: JSON-spec textarea, "Load example" (the fixtures/failing-test job), Create → redirect to the new job - components/approve-button.tsx: shown only for paused jobs on the detail page - components/ui/button.tsx: minimal button primitive - layout: "+ New job" nav link Run/cancel/config/auth remain out of scope (later phases). Console stays outside the Bun gate; verified with next build (6 routes) + the root gate stays green.
📝 WalkthroughWalkthroughThe PR adds job creation and approval capabilities to the console app. A new ChangesConsole: Create and Approve Job Flows
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
apps/console/components/ui/button.tsx (1)
8-8: 💤 Low valueConsider adding disabled styling for the secondary variant.
The primary variant includes
disabled:bg-neutral-400, but the secondary variant has no disabled background color. For consistency, consider adding a disabled state likedisabled:bg-neutral-100ordisabled:text-neutral-400.✨ Suggested enhancement
- secondary: "border border-neutral-300 bg-white text-neutral-800 hover:bg-neutral-100", + secondary: "border border-neutral-300 bg-white text-neutral-800 hover:bg-neutral-100 disabled:bg-neutral-50 disabled:text-neutral-400",🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/console/components/ui/button.tsx` at line 8, The secondary variant in the button styles is missing disabled state styling, which creates an inconsistency with the primary variant that includes disabled styling. Locate the secondary variant style definition and add disabled styling similar to what exists in the primary variant, such as disabled:bg-neutral-100 or disabled:text-neutral-400, to ensure consistent behavior and visual feedback when the button is in a disabled state.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@apps/console/components/ui/button.tsx`:
- Line 8: The secondary variant in the button styles is missing disabled state
styling, which creates an inconsistency with the primary variant that includes
disabled styling. Locate the secondary variant style definition and add disabled
styling similar to what exists in the primary variant, such as
disabled:bg-neutral-100 or disabled:text-neutral-400, to ensure consistent
behavior and visual feedback when the button is in a disabled state.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0ef16053-4533-45e8-b0c7-ee6dbcdbe547
📒 Files selected for processing (8)
apps/console/app/jobs/[id]/page.tsxapps/console/app/jobs/new/page.tsxapps/console/app/layout.tsxapps/console/components/approve-button.tsxapps/console/components/new-job-form.tsxapps/console/components/ui/button.tsxapps/console/lib/actions.tsapps/console/lib/api.ts
Addresses CodeRabbit nitpick on PR #10 — the secondary variant (used by the "Load example" button, which is disabled while a create is pending) had no disabled state; mirror the primary variant for consistency.
POST /jobs/:id/run kicks the existing Worker in the API process in the background (fire-and-forget) and returns 202, so the request never blocks on a long job. The console drives this; jobs execute end-to-end locally (Local sandbox, no Docker). - apps/api/src/runner.ts: createRunner builds one Worker (AnthropicProvider + selectDriver + AURIGA_MODEL/MODELS.sonnet) with a single-flight in-flight Set; returns undefined when ANTHROPIC_API_KEY is unset - app.ts: optional runJob dep + the route — 401 no actor, 404 cross-tenant, 503 no runner (no key), 409 already-active or needs-approval, else 202 + audit (job.run_requested); add @auriga/provider + @auriga/sandbox deps - index.ts: wire createRunner(store, audit)?.run - app.test.ts: 202 happy path, 503, 409-active, 401/404 parity (fake runJob, hermetic) Dev-grade and in-process by design; production moves execution to a separate worker via the scaffolded GraphileQueue + RUN_JOB_TASK (deferred).
Phase 2 of the interactive console: run a job from the web and watch it progress. - lib/actions.ts: runJob server action → POST /jobs/:id/run (maps 409/503/404 to inline errors) - components/run-button.tsx: shown when a job is runnable (pending, failed, or paused+approved); keeps the Phase-1 Approve for paused+unapproved - components/job-progress.tsx: auto-refreshes the page every 2s while the job is active (planning/running/verifying), stops on terminal/paused - lib/api.ts: Job type gains `approved` + spec.require_approval (returned by the API) - app/jobs/[id]/page.tsx: wire Run/Approve/progress by state
Interactive console — Phases 1 & 2
Makes the (previously read-only) Capella console able to act, wiring the write operations over the existing control plane. Cancel / configuration / auth remain later phases.
Phase 1 — create + approve (no backend changes)
/jobs/new(JSON-spec textarea, "Load example", inline validation/policy errors) →POST /jobs.POST /jobs/:id/approve.Phase 2 — run from the web
POST /jobs/:id/run.Workerin-process, in the background (apps/api/src/runner.ts) and returns202; the detail page auto-refreshes every 2s while the job is active so you watchplanning → running → verifying → done.401no actor ·404cross-tenant ·503no runner (noANTHROPIC_API_KEY) ·409already-active or needs-approval ·202accepted (+job.run_requestedaudit).Execution model (honest scope)
Runs in the API process — dev-grade and fire-and-forget: a mid-run API restart drops the run (the job stays checkpoint-resumable, re-runnable). The production path is a separate worker draining
GraphileQueue(RUN_JOB_TASK), which needs Postgres and so isn't testable on a laptop — deferred. Running needsANTHROPIC_API_KEY; Local sandbox unlessAURIGA_REQUIRE_DOCKER=1.Verification
bun run checkgreen (201 pass / 12 skip / 0 fail) — the new/jobs/:id/runroute is covered inapp.test.tswith a hermetic fake runner (202 / 503 / 409 / 401 / 404).cd apps/console && bun run buildpasses (6 routes).AURIGA_ROLE=dev); create thefixtures/failing-testjob → Run → watch it auto-refresh todone; Run without a key →503; arequire_approvaljob → "approve first" until approved.🤖 Generated with Claude Code