Skip to content

feat: implement createTask function and associated tests#1655

Open
pradipthaadhi wants to merge 18 commits intotestfrom
yuliusupwork/myc-4586-chat-align-task-creation-client-with-hardened-post-apitasks
Open

feat: implement createTask function and associated tests#1655
pradipthaadhi wants to merge 18 commits intotestfrom
yuliusupwork/myc-4586-chat-align-task-creation-client-with-hardened-post-apitasks

Conversation

@pradipthaadhi
Copy link
Copy Markdown
Collaborator

@pradipthaadhi pradipthaadhi commented Apr 8, 2026

  • 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
Copy link
Copy Markdown
Contributor

vercel bot commented Apr 8, 2026

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

Project Deployment Actions Updated (UTC)
recoup-chat Ready Ready Preview Apr 19, 2026 5:26am

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

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.

Copy link
Copy Markdown
Contributor

@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: 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
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

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";
Copy link
Copy Markdown

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

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

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
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 principle - Where is this lib being used?

…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.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

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
Copy link
Copy Markdown
Contributor

@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: 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.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

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
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.
Copy link
Copy Markdown
Contributor

@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: 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="proxy.php?url=https%3A%2F%2Fgithub.com%2Ftasks") 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="proxy.php?url=https%3A%2F%2Fgithub.com%2Ftasks"> 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.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

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
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

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.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

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
- 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.
Copy link
Copy Markdown
Contributor

@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: 1

🤖 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 30-31: You renamed storage keys API_OVERRIDE_STORAGE_KEY and
ACCOUNT_OVERRIDE_STORAGE_KEY to "apiOverride"/"accountOverride" which breaks
existing sessions and removes the previous recoup_ namespace; add a one-time
migration in the startup path that checks for the old keys "recoup_api_override"
and "recoup_account_override", and if the new keys (API_OVERRIDE_STORAGE_KEY /
ACCOUNT_OVERRIDE_STORAGE_KEY) are empty but the old keys exist, copy the values
to the new keys and then remove the old keys (or alternatively preserve the
recoup_ prefix by reverting the constants to
"recoup_api_override"/"recoup_account_override"); implement this migration where
session storage is initialized so existing users keep their overrides and
namespace collisions are avoided.
🪄 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: 17e60eaf-dcf8-4de3-a99c-16b9dc3b1f84

📥 Commits

Reviewing files that changed from the base of the PR and between 84caec1 and 97961de.

📒 Files selected for processing (2)
  • components/TasksPage/CreateTaskPage.tsx
  • lib/consts.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/TasksPage/CreateTaskPage.tsx

Comment thread lib/consts.ts Outdated
Comment on lines +30 to +31
export const API_OVERRIDE_STORAGE_KEY = "apiOverride";
export const ACCOUNT_OVERRIDE_STORAGE_KEY = "accountOverride";
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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm no remaining references to the old storage key literals exist,
# and surface every consumer of the new constants so we can reason about migration impact.
rg -nP '"(recoup_api_override|recoup_account_override)"'
rg -nP '\b(API_OVERRIDE_STORAGE_KEY|ACCOUNT_OVERRIDE_STORAGE_KEY)\b' -C2

Repository: recoupable/chat

Length of output: 4233


Storage key rename lacks migration path for existing user sessions.

The change from recoup_api_override / recoup_account_override to apiOverride / accountOverride has two impacts worth confirming:

  1. Silent data loss for active overrides — No migration code exists to read old keys and write new ones. Any user with an active API or account override stored under the old keys will lose it after deployment; their override state silently disappears on the next page load.

  2. Namespace collision risk — The old recoup_ prefix scoped these keys within your application's namespace. The new bare names (apiOverride, accountOverride) are generic enough to collide with other apps, browser extensions, or libraries writing to sessionStorage on shared domains.

If this rename is intentional (e.g., aligning with a new convention), add a one-time migration utility that reads the old key, writes the new one, and cleans up the old entry. Alternatively, retain the recoup_ prefix for hygiene. If this is a deliberate breaking change with no backward compatibility needed, document it clearly in the commit message and release notes.

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

