Skip to content

feat: implement createTask function and associated tests#1655

Merged
sweetmantech merged 32 commits into
testfrom
yuliusupwork/myc-4586-chat-align-task-creation-client-with-hardened-post-apitasks
Apr 23, 2026
Merged

feat: implement createTask function and associated tests#1655
sweetmantech merged 32 commits into
testfrom
yuliusupwork/myc-4586-chat-align-task-creation-client-with-hardened-post-apitasks

Conversation

@pradipthaadhi

@pradipthaadhi pradipthaadhi commented Apr 8, 2026

Copy link
Copy Markdown
Collaborator
  • Added createTask function to handle task creation via the Recoup API, including error handling for various response scenarios.
  • Created unit tests for createTask to verify correct API interaction, including optional parameters and error cases.

Summary by CodeRabbit

  • New Features

    • Create Task flow: new page and form to create scheduled tasks (title, prompt, schedule with presets and validation, artist selection, optional model); success/error toasts and redirect to Tasks list.
    • Quick access: “Create Task” button added to Tasks page.
    • Improved schedule UI: human-readable cron preview and presets.
  • Chores

    • API base URL can be overridden via env configuration.
    • Session storage migration/cleanup for override keys.
    • Default model changed to "openai/gpt-5-mini" and new prompt for suggestion generation added.

- Added createTask function to handle task creation via the Recoup API, including error handling for various response scenarios.
- Created unit tests for createTask to verify correct API interaction, including optional parameters and error cases.
@vercel

vercel Bot commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
recoup-chat Ready Ready Preview Apr 23, 2026 10:08pm

Request Review

@coderabbitai

coderabbitai Bot commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds a client-side "Create Task" flow: UI, form hook, schedule/model fields, cron validation, sessionStorage migration/cleanup, a createTask API client, API-base override via NEXT_PUBLIC_API_BASE_URL, and related constants and types.

Changes

Cohort / File(s) Summary
Task creation API
lib/tasks/createTask.ts, lib/tasks/createTaskApiResponse.ts
New createTask(accessToken, params) client that POSTs JSON to ${getClientApiBaseUrl()}/api/tasks, conditionally includes account_id and model, throws on non-2xx or API status: "error", validates response and returns the first Task. Adds CreateTaskApiResponse type.
Create Task UI + header
components/TasksPage/CreateTaskPage.tsx, components/TasksPage/CreateTaskPageHeader.tsx
New client components: full Create Task form page and header with optional override display and back link.
Form fields/components
components/TasksPage/ScheduleField.tsx, components/TasksPage/ModelField.tsx, components/TasksPage/schedulePresets.ts
New Schedule and Model form field components, schedule presets, cron preview, and model selection with default-sentinel handling and loading/error states.
Form hook
hooks/useCreateTaskForm.ts
New hook encapsulating form state, validation (including cron via validateCronExpression), submission logic (fetch access token, call createTask), toasts, navigation, and option derivation for artists/models.
Routing / page
app/tasks/new/page.tsx, components/TasksPage/TasksPage.tsx
Adds /tasks/new page that renders CreateTaskPage and updates TasksPage to include a "Create Task" CTA linking to that route.
Cron validation util
lib/tasks/validateCronExpression.ts
New cron validation and human-preview helpers: validateCronExpression and getCronHumanPreview (uses cronstrue).
SessionStorage migration / overrides
lib/sessionStorage/migrateRecoupOverrideKeys.ts, lib/accounts/override/getStoredAccountOverride.ts, lib/accounts/override/clearStoredAccountOverride.ts, lib/api/getClientApiBaseUrl.ts, providers/ApiOverrideSync.tsx
Adds migration/cleanup utilities for legacy sessionStorage keys, integrates migration calls before reading overrides, and replaces direct removal with canonical clear helpers.
Constants
lib/consts.ts
NEW_API_BASE_URL now prefers process.env.NEXT_PUBLIC_API_BASE_URL with prior IS_PROD fallback; DEFAULT_MODEL changed to "openai/gpt-5-mini"; PROMPT_SUGGESTIONS_SYSTEM_PROMPT added; storage key constants relocated but values unchanged.

Sequence Diagram(s)

sequenceDiagram
  participant Browser as "Browser (CreateTaskPage)"
  participant Hook as "useCreateTaskForm"
  participant ClientUtil as "createTask (lib/tasks/createTask.ts)"
  participant API as "Server API /api/tasks"
  participant DB as "Database"

  Browser->>Hook: user fills form, submits
  Hook->>Hook: validate fields & cron
  Hook->>Hook: get access token (usePrivy)
  Hook->>ClientUtil: createTask(accessToken, params)
  ClientUtil->>API: POST /api/tasks (Authorization, JSON body)
  API->>DB: persist scheduled task
  DB-->>API: return created task
  API-->>ClientUtil: 200 OK with tasks array
  ClientUtil-->>Hook: return created Task
  Hook-->>Browser: toast success, navigate /tasks, router.refresh()
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • sweetmantech

