Repository navigation
Conversation
Add POST /api/tasks, the dashboard equivalent of `devintern worker add`,
and a "New task" form in the dashboard header.
- Extract the core enqueue (markdown prompt + durable injected_tasks row,
active-only idempotency dedupe, task-key race retry) from add-command.ts
into workspace/injected-enqueue.ts; the CLI and the dashboard share it.
- POST /api/tasks parses { repo, title, body, idempotencyKey? } into a
DashboardTaskRequest, checks the repo against workspace [[repos]], caps
the body at 20,000 chars, and returns { id, taskKey, repo, status,
deduplicated } (201 new, 200 deduplicated, 400 invalid, 409 no usable
workspace, 500 write failure). It sits behind the central request guard
(403 cross-origin, 415 non-JSON). No worker is started.
- Move the non-GET /api routes into a routeWrite helper to keep the
server's fetch handler under the complexity limit.
- UI: #/tasks/new form with repo select, title, Markdown description with
a character counter, a per-form idempotency key, a quiet-hours/PR notice,
and a link to the resulting run once the worker records it.
- Docs: dashboard.md "Delegate a task"; delegation.md points at it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
danii1
force-pushed
the
feat/dashboard-delegate-task
branch
from
November 14, 2026 07:55
249a9c4 to
6b9ecab
Compare
Replace the #/tasks/new page with a dialog opened from the top-bar "New task" button, built on shadcn/ui components. - Add dialog, field, input, textarea, label, and checkbox (plus the separator that field depends on) with `bunx shadcn add`. Adjusted to the repo's lint rules: named React type imports instead of `import * as React`, `@/lib/utils` for `cn` (the CLI wrote a bare "cn" import and added an unrelated "cn" npm package, which was reverted), a stable key in FieldError. checkbox is unused here; #421 will reuse it. - Add the popover color tokens the dialog surface uses. - NewTaskDialog: repo preselect, char counter, quiet-hours/--create-pr notice, one idempotency key per dialog session (fresh on reopen and on "Create another task"), title focused on open, Enter in the title does not submit, Cmd/Ctrl+Enter in the description does. Drafts are protected: outside clicks are ignored and Esc/Cancel/close ask before discarding. Submitting locks the fields and blocks closing. The result (task key, status, Already queued, Open run #N, View runs) shows inside the dialog. - Remove the #/tasks/new route and NEW_TASK_HASH. - Docs: dashboard.md describes the dialog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Lets users delegate work from the local observability dashboard: a New task button in the top bar opens a shadcn/ui dialog that enqueues an ad-hoc task, the dashboard equivalent of
devintern worker add.Endpoint:
POST /api/tasksRequest (JSON):
{ repo, title, body?, idempotencyKey? }idempotencyKeywas reused (deduplicated: true)[[repos]]names)workspace.toml, or no repos), e.g. a standalone legacy dashboardOrigin(403), non-JSONContent-Type(415)errorcarries the cause)Success body matches
worker add --json:{ id, taskKey, repo, status, deduplicated }. The row goes into the same workspace queue DB the dashboard already uses for ad-hoc retries (DashboardData.getInjectedTaskStore()), with the prompt under<workspace>/injected/. Nothing is spawned and no worker/daemon is started; the running worker drains it on its next poll. Rows are recorded with actorlocal:dashboard. Entitlement is not re-checked per request: both dashboard entry points enforce the automation license at startup and the worker re-checks before running.Server routing
The non-GET
/apiroutes (retry, run now, new task) moved into arouteWritehelper insidestartDashboardServer, which runs after the guard. Adding the route inline pushed thefetchhandler over the lint complexity limit.Shared refactor
The core enqueue moved from
add-command.tsintopackages/code/src/lib/workspace/injected-enqueue.ts(enqueueInjectedTask, plusMAX_INJECTED_BODY_CHARS,slugifyTitle,deriveInjectedTaskKey,buildInjectedTaskMarkdown): active-only idempotency dedupe, owner-only prompt file, task-key race retry, cleanup of a losing prompt.runWorkerAddnow calls it and keeps only CLI output/wait handling, with no behavior change (existingworker-add-commandtests pass unchanged apart from the import path). The new module does not pull license-check/bootstrap into the dashboard server.UI
App.tsx) opensNewTaskDialog(packages/dashboard-ui/src/components/NewTaskDialog.tsx). There is no route; the earlier#/tasks/newpage andNEW_TASK_HASHare gone.bunx shadcn@latest add dialog field input textarea label checkbox(radix-mira style), plusseparator, whichfielddepends on. The existingbutton.tsxwas kept. Changes to the generated files:import { cn } from "cn"and added an unrelatedcnnpm package. The imports now use@/lib/utils, and thepackage.json/bun.lockchange was reverted.import * as Reactbecame named type imports (import/no-namespace), and inlinetypespecifiers moved to top-level type imports.FieldErrorkeys its items by message instead of index (react/no-array-index-key).--popover/--popover-foregroundtokens were added tostyles.cssfor the dialog surface.checkboxis unused here; it is for feat(dashboard): report a bug from a run #421.sm:max-w-2xl, capped at the viewport height); liven / 20,000counter (submit disabled and counter flagged when over); quiet-hours /--create-prnotice.window.confirm). A clean dialog closes normally. While submitting, the fields, Cancel, and close are disabled and Esc/outside clicks do nothing.crypto.randomUUID()key per dialog session. The session is mounted only while open, so reopening, or Create another task, starts a fresh draft and key./api/runs?taskKey=…returns the run.Docs
docs/code/dashboard.md: new "Delegate a task" section (dialog behavior, endpoint contract, curl example), and the write-endpoint security notes now listPOST /api/tasks.docs/code/delegation.md: points at the dashboard alternative; licensing note.Test plan
packages/code/tests/dashboard-tasks.test.ts: success (row + markdown prompt + actor), dedupe (200, single row; new key gives a suffixed task key), unknown repo, oversized body (and exactly-at-cap accepted), no workspace (409, DB not created), invalid workspace (409), write failure (500), server routing with a same-originOrigin(201, malformed JSON 400), and a cross-origin POST that gets 403 and enqueues nothing, and request parsingpackages/dashboard-ui/src/components/NewTaskDialog.test.tsx(static markup; the package has no DOM test setup): closed dialog renders nothing and#/tasks/newis no longer a route, draft validation, dirty-draft detection, Cmd/Ctrl+Enter shortcut, per-session keys, notice/fields/counter on the shared field components, disabled submit for incomplete/oversized drafts, inputs and Cancel disabled while submitting, error display, result links (run vs runs list), deduplicated message,createTasksends a same-origin JSON POST and surfaces server errorsbun run test(code 2943 pass, dashboard-ui 252 pass, all packages green), rootformat,lint,typechecklocal-fix-the-flaky-login-testand showed the result in the dialog; Create another task reset the form🤖 Generated with Claude Code