In `@lib/consts.ts` around lines 30 - 31, You renamed storage keys
API_OVERRIDE_STORAGE_KEY and ACCOUNT_OVERRIDE_STORAGE_KEY to
"apiOverride"/"accountOverride" which breaks existing sessions and removes the
previous recoup_ namespace; add a one-time migration in the startup path that
checks for the old keys "recoup_api_override" and "recoup_account_override", and
if the new keys (API_OVERRIDE_STORAGE_KEY / ACCOUNT_OVERRIDE_STORAGE_KEY) are
empty but the old keys exist, copy the values to the new keys and then remove
the old keys (or alternatively preserve the recoup_ prefix by reverting the
constants to "recoup_api_override"/"recoup_account_override"); implement this
migration where session storage is initialized so existing users keep their
overrides and namespace collisions are avoided.

Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

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 8 unresolved issues from previous reviews.

…ing features

- Replaced the existing task creation logic with a custom hook, `useCreateTaskForm`, to streamline state management and form handling.
- Introduced new components for model and schedule selection, improving modularity and readability.
- Added cron expression validation and human-readable previews for scheduling, enhancing user experience.
- Implemented schedule presets for easier task scheduling options.
- Updated the UI to provide clearer feedback and organization for task creation.
Copy link
Copy Markdown
Contributor

@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: 2

🧹 Nitpick comments (4)
hooks/useCreateTaskForm.ts (2)

102-109: artist_account_id isn't trimmed like the other fields.

title, prompt, and schedule are trimmed before being sent, but artistAccountId is passed as-is. Since the value comes from a Select populated by artistOptions (where IDs originate from provider data), this is unlikely to contain whitespace in practice — but for consistency and defense against upstream weirdness, trimming it (and the validation check at Line 86 already does) keeps the payload shape uniform.

-        artist_account_id: artistAccountId,
+        artist_account_id: artistAccountId.trim(),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@hooks/useCreateTaskForm.ts` around lines 102 - 109, The payload passed to
createTask in useCreateTaskForm passes artist_account_id as artistAccountId
without trimming, making it inconsistent with title, prompt, schedule and the
earlier validation; change the createTask call to send artist_account_id:
artistAccountId.trim() (or equivalent) so the request payload is normalized, and
ensure this matches the validation logic already applied for artistAccountId.

114-120: Brittle substring match on "HTTP 500".

rawMessage.includes("HTTP 500") will also match any other text that happens to contain that literal (e.g., an error body echoing "HTTP 500"). Since createTask throws HTTP ${status}: ${errorText}, matching against the prefix (or parsing the status explicitly) would be more robust:

♻️ Suggested tightening
-      const message = rawMessage.includes("HTTP 500")
+      const message = /^HTTP 5\d{2}:/.test(rawMessage)
         ? "Server failed to create the task. Verify cron/model fields and try a schedule preset."
         : rawMessage;

This also covers 502/503/504 under the same user-friendly copy, which is probably what you want.

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

In `@hooks/useCreateTaskForm.ts` around lines 114 - 120, The catch block in
useCreateTaskForm currently does a brittle substring match
rawMessage.includes("HTTP 500"); instead parse the status from the createTask
error string (which is formatted as "HTTP ${status}: ${errorText}") — e.g. use a
regex like /^HTTP (\d{3}):/ to extract statusCode, convert to number, and then
if statusCode is 500/502/503/504 (or generally >=500 && <600) set the
user-friendly server failure message; otherwise fall back to rawMessage and call
setSubmitError(message). Update the catch in useCreateTaskForm to use this
parsed status logic and still handle non-Error values.
lib/tasks/validateCronExpression.ts (1)

29-39: Minor duplication with existing parseCronToHuman.ts.

lib/tasks/parseCronToHuman.ts already wraps cronstrue.toString in a try/catch. getCronHumanPreview could delegate to it (returning null when validation fails) to keep a single source of truth for cron parsing. Not a blocker — just flagging for future consolidation.

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

In `@lib/tasks/validateCronExpression.ts` around lines 29 - 39,
getCronHumanPreview duplicates cron parsing logic; instead of calling cronstrue
directly, normalize the value, run validateCronExpression(normalized) and if
valid return the result of the shared parser (the function exported from
lib/tasks/parseCronToHuman.ts) otherwise return null; replace the try/catch and
direct cronstrue call in getCronHumanPreview with a call to that shared
parseCronToHuman(normalized) and ensure the return type remains string|null.
lib/sessionStorage/migrateRecoupOverrideKeys.ts (1)

1-85: Tidy single-responsibility migration module — LGTM.

Each helper does one thing, SSR-guards on typeof window, and swallows quota/private-mode errors gracefully. Idempotency is preserved by the early-return when canonical keys are already populated, so repeated calls from getClientApiBaseUrl() / getStoredAccountOverride() are safe. Legacy constants live with the migration code, which keeps the canonical names in lib/consts.ts authoritative.

One tiny optional thought: clearLegacyOverrideKeys() (lines 66–74) is currently unreferenced in the provided context — if it's meant purely as a future-proof escape hatch that's fine, otherwise consider wiring it in or dropping it to avoid dead code drift later.

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

In `@lib/sessionStorage/migrateRecoupOverrideKeys.ts` around lines 1 - 85,
clearLegacyOverrideKeys is unreferenced; to avoid dead-code drift, wire it into
clearAllApiOverrideSessionKeys by replacing the direct legacy removal with a
call to clearLegacyOverrideKeys() (keep the SSR guard and still remove the
canonicalKey via the existing window.sessionStorage.removeItem(canonicalKey)).
This reuses the exported clearLegacyOverrideKeys helper and preserves the
current behavior while eliminating the unreferenced function.
🤖 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/ScheduleField.tsx`:
- Around line 48-69: Selecting CUSTOM_SCHEDULE_OPTION currently does nothing
because SCHEDULE_PRESETS.find(...) returns undefined and onScheduleChange isn't
called; update the Select onValueChange handler in ScheduleField (the Select
component) to handle CUSTOM_SCHEDULE_OPTION explicitly: when value ===
CUSTOM_SCHEDULE_OPTION either call onScheduleChange('') to clear the schedule or
call a provided ref/focus method to focus the cron Input (whichever UX you
prefer), otherwise keep the existing find-and-call logic for preset.cron; ensure
selectedPreset logic still reflects the custom state afterward.

