Skip to content

feat(onboarding): first-task weekly report actually emails — LA EQUIS template + Kimi K3 (chat#1867) - #1877

Merged
sweetmantech merged 5 commits into
testfrom
feat/onboarding-first-task-email
Jul 21, 2026
Merged

feat(onboarding): first-task weekly report actually emails — LA EQUIS template + Kimi K3 (chat#1867)#1877
sweetmantech merged 5 commits into
testfrom
feat/onboarding-first-task-email

Conversation

@sweetmantech

@sweetmantech sweetmantech commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

What & why

Fast-follow to #1871. The onboarding first task generated a report but never emailed it — email is a prompt-driven send_email tool call, and buildFirstTaskPrompt said "write the report" with no recipient + no send instruction. Proven 2026-07-21 by firing the created task: the agent ran and persisted a report (chat_messages assistant row under chat bfcb927c…, ~14.3 KB), but email_send_log stayed empty. Emailing the weekly report is the whole point of the task.

Two root causes, both fixed here:

  1. The prompt never instructed a send. Generalized buildFirstTaskPrompt from the proven LA EQUIS weekly report (scheduled_action 39fb5f68…, running week over week to info@shiftedculture.com): resolve the artist's Spotify id from its connected profile → capture this week's Spotify play counts → compute real week-over-week per-track deltas (diff latest vs most-recent-prior-day capture) → build a fully hex-specced inline-CSS HTML email → send via the recoup-platform-email-helper skill → confirm the Resend id. The sandbox no-python and anti-fabrication guardrails are carried over verbatim — they're what make the send reliable. Threads the account email (recipientEmail) + artistAccountId through both callers so the pre-run preview and the Monday scheduled task both deliver to the account holder.

  2. The model gave up before composing the email. Both surfaces ran on DEFAULT_MODEL (openai/gpt-5.4-mini) — the exact model the LA EQUIS notes flag as "completes the data calls but gives up before composing the long HTML email." Added REPORT_MODEL = moonshotai/kimi-k3 and pointed both the pre-run (useFirstTaskReport) and the scheduled task (useConfirmFirstTask) at it.

Per the discussion, one prompt drives both the on-screen pre-run and the scheduled task (byte-for-byte parity preserved) — so the user also gets a real email during onboarding.

Changes

  • lib/onboarding/buildFirstTaskPrompt.ts — generalized LA EQUIS template; input now { artistName, artistAccountId, recipientEmail, catalogName? }.
  • lib/onboarding/buildFirstTaskParams.ts — thread recipientEmail through.
  • hooks/useConfirmFirstTask.ts — source the account email (Privy user.email), EMAIL_REQUIRED guard, REPORT_MODEL.
  • hooks/useFirstTaskReport.ts — pre-run uses REPORT_MODEL.
  • components/Onboarding/FirstTaskStep.tsx — source email + artistAccountId; gate the pre-run until both are ready.
  • lib/consts.tsREPORT_MODEL = "moonshotai/kimi-k3" (verified in the gateway catalog).

Tests (TDD, red→green)

  • buildFirstTaskPrompt.test.ts — RED first (6 failing on the new assertions), then GREEN: embeds recipient email, embeds artist_account_id, instructs the email send (recoup-platform-email-helper), carries the no-python guardrail, forbids fabrication, catalog-by-name, pure. 9/9.
  • buildFirstTaskParams.test.ts — threads the recipient email into the scheduled prompt. 3/3.
  • Full lib/onboarding suite 62/62; tsc --noEmit clean on all touched files (the 10 repo-wide tsc errors are pre-existing @testing-library/react / extractSendEmailResults drift, none touch this change); prettier clean.

Verification (pending)

Preview verification on the sha-checked deployment: run onboarding to the first-task step → confirm the pre-run streams and an email lands in the account inbox, and the scheduled scheduled_actions row emails on its next run (check email_send_log for a sent row + Resend id). Base test per the #1867 branch decision.

Tracking: chat#1867 (🔴 first-task email-prompt gap).


Summary by cubic

Fixes the onboarding first task so the weekly report is actually emailed. Uses the LA EQUIS template and sets DEFAULT_MODEL to moonshotai/kimi-k3 for reliable compose-and-send; refreshes the featured model list.

  • Bug Fixes

    • Prompt includes recipientEmail, artist_account_id, and a recoup-platform-email-helper send; threaded through buildFirstTaskPrompt/params and both hooks so pre-run and the scheduled task match.
    • Clear “add an email” alert for Privy logins without email; useConfirmFirstTask throws EMAIL_REQUIRED with a targeted toast.
    • Keeps no-python and anti-fabrication guardrails; adds single-argument quoting to prevent shell injection.
  • Refactors

    • Collapsed REPORT_MODEL into DEFAULT_MODEL; app-wide default is now moonshotai/kimi-k3. Featured picker shows “Kimi K3” with an updated tooltip.
    • Refreshed featured models to latest: openai/gpt-5.5, anthropic/claude-opus-4.8, anthropic/claude-sonnet-5, google/gemini-3.5-flash-lite, google/gemini-3.1-pro-preview, xai/grok-4.5.
    • Gates the preview until artist id and account email are available.
    • Pre-run preview now reuses chat Message/MessageParts to render tool calls/results; decoupled from VercelChatProvider via optional status/reload and an exported context.

Written for commit 637ef1a. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Onboarding now requires an email address before scheduling the first task.
    • Weekly reports are sent to the authenticated account email.
    • First-task reports now include artist-specific streaming performance data and comprehensive HTML email formatting.
  • Bug Fixes
    • Added clearer messaging when an email address is missing.
    • Reports now explicitly identify unavailable data instead of estimating figures.
  • Updates
    • Kimi K3 is now the default model and appears as a featured option.

…template + Kimi K3)

The first-task prompt generated a report but never emailed it (email is a
prompt-driven send_email call, and the prompt had no recipient or send
instruction) — proven by firing the created task: the agent ran and persisted
a report, but email_send_log stayed empty.

Generalize buildFirstTaskPrompt from the proven LA EQUIS weekly report
(scheduled_action 39fb5f68, running week over week): resolve the artist's
Spotify id from its connected profile -> capture this week's play counts ->
compute real week-over-week per-track deltas -> build a fully hex-specced
inline-CSS HTML email -> send via the recoup-platform-email-helper skill ->
confirm the Resend id. Carries the sandbox no-python and anti-fabrication
guardrails verbatim (what makes the send reliable). Thread the account email
(recipientEmail) + artistAccountId through both callers so the pre-run preview
and the Monday scheduled task both send to the account holder.

Bump both surfaces from DEFAULT_MODEL (openai/gpt-5.4-mini — completes the data
calls but gives up before composing the long HTML email, the LA EQUIS lesson)
to a new REPORT_MODEL (moonshotai/kimi-k3), which reliably composes and sends.

chat#1867 (first-task email-prompt gap).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@cursor

cursor Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@vercel

vercel Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
chat Ready Ready Preview Jul 21, 2026 11:15pm

Request Review

@coderabbitai

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@sweetmantech, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 23 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 106ce5fd-8456-413a-92c5-76ddfc25cc00

📥 Commits

Reviewing files that changed from the base of the PR and between cd8e017 and 637ef1a.

📒 Files selected for processing (7)
  • components/Onboarding/FirstTaskReportRun.tsx
  • components/VercelChat/MessageParts.tsx
  • components/VercelChat/message.tsx
  • hooks/useFirstTaskReport.ts
  • lib/ai/featuredModels.ts
  • lib/consts.ts
  • providers/VercelChatProvider.tsx
📝 Walkthrough

Walkthrough

The onboarding flow now derives the authenticated recipient email and artist account ID, requires both before report preparation, and passes them into expanded weekly streaming report prompts. Scheduled tasks validate email availability, while Kimi K3 becomes the default model and featured model entry.

Changes

Weekly Report Onboarding

Layer / File(s) Summary
Report model and prompt contract
lib/consts.ts, lib/ai/featuredModels.ts, lib/onboarding/buildFirstTaskPrompt.ts
The default model changes to Kimi K3, and the prompt contract now requires artist and recipient details for comprehensive HTML streaming reports.
Task parameter propagation
lib/onboarding/buildFirstTaskParams.ts
First-task parameters require recipientEmail and forward it with artist details into prompt construction.
Onboarding report and scheduling flow
components/Onboarding/FirstTaskStep.tsx, hooks/useConfirmFirstTask.ts
The onboarding preview gates on artist and email readiness, displays a missing-email alert, and validates the recipient before scheduling tasks.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

Poem

Kimi hums a weekly tune,
Artist stats beneath the moon.
Email finds its destined flight,
HTML tables dressed just right.
No guessed streams—truth takes the stage.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Solid & Clean Code ✅ Passed PASS: The changes keep shared prompt/param logic centralized, use focused hooks/components, and avoid clear duplication or deep nesting.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/onboarding-first-task-email

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.

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 08e3d95f43

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread lib/consts.ts Outdated
// send them — the default fast model completes the data calls but gives up before
// finishing the email (proven on the LA EQUIS weekly report). Kimi K3 handles the
// long-form compose-and-send reliably.
export const REPORT_MODEL = "moonshotai/kimi-k3";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Don't force paid Kimi for onboarding reports

Vercel lists Kimi K3 as billed at API rates, and the model picker blocks non-free models for non-subscribed accounts (components/ModelSelect/ModelSelect.tsx:31-38). Because both useFirstTaskReport and useConfirmFirstTask now send REPORT_MODEL unconditionally, any non-Pro user reaching this onboarding step will either be rejected by the API/paywall or create a Monday task that runs a paid model they cannot select manually; gate this path on isSubscribed/credits or use a free fallback.

Useful? React with 👍 / 👎.


Sending (mandatory - the run is not complete until this succeeds):
- Write the finished HTML to a file (report.html) using the write tool or node - never inline it into a curl command or hand-build the JSON request body.
- Load the recoup-platform-email-helper skill (via the skill tool) and send the email exactly the way it documents, passing --subject "${artistName} - Weekly Streaming Report", --html-file report.html, and --to ${recipientEmail}.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Pass email details through a structured tool

Roster artist names are user-entered (createRosterArtist posts the raw name), so an artist named with ", ;, or $() reaches this prompt. This line tells the agent to invoke a CLI helper with --subject "${artistName} ..."; if the agent follows that in the sandbox shell, the generated command can fail or execute the injected shell fragment. Use the structured send_email MCP tool, or at least shell-escape each arg, instead of embedding user-controlled text in CLI flags.

Useful? React with 👍 / 👎.

@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

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@components/Onboarding/FirstTaskStep.tsx`:
- Around line 21-26: Update components/Onboarding/FirstTaskStep.tsx lines 21-26
and 54-58 to destructure ready from usePrivy and show a role="alert"
email-linking message when ready, artistName, and artistAccountId are present
but recipientEmail is missing; retain the loading state otherwise. Update
hooks/useConfirmFirstTask.ts lines 45-60 so the mutation onError detects
EMAIL_REQUIRED and displays an email-linking message, while preserving the
existing retry message for other errors.
🪄 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: 64b28eb9-7374-40f7-b8cf-87f8ac02db6e

📥 Commits

Reviewing files that changed from the base of the PR and between d41dec3 and 08e3d95.

⛔ Files ignored due to path filters (2)
  • lib/onboarding/__tests__/buildFirstTaskParams.test.ts is excluded by !**/*.test.* and included by lib/**
  • lib/onboarding/__tests__/buildFirstTaskPrompt.test.ts is excluded by !**/*.test.* and included by lib/**
📒 Files selected for processing (6)
  • components/Onboarding/FirstTaskStep.tsx
  • hooks/useConfirmFirstTask.ts
  • hooks/useFirstTaskReport.ts
  • lib/consts.ts
  • lib/onboarding/buildFirstTaskParams.ts
  • lib/onboarding/buildFirstTaskPrompt.ts

Comment thread components/Onboarding/FirstTaskStep.tsx Outdated
Comment thread lib/consts.ts Outdated
// send them — the default fast model completes the data calls but gives up before
// finishing the email (proven on the LA EQUIS weekly report). Kimi K3 handles the
// long-form compose-and-send reliably.
export const REPORT_MODEL = "moonshotai/kimi-k3";

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.

KISS - instead of making a new REPORT_MODEL, update the existing DEFAULT_MODEL with the new model moonshotai/kimi-k3.

@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 8 files

Confidence score: 3/5

  • In lib/consts.ts, onboarding paths useFirstTaskReport and useConfirmFirstTask appear to unconditionally select paid Kimi K3, which can create immediate token spend during first-run flows and surprise users/operators if usage scales — gate this model behind config/entitlement checks or add a lower-cost/default fallback before merging.
  • In lib/onboarding/buildFirstTaskPrompt.ts, buildFirstTaskPrompt now combines multiple responsibilities in one large template, which raises maintenance risk and makes future prompt changes easier to break silently — split it into smaller prompt-builder sections/helpers to align with SRP and reduce regression risk.
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/onboarding/buildFirstTaskPrompt.ts">

<violation number="1" location="lib/onboarding/buildFirstTaskPrompt.ts:32">
P2: This function now bundles data-fetching steps, HTML/design spec, and send instructions into one large template literal, which exceeds the repo's function-length/SRP conventions for lib/**/*.ts. Consider splitting the prompt into composed sections (e.g. buildSandboxRules(), buildDesignSpec(), buildSendInstructions()) that are concatenated, so each piece is independently testable and readable.</violation>
</file>

<file name="lib/consts.ts">

<violation number="1" location="lib/consts.ts:50">
P2: Kimi K3 is a paid model ($3/$15 per million input/output tokens, no free tier on Vercel AI Gateway). Both `useFirstTaskReport` and `useConfirmFirstTask` send this model unconditionally during onboarding. If the app enforces a model paywall for non-subscribed accounts (as referenced in `ModelSelect`), free-tier users reaching this onboarding step will either have requests rejected or create a scheduled task that runs a paid model they cannot otherwise select. Consider gating on subscription status or falling back to a free-eligible model for non-Pro users.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread components/Onboarding/FirstTaskStep.tsx
Comment thread hooks/useConfirmFirstTask.ts
].join("\n");
? `their "${catalogName}" catalog`
: "their catalog";
return `Generate a weekly streaming performance report for the artist ${artistName}, covering ${catalogClause}, then email it as a styled HTML email to ${recipientEmail}.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: This function now bundles data-fetching steps, HTML/design spec, and send instructions into one large template literal, which exceeds the repo's function-length/SRP conventions for lib/**/*.ts. Consider splitting the prompt into composed sections (e.g. buildSandboxRules(), buildDesignSpec(), buildSendInstructions()) that are concatenated, so each piece is independently testable and readable.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/onboarding/buildFirstTaskPrompt.ts, line 32:

<comment>This function now bundles data-fetching steps, HTML/design spec, and send instructions into one large template literal, which exceeds the repo's function-length/SRP conventions for lib/**/*.ts. Consider splitting the prompt into composed sections (e.g. buildSandboxRules(), buildDesignSpec(), buildSendInstructions()) that are concatenated, so each piece is independently testable and readable.</comment>

<file context>
@@ -1,28 +1,79 @@
-  ].join("\n");
+    ? `their "${catalogName}" catalog`
+    : "their catalog";
+  return `Generate a weekly streaming performance report for the artist ${artistName}, covering ${catalogClause}, then email it as a styled HTML email to ${recipientEmail}.
+
+CONTEXT VALUES: artist_name = ${artistName} ; artist_account_id = ${artistAccountId} ; recipient email = ${recipientEmail}. Use the artist_account_id directly — do not guess or search for a different artist.
</file context>

Comment thread lib/onboarding/buildFirstTaskPrompt.ts
Comment thread lib/consts.ts Outdated
// send them — the default fast model completes the data calls but gives up before
// finishing the email (proven on the LA EQUIS weekly report). Kimi K3 handles the
// long-form compose-and-send reliably.
export const REPORT_MODEL = "moonshotai/kimi-k3";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Kimi K3 is a paid model ($3/$15 per million input/output tokens, no free tier on Vercel AI Gateway). Both useFirstTaskReport and useConfirmFirstTask send this model unconditionally during onboarding. If the app enforces a model paywall for non-subscribed accounts (as referenced in ModelSelect), free-tier users reaching this onboarding step will either have requests rejected or create a scheduled task that runs a paid model they cannot otherwise select. Consider gating on subscription status or falling back to a free-eligible model for non-Pro users.

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

<comment>Kimi K3 is a paid model ($3/$15 per million input/output tokens, no free tier on Vercel AI Gateway). Both `useFirstTaskReport` and `useConfirmFirstTask` send this model unconditionally during onboarding. If the app enforces a model paywall for non-subscribed accounts (as referenced in `ModelSelect`), free-tier users reaching this onboarding step will either have requests rejected or create a scheduled task that runs a paid model they cannot otherwise select. Consider gating on subscription status or falling back to a free-eligible model for non-Pro users.</comment>

<file context>
@@ -43,6 +43,11 @@ export const SOCIAL_DEFAULT_PLATFORMS = [
+// send them — the default fast model completes the data calls but gives up before
+// finishing the email (proven on the LA EQUIS weekly report). Kimi K3 handles the
+// long-form compose-and-send reliably.
+export const REPORT_MODEL = "moonshotai/kimi-k3";
 export const FAST_MODEL = "openai/gpt-5-nano";
 // Fastest model for lightweight tasks e.g generating chat titles etc.
</file context>

Comment thread lib/onboarding/buildFirstTaskPrompt.ts
…ess + shell paths

Address review feedback on #1877:

- Sweets (KISS): collapse the new REPORT_MODEL constant into DEFAULT_MODEL —
  set DEFAULT_MODEL = moonshotai/kimi-k3 app-wide (the fast gpt-5.4-mini default
  gave up before composing the long HTML report email). Revert both onboarding
  hooks to DEFAULT_MODEL; relabel the featured picker entry (id: DEFAULT_MODEL)
  from "GPT-5.4 Mini" to "Kimi K3" so it isn't mislabeled.
- CodeRabbit (email-less login): a wallet/phone/social-only Privy account has no
  email — FirstTaskStep now shows a distinct "add an email" alert (not an
  infinite "Preparing…"), and useConfirmFirstTask's onError distinguishes
  EMAIL_REQUIRED from a generic retry.
- Codex (shell-safety): artist/track/album names are user-entered; add a prompt
  guardrail to pass them as single quoted args so a name with quotes/;/$() can't
  alter the email-helper command.

Codex "non-Pro users get rejected" P2 is a false positive: the prior default
(gpt-5.4-mini) is also non-free by isFreeModel yet ships as the default, so the
API does not hard-block non-free default models.

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

@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 6 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/onboarding/buildFirstTaskPrompt.ts">

<violation number="1" location="lib/onboarding/buildFirstTaskPrompt.ts:75">
P2: This mitigation for command/shell injection lives entirely in the prompt text sent to the LLM agent, so it depends on the agent faithfully complying rather than on real argument escaping. Consider having the recoup-platform-email-helper skill (or a pre-processing step) actually sanitize/escape artist, track, and album names before they reach any shell command, instead of relying solely on an instruction the agent could miss or misapply.</violation>
</file>

<file name="lib/consts.ts">

<violation number="1" location="lib/consts.ts:49">
P2: Consolidating REPORT_MODEL into DEFAULT_MODEL (per KISS feedback) now applies the Kimi K3 model — chosen specifically because it reliably finishes long HTML report emails — to every consumer of DEFAULT_MODEL app-wide (chat default, task creation, task edit dialog, generateText fallback). This is a broader latency/cost change than the original report-emailing fix and doesn't appear covered by the PR's stated test evidence (onboarding suite only), so regular chat/task flows could see slower responses or higher token costs as a side effect.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Sending (mandatory - the run is not complete until this succeeds):
- Write the finished HTML to a file (report.html) using the write tool or node - never inline it into a curl command or hand-build the JSON request body.
- Load the recoup-platform-email-helper skill (via the skill tool) and send the email exactly the way it documents, passing --subject "${artistName} - Weekly Streaming Report", --html-file report.html, and --to ${recipientEmail}.
- Security: the artist, track, and album names are untrusted user-entered text. When passing any of them as a command argument (e.g. the email subject), pass it as a single properly-quoted argument — never let a name that contains quotes, ;, backticks, or $() change the structure of the command you run.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: This mitigation for command/shell injection lives entirely in the prompt text sent to the LLM agent, so it depends on the agent faithfully complying rather than on real argument escaping. Consider having the recoup-platform-email-helper skill (or a pre-processing step) actually sanitize/escape artist, track, and album names before they reach any shell command, instead of relying solely on an instruction the agent could miss or misapply.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/onboarding/buildFirstTaskPrompt.ts, line 75:

<comment>This mitigation for command/shell injection lives entirely in the prompt text sent to the LLM agent, so it depends on the agent faithfully complying rather than on real argument escaping. Consider having the recoup-platform-email-helper skill (or a pre-processing step) actually sanitize/escape artist, track, and album names before they reach any shell command, instead of relying solely on an instruction the agent could miss or misapply.</comment>

<file context>
@@ -72,6 +72,7 @@ HTML email design spec (send the email with an HTML body; build it exactly to th
 Sending (mandatory - the run is not complete until this succeeds):
 - Write the finished HTML to a file (report.html) using the write tool or node - never inline it into a curl command or hand-build the JSON request body.
 - Load the recoup-platform-email-helper skill (via the skill tool) and send the email exactly the way it documents, passing --subject "${artistName} - Weekly Streaming Report", --html-file report.html, and --to ${recipientEmail}.
+- Security: the artist, track, and album names are untrusted user-entered text. When passing any of them as a command argument (e.g. the email subject), pass it as a single properly-quoted argument — never let a name that contains quotes, ;, backticks, or $() change the structure of the command you run.
 - Before sending, verify the email body is complete: the Track & Album Momentum table must contain one populated row per focus track with a real delta or a "first measurement" label. Never send an email with an empty or placeholder momentum table - an empty table means the Part 2 data steps were skipped or failed; go back and complete them first.
 - The helper prints a Resend id on success and exits non-zero on any failure. Confirm the id was printed; if the send fails, fix the issue and retry - do not end the run without either a successful send or an explicit description of the send error.
</file context>

Comment thread lib/consts.ts
// fast default (openai/gpt-5.4-mini) completed the data calls but gave up before
// finishing the email (proven on the LA EQUIS weekly report). Used app-wide as
// the default model for chat, task creation, and text generation.
export const DEFAULT_MODEL = "moonshotai/kimi-k3";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: Consolidating REPORT_MODEL into DEFAULT_MODEL (per KISS feedback) now applies the Kimi K3 model — chosen specifically because it reliably finishes long HTML report emails — to every consumer of DEFAULT_MODEL app-wide (chat default, task creation, task edit dialog, generateText fallback). This is a broader latency/cost change than the original report-emailing fix and doesn't appear covered by the PR's stated test evidence (onboarding suite only), so regular chat/task flows could see slower responses or higher token costs as a side effect.

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

<comment>Consolidating REPORT_MODEL into DEFAULT_MODEL (per KISS feedback) now applies the Kimi K3 model — chosen specifically because it reliably finishes long HTML report emails — to every consumer of DEFAULT_MODEL app-wide (chat default, task creation, task edit dialog, generateText fallback). This is a broader latency/cost change than the original report-emailing fix and doesn't appear covered by the PR's stated test evidence (onboarding suite only), so regular chat/task flows could see slower responses or higher token costs as a side effect.</comment>

<file context>
@@ -42,12 +42,11 @@ export const SOCIAL_DEFAULT_PLATFORMS = [
+// fast default (openai/gpt-5.4-mini) completed the data calls but gave up before
+// finishing the email (proven on the LA EQUIS weekly report). Used app-wide as
+// the default model for chat, task creation, and text generation.
+export const DEFAULT_MODEL = "moonshotai/kimi-k3";
 export const FAST_MODEL = "openai/gpt-5-nano";
 // Fastest model for lightweight tasks e.g generating chat titles etc.
</file context>

Comment thread lib/consts.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.

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

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

Re-trigger cubic

Sweets: while touching the featured list, bring every entry to its series'
latest (verified present in the AI Gateway catalog):
- GPT-5.2 -> GPT-5.5
- Claude Opus 4.5 -> 4.8
- Claude Sonnet 4.5 -> Sonnet 5
- Gemini 2.5 Flash Lite -> Gemini 3.5 Flash Lite
- Gemini 3 Pro -> Gemini 3.1 Pro
- Grok 4 -> Grok 4.5
(Kimi K3 already latest.)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@sweetmantech

Copy link
Copy Markdown
Collaborator Author

Preview verification

Verified on the sha-checked preview https://chat-lvtipoo4e-recoup.vercel.app (deployment 5546858975, built from cd8e0171 — the review-fix commit with the Kimi K3 default + no-email UX + shell guardrail). Logged in as an onboarded account whose only task is disabled (so the task checkpoint is unmet) and opened the first-task step at /onboarding/first-task.

What the pre-run did (LA EQUIS template on Kimi K3)

The pre-run fired the new prompt through the chat pipeline and streamed the agent following the exact new structure:

  • Part 0 — resolve Spotify id: tried GET /api/artists/{id}/socials, and when the sandbox token didn't authorize it, fell back to /api/spotify/search?q=Del Water Gap&type=artist and found 0xPoVNPnxIIUS1vrxAYV00. (Both paths in the prompt work.)
  • Part 1 — albums + measurement: fetched the artist's albums, then tried POST /api/research/measurement-jobs.

It then hit "authentication is failing for POST" on the measurement. This is the known preview-sandbox-only auth gap — the interactive pre-run's sandbox receives a Privy JWT rather than the ephemeral API key (tracked as the 🟡 preview-only item in chat#1867). It is not a #1877 defect: the scheduled/fired run authenticates in the prod sandbox (chat#1871's fired run completed in 41s). The confirm question appeared as designed.

Confirm → created the real scheduled task (the core proof)

Clicking "Yes, every Monday" created a scheduled_action through this build. Inspecting it in the DB is a clean before/after vs the old #1871 task on the same account:

Field New (e02a8e1c, this PR) Old (38de1728, #1871)
model moonshotai/kimi-k3 openai/gpt-5.4-mini
enabled / schedule true / 0 9 * * 1 false / 0 9 * * 1
trigger_schedule_id sched_uv7ohgig7m9uehn9ot3z6 ✅ (minted) sched_9c5…
prompt embeds recipient email true false
prompt has recoup-platform-email-helper true false
LA EQUIS structure (capture → WoW deltas → HTML email) true false
no-python + anti-fabrication + shell guardrails all true all false
prompt length 8425 579

Also confirmed: the homepage model picker now defaults to "Kimi K3" (the DEFAULT_MODEL change + featured relabel took effect — not mislabeled as "GPT-5.4 Mini"), and the console was clean (0 errors) across the flow. "Weekly report scheduled — Next run: At 09:00 AM, only on Monday."

Still to confirm

Live email delivery — firing e02a8e1c runs the agent in the prod sandbox (authenticates, unlike the preview pre-run) and should land a real HTML report in the inbox + a sent row in email_send_log. Firing sends a real email + spends credits/Kimi tokens, so I've held off pending a go-ahead (chat#1871's fired run already proved the scheduled sandbox authenticates and completes).

Review triage

  • @sweetmantech (KISS, consts.ts) — done: dropped the separate REPORT_MODEL; DEFAULT_MODEL is now moonshotai/kimi-k3 app-wide, both onboarding hooks use DEFAULT_MODEL, and the featured picker entry is relabeled to "Kimi K3". (Follow-on commit also refreshed the rest of the featured list to each series' latter.)
  • Codex P2 — "Don't force paid Kimi for onboarding" (consts.ts)false positive on the breakage claim: the prior default openai/gpt-5.4-mini is also non-free by isFreeModel (input $0.75/M > $0.5 threshold) yet ships as the default, so the API does not hard-block non-free default models. The ModelSelect gate the bot cited only fires on manual selection, not the model passed in the request body. Making Kimi K3 the default was a deliberate product call (it reliably composes/sends the long HTML report; gpt-5.4-mini gave up). Net effect is cost, not rejection.
  • Codex P2 — shell-safety in the email subject (buildFirstTaskPrompt.ts)fixed: added a guardrail instructing the agent to treat artist/track/album names as untrusted text and pass them as single quoted arguments so a name containing ", ;, backticks, or $() can't alter the command. Verified present in the created row (prompt_shell_guardrail = true).
  • CodeRabbit Major — no UX path for an email-less login (FirstTaskStep.tsx, useConfirmFirstTask.ts)fixed: FirstTaskStep now shows a distinct role="alert" "add an email address" message (instead of an infinite "Preparing…") when Privy is ready and the artist is known but no email is linked; useConfirmFirstTask's onError distinguishes EMAIL_REQUIRED from the generic retry toast.

Tests: TDD red→green — buildFirstTaskPrompt 9/9, buildFirstTaskParams 3/3, full lib/onboarding suite 62/62; tsc --noEmit clean on all touched files; prettier clean.

@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="lib/ai/featuredModels.ts">

<violation number="1" location="lib/ai/featuredModels.ts:73">
P3: Tooltip text for the Gemini 3.1 Pro entry wasn't updated to match the new display name. The tooltip still says "Google's newest Gemini 3 Pro preview model" while the displayName is now "Gemini 3.1 Pro". Consider updating the tooltip to reference "Gemini 3.1 Pro" to stay consistent.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

Re-trigger cubic

Comment thread lib/ai/featuredModels.ts
id: "google/gemini-3-pro-preview",
displayName: "Gemini 3 Pro",
id: "google/gemini-3.1-pro-preview",
displayName: "Gemini 3.1 Pro",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: Tooltip text for the Gemini 3.1 Pro entry wasn't updated to match the new display name. The tooltip still says "Google's newest Gemini 3 Pro preview model" while the displayName is now "Gemini 3.1 Pro". Consider updating the tooltip to reference "Gemini 3.1 Pro" to stay consistent.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At lib/ai/featuredModels.ts, line 73:

<comment>Tooltip text for the Gemini 3.1 Pro entry wasn't updated to match the new display name. The tooltip still says "Google's newest Gemini 3 Pro preview model" while the displayName is now "Gemini 3.1 Pro". Consider updating the tooltip to reference "Gemini 3.1 Pro" to stay consistent.</comment>

<file context>
@@ -45,39 +45,39 @@ export const FEATURED_MODELS: FeaturedModelConfig[] = [
-    id: "google/gemini-3-pro-preview",
-    displayName: "Gemini 3 Pro",
+    id: "google/gemini-3.1-pro-preview",
+    displayName: "Gemini 3.1 Pro",
     isPro: true,
     description: "Google's latest model",
</file context>

…-run (DRY)

The onboarding pre-run rendered only the last assistant message's TEXT
(getReportTextFromMessages -> <Response>), dropping tool-call and reasoning
components — a downgraded, diverging copy of the chat UI. #1877's tool-heavy
email workflow made the gap obvious (raw narration, no tool components).

Reuse the normal chat's <Message>/<MessageParts> so tool calls/results get their
real components. MessageParts was coupled to useVercelChatContext (status +
reload); decouple it: both are now optional props with a context fallback, so
the normal chat is behaviorally unchanged (props omitted -> context used) while
the pre-run supplies them without mounting a provider (VercelChatProvider owns
its own useChat instance, so wrapping the pre-run would double the chat).

- VercelChatProvider: export VercelChatContext for the optional read.
- MessageParts/Message: optional status/reload props, fall back to context.
- useFirstTaskReport: expose messages + status.
- FirstTaskReportRun: render assistant messages via <Message> (filtering out the
  auto-sent prompt user message), not a text dump.

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

@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 5 files (changes from recent commits).

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

Re-trigger cubic

@sweetmantech

Copy link
Copy Markdown
Collaborator Author

Follow-up: reuse the real chat renderer in the pre-run (DRY) — 637ef1a1

Per review discussion, folded the renderer de-dup into this PR rather than a separate one.

Before: the pre-run rendered only the last assistant message's text (getReportTextFromMessages<Response>), dropping tool-call and reasoning components — a diverging, downgraded copy of the chat UI. The LA EQUIS email workflow made it obvious (raw narration, no tool components).

Change: reuse the normal chat's <Message>/<MessageParts>. MessageParts was coupled to useVercelChatContext (status + reload); decoupled it — both are now optional props with a context fallback, so the normal chat is behaviorally unchanged (props omitted → context used) while the pre-run supplies them without mounting a provider (VercelChatProvider owns its own useChat, so wrapping the pre-run would double the chat instance). FirstTaskReportRun renders the assistant messages through <Message> (filtering out the auto-sent prompt user message).

Dual preview verification (sha 637ef1a1, chat-git-feat-onboarding-first-task-email)

  • Onboarding pre-run — tool components now render ✅ Each bash / skill / web_fetch step renders its real tool component (✓ status, tool name, Copy, "Data processed") instead of the old flat text dump. (Run again hit the known preview-sandbox 401 auth gap — expected, unrelated.)
  • Normal chat — no regression ✅ Sent a message in a fresh chat: the assistant reply rendered a reasoning component (EnhancedReasoning, driven by status from context), the text, and Retry (uses reload from context) + Copy actions — every MessageParts feature still works via the context fallback.

tsc --noEmit: 10 pre-existing errors, 0 new from the shared-component change. lib/onboarding suite green (incl. getReportTextFromMessages, still used for the pre-run phase). Prettier clean.

Files

  • providers/VercelChatProvider.tsx — export VercelChatContext.
  • components/VercelChat/MessageParts.tsx, message.tsx — optional status/reload props, context fallback.
  • hooks/useFirstTaskReport.ts — expose messages + status.
  • components/Onboarding/FirstTaskReportRun.tsx — render assistant <Message>s instead of a text dump.

@sweetmantech

Copy link
Copy Markdown
Collaborator Author

Screenshots — shared renderer in both surfaces

Onboarding first-task pre-run — now renders real tool components (skill, bash … each with a ✓ status, tool name, and Copy), via the shared <Message>/<MessageParts>:

First-task pre-run with tool components

Normal chat — the same components, unchanged: reasoning block, response text, and Retry/Copy actions (regression check):

Normal chat with shared component

@sweetmantech
sweetmantech merged commit 0391fe0 into test Jul 21, 2026
3 of 4 checks passed
sweetmantech added a commit that referenced this pull request Jul 22, 2026
…5.5 (post-#1877)

The featured-models refresh in #1877 replaced openai/gpt-5.2 with openai/gpt-5.5,
but organizeModels.test.ts still asserted gpt-5.2 was featured — failing CI on
test. Update the fixture + assertion to a currently-featured id.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
sweetmantech added a commit that referenced this pull request Jul 22, 2026
… artist add (chat#1867) (#1879)

* feat(onboarding): seed the catalog by kicking /api/valuation on first artist add

Closes the onboarding catalog dead-end for direct signups. When the first artist
is added via the Spotify search (#1878 already links their Spotify profile),
useAddSpotifyArtist now fire-and-forget kicks POST /api/valuation
{ spotify_artist_id } (api#776) — only when the account has no catalog yet —
which materializes the catalog + value band in ~20s. So the "Claim your catalog"
step is already complete by the time the user reaches it, and they flow through
to the first-task step (weekly report emails).

- lib/valuation/runValuation.ts — client POST /api/valuation (TDD 2/2).
- hooks/useAddSpotifyArtist.ts — background kick on first add (gated on an empty,
  loaded catalogs query), surfaced via a toast.promise ("Valuing … → Your catalog
  is ready"), invalidating the catalogs query on success so the sequence advances.

chat#1867 (seed onboarding catalog on first artist add).

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

* test(models): fix organizeModels fixture — featured id gpt-5.2 → gpt-5.5 (post-#1877)

The featured-models refresh in #1877 replaced openai/gpt-5.2 with openai/gpt-5.5,
but organizeModels.test.ts still asserted gpt-5.2 was featured — failing CI on
test. Update the fixture + assertion to a currently-featured id.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
sweetmantech added a commit that referenced this pull request Jul 22, 2026
…t-task emails, add-artist, Kimi K3 (#1880)

* feat(onboarding): state-derived onboarding router with resume-on-landing + skip-for-now (#1872)

* feat(onboarding): state-derived onboarding router with resume-on-landing + skip-for-now

On every authenticated landing, chat home derives the onboarding step
from the activation checkpoint predicates (has artists -> artists have
socials -> has claimed catalog -> has enabled task) over existing data
sources, never a stored wizard cursor (#1867). Incomplete
accounts resume the sequence at the first unmet step; an always-visible
skip drops to the app with a dismissible session-scoped checklist;
activated accounts never see it. Step cards are titled placeholders
linking to the existing pages until the step PRs land.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(onboarding): review fixes — checklist clipped by right rail, account-scoped session flags, storage safety, fresh catalogs per landing, roster-error fail-open, h3 card heading

Fixes the preview-verified dismiss bug and all cubic findings on #1872:

- Dismiss live bug: the checklist was position:fixed to the viewport, so
  the z-[65] ArtistsSidebar rail overlapped its right edge and clipped
  the dismiss button off-screen. Now absolute within the (relative) home
  content area. Gate state was already single-owner (HomePage passes
  callbacks down) — locked in with a HomePage-level dismiss/skip/resume
  test.
- P1 cross-account leak: skip/dismiss sessionStorage keys are scoped by
  account id and re-derived when the account in the tab changes
  (useOnboardingSessionFlags).
- P2 storage safety: read/write go through safe helpers that no-op on
  throwing storage so the gate fails open instead of crashing.
- P2 catalogs staleness: useRefreshCatalogsOnLanding invalidates the
  catalogs cache on onboarding mount for one fresh read per landing;
  useCatalogs behavior unchanged for other consumers.
- P2 roster-error fail-open: useArtistsRoster now exposes isError;
  deriveOnboardingState keeps isReady false on roster error so the view
  resolves to none.
- P3 hook length: projection extracted to pure deriveOnboardingState.
- P3 heading hierarchy: step card h1 -> h3 under the container h2.

Tests: TDD red-first; +26 tests (jsdom + @testing-library/react added as
devDeps; vitest include extended to .test.tsx). 174 passing; tsc clean
apart from the 7 pre-existing main errors in lib/emails tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(onboarding): make the skip checklist a persistent, non-dismissible reminder

Remove the checklist-dismissed escape hatch entirely. After "skip for now"
the bottom-right "Finish setting up" card is now always present while
onboarding is incomplete (survives refresh via the session skip flag), and
clicking it re-opens the sequence — there is no way to permanently hide it.

Net-removal: drops the second session flag, its storage read/write usage,
the dismissChecklist callback threading, the X/Dismiss button, and the
separate "Resume setup" button (the card header is now the resume trigger).
getOnboardingView collapses to two views (sequence | checklist) and
OnboardingFlag to a single member.

+47 / -134. Full suite 181 pass, tsc clean on touched files, prettier clean.

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

* refactor(onboarding): restore plain title + add explicit "Continue" button

The checklist header "Finish setting up" is a plain title again (it read as
non-clickable). Re-opening the sequence is now a clearly-styled primary
"Continue" button at the bottom of the card, replacing the ambiguous
click-the-header affordance.

Suite 181 pass, tsc clean on touched files, prettier clean.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* feat: post-valuation catalog report on /catalogs/[catalogId] (chat#1867 sequence step 1) (#1873)

The marketing valuation CTA landed on a bare Catalog Songs admin screen.
/catalogs/{id} now opens as a real catalog report: valuation echo
(central value + range via the ported marketing formula), lifetime
streams, tracks/releases measured, per-release table with album art,
per-section diagnosis + prescription copy, and one primary CTA
(set up your weekly report -> /tasks). The existing songs/ISRC
management UI stays reachable as a secondary Manage songs tab.

Valuation math mirrors marketing/lib/valuation (identical constants;
age from the api's catalog_age_years, 5y default). Release rollups are
null-safe on album/name/artist metadata independently of the sibling
crash-fix PR. Reuses the chat#1852 useCatalogMeasurements hook,
extending its response type with the v2 aggregate fields as optionals.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* feat: roster + socials verification onboarding steps (chat#1867) (#1870)

* feat: roster + socials verification onboarding steps (chat#1867)

Two self-contained onboarding step components for the chat#1867
sequence, mounted standalone at /onboarding/roster so the slice is
user-testable before the onboarding router lands:

- ConfirmRosterStep: shows the auto-created artist(s) from the
  valuation flow, inline "add another artist" for multi-artist
  managers (existing POST /api/artists endpoint), confirm to advance.
- VerifySocialsStep: per rostered artist, lists the auto-matched
  socials with handle + follower count; each match is confirmed or
  rejected, wrong matches are fixed by pasting the correct profile
  link (existing PATCH /api/artists/{id} profileUrls path), and
  artists with no socials record an explicit "none".

Verification state is pure client-side logic (lib/onboarding/*,
TDD'd): verdict reducers, per-artist and step-level resolution
predicates, PATCH payload building with APPPLE->APPLE platform-key
normalization, and a follower-count accessor tolerant of the API's
snake_case rows behind chat's camelCase SOCIAL type.

No new endpoints, tables, or context providers - hooks compose the
existing ArtistProvider / OrganizationProvider / Privy auth.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* refactor(onboarding): simplify verify-socials to accept-by-default (chat#1867)

Per design review: drop the per-social Correct/Wrong verdicts, the
resolution/gating machinery, and the explicit "This artist has no socials"
button. Matches are accepted by default; the only actions are Edit (fix a
wrong link, existing PATCH profileUrls path) and — for an artist with no
matches — a soft nudge + "Add a profile". Continue always proceeds (empty
= implicit none).

Deletes the verdict code that would otherwise be merged and immediately
removed: socialVerificationTypes, applySocialVerdict, isArtistSocialsResolved,
areAllArtistsResolved, markArtistHasNoSocials, findSocialIdByPlatform,
useSocialsVerification, SocialVerifyRow (+ their tests). Net −13 files.

Note: "Remove/delete a social" (the other half of the design) needs a new
api endpoint — the api's PATCH profileUrls is per-platform merge with no
delete-social path — tracked as a fast-follow on chat#1867.

Full suite 144 pass, tsc clean on touched files, lint + prettier clean.

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

* fix(onboarding): always offer Add-a-profile per artist, not just empty ones

A matched-social list can still be missing platforms (e.g. Spotify found
but no Instagram). Surface 'Add a profile' below every artist's socials,
not only when zero were auto-matched.

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* feat(onboarding): first-task step — pre-run weekly report + confirm schedule (#1871)

Sequence final step for chat#1867 (respec 2026-07-20, pre-run shape):
generate the first weekly catalog report immediately through the normal
chat pipeline (same POST /api/chat transport a send uses), show it
finished, then ask one question — "get this every Monday?". Confirm
creates the enabled weekly task via the existing POST /api/tasks path
(which also mints the schedule) and shows the next-run time; decline
creates nothing.

Mounted standalone at /onboarding/first-task so the step is
user-testable before the OnboardingSequence container exists. The
pre-run prompt and the scheduled task prompt come from one builder, so
the preview the user confirms is byte-for-byte what Monday's run uses.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

* feat(onboarding): first-task weekly report actually emails — LA EQUIS template + Kimi K3 (chat#1867) (#1877)

* feat(onboarding): first-task weekly report actually emails (LA EQUIS template + Kimi K3)

The first-task prompt generated a report but never emailed it (email is a
prompt-driven send_email call, and the prompt had no recipient or send
instruction) — proven by firing the created task: the agent ran and persisted
a report, but email_send_log stayed empty.

Generalize buildFirstTaskPrompt from the proven LA EQUIS weekly report
(scheduled_action 39fb5f68, running week over week): resolve the artist's
Spotify id from its connected profile -> capture this week's play counts ->
compute real week-over-week per-track deltas -> build a fully hex-specced
inline-CSS HTML email -> send via the recoup-platform-email-helper skill ->
confirm the Resend id. Carries the sandbox no-python and anti-fabrication
guardrails verbatim (what makes the send reliable). Thread the account email
(recipientEmail) + artistAccountId through both callers so the pre-run preview
and the Monday scheduled task both send to the account holder.

Bump both surfaces from DEFAULT_MODEL (openai/gpt-5.4-mini — completes the data
calls but gives up before composing the long HTML email, the LA EQUIS lesson)
to a new REPORT_MODEL (moonshotai/kimi-k3), which reliably composes and sends.

chat#1867 (first-task email-prompt gap).

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

* refactor(onboarding): default model -> Kimi K3 (KISS); harden email-less + shell paths

Address review feedback on #1877:

- Sweets (KISS): collapse the new REPORT_MODEL constant into DEFAULT_MODEL —
  set DEFAULT_MODEL = moonshotai/kimi-k3 app-wide (the fast gpt-5.4-mini default
  gave up before composing the long HTML report email). Revert both onboarding
  hooks to DEFAULT_MODEL; relabel the featured picker entry (id: DEFAULT_MODEL)
  from "GPT-5.4 Mini" to "Kimi K3" so it isn't mislabeled.
- CodeRabbit (email-less login): a wallet/phone/social-only Privy account has no
  email — FirstTaskStep now shows a distinct "add an email" alert (not an
  infinite "Preparing…"), and useConfirmFirstTask's onError distinguishes
  EMAIL_REQUIRED from a generic retry.
- Codex (shell-safety): artist/track/album names are user-entered; add a prompt
  guardrail to pass them as single quoted args so a name with quotes/;/$() can't
  alter the email-helper command.

Codex "non-Pro users get rejected" P2 is a false positive: the prior default
(gpt-5.4-mini) is also non-free by isFreeModel yet ships as the default, so the
API does not hard-block non-free default models.

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

* Update lib/consts.ts

* chore(models): refresh featured model list to latest per series

Sweets: while touching the featured list, bring every entry to its series'
latest (verified present in the AI Gateway catalog):
- GPT-5.2 -> GPT-5.5
- Claude Opus 4.5 -> 4.8
- Claude Sonnet 4.5 -> Sonnet 5
- Gemini 2.5 Flash Lite -> Gemini 3.5 Flash Lite
- Gemini 3 Pro -> Gemini 3.1 Pro
- Grok 4 -> Grok 4.5
(Kimi K3 already latest.)

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

* refactor(chat): reuse the real Message renderer in the first-task pre-run (DRY)

The onboarding pre-run rendered only the last assistant message's TEXT
(getReportTextFromMessages -> <Response>), dropping tool-call and reasoning
components — a downgraded, diverging copy of the chat UI. #1877's tool-heavy
email workflow made the gap obvious (raw narration, no tool components).

Reuse the normal chat's <Message>/<MessageParts> so tool calls/results get their
real components. MessageParts was coupled to useVercelChatContext (status +
reload); decouple it: both are now optional props with a context fallback, so
the normal chat is behaviorally unchanged (props omitted -> context used) while
the pre-run supplies them without mounting a provider (VercelChatProvider owns
its own useChat instance, so wrapping the pre-run would double the chat).

- VercelChatProvider: export VercelChatContext for the optional read.
- MessageParts/Message: optional status/reload props, fall back to context.
- useFirstTaskReport: expose messages + status.
- FirstTaskReportRun: render assistant messages via <Message> (filtering out the
  auto-sent prompt user message), not a text dump.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(artists): Add New Artist opens a shared Spotify search — kills the /artists loop (chat#1867) (#1878)

* fix(artists): Add New Artist opens a shared Spotify search (kills the /artists loop)

"Add New Artist" (and the sidebar/header equivalents) called toggleCreation,
which pushed `/?q=create a new artist` and relied on the home CHAT to interpret
it. The onboarding router (chat#1872) now intercepts the home for incomplete
accounts and renders "Finish setting up" instead, so the query never ran — the
"Open your roster" CTA bounced back to /artists → infinite loop.

Replace the redirect with a shared Spotify artist-search dialog:
- lib/spotify/parseSpotifyArtistResults.ts — pure parser for the
  GET /api/spotify/search?type=artist envelope (id/name/imageUrl/profileUrl/followers).
- lib/artists/addSpotifyArtist.ts — create by name (POST /api/artists) then link
  the Spotify image + profile URL (PATCH), so the roster gets real data.
- useSpotifyArtistSearch (debounced, abortable) + useAddSpotifyArtist hooks.
- components/Artists/SpotifyArtistSearch.tsx — the shared typeahead (reused next
  for social enrichment + verify-socials).
- components/Artists/AddArtistDialog.tsx — global dialog, mounted in layout.
- useArtistMode.toggleCreation now opens the dialog (isCreationOpen/closeCreation)
  instead of the redirect — fixes /artists, sidebar, and header at once.

TDD: parseSpotifyArtistResults 5/5, addSpotifyArtist 2/2, red→green. tsc clean on
touched files, eslint/prettier clean.

chat#1867 (/artists add-artist loop).

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

* refactor(artists): reuse canonical Spotify types; drop redundant parser (−106 LOC)

Simplification pass on the add-artist slice:
- Delete lib/spotify/parseSpotifyArtistResults.ts (+ its 5 tests) and the custom
  SpotifyArtistResult type — types/spotify.ts already exports SpotifySearchResponse
  ({ artists?: { items: SpotifyArtistSearchResult[] } }) and SpotifyArtistSearchResult
  (id/name/external_urls/images/followers). useSpotifyArtistSearch now reads
  `data.artists?.items` directly and returns the canonical type; the component and
  addSpotifyArtist read images[0].url / followers.total / external_urls.spotify inline.

Kept (checked, "no simpler"): addSpotifyArtist's two-step create→link, because
POST /api/artists only accepts a name; and useSpotifyArtistSearch, since there is
no existing debounced-search hook to reuse.

Net -106 LOC; behavior unchanged; addSpotifyArtist 2/2 green, tsc 0 new.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* feat(onboarding): seed the catalog by kicking /api/valuation on first artist add (chat#1867) (#1879)

* feat(onboarding): seed the catalog by kicking /api/valuation on first artist add

Closes the onboarding catalog dead-end for direct signups. When the first artist
is added via the Spotify search (#1878 already links their Spotify profile),
useAddSpotifyArtist now fire-and-forget kicks POST /api/valuation
{ spotify_artist_id } (api#776) — only when the account has no catalog yet —
which materializes the catalog + value band in ~20s. So the "Claim your catalog"
step is already complete by the time the user reaches it, and they flow through
to the first-task step (weekly report emails).

- lib/valuation/runValuation.ts — client POST /api/valuation (TDD 2/2).
- hooks/useAddSpotifyArtist.ts — background kick on first add (gated on an empty,
  loaded catalogs query), surfaced via a toast.promise ("Valuing … → Your catalog
  is ready"), invalidating the catalogs query on success so the sequence advances.

chat#1867 (seed onboarding catalog on first artist add).

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

* test(models): fix organizeModels fixture — featured id gpt-5.2 → gpt-5.5 (post-#1877)

The featured-models refresh in #1877 replaced openai/gpt-5.2 with openai/gpt-5.5,
but organizeModels.test.ts still asserted gpt-5.2 was featured — failing CI on
test. Update the fixture + assertion to a currently-featured id.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant