Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
17 changes: 13 additions & 4 deletions app/api/ai/design/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof designAgentTask>("design-agent", {
prompt: parsedBody.prompt,
roomId: parsedBody.roomId,
});
let handle: Awaited<ReturnType<typeof tasks.trigger<typeof designAgentTask>>>;
try {
handle = await tasks.trigger<typeof designAgentTask>("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: {
Expand Down
34 changes: 34 additions & 0 deletions context/progress-tracker.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
2 changes: 2 additions & 0 deletions hooks/use-design-agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down