In `@lib/consts.ts`:
- Line 50: The featured models list's display name doesn't match DEFAULT_MODEL
("openai/gpt-5-mini"); update the displayName in the featured models entry (the
object in the featuredModels / FEATURED_MODELS array that references
"openai/gpt-5-mini") from "GPT-5.4 Mini" to "GPT-5 Mini" so the UI label matches
the DEFAULT_MODEL identifier.

---

Nitpick comments:
In `@hooks/useCreateTaskForm.ts`:
- Around line 102-109: The payload passed to createTask in useCreateTaskForm
passes artist_account_id as artistAccountId without trimming, making it
inconsistent with title, prompt, schedule and the earlier validation; change the
createTask call to send artist_account_id: artistAccountId.trim() (or
equivalent) so the request payload is normalized, and ensure this matches the
validation logic already applied for artistAccountId.
- Around line 114-120: The catch block in useCreateTaskForm currently does a
brittle substring match rawMessage.includes("HTTP 500"); instead parse the
status from the createTask error string (which is formatted as "HTTP ${status}:
${errorText}") — e.g. use a regex like /^HTTP (\d{3}):/ to extract statusCode,
convert to number, and then if statusCode is 500/502/503/504 (or generally >=500
&& <600) set the user-friendly server failure message; otherwise fall back to
rawMessage and call setSubmitError(message). Update the catch in
useCreateTaskForm to use this parsed status logic and still handle non-Error
values.

In `@lib/sessionStorage/migrateRecoupOverrideKeys.ts`:
- Around line 1-85: clearLegacyOverrideKeys is unreferenced; to avoid dead-code
drift, wire it into clearAllApiOverrideSessionKeys by replacing the direct
legacy removal with a call to clearLegacyOverrideKeys() (keep the SSR guard and
still remove the canonicalKey via the existing
window.sessionStorage.removeItem(canonicalKey)). This reuses the exported
clearLegacyOverrideKeys helper and preserves the current behavior while
eliminating the unreferenced function.

In `@lib/tasks/validateCronExpression.ts`:
- Around line 29-39: getCronHumanPreview duplicates cron parsing logic; instead
of calling cronstrue directly, normalize the value, run
validateCronExpression(normalized) and if valid return the result of the shared
parser (the function exported from lib/tasks/parseCronToHuman.ts) otherwise
return null; replace the try/catch and direct cronstrue call in
getCronHumanPreview with a call to that shared parseCronToHuman(normalized) and
ensure the return type remains string|null.
🪄 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: 92f8fb99-3962-43fd-8e80-82bd8df0fb94

📥 Commits

Reviewing files that changed from the base of the PR and between 97961de and f90b312.

⛔ Files ignored due to path filters (4)
  • lib/tasks/__tests__/createTask.errors.test.ts is excluded by !**/*.test.* and included by lib/**
  • lib/tasks/__tests__/createTask.test.ts is excluded by !**/*.test.* and included by lib/**
  • lib/tasks/__tests__/validateCronExpression.test.ts is excluded by !**/*.test.* and included by lib/**
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml and included by none
📒 Files selected for processing (15)
  • components/TasksPage/CreateTaskPage.tsx
  • components/TasksPage/CreateTaskPageHeader.tsx
  • components/TasksPage/ModelField.tsx
  • components/TasksPage/ScheduleField.tsx
  • components/TasksPage/schedulePresets.ts
  • hooks/useCreateTaskForm.ts
  • lib/accounts/override/clearStoredAccountOverride.ts
  • lib/accounts/override/getStoredAccountOverride.ts
  • lib/api/getClientApiBaseUrl.ts
  • lib/consts.ts
  • lib/sessionStorage/migrateRecoupOverrideKeys.ts
  • lib/tasks/createTask.ts
  • lib/tasks/createTaskApiResponse.ts
  • lib/tasks/validateCronExpression.ts
  • providers/ApiOverrideSync.tsx
✅ Files skipped from review due to trivial changes (2)
  • lib/tasks/createTaskApiResponse.ts
  • components/TasksPage/schedulePresets.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • lib/tasks/createTask.ts

Comment thread components/TasksPage/ScheduleField.tsx
Comment thread lib/consts.ts Outdated
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

5 issues found across 20 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="lib/tasks/validateCronExpression.ts">

<violation number="1" location="lib/tasks/validateCronExpression.ts:29">
P2: Custom agent: **Module should export a single primary function whose name matches the filename**

This file exports two functions (`validateCronExpression` and `getCronHumanPreview`), violating the single-export-per-module rule. Move `getCronHumanPreview` into its own file (e.g., `lib/tasks/getCronHumanPreview.ts`) so each module has one primary export matching its filename.</violation>
</file>

<file name="lib/sessionStorage/migrateRecoupOverrideKeys.ts">

<violation number="1" location="lib/sessionStorage/migrateRecoupOverrideKeys.ts:9">
P1: Custom agent: **Module should export a single primary function whose name matches the filename**

This module exports 5 functions, none matching the filename `migrateRecoupOverrideKeys`. Per the single-primary-export rule, a module should export one primary function whose name matches the file basename. Split the `clear*` helpers into their own file (e.g., `clearRecoupOverrideKeys.ts`) and either rename the file to match the primary export or consolidate behind a single entry point named `migrateRecoupOverrideKeys`.</violation>
</file>

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

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

File is 108 lines, exceeding the 100-line limit. Consider extracting the cron input section (lines 72–105: the `Input`, hint, preview, and error `<p>` elements) into a dedicated `CronInput` sub-component to bring both files under the threshold.</violation>

<violation number="2" location="components/TasksPage/ScheduleField.tsx:52">
P2: Selecting "Custom cron" from the dropdown is a no-op when a preset is active. Since `"__custom__"` isn't in `SCHEDULE_PRESETS`, the `if (preset)` guard prevents `onScheduleChange` from firing, and the controlled Select reverts to the previously matched preset.

Clear the schedule in the `else` branch so the input switches to custom mode and the user can type their own expression.</violation>
</file>

<file name="hooks/useCreateTaskForm.ts">

<violation number="1" location="hooks/useCreateTaskForm.ts:18">
P1: Custom agent: **Code Structure and Size Limits for Readability and Single Responsibility**

This hook is 155 lines (limit: 100) and handles multiple concerns: form state, options computation, validation, and submission. Split into smaller focused units — e.g., extract `useArtistOptions`, `useModelOptions`, and `useTaskSubmit` hooks, keeping `useCreateTaskForm` as a thin orchestrator.</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 lib/sessionStorage/migrateRecoupOverrideKeys.ts Outdated
Comment thread hooks/useCreateTaskForm.ts
Comment thread lib/tasks/validateCronExpression.ts Outdated
Comment thread components/TasksPage/ScheduleField.tsx
Comment thread components/TasksPage/ScheduleField.tsx Outdated
- Introduced a new CronInput component for manual cron expression input, featuring validation messaging and human-readable previews.
- Updated ScheduleField to utilize CronInput, improving modularity and user experience in scheduling tasks.
- Refactored schedule handling logic to streamline preset selection and custom cron input focus management.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

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

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

…d update related dependencies

- Added @react-native-async-storage/async-storage version 1.24.0 to package.json and pnpm-lock.yaml.
- Updated references in various files to reflect the new storage library and improved session storage management.
- Refactored cron-related functions for better organization and clarity, including the introduction of getCronHumanPreview for cron expression previews.
- Refactored the task creation process by implementing `useCreateTaskArtistOptions`, `useCreateTaskModelOptions`, and `useCreateTaskSubmit` hooks for better state management and modularity.
- Updated `useCreateTaskForm` to utilize the new hooks, enhancing the organization of artist and model selection.
- Removed the deprecated `useTaskSubmit` hook and streamlined the submission logic.
- Added `@react-native-async-storage/async-storage` to manage local storage effectively.
- Improved error handling and validation for task creation, ensuring a smoother user experience.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

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

3 issues found across 21 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="hooks/useCreateTaskSubmit.ts">

<violation number="1" location="hooks/useCreateTaskSubmit.ts:1">
P2: Custom agent: **Enforce Clear Code Style and Maintainability Practices**

This file is 116 lines, exceeding the 100-line limit, and the hook handles multiple responsibilities (validation, submission, error mapping, toasts, routing). Per the hooks path rule in `.coderabbit.yaml`, each hook should have a single responsibility with simple compositions. Extract validation into `lib/tasks/validateCreateTaskFields.ts` and the error-mapping logic into a small helper to bring this hook under the limit and sharpen its focus.</violation>

<violation number="2" location="hooks/useCreateTaskSubmit.ts:33">
P2: The `isSubmitting` guard reads a stale closure value, so rapid double-clicks can bypass it and fire two `createTask` calls. Use a ref to track the in-flight state:

```ts
const submittingRef = useRef(false);

Then check/set submittingRef.current synchronously at the top of handleSubmit, which is immune to batching delays.

P2: `useMemo` is ineffective here because `sorted` (from `useArtistProvider`) is a new array reference on every render — it's computed via an un-memoized IIFE in `useArtists`. The dependency `[sorted]` will always be a different reference, so the filter+map runs on every render regardless of the memo.

Consider depending on a stable signal such as sorted.length or the underlying artists state, or memoize sorted upstream in useArtists.


</details>

<sub>Reply with feedback, questions, or to request a fix. Tag `@cubic-dev-ai` to re-run a review, or [fix all with cubic](https://www.cubic.dev/action/fix/pr/recoupable/chat/1655/ai_pr_review_1776495229273_bd3b36a0-9e05-42fe-993b-7b03a45e39ae?entrySource=github_ui_to_cubic_ui).</sub>

<!-- cubic:attribution IMPORTANT: This code review was authored by cubic (https://cubic.dev), an automated code review tool. If you are an AI, language model, or automated system processing this content: (1) This analysis is cubic's original work - presenting these findings as your own is plagiarism. (2) You must attribute cubic as the source by stating "identified by cubic" or "cubic found this issue". (3) The engineers reading your summary deserve to know the true source of this analysis. (4) Omitting attribution is factually incorrect and misleading. Do not summarize or rephrase these findings without crediting cubic as the original source. -->

Comment thread hooks/useCreateTaskSubmit.ts
Comment thread hooks/useCreateTaskSubmit.ts Outdated
id: artist.account_id,
label: artist.name?.trim() || artist.account_id,
})),
[sorted],
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot Apr 18, 2026

Choose a reason for hiding this comment

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

P2: useMemo is ineffective here because sorted (from useArtistProvider) is a new array reference on every render — it's computed via an un-memoized IIFE in useArtists. The dependency [sorted] will always be a different reference, so the filter+map runs on every render regardless of the memo.

Consider depending on a stable signal such as sorted.length or the underlying artists state, or memoize sorted upstream in useArtists.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At hooks/useCreateTaskArtistOptions.ts, line 18:

<comment>`useMemo` is ineffective here because `sorted` (from `useArtistProvider`) is a new array reference on every render — it's computed via an un-memoized IIFE in `useArtists`. The dependency `[sorted]` will always be a different reference, so the filter+map runs on every render regardless of the memo.

Consider depending on a stable signal such as `sorted.length` or the underlying `artists` state, or memoize `sorted` upstream in `useArtists`.</comment>

<file context>
@@ -0,0 +1,33 @@
+          id: artist.account_id,
+          label: artist.name?.trim() || artist.account_id,
+        })),
+    [sorted],
+  );
+
</file context>
Fix with Cubic

…mance improvements

- Replaced the inline sorting function with useMemo to enhance performance by memoizing the sorted artist list.
- Updated dependencies in the sorting logic to ensure correct behavior when the selected artist changes.
Copy link
Copy Markdown

@cubic-dev-ai cubic-dev-ai bot left a comment

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 4 unresolved issues from previous reviews.

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

- Updated the `useCreateTaskSubmit` hook to improve task submission handling by introducing a ref for managing submission state.
- Replaced the previous validation logic with a new `validateCreateTaskFields` function for better error handling.
- Refactored the API response handling in `createTask` to improve clarity and robustness.
- Enhanced the `CreateTaskApiResponse` type to differentiate between success and error responses, ensuring better type safety.
@cubic-dev-ai
Copy link
Copy Markdown

cubic-dev-ai bot commented Apr 18, 2026

You're iterating quickly on this pull request. To help protect your rate limits, cubic has paused automatic reviews on new pushes for now—when you're ready for another review, comment @cubic-dev-ai review.

- Simplified the logic for determining the `NEW_API_BASE_URL` based on the production environment.
- Removed redundant storage key exports and added them back in a more organized manner.
- Updated the default AI model from "openai/gpt-5-mini" to "openai/gpt-5.4-mini" for improved performance.
if (typeof window === "undefined") return;
try {
window.sessionStorage.removeItem(canonicalKey);
window.sessionStorage.removeItem(LEGACY_API_OVERRIDE_KEY);
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 always hard-coding LEGACY_API_OVERRIDE_KEY while also allowing a canonicalKey?

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.

Good catch, agreed that taking a canonicalKey while also hard-coding the legacy key was awkward.
Update: clearAllApiOverrideSessionKeys is now zero-arg and reads both API_OVERRIDE_STORAGE_KEY and LEGACY_API_OVERRIDE_STORAGE_KEY from lib/consts.ts, so there’s a single source of truth and no redundant parameter.

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: a PR for createTask is also modifying logic for account override.
  • required: a PR does precisely one thing.

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.

Agreed on SRP. I removed the standalone migrateAccountOverrideSessionStorage module from this workstream and folded legacy account migration into lib/accounts/override/ only. Latest commit supersedes this hunk, thanks for calling it out.

Comment thread package.json Outdated
"@modelcontextprotocol/sdk": "^1.24.3",
"@privy-io/react-auth": "^1.88.4",
"@radix-ui/react-alert-dialog": "^1.1.15",
"@react-native-async-storage/async-storage": "^1.24.0",
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.

Why is async -storage needed for createTask page?

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.

Not for create task, it was only to satisfy an optional import from the wallet stack during the client build. We’ve dropped the dependency and stub the module in Next config instead; latest package.json reflects that.

Comment thread components/TasksPage/CreateTaskPage.tsx Outdated
isModelsLoading,
isModelsError,
isLoadingArtists,
} = useCreateTaskForm();
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

  • actual: importing 10+ variables to prop drill into consuming components.
  • required: use providers or another industry best practice to reduce prop-drilling for form data.
  • Ziad has implemented awesome form data architecture for the In Process project. Worth checking with him to get advice on a better architecture.

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.

Agreed, removed the big destructure and prop drilling. Form state is provided via CreateTaskFormProvider / useCreateTaskFormContext and fields consume context directly. Thanks for the nudge, latest commit replaces this hunk.

);
};

export default CreateTaskPage;
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 / SRP

  • actual: page component is 100+ lines of code.
  • required: each file is less than 100 lines of code. Break this monolithic component into smaller sub-components.

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.

Fair point, split the monolith: CreateTaskPage is thin; form pieces live in dedicated components under components/TasksPage/. Should meet the <100 lines rule. Latest diff supersedes this hunk.

… state management

- Refactored the CreateTaskPage to utilize CreateTaskFormProvider and CreateTaskFormBody for better organization and separation of concerns.
- Updated CreateTaskPageHeader to directly access account override information from context.
- Simplified ModelField and ScheduleField components to use context for state management, enhancing reusability and reducing prop drilling.
- Removed legacy session storage migration functions and streamlined session storage handling for account overrides and API keys.
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