Poem

A cron-line hums, a prompt takes wing,
Tokens whispered, POSTs that sing.
Env points the way, migrations tidy the keys,
A task is born on scheduled breeze. ✨

🚥 Pre-merge checks | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Solid & Clean Code ⚠️ Warning PR violates DRY principle with 4+ identical error rendering patterns and SRP with bloated components mixing multiple concerns. Extract FormFieldError and FormField components, refactor hooks into focused concerns, and consolidate migration logic into a generic factory function.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch yuliusupwork/myc-4586-chat-align-task-creation-client-with-hardened-post-apitasks

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
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
lib/tasks/createTask.ts (1)

20-23: Consider validating accessToken to fail fast.

The function accepts any string for accessToken, including empty strings. While the API will reject invalid tokens, failing fast with a descriptive error improves debugging experience.

🛡️ Optional: Add input validation
 export async function createTask(
   accessToken: string,
   params: CreateTaskParams,
 ): Promise<ScheduledAction> {
+  if (!accessToken) {
+    throw new Error("accessToken is required");
+  }
+
   const response = await fetch(`${getClientApiBaseUrl()}/api/tasks`, {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/tasks/createTask.ts` around lines 20 - 23, The createTask function
currently accepts any string for accessToken which can be empty; add a fast-fail
input validation at the top of createTask to check that accessToken is a
non-empty string (and optionally well-formed if you have a pattern), and throw a
clear, descriptive error (e.g., Error('createTask: accessToken is required'))
before proceeding to use CreateTaskParams or returning a ScheduledAction; update
callers/tests if necessary to handle the thrown error.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/tasks/createTask.ts`:
- Around line 35-36: The spread construction in createTask.ts is inconsistent:
it uses a truthy check for params.account_id but an explicit !== undefined check
for params.model, which treats empty string and null differently; update both
spreads to use the same explicit presence check (e.g., params.account_id !==
undefined and params.model !== undefined) or, if you intend to exclude empty
strings for account_id, add a clarifying comment above the account_id spread
explaining that empty strings must be excluded—refer to the params object fields
account_id and model and make the checks consistent across the two spreads.

---

Nitpick comments:
In `@lib/tasks/createTask.ts`:
- Around line 20-23: The createTask function currently accepts any string for
accessToken which can be empty; add a fast-fail input validation at the top of
createTask to check that accessToken is a non-empty string (and optionally
well-formed if you have a pattern), and throw a clear, descriptive error (e.g.,
Error('createTask: accessToken is required')) before proceeding to use
CreateTaskParams or returning a ScheduledAction; update callers/tests if
necessary to handle the thrown error.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 02121c33-755b-4889-bc74-6bc63c9cae1e

📥 Commits

Reviewing files that changed from the base of the PR and between ee82aa4 and 644a4ce.

⛔ Files ignored due to path filters (1)
  • lib/tasks/__tests__/createTask.test.ts is excluded by !**/*.test.* and included by lib/**
📒 Files selected for processing (1)
  • lib/tasks/createTask.ts

Comment thread lib/tasks/createTask.ts Outdated

@cubic-dev-ai cubic-dev-ai 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.

3 issues found across 2 files

Confidence score: 4/5

  • This PR looks safe to merge overall; the main issues are minor consistency concerns rather than clear runtime breakage.
  • Most notable: lib/tasks/createTask.ts returns ScheduledAction but parses Task-typed data, which could confuse consumers or type checks.
  • Inconsistent optional param handling in lib/tasks/createTask.ts (account_id truthy check vs model !== undefined) may lead to subtle input differences but is not likely to be merge-blocking.
  • Pay close attention to lib/tasks/createTask.ts - return type consistency and optional parameter checks.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="lib/tasks/__tests__/createTask.test.ts">

<violation number="1" location="lib/tasks/__tests__/createTask.test.ts:1">
P1: Custom agent: **Code Structure and Size Limits for Readability and Single Responsibility**

This test file is 136 lines, exceeding the 100-line limit. Split the error-case tests (HTTP error, API error status, empty tasks) into a separate file like `createTask.errors.test.ts` to keep each file under 100 lines.</violation>
</file>

<file name="lib/tasks/createTask.ts">

<violation number="1" location="lib/tasks/createTask.ts:23">
P2: Return type should be `Task` instead of `ScheduledAction` to match the parsed response type (`GetTasksResponse.tasks` is `Task[]`) and stay consistent with `getTasks`, which returns `Task[]`. The current `ScheduledAction` return type discards the optional `recent_runs` and `upcoming` fields, which callers can't access without casting.</violation>

<violation number="2" location="lib/tasks/createTask.ts:35">
P3: Inconsistent conditional patterns for optional parameters: `account_id` uses a truthiness check (excluding `undefined` and `""`) while `model` uses `!== undefined` (allowing `null` and `""`). If both should follow the same exclusion logic, align them. If the difference is intentional (e.g., `model` deliberately allows `null` to clear the field), add a brief comment to make that intent explicit.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

@@ -0,0 +1,136 @@
import { beforeEach, describe, expect, it, vi } from "vitest";

@cubic-dev-ai cubic-dev-ai Bot Apr 8, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1: Custom agent: Code Structure and Size Limits for Readability and Single Responsibility

This test file is 136 lines, exceeding the 100-line limit. Split the error-case tests (HTTP error, API error status, empty tasks) into a separate file like createTask.errors.test.ts to keep each file under 100 lines.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/tasks/__tests__/createTask.test.ts, line 1:

<comment>This test file is 136 lines, exceeding the 100-line limit. Split the error-case tests (HTTP error, API error status, empty tasks) into a separate file like `createTask.errors.test.ts` to keep each file under 100 lines.</comment>

<file context>
@@ -0,0 +1,136 @@
+import { beforeEach, describe, expect, it, vi } from "vitest";
+import { createTask } from "@/lib/tasks/createTask";
+import { getClientApiBaseUrl } from "@/lib/api/getClientApiBaseUrl";
</file context>
Fix with Cubic

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Now the test file is 83 lines

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks for the update—glad it’s under 100 lines now.

Comment thread lib/tasks/createTask.ts Outdated
Comment thread lib/tasks/createTask.ts Outdated
@pradipthaadhi pradipthaadhi changed the base branch from main to test April 8, 2026 10:08
Comment thread lib/tasks/createTask.ts
…umentation

- Removed the optional account_id parameter from the CreateTaskParams interface.
- Updated the createTask function documentation to clarify that the task owner account is resolved from the bearer token, eliminating the need to send account_id in the request body.
- Adjusted related tests to reflect the removal of account_id.
- Added optional account_id parameter to CreateTaskParams interface, allowing for more flexible task creation.
- Updated createTask function to include account_id in the request body when provided.
- Modified API base URL logic to support an environment variable override for better configuration in different environments.
- Adjusted related tests to verify the inclusion of account_id and updated API base URL behavior.

@cubic-dev-ai cubic-dev-ai 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.

0 issues found across 3 files (changes from recent commits).

Requires human review: Auto-approval blocked by 2 unresolved issues from previous reviews.

…upwork/myc-4586-chat-align-task-creation-client-with-hardened-post-apitasks

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (3)
lib/consts.ts (1)

4-9: Minor: simplify with nullish coalescing and guard against empty-string env values.

A blank NEXT_PUBLIC_API_BASE_URL="" would currently be treated as "not set" by the truthy check (fine), but the nested ternary is harder to read than necessary. Consider:

♻️ Suggested tidy-up
-const apiBaseUrlOverride = process.env.NEXT_PUBLIC_API_BASE_URL;
-export const NEW_API_BASE_URL = apiBaseUrlOverride
-  ? apiBaseUrlOverride
-  : IS_PROD
-  ? "https://recoup-api.vercel.app"
-  : "https://test-recoup-api.vercel.app";
+const DEFAULT_API_BASE_URL = IS_PROD
+  ? "https://recoup-api.vercel.app"
+  : "https://test-recoup-api.vercel.app";
+export const NEW_API_BASE_URL =
+  process.env.NEXT_PUBLIC_API_BASE_URL?.trim() || DEFAULT_API_BASE_URL;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/consts.ts` around lines 4 - 9, Replace the nested ternary that computes
NEW_API_BASE_URL with a clearer nullish-coalescing approach that also guards
against empty strings: use the environment variable apiBaseUrlOverride
(NEXT_PUBLIC_API_BASE_URL) after trimming and check for a non-empty value,
otherwise fall back to the IS_PROD conditional defaults; update references to
apiBaseUrlOverride and NEW_API_BASE_URL accordingly so the env value "" is
treated as unset and the ternary nesting is removed for readability.
lib/tasks/createTask.ts (2)

55-55: Defensive: verify data.tasks[0] actually matches Tables<"scheduled_actions">.

The function signature promises ScheduledAction, but the runtime value is whatever the API returns — no validation is performed. For a client helper that feeds downstream type-safe code, consider a lightweight schema check (e.g. Zod) or at minimum assert the presence of required fields (id, artist_account_id, etc.) before returning. Otherwise a subtly-shaped API response becomes a hard-to-diagnose undefined read far from the call site.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/tasks/createTask.ts` at line 55, The return of data.tasks[0] in
createTask (which is claimed to be a ScheduledAction) is not validated; add a
lightweight runtime check before returning to ensure the object matches
ScheduledAction (at minimum verify required fields like id and artist_account_id
exist and have expected types). Implement this by either using a small Zod
schema for ScheduledAction and parsing data.tasks[0] (e.g.,
scheduledActionSchema.parse) or by adding explicit assertions that required keys
are present and of correct primitive types, and throw a clear error if
validation fails so callers never receive an invalid value.

21-56: Request-cancellation & timeout are missing.

fetch here has no AbortSignal and no timeout. In a UI flow a user may navigate away mid-request, leaving the promise to settle against an unmounted component; and a hung Recoup API call will block indefinitely. Accepting an optional signal?: AbortSignal (commonly threaded through from React Query / SWR / TanStack mutations) is a cheap resilience win:

♻️ Thread an AbortSignal through
 export async function createTask(
   accessToken: string,
   params: CreateTaskParams,
+  options?: { signal?: AbortSignal },
 ): Promise<ScheduledAction> {
   const response = await fetch(`${getClientApiBaseUrl()}/api/tasks`, {
     method: "POST",
+    signal: options?.signal,
     headers: {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/tasks/createTask.ts` around lines 21 - 56, The createTask function should
accept an optional signal?: AbortSignal parameter and use it to enable request
cancellation and a timeout; update the createTask signature to include signal?:
AbortSignal, pass that signal into the fetch options, and implement a short
configurable timeout by creating an AbortController inside createTask (tie its
signal to the passed signal so an external cancel also aborts) and use it to
abort the fetch after the timeout period; ensure you clean up any timers and
prefer the passed-in signal when present so React Query/SWR can cancel the
request.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@lib/consts.ts`:
- Around line 4-9: Restore and export the two missing constants
API_OVERRIDE_STORAGE_KEY and ACCOUNT_OVERRIDE_STORAGE_KEY from lib/consts.ts so
existing consumers compile; add definitions (e.g. const API_OVERRIDE_STORAGE_KEY
= 'apiOverride' and const ACCOUNT_OVERRIDE_STORAGE_KEY = 'accountOverride') and
export them alongside NEW_API_BASE_URL, ensuring the identifiers referenced by
getClientApiBaseUrl, ApiOverrideSync, getStoredAccountOverride,
setStoredAccountOverride, and clearStoredAccountOverride match exactly.

In `@lib/tasks/createTask.ts`:
- Around line 46-55: The code in createTask.ts currently types the POST response
as GetTasksResponse (from getTasks.ts) which is for the list endpoint; change
this to an endpoint-specific response type (e.g., CreateTaskResponse with shape
{ status: string; task: Task; error?: string }) or introduce a generic envelope
type (e.g., TaskEnvelope<T>) exported from getTasks.ts and use
TaskEnvelope<Task> here; update createTask.ts (and similarly updateTask.ts) to
import and use the new CreateTaskResponse/TaskEnvelope type, adjust the runtime
checks to read data.task instead of data.tasks, and throw errors from data.error
when present.

---

Nitpick comments:
In `@lib/consts.ts`:
- Around line 4-9: Replace the nested ternary that computes NEW_API_BASE_URL
with a clearer nullish-coalescing approach that also guards against empty
strings: use the environment variable apiBaseUrlOverride
(NEXT_PUBLIC_API_BASE_URL) after trimming and check for a non-empty value,
otherwise fall back to the IS_PROD conditional defaults; update references to
apiBaseUrlOverride and NEW_API_BASE_URL accordingly so the env value "" is
treated as unset and the ternary nesting is removed for readability.

In `@lib/tasks/createTask.ts`:
- Line 55: The return of data.tasks[0] in createTask (which is claimed to be a
ScheduledAction) is not validated; add a lightweight runtime check before
returning to ensure the object matches ScheduledAction (at minimum verify
required fields like id and artist_account_id exist and have expected types).
Implement this by either using a small Zod schema for ScheduledAction and
parsing data.tasks[0] (e.g., scheduledActionSchema.parse) or by adding explicit
assertions that required keys are present and of correct primitive types, and
throw a clear error if validation fails so callers never receive an invalid
value.
- Around line 21-56: The createTask function should accept an optional signal?:
AbortSignal parameter and use it to enable request cancellation and a timeout;
update the createTask signature to include signal?: AbortSignal, pass that
signal into the fetch options, and implement a short configurable timeout by
creating an AbortController inside createTask (tie its signal to the passed
signal so an external cancel also aborts) and use it to abort the fetch after
the timeout period; ensure you clean up any timers and prefer the passed-in
signal when present so React Query/SWR can cancel the request.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: dd954806-7643-4cee-96b3-380ef38f0e77

📥 Commits

Reviewing files that changed from the base of the PR and between 644a4ce and 548694c.

⛔ Files ignored due to path filters (1)
  • lib/tasks/__tests__/createTask.test.ts is excluded by !**/*.test.* and included by lib/**
📒 Files selected for processing (2)
  • lib/consts.ts
  • lib/tasks/createTask.ts

Comment thread lib/consts.ts Outdated
Comment thread lib/tasks/createTask.ts Outdated
Comment on lines +46 to +55
const data: GetTasksResponse = await response.json();
if (data.status === "error") {
throw new Error(data.error || "Unknown error occurred");
}

if (!data.tasks || data.tasks.length === 0) {
throw new Error("API returned success but no task was created");
}

return data.tasks[0];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm the shape of GetTasksResponse and whether any dedicated create-response type exists.
fd -e ts getTasks.ts lib/tasks
rg -nP 'GetTasksResponse|CreateTaskResponse' --type=ts -C2

Repository: recoupable/chat

Length of output: 1732


Replace GetTasksResponse with an endpoint-specific type to avoid conflating contracts.

GetTasksResponse is designed for the GET /api/tasks endpoint (returns { tasks: Task[] }), but it's being reused as the response type for POST and PATCH operations in createTask.ts and updateTask.ts. This couples distinct API contracts together. The create/update endpoints likely have different response shapes—returning a single task object is common REST convention—and using the list endpoint's type masks that difference.

Create a dedicated CreateTaskResponse type (e.g., { status, task: Task, error? }) or share a generic envelope (e.g., TaskEnvelope<T>) exported from getTasks.ts. Each endpoint should have its own contract type; the single-responsibility principle applies to types as much as functions.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@lib/tasks/createTask.ts` around lines 46 - 55, The code in createTask.ts
currently types the POST response as GetTasksResponse (from getTasks.ts) which
is for the list endpoint; change this to an endpoint-specific response type
(e.g., CreateTaskResponse with shape { status: string; task: Task; error?:
string }) or introduce a generic envelope type (e.g., TaskEnvelope<T>) exported
from getTasks.ts and use TaskEnvelope<Task> here; update createTask.ts (and
similarly updateTask.ts) to import and use the new
CreateTaskResponse/TaskEnvelope type, adjust the runtime checks to read
data.task instead of data.tasks, and throw errors from data.error when present.

- Introduced a new CreateTaskPage component for task creation, featuring a form with validation for title, prompt, schedule, and artist selection.
- Updated TasksPage to include a "Create Task" button, enhancing navigation for users to create new tasks.
- Implemented state management and error handling for form submissions, ensuring a smooth user experience.

@cubic-dev-ai cubic-dev-ai 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.

2 issues found across 3 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="components/TasksPage/CreateTaskPage.tsx">

<violation number="1" location="components/TasksPage/CreateTaskPage.tsx:28">
P1: Custom agent: **Code Structure and Size Limits for Readability and Single Responsibility**

At 228 lines this file is more than double the 100-line limit. Extract a `useCreateTaskForm` hook (state, validation, submit handler) into a separate file and keep this component focused on rendering. The `.coderabbit.yaml` path rule for `components/**/*.tsx` also calls for <200 lines and extracting custom hooks.</violation>

<violation number="2" location="components/TasksPage/CreateTaskPage.tsx:216">
P2: `disabled` prop has no effect on `<Button asChild>` wrapping a `<Link>`. The `<a>` element doesn't support `disabled`, and the `:disabled` CSS pseudo-class won't match it. During submission, the cancel link remains fully clickable and unstyled.

Consider conditionally rendering a plain `<Button>` (not `asChild`) when disabled, or using `aria-disabled` with `onClick={e => e.preventDefault()}` to block navigation.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Comment thread components/TasksPage/CreateTaskPage.tsx Outdated
Comment thread components/TasksPage/CreateTaskPage.tsx Outdated
…e new dependencies

- Added @react-native-async-storage/async-storage version 3.0.2.
- Added @valibot/to-json-schema version 1.6.0.
- Added effect version 3.21.0.
- Updated dependency versions in pnpm-lock.yaml to reflect the new additions and ensure compatibility.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
components/TasksPage/CreateTaskPage.tsx (1)

60-106: Consider extracting validation for single-responsibility.

handleSubmit currently juggles validation, token retrieval, request dispatch, navigation, and error surfacing. Pulling the field-validation block into a small validate(...) helper (returning FormErrors) would tighten the handler's responsibility and make the rules independently testable. Not blocking — happy to leave as-is if you'd rather keep it inline.

As per coding guidelines: "Each function should have a single responsibility and clear naming".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/TasksPage/CreateTaskPage.tsx` around lines 60 - 106, Extract the
inline field-validation in handleSubmit into a new helper function named
validate that accepts the form fields (title, prompt, schedule, artistAccountId,
model, accountIdOverride as needed) and returns a FormErrors object; then have
handleSubmit call validate, setErrors(nextErrors) and setSubmitError(null) based
on the returned errors and early-return if any keys exist — leaving token
retrieval, createTask call, navigation and error handling in handleSubmit
unchanged. Ensure validate is exported/typed as returning FormErrors so it can
be unit-tested independently and update references in handleSubmit (still using
setIsSubmitting, getAccessToken, createTask, toast, router) to use the helper.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@components/TasksPage/CreateTaskPage.tsx`:
- Around line 131-200: Add accessible error wiring: for each field
(Input/Textarea/Select) — task-title, task-prompt, task-schedule, task-artist —
set aria-invalid={!!errors.<field>} and set aria-describedby to a stable id when
an error exists (e.g. title-error, prompt-error, schedule-error, artist-error).
Render the corresponding error <p> with the matching id and ensure
aria-describedby is omitted or null when no error is present so screen readers
only reference existing messages; apply the same pattern to the SelectTrigger
used for task-artist.
- Around line 216-218: The Cancel control currently uses <Button asChild>
wrapping the Next.js <Link> (Link href="/tasks") so the disabled prop
(isSubmitting) is ineffective; change it so when isSubmitting is true you render
a real <button disabled> (e.g., a Button without asChild) to prevent
interaction, and when not submitting render the <Button asChild> with <Link
href="/tasks"> as before, or alternatively replace the Link variant with a
regular Button that calls router.push('/tasks') on click and respects disabled;
update the component where Button asChild and isSubmitting are used (the Cancel
button rendering) to implement one of these two approaches.

---

Nitpick comments:
In `@components/TasksPage/CreateTaskPage.tsx`:
- Around line 60-106: Extract the inline field-validation in handleSubmit into a
new helper function named validate that accepts the form fields (title, prompt,
schedule, artistAccountId, model, accountIdOverride as needed) and returns a
FormErrors object; then have handleSubmit call validate, setErrors(nextErrors)
and setSubmitError(null) based on the returned errors and early-return if any
keys exist — leaving token retrieval, createTask call, navigation and error
handling in handleSubmit unchanged. Ensure validate is exported/typed as
returning FormErrors so it can be unit-tested independently and update
references in handleSubmit (still using setIsSubmitting, getAccessToken,
createTask, toast, router) to use the helper.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: fe7f6485-058f-4f92-ae7b-11da9189aa9f

📥 Commits

Reviewing files that changed from the base of the PR and between 548694c and 84caec1.

⛔ Files ignored due to path filters (2)
  • package.json is excluded by none and included by none
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml and included by none
📒 Files selected for processing (3)
  • app/tasks/new/page.tsx
  • components/TasksPage/CreateTaskPage.tsx
  • components/TasksPage/TasksPage.tsx

Comment thread components/TasksPage/CreateTaskPage.tsx Outdated
Comment thread components/TasksPage/CreateTaskPage.tsx Outdated
- Introduced API_OVERRIDE_STORAGE_KEY and ACCOUNT_OVERRIDE_STORAGE_KEY constants for improved API management.
- Updated DEFAULT_MODEL to "openai/gpt-5-mini" for better performance.
- Added a comprehensive PROMPT_SUGGESTIONS_SYSTEM_PROMPT to guide users in generating actionable follow-up prompts related to music industry insights and marketing strategies.

@cubic-dev-ai cubic-dev-ai 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.

3 issues found across 2 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="package.json">

<violation number="1" location="package.json:49">
P1: Custom agent: **Flag AI Slop and Fabricated Changes**

This dependency is a React Native library with no imports anywhere in the codebase. Adding a mobile-platform storage package to a Next.js web app strongly suggests AI-generated dependency additions that don't correspond to any actual implementation. Remove unused dependencies.</violation>

<violation number="2" location="package.json:57">
P2: Custom agent: **Flag AI Slop and Fabricated Changes**

Unused dependency — `@valibot/to-json-schema` is not imported anywhere in the codebase. Remove it to avoid bloating `node_modules` with unused packages.</violation>

<violation number="3" location="package.json:69">
P1: Custom agent: **Flag AI Slop and Fabricated Changes**

Unused dependency — `effect` is a heavyweight FP framework not imported anywhere in the codebase. Adding large unused dependencies inflates bundle/install size for no benefit.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Comment thread package.json Outdated
Comment thread package.json Outdated
Comment thread package.json Outdated

@cubic-dev-ai cubic-dev-ai 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.

0 issues found across 1 file (changes from recent commits).

Requires human review: Auto-approval blocked by 7 unresolved issues from previous reviews.

- Added cron expression validation to ensure correct scheduling input.
- Introduced schedule presets for easier task scheduling.
- Implemented a dropdown for model selection, allowing users to choose from available models or use a default option.
- Updated UI to provide feedback on cron expression validity and model loading status.

@cubic-dev-ai cubic-dev-ai 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.

1 issue found across 1 file (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="components/TasksPage/CreateTaskPage.tsx">

<violation number="1" location="components/TasksPage/CreateTaskPage.tsx:8">
P1: Custom agent: **Code Structure and Size Limits for Readability and Single Responsibility**

At 339 lines this component is over 3× the 100-line limit. The new code adds three independent concerns — cron validation, schedule-preset selection, and model-selection dropdown — all inlined in one file. Extract them into focused units:

- `validateCronExpression` → `lib/tasks/validateCronExpression.ts`
- Schedule preset picker + cron preview → a `ScheduleField` component
- Model dropdown + loading/error states → a `ModelField` component

This aligns with the repo's `.coderabbit.yaml` guidance: _"one responsibility per component; <200 lines; extract custom hooks."_</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review, or fix all with cubic.

Comment thread components/TasksPage/CreateTaskPage.tsx Outdated
- Added a label to show the currently configured default model in the model selection dropdown.
- Enhanced the UI to include a description of the default model below the model selection, improving user clarity on the selected model option.
…t wrapper

Both paths into GatewayModelSelect were ultimately hitting the same
useAvailableModels() hook (same endpoint, same React Query key, same
staleTime) — the chat path just wrapped it through VercelChatContext.
Drop availableModels / isLoading / isError as props and call the hook
inside GatewayModelSelect. React Query's queryKey dedupe means zero
extra requests.

- GatewayModelSelect: calls useAvailableModels() internally
- ModelSelect: drops availableModels forwarding
- EditableGatewayModelSelect: deleted — was a 15-line wrapper whose
  only purpose was forwarding availableModels and setting default
  triggerId/hintId, both of which callers passed explicitly anyway
- TasksPage/ModelField + TaskDetailsDialogContent: import
  GatewayModelSelect directly

One exported component, two callers, no duplicated data fetching.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ULT_MODEL

The task form's initial model state was "" — which forced GatewayModelSelect
to carry a "Use default" row (includeDefaultOption) as the only UI affordance
for representing or returning to that empty string. Seed the form with
DEFAULT_MODEL instead (same as the chat), and the "Use default" concept is no
longer needed: the default model simply appears in the list like any other.

Then GatewayModelSelect collapses to two props (value, onValueChange).
Form-mode concerns — label, triggerId, hintId, disabled — move out of the
component; TaskDetailsDialogContent's edit branch now wraps the select with
its own <label> so it matches the non-edit branch. ModelField is a trivial
4-line wrapper matching its sibling *Field components.

- hooks/useCreateTaskForm.ts: initial model = DEFAULT_MODEL
- GatewayModelSelect: delete includeDefaultOption, DEFAULT_OPTION, form-mode
  label/triggerId/hintId/isError/disabled branches, defaultModelLabel useMemo,
  hint paragraph — props are now just {value, onValueChange}
- ModelField: drop all form-mode props
- TaskDetailsDialogContent: drop form-mode props, wrap with its own <label>

Trade-off: new tasks now persist a concrete model id (e.g.
"openai/gpt-5.4-mini") instead of "". If DEFAULT_MODEL changes later, existing
tasks stay pinned to the old value until manually edited. Existing tasks with
"" in the DB continue to work — backend interprets them as "use default".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Comment thread components/TasksPage/ScheduleField.tsx Outdated
Comment on lines +6 to +9
const CronEditor = dynamic(
() => import("@/components/CronEditor").then((m) => ({ default: m.CronEditor })),
{ ssr: false },
);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

KISS principle

  • do not dynamically import components unnecessarily
Image

Comment on lines +15 to +18
const CronEditor = dynamic(
() => import("@/components/CronEditor").then((mod) => mod.CronEditor),
{ ssr: false },
);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

KISS principle

  • do not dynamically import components unnecessarily

This commit deletes outdated components and hooks related to task creation, including CreateTaskPage, CreateTaskFormBody, and various field components. The removal streamlines the codebase and eliminates unused functionality, paving the way for a more modular and maintainable task creation process.

- Deleted components: CreateTaskPage, CreateTaskFormBody, CreateTaskArtistField, CreateTaskFormActions, CreateTaskPromptField, CreateTaskTitleField, ModelField, ScheduleField, and CreateTaskPageHeader.
- Removed associated hooks and context providers to simplify the task creation logic.
Changed the default prompt in the useCreateTask hook from an empty string to a more descriptive placeholder: "New task — replace with your instructions." This enhancement improves user guidance during task creation.
…ask creation flow

- Removed unnecessary props from TaskDetailsDialog, including defaultOpen and onClose, to streamline its usage.
- Updated the useTaskDetailsDialog hook to initialize dialog state without defaultOpen.
- Added a data attribute for task dialog triggers in TasksList to improve interaction handling.
- Refactored the task creation logic in useCreateTask to utilize React Query's mutation for better state management and error handling, enhancing user experience during task creation.
defaultOpen = false,
}: UseTaskDetailsDialogParams) => {
const [isDialogOpen, setIsDialogOpen] = useState(false);
const [isDialogOpen, setIsDialogOpen] = useState(defaultOpen);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

KISS / YAGNI - Why are you allowing defaultOpen to be a prop?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Image

Comment thread hooks/useCreateTask.ts Outdated
const [isCreating, setIsCreating] = useState(false);
const [createdTask, setCreatedTask] = useState<Task | null>(null);

const handleCreateTask = async () => {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

KISS - Tanstack

  • actual: manually defining loading states and result states. Not using Tanstack useMutation.
  • required: use tanStack useMutation.

Comment thread components/TasksPage/TasksPage.tsx Outdated
Comment on lines +23 to +29
<Button
onClick={() => handleCreateTask()}
disabled={isCreating}
className="w-full sm:w-auto"
>
{isCreating ? "Creating..." : "Create Task"}
</Button>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

OCP

  • actual: added net new button code to the existing TaskPage.
  • required: new file for the button + hook.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

YAGNI - are these file changes necessary for this PR?

  • why not revert this change and focus on the New Task UX?

Comment thread lib/ai/featuredModels.ts Outdated
{
id: DEFAULT_MODEL,
displayName: "GPT-5.4 Mini",
displayName: "GPT-5 Mini",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

KISS - why are you changing the displayName in this PR?

Comment thread lib/cron/cronPresetSchedules.ts Outdated

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

KISS - same question here, why are these changes required for the New Task flow? Why not remove them?

Comment thread lib/shims/async-storage-web-stub.js Outdated

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

KISS - What is the purpose of this file?

  • What happens if we remove it?

…eprecated components

- Replaced the GatewayModelSelect component with a more streamlined implementation directly in ModelSelect, enhancing the model selection process.
- Updated the TaskDetailsDialogContent to utilize the new model selection approach, improving consistency across components.
- Removed the CRON_SIMPLE_PRESETS and related legacy code, replacing it with a new PRESET_SCHEDULES array for better maintainability.
- Simplified the webpack configuration in next.config.mjs by removing unnecessary aliasing and stubs, optimizing the build process.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

KISS - why are any changes needed to this file?

Comment thread components/TasksPage/TasksPage.tsx Outdated
Comment on lines +12 to +20
<div className="mb-6 flex flex-col gap-4 sm:flex-row sm:items-start sm:justify-between">
<div>
<h1 className="text-left font-heading text-3xl font-bold dark:text-white mb-4">
Tasks
</h1>
<p className="text-lg text-muted-foreground text-left font-light font-sans max-w-2xl">
View and manage all the tasks for your selected artist.
</p>
</div>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

SRP

  • actual: new div containing a Header section defined within the Page component.
  • required: new component file for the header div you added.

Comment thread lib/consts.ts

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

KISS - Why are changes needed to this file?

Comment thread next.config.mjs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

KISS - Why are changes required to this file?

Comment thread pnpm-lock.yaml

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

KISS - Why are changes required to this file?

…agement

- Introduced a new TasksPageHeader component to encapsulate the header logic, improving readability and maintainability.
- Updated the TasksPage to utilize the new TasksPageHeader, streamlining the layout and reducing clutter.
- Refactored ModelSelect imports for consistency and clarity.
- Simplified the webpack configuration in next.config.mjs by removing unnecessary stubs, optimizing the build process.
- Updated pnpm-lock.yaml to reflect dependency changes and ensure consistency across the project.
- Improved the task creation flow by integrating a new dynamic CronEditor, replacing the outdated CronInput and schedulePresets.
- Updated ModelField to utilize EditableGatewayModelSelect for streamlined model selection.
- Removed legacy code and unused hooks to simplify the codebase and improve maintainability.
- Enhanced ScheduleField for better management of cron expressions, improving user experience.
…-chat-align-task-creation-client-with-hardened-post-apitasks

# Conflicts:
#	lib/tools/getMcpTools.ts

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

KISS - Why are changes to this file necessary?

Comment thread lib/tasks/validateCronExpression.ts Outdated

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

KISS - Why are changes to this file necessary?

Both files were added to this branch as empty placeholders and aren't
present on `test`. Reverting by deletion so the PR stays focused.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…view

The pre-validation guard referenced a now-deleted module. cronstrue's
try/catch already handles invalid cron expressions — the guard was
redundant. Removes the broken import and simplifies the function.

Fixes the 2 failing getCronHumanPreview tests.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@sweetmantech sweetmantech merged commit 1375507 into test Apr 23, 2026
3 checks passed
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.

2 participants