Skip to content

feat(console): interactive job actions — create, approve, run (Phases 1–2) - #10

Merged
hutusi merged 4 commits into
mainfrom
console-actions
Jun 19, 2026
Merged

feat(console): interactive job actions — create, approve, run (Phases 1–2)#10
hutusi merged 4 commits into
mainfrom
console-actions

Conversation

@hutusi

@hutusi hutusi commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

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)

  • Create a job/jobs/new (JSON-spec textarea, "Load example", inline validation/policy errors) → POST /jobs.
  • Approve a paused job → button on the detail page → POST /jobs/:id/approve.
  • Wired via Next.js Server Actions (server-to-server with the auth headers) — no CORS, browser never hits the API directly.

Phase 2 — run from the web

  • Run a runnable job (pending / failed / approved-but-paused) → POST /jobs/:id/run.
  • The API kicks the existing Worker in-process, in the background (apps/api/src/runner.ts) and returns 202; the detail page auto-refreshes every 2s while the job is active so you watch planning → running → verifying → done.
  • Endpoint: 401 no actor · 404 cross-tenant · 503 no runner (no ANTHROPIC_API_KEY) · 409 already-active or needs-approval · 202 accepted (+ job.run_requested audit).

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 needs ANTHROPIC_API_KEY; Local sandbox unless AURIGA_REQUIRE_DOCKER=1.

Verification

  • Root bun run check green (201 pass / 12 skip / 0 fail) — the new /jobs/:id/run route is covered in app.test.ts with a hermetic fake runner (202 / 503 / 409 / 401 / 404).
  • cd apps/console && bun run build passes (6 routes).
  • Local smoke: run API (with key) + console (AURIGA_ROLE=dev); create the fixtures/failing-test job → Run → watch it auto-refresh to done; Run without a key → 503; a require_approval job → "approve first" until approved.

🤖 Generated with Claude Code

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.
@coderabbitai

coderabbitai Bot commented Jun 19, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The PR adds job creation and approval capabilities to the console app. A new /jobs/new route renders a NewJobForm for submitting a JSON job spec. A new ApproveButton appears on the job detail page when a job is in the "paused" state. Both forms are backed by new server actions (createJob, approveJob) in lib/actions.ts. The BASE API URL constant is exported, and a shared Button UI component is added.

Changes

Console: Create and Approve Job Flows

Layer / File(s) Summary
Shared Button component and BASE export
apps/console/components/ui/button.tsx, apps/console/lib/api.ts
Adds a reusable Button component with primary/secondary variants using Tailwind. Promotes the BASE API URL constant to an export so it can be consumed by the new actions module.
Server actions: createJob and approveJob
apps/console/lib/actions.ts
Adds a "use server" module exporting CreateResult and ApproveResult union types, a local errorOf helper, and two async action functions that POST to the control-plane API with auth headers, map errors to { ok: false, error }, revalidate Next.js paths on success, and return typed result objects.
New Job form, route, and nav link
apps/console/app/jobs/new/page.tsx, apps/console/components/new-job-form.tsx, apps/console/app/layout.tsx
Adds the NewJobForm client component with a spec textarea, example loader, pending state via useTransition, and createJob submission that navigates to the created job on success. Wraps it in the NewJobPage route and adds a "New job" nav link to the console layout header.
Approve button on job detail page
apps/console/components/approve-button.tsx, apps/console/app/jobs/[id]/page.tsx
Adds the ApproveButton client component that calls approveJob(id) inside useTransition, refreshes the router on success, and displays inline errors on failure. Renders it conditionally in the job detail page when job.state === "paused".

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 A button to approve, a form to create,
New jobs hop in through a shiny new gate!
The spec flows as JSON, the action says "POST",
On paused jobs, a click matters most.
The console now wields both carrot and key —
This bunny reviewed it all, hop hop, whee! 🥕

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.86% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the primary changes: introducing interactive job actions (create and approve) as the first two phases of a larger feature set, matching the PR's main objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch console-actions

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
apps/console/components/ui/button.tsx (1)

8-8: 💤 Low value

Consider 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 like disabled:bg-neutral-100 or disabled: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

📥 Commits

Reviewing files that changed from the base of the PR and between 8c43f88 and 477bb39.

📒 Files selected for processing (8)
  • apps/console/app/jobs/[id]/page.tsx
  • apps/console/app/jobs/new/page.tsx
  • apps/console/app/layout.tsx
  • apps/console/components/approve-button.tsx
  • apps/console/components/new-job-form.tsx
  • apps/console/components/ui/button.tsx
  • apps/console/lib/actions.ts
  • apps/console/lib/api.ts

hutusi added 3 commits June 19, 2026 19:00
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
@hutusi hutusi changed the title feat(console): create + approve job actions from the web feat(console): interactive job actions — create, approve, run (Phases 1–2) Jun 19, 2026
@hutusi
hutusi merged commit 905d993 into main Jun 19, 2026
3 checks passed
@hutusi
hutusi deleted the console-actions branch June 19, 2026 11:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant