diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 193267c..5ac03e3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -60,3 +60,40 @@ jobs: - name: Build run: pnpm build + + deploy-trigger: + name: Deploy Trigger.dev tasks + # Only a merge into main ships tasks. Pull requests stop at the `ci` job so a + # branch never overwrites the prod deployment. + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + needs: ci + runs-on: ubuntu-latest + + env: + # `trigger deploy` authenticates with a Personal Access Token (tr_pat_...), + # NOT the TRIGGER_SECRET_KEY the app uses at runtime. Generate one at + # https://cloud.trigger.dev/account/tokens and store it as a repo secret. + TRIGGER_ACCESS_TOKEN: ${{ secrets.TRIGGER_ACCESS_TOKEN }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + # No `prisma generate` here: nothing under `src/trigger` imports the Prisma + # client, so the deploy bundle never resolves `@/app/generated/prisma`. + # Secrets the tasks need at runtime (GOOGLE_GENERATIVE_AI_API_KEY, + # LIVEBLOCKS_SECRET_KEY) are set in the Trigger.dev dashboard, not here. + - name: Deploy tasks + run: pnpm exec trigger deploy diff --git a/app/api/ai/design/route.ts b/app/api/ai/design/route.ts index 5c8ff66..0558aba 100644 --- a/app/api/ai/design/route.ts +++ b/app/api/ai/design/route.ts @@ -68,10 +68,19 @@ export async function POST(request: Request) { return Response.json({ error: "roomId must match projectId" }, { status: 400 }); } - const handle = await tasks.trigger("design-agent", { - prompt: parsedBody.prompt, - roomId: parsedBody.roomId, - }); + let handle: Awaited>>; + try { + handle = await tasks.trigger("design-agent", { + prompt: parsedBody.prompt, + roomId: parsedBody.roomId, + }); + } catch (error) { + // wrong-environment TRIGGER_SECRET_KEY, a branch env that does not exist, or + // no deployed version of the task. Surface it as 502 so the client does not + // mistake it for an expired session, and log the cause for the server side. + console.error("[api/ai/design] failed to trigger design-agent", error); + return Response.json({ error: "Design service unavailable" }, { status: 502 }); + } await prisma.taskRun.create({ data: { diff --git a/context/progress-tracker.md b/context/progress-tracker.md index db4ef30..46a3b20 100644 --- a/context/progress-tracker.md +++ b/context/progress-tracker.md @@ -672,3 +672,37 @@ Update this file whenever the current phase, active feature, or implementation s - Prisma create/update/delete verified against the restored schema: slug ids, client-side `cuid()` generation and `@updatedAt` all behave as before; test rows deleted - `pnpm lint`, `pnpm typecheck`, and `pnpm build` passed +- Fixed the production AI design generation break on 2026-09-11: + - Symptom: prompting the AI in production returned nothing. Server log showed + `TriggerApiError: No matching branch env` (401) from `tasks.trigger`. Local runs were unaffected. + - Root cause: `@trigger.dev/core` resolves a preview branch from + `previewBranch ?? TRIGGER_PREVIEW_BRANCH ?? VERCEL_GIT_COMMIT_REF` and attaches it as the + `x-trigger-branch` header on every request. Vercel always sets `VERCEL_GIT_COMMIT_REF` (`main` on + production deploys), so the deployed app sent a branch header that resolved against a preview + secret key with no matching branch environment. Locally neither variable is set, so no header is + sent and the `tr_dev_` key resolves against the dev environment. + - Fix, part 1 (outside the repo): the production `TRIGGER_SECRET_KEY` on Vercel was replaced with a + `tr_prod_` key. + - Fix, part 2 — `.github/workflows/ci.yml`: + - Added a `deploy-trigger` job that runs `pnpm exec trigger deploy` after `ci` passes, gated to + pushes on `main` so pull requests never overwrite the prod deployment. Previously nothing in CI + deployed tasks at all, so the prod environment had no deployed version of `design-agent`. + - The job authenticates with `TRIGGER_ACCESS_TOKEN` (a `tr_pat_` Personal Access Token, which is a + different credential from the runtime `TRIGGER_SECRET_KEY`) and must be added as a repo secret. + - No `prisma generate` step: nothing under `src/trigger` imports the Prisma client. + - Fix, part 3 — `app/api/ai/design/route.ts`: + - Wrapped `tasks.trigger` in try/catch. The call was previously unguarded, so any Trigger.dev + failure escaped as an unhandled 500 and the sidebar showed only a generic message. Failures now + log server-side and return 502. + - Fix, part 4 — `hooks/use-design-agent.ts`: + - Added a 502 case to `describeRequestFailure` so an unreachable design service reads as such + rather than falling through to the generic error. 502 was chosen over 401 deliberately: the + hook maps 401 to "your session expired", which would misreport an infrastructure failure. + - Validation checks: + - `pnpm lint`, `pnpm typecheck`, and `pnpm build` passed + - Open items: + - `app/api/trigger/hello/route.ts` has the same unguarded `tasks.trigger` call. Left as is — it is + a sample route outside this fix's scope. + - `.claude/skills/trigger-setup/references/environment-setup.md` documents `TRIGGER_SECRET_KEY` for + `trigger deploy` in CI, which is wrong (the CLI requires `TRIGGER_ACCESS_TOKEN`). It is vendored + third-party skill content, so it was not edited. diff --git a/hooks/use-design-agent.ts b/hooks/use-design-agent.ts index 48d49dc..0c150ec 100644 --- a/hooks/use-design-agent.ts +++ b/hooks/use-design-agent.ts @@ -53,6 +53,8 @@ function describeRequestFailure(status: number): string { return "Your session expired. Sign in again to keep designing."; case 403: return "You do not have access to this project, so the design was not generated."; + case 502: + return "The design service is unreachable right now, so nothing was generated. Try again shortly."; default: return GENERIC_ERROR; }