Skip to content

refactor: create tasks in coderd instead of frontend #19280

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Aug 12, 2025

Conversation

DanielleMaywood
Copy link
Contributor

Instead of creating tasks with a specialised call to CreateWorkspace on the frontend, we instead lift this to the backend and allow the frontend to simply call CreateAITask.

Instead of creating tasks with a specialised call to `CreateWorkspace`
on the frontend, we instead lift this to the backend and allow the
frontend to simply call `CreateAITask`.
@DanielleMaywood DanielleMaywood changed the title refactor: lift task creation to coderd refactor: create tasks in coderd instead of frontend Aug 11, 2025
@DanielleMaywood
Copy link
Contributor Author

@coderabbitai review

Copy link

coderabbitai bot commented Aug 11, 2025

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Comment on lines 95 to 108
hasAIPrompt, err := api.Database.GetTemplateVersionHasAIPrompt(ctx, req.TemplateVersionID)
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error fetching if template version has ai prompt.",
Detail: err.Error(),
})
return
}
if !hasAIPrompt {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: `Template does not have required parameter "AI Prompt"`,
})
return
}
Copy link
Contributor Author

Choose a reason for hiding this comment

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

As we're moving creation of tasks to the backend, I think it makes sense to validate the given template version ID actually corresponds to a task.

Copy link

coderabbitai bot commented Aug 11, 2025

📝 Walkthrough

Walkthrough

Introduces an experimental POST /api/experimental/aitasks endpoint to create AI tasks by embedding a prompt into a workspace creation request. Adds SDK and frontend support, database query and authorization checks for AI-prompt capability on template versions, metrics and mocks, routing, and tests.

Changes

Cohort / File(s) Summary
Experimental AI Tasks API
coderd/aitasks.go, coderd/coderd.go
Adds aiTasksCreate handler and routes POST /api/experimental/aitasks. Validates API key, fetches user, checks template version supports AI prompt, constructs workspace creation payload with prompt, audits, and invokes createWorkspace.
SDK and Web API Bindings
codersdk/aitasks.go, site/src/api/api.ts, site/src/api/typesGenerated.ts
Adds CreateAITasksRequest model and ExperimentalClient.AITasksCreate. Frontend ExperimentalApiMethods.createAITask posts to new endpoint. Generates TS interface for request.
UI Tasks Page Integration
site/src/pages/TasksPage/TasksPage.tsx
Updates createTask to use experimental createAITask; removes userId parameter and switches payload to include prompt directly.
DB Query and SQL
coderd/database/queries/templateversions.sql, coderd/database/queries.sql.go, coderd/database/querier.go
Introduces GetTemplateVersionHasAIPrompt SQL and method on sqlQuerier to check has_ai_task flag for a template version.
DB Authorization
coderd/database/dbauthz/dbauthz.go
Adds dbauthz.GetTemplateVersionHasAIPrompt with authz via GetTemplateVersionByID; refines authz for GetTemplateVersionByTemplateIDAndName and GetTemplateVersionParameters.
DB Metrics Wrapper
coderd/database/dbmetrics/querymetrics.go
Wraps GetTemplateVersionHasAIPrompt with latency metrics.
DB Mocks
coderd/database/dbmock/dbmock.go
Adds gomock methods for GetTemplateVersionHasAIPrompt.
Tests
coderd/aitasks_test.go, coderd/database/dbauthz/dbauthz_test.go
Adds tests for AI task creation success/failure and dbauthz coverage for AI prompt check and template-level authorization.

Sequence Diagram(s)

sequenceDiagram
  participant Web as Frontend (TasksPage)
  participant SDK as SDK ExperimentalClient
  participant API as Server /api/experimental/aitasks
  participant MW as API Key Middleware
  participant DB as DB + dbauthz
  participant AUD as Audit
  participant WS as Workspace Creator

  Web->>SDK: AITasksCreate(request)
  SDK->>API: POST /api/experimental/aitasks
  API->>MW: Authenticate API key
  MW-->>API: User context
  API->>DB: Get user by API key
  API->>DB: GetTemplateVersionHasAIPrompt(tvID)
  DB-->>API: bool
  API->>AUD: audit.InitRequest(WorkspaceCreate)
  API->>WS: createWorkspace(payload with prompt)
  WS-->>API: Workspace
  API->>AUD: commit
  API-->>SDK: 201 Workspace
  SDK-->>Web: Workspace
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~40 minutes

Suggested reviewers

  • johnstcn
  • BrunoQuaresma
  • aslilac
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch danielle/tasks/move-to-be

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (9)
coderd/database/queries/templateversions.sql (1)

238-238: Rename to align with column and existing query names ("AITask" vs "AIPrompt").

The column is has_ai_task and related queries use AITask (e.g., UpdateTemplateVersionAITaskByJobID). Rename this query to avoid mixed terminology.

Apply within this file, then regenerate sqlc:

- -- name: GetTemplateVersionHasAIPrompt :one
+ -- name: GetTemplateVersionHasAITask :one
site/src/api/api.ts (1)

2669-2678: Allow cancellation (AbortSignal) for better UX on long/aborted submissions.

Add optional AbortSignal support so callers can cancel in-flight requests (e.g., route changes).

-  createAITask = async (
-    req: TypesGen.CreateAITasksRequest,
-  ): Promise<TypesGen.Workspace> => {
-    const response = await this.axios.post<TypesGen.Workspace>(
-      "/api/experimental/aitasks",
-      req,
-    );
- 
-    return response.data;
-  };
+  createAITask = async (
+    req: TypesGen.CreateAITasksRequest,
+    signal?: AbortSignal,
+  ): Promise<TypesGen.Workspace> => {
+    const response = await this.axios.post<TypesGen.Workspace>(
+      "/api/experimental/aitasks",
+      req,
+      { signal },
+    );
+    return response.data;
+  };

Nit: consider naming the method createAITasks to mirror the plural route/type. Only do this if you can update call sites in the same PR to avoid breakage.

coderd/database/querier.go (1)

358-358: Naming consistency: prefer “HasAITask” to match schema and existing queries.

Interface name stems from SQL. If you adopt the suggested SQL rename (GetTemplateVersionHasAITask), regenerate sqlc so this becomes:

  • GetTemplateVersionHasAITask(ctx context.Context, id uuid.UUID) (bool, error)

Do not hand-edit this file; re-generate from the .sql change.

coderd/database/dbmetrics/querymetrics.go (1)

1541-1546: Align wrapper and metric label with “AITask” naming if SQL is renamed.

If you rename the SQL to GetTemplateVersionHasAITask, update this wrapper and label accordingly.

-func (m queryMetricsStore) GetTemplateVersionHasAIPrompt(ctx context.Context, id uuid.UUID) (bool, error) {
+func (m queryMetricsStore) GetTemplateVersionHasAITask(ctx context.Context, id uuid.UUID) (bool, error) {
   start := time.Now()
-  r0, r1 := m.s.GetTemplateVersionHasAIPrompt(ctx, id)
-  m.queryLatencies.WithLabelValues("GetTemplateVersionHasAIPrompt").Observe(time.Since(start).Seconds())
+  r0, r1 := m.s.GetTemplateVersionHasAITask(ctx, id)
+  m.queryLatencies.WithLabelValues("GetTemplateVersionHasAITask").Observe(time.Since(start).Seconds())
   return r0, r1
 }
coderd/database/dbauthz/dbauthz_test.go (1)

1446-1459: RBAC assertion for TemplateVersion AI-prompt check looks correct

This follows the established pattern where TemplateVersion reads authorize on the parent Template via RBACObject. No issues.

Optional:

  • Add a brief comment clarifying the test intent (TemplateVersion read → Template read) to improve readability.
  • Consider a negative-path variant (e.g., WithNotAuthorized) similar to other tests where applicable.
coderd/database/queries.sql.go (1)

12873-12887: Nit: align naming with underlying column (has_ai_task) to avoid confusion

The method/SQL name uses “AIPrompt” while the column is has_ai_task. Since this file is generated, consider renaming the query in the source SQL to keep terminology consistent (e.g., “GetTemplateVersionHasAITask”). sqlc will then regenerate matching Go.

Example in coderd/database/queries/templateversions.sql:

-- name: GetTemplateVersionHasAITask :one
SELECT EXISTS (
  SELECT 1
  FROM template_versions
  WHERE id = $1 AND has_ai_task
);
coderd/aitasks.go (1)

95-102: Clarify error message wording (nit)

Small copy nit: “whether template version has AI Prompt” reads clearer than “if template version has ai prompt.”

-           Message: "Internal error fetching if template version has ai prompt.",
+           Message: "Internal error checking whether the template version has AI Prompt.",
codersdk/aitasks.go (1)

56-64: Drop trailing slash in POST path for consistency

Standardize the path to avoid relying on router slash normalization.

-   res, err := c.Request(ctx, http.MethodPost, "/api/experimental/aitasks/", request)
+   res, err := c.Request(ctx, http.MethodPost, "/api/experimental/aitasks", request)
site/src/pages/TasksPage/TasksPage.tsx (1)

742-747: Use conditional property for preset_id (minor nit)

Avoid sending undefined fields by conditionally including template_version_preset_id. Keeps payload minimal and avoids any strict decoder issues.

-    const workspace = await API.experimental.createAITask({
-      name: `task-${generateWorkspaceName()}`,
-      template_version_id: templateVersionId,
-      template_version_preset_id: preset_id || undefined,
-      prompt,
-    });
+    const req = {
+      name: `task-${generateWorkspaceName()}`,
+      template_version_id: templateVersionId,
+      prompt,
+      ...(preset_id ? { template_version_preset_id: preset_id } : {}),
+    };
+    const workspace = await API.experimental.createAITask(req);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dadeab8 and 0ad6ce3.

📒 Files selected for processing (14)
  • coderd/aitasks.go (2 hunks)
  • coderd/aitasks_test.go (2 hunks)
  • coderd/coderd.go (1 hunks)
  • coderd/database/dbauthz/dbauthz.go (1 hunks)
  • coderd/database/dbauthz/dbauthz_test.go (1 hunks)
  • coderd/database/dbmetrics/querymetrics.go (1 hunks)
  • coderd/database/dbmock/dbmock.go (1 hunks)
  • coderd/database/querier.go (1 hunks)
  • coderd/database/queries.sql.go (1 hunks)
  • coderd/database/queries/templateversions.sql (1 hunks)
  • codersdk/aitasks.go (1 hunks)
  • site/src/api/api.ts (1 hunks)
  • site/src/api/typesGenerated.ts (1 hunks)
  • site/src/pages/TasksPage/TasksPage.tsx (2 hunks)
🧰 Additional context used
🧠 Learnings (4)
📚 Learning: 2025-07-21T14:32:43.064Z
Learnt from: CR
PR: coder/coder#0
File: .cursorrules:0-0
Timestamp: 2025-07-21T14:32:43.064Z
Learning: Applies to coderd/coderd.go : The REST API is defined in `coderd/coderd.go` and uses Chi for HTTP routing.

Applied to files:

  • coderd/coderd.go
  • coderd/aitasks_test.go
📚 Learning: 2025-07-21T14:32:43.064Z
Learnt from: CR
PR: coder/coder#0
File: .cursorrules:0-0
Timestamp: 2025-07-21T14:32:43.064Z
Learning: Applies to coderd/coderdtest/**/* : The `coderdtest` package in `coderd/coderdtest/` provides utilities for creating test instances of the Coder server, setting up test users and workspaces, and mocking external components.

Applied to files:

  • coderd/aitasks_test.go
📚 Learning: 2025-07-21T14:32:43.064Z
Learnt from: CR
PR: coder/coder#0
File: .cursorrules:0-0
Timestamp: 2025-07-21T14:32:43.064Z
Learning: Applies to coderdenttest/**/* : Enterprise features have dedicated test utilities in the `coderdenttest` package.

Applied to files:

  • coderd/aitasks_test.go
📚 Learning: 2025-07-21T14:32:43.064Z
Learnt from: CR
PR: coder/coder#0
File: .cursorrules:0-0
Timestamp: 2025-07-21T14:32:43.064Z
Learning: Applies to coderd/dbauthz/*.go : The database authorization (dbauthz) system enforces fine-grained access control across all database operations. All database operations must pass through this layer to ensure security.

Applied to files:

  • coderd/database/dbauthz/dbauthz.go
🧬 Code Graph Analysis (10)
site/src/api/typesGenerated.ts (1)
codersdk/aitasks.go (1)
  • CreateAITasksRequest (48-53)
coderd/database/querier.go (1)
testutil/ctx.go (1)
  • Context (9-13)
coderd/database/dbmetrics/querymetrics.go (2)
testutil/ctx.go (1)
  • Context (9-13)
coderd/database/dbtime/dbtime.go (1)
  • Now (6-8)
site/src/api/api.ts (2)
codersdk/aitasks.go (1)
  • CreateAITasksRequest (48-53)
site/src/api/typesGenerated.ts (2)
  • CreateAITasksRequest (426-431)
  • Workspace (3489-3521)
codersdk/aitasks.go (3)
codersdk/client_experimental.go (1)
  • ExperimentalClient (6-8)
site/src/api/typesGenerated.ts (2)
  • CreateAITasksRequest (426-431)
  • Workspace (3489-3521)
codersdk/client.go (1)
  • ReadBodyAsError (391-465)
coderd/aitasks_test.go (8)
testutil/ctx.go (1)
  • Context (9-13)
testutil/duration.go (1)
  • WaitShort (10-10)
coderd/coderdtest/coderdtest.go (3)
  • CreateFirstUser (743-754)
  • AwaitTemplateVersionJobCompleted (1065-1081)
  • AwaitWorkspaceBuildJobCompleted (1084-1099)
provisioner/echo/serve.go (1)
  • Responses (227-241)
site/src/api/typesGenerated.ts (2)
  • Response (2563-2567)
  • CreateAITasksRequest (426-431)
codersdk/client_experimental.go (1)
  • NewExperimentalClient (10-14)
codersdk/aitasks.go (1)
  • CreateAITasksRequest (48-53)
codersdk/client.go (1)
  • Error (469-477)
coderd/database/dbauthz/dbauthz_test.go (4)
coderd/database/models.go (4)
  • Organization (3299-3309)
  • User (3760-3785)
  • Template (3509-3546)
  • TemplateVersion (3620-3638)
coderd/database/modelmethods.go (1)
  • TemplateVersion (180-183)
coderd/rbac/policy/policy.go (1)
  • ActionRead (10-10)
codersdk/rbacresources_gen.go (1)
  • ActionRead (57-57)
coderd/aitasks.go (6)
site/src/api/typesGenerated.ts (5)
  • APIKey (18-29)
  • CreateAITasksRequest (426-431)
  • Response (2563-2567)
  • CreateWorkspaceRequest (598-607)
  • WorkspaceBuildParameter (3880-3883)
codersdk/aitasks.go (1)
  • CreateAITasksRequest (48-53)
coderd/httpapi/httpapi.go (3)
  • Is404Error (121-131)
  • ResourceNotFound (153-155)
  • Write (191-212)
coderd/audit/request.go (2)
  • InitRequest (350-446)
  • RequestParams (26-36)
coderd/database/models.go (1)
  • WorkspaceTable (4275-4295)
coderd/audit/audit.go (1)
  • AdditionalFields (19-25)
coderd/database/dbmock/dbmock.go (1)
testutil/ctx.go (1)
  • Context (9-13)
site/src/pages/TasksPage/TasksPage.tsx (2)
site/src/pages/TaskPage/TaskPage.tsx (1)
  • data (206-231)
coderd/coderd.go (1)
  • API (1636-1718)
🔇 Additional comments (10)
site/src/api/typesGenerated.ts (2)

425-432: CreateAITasksRequest matches server schema (LGTM).

Fields and optionality align with codersdk/aitasks.go (UUIDs as strings, preset_id optional). Generated location and naming look consistent with existing request types.


425-432: createAITask payloads include required fields and use snake_case

I’ve verified the lone caller of API.experimental.createAITask in site/src/pages/TasksPage/TasksPage.tsx (line 742) supplies:

  • name (required)
  • template_version_id (required)
  • prompt (required)
  • template_version_preset_id (optional)

All keys are snake_case. No changes needed.

coderd/database/queries.sql.go (2)

12873-12887: LGTM: correct EXISTS query and bool mapping

The query and sqlc mapping to a boolean look correct and efficient. Returning false when the row is missing or has_ai_task is false is appropriate, especially given prior authorization/version lookups upstream.


12873-12887: Confirm whether archived template versions should be excluded

If the intent is to allow AI tasks only on active (non-archived) versions, consider adding AND archived = false. If this is enforced elsewhere (e.g., creating a workspace is already blocked for archived versions), feel free to ignore.

Proposed SQL (in the source .sql file):

SELECT EXISTS (
  SELECT 1
  FROM template_versions
  WHERE id = $1 AND has_ai_task AND archived = false
);
coderd/aitasks_test.go (1)

198-230: Good negative-path coverage (400 on non-task template)

The check for HTTP 400 and error type is solid. This guards the server-side validation as intended.

coderd/database/dbmock/dbmock.go (1)

3274-3287: Mock addition LGTM

Method and recorder follow gomock conventions and match the interface signature.

coderd/aitasks.go (1)

20-66: Verify RBAC for prompts retrieval to prevent data leakage

The prompts endpoint fetches build parameters by IDs. Ensure the underlying store enforces authorization for the caller (e.g., via dbauthz wrapper or the authorized query variant) so members cannot see prompts of builds they don’t have access to. The MultipleBuilds test expects this.

If RBAC is not guaranteed by the wrapper, switch to the authorized query (GetAuthorizedWorkspaceBuildParametersByBuildIDs) with the appropriate prepared authorization from the request context.

codersdk/aitasks.go (1)

48-53: SDK request and handler flow looks good

Request shape matches server expectations, status check and decode logic are correct.

Also applies to: 55-72

site/src/pages/TasksPage/TasksPage.tsx (1)

295-295: No remaining createTask call sites to update
Verified that createTask( only appears in TasksPage.tsx (lines 295 & 727) and that workspace APIs are untouched. No stale or legacy calls found—changes are good to go.

coderd/database/dbauthz/dbauthz.go (1)

2877-2886: Authz wrapper correctly gates AI-prompt presence check

This follows the established dbauthz pattern: authorize via GetTemplateVersionByID, then query the store. Consistent with the requirement that all DB operations flow through dbauthz.

@DanielleMaywood DanielleMaywood marked this pull request as ready for review August 11, 2025 15:11
Copy link
Collaborator

@BrunoQuaresma BrunoQuaresma left a comment

Choose a reason for hiding this comment

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

FE code looks good to me.

Comment on lines 98 to 116
hasAITask, err := api.Database.GetTemplateVersionHasAITask(ctx, req.TemplateVersionID)
if err != nil {
if errors.Is(err, sql.ErrNoRows) || rbac.IsUnauthorizedError(err) {
httpapi.ResourceNotFound(rw)
return
}

httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "Internal error fetching whether the template version has an AI task.",
Detail: err.Error(),
})
return
}
if !hasAITask {
httpapi.Write(ctx, rw, http.StatusBadRequest, codersdk.Response{
Message: `Template does not have required parameter "` + codersdk.AITaskPromptParameterName + `"`,
})
return
}
Copy link
Member

Choose a reason for hiding this comment

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

suggestion: This might be better moved inside wsbuilder as it runs inside a tx and has access to the parameters.

@DanielleMaywood DanielleMaywood merged commit f349edc into main Aug 12, 2025
108 of 118 checks passed
@DanielleMaywood DanielleMaywood deleted the danielle/tasks/move-to-be branch August 12, 2025 10:23
@github-actions github-actions bot locked and limited conversation to collaborators Aug 12, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants