Skip to content

fix: comprehensive schema normalization and constraint fixes#714

Merged
ngoiyaeric merged 2 commits into
QueueLab:mainfrom
ngoiyaeric:fix/comprehensive-schema-fix
Jul 9, 2026
Merged

fix: comprehensive schema normalization and constraint fixes#714
ngoiyaeric merged 2 commits into
QueueLab:mainfrom
ngoiyaeric:fix/comprehensive-schema-fix

Conversation

@ngoiyaeric

@ngoiyaeric ngoiyaeric commented Jul 9, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR addresses all identified future failure modes from a comprehensive schema audit:

Code Fixes

  1. updateDrawingContext role fix (lib/actions/chat.ts)

    • Changed message role from 'data' to 'tool'
    • The messages_role_check constraint only allowed: user, assistant, system, tool
    • Role 'data' would have crashed every drawing context save
  2. Calendar note message role fix (lib/actions/calendar.ts)

    • Same issue: calendar notes created messages with role 'data'
    • Changed to 'tool' for the same reason

Database Migrations

  1. Migration 0007 — Create missing prompt_generation_jobs table

    • Table was defined in schema.ts and migration 0002 but was never applied to production
    • Uses NOT VALID + VALIDATE pattern for the FK constraint
  2. Migration 0008 — Add 'data' to messages_role_check

    • Preventive: if any future code needs role 'data', it will be available
    • Uses NOT VALID + VALIDATE pattern

All Previous Fixes (Already Merged)

Applied to Production

All database changes have been applied directly to the QCX-BACKEND Supabase project via Supabase MCP.

Summary by CodeRabbit

  • New Features

    • Added support for storing prompt-generation jobs, including status, results, and error details.
    • Expanded message handling to support an additional internal message type.
  • Bug Fixes

    • Improved message saving to ensure new messages always get a unique ID.
    • Updated calendar and drawing context messages to use the correct message role for better consistency.

- Fix updateDrawingContext: change message role from 'data' to 'tool'
  (messages_role_check only allowed user/assistant/system/tool)
- Fix calendar.ts: change calendar note message role from 'data' to 'tool'
  (same constraint violation in saveNote path)
- Create migration 0007: create missing prompt_generation_jobs table
  (schema defined it but table was never created in production)
- Create migration 0008: add 'data' to messages_role_check as a valid role
  (preventive: future-proof the constraint if 'data' role is needed)

All migrations use NOT VALID + VALIDATE pattern to avoid write blocking.
@vercel

vercel Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Someone is attempting to deploy a commit to the QCX-MAIN Team on Vercel.

A member of the Team first needs to authorize it.

@qodo-code-review

Copy link
Copy Markdown
Contributor

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds prompt-generation job persistence, permits the data message role, changes persisted context messages to tool, and generates UUIDs for chat messages without IDs.

Changes

Prompt generation job storage

Layer / File(s) Summary
Create prompt-generation jobs table
drizzle/migrations/0007_create_prompt_generation_jobs.sql
Creates the jobs table with metadata, optional outputs, timestamps, and a validated cascading user foreign key.

Message role persistence

Layer / File(s) Summary
Expand message role constraint
drizzle/migrations/0008_allow_data_message_role.sql
Updates messages_role_check to allow the data role.
Update persisted context roles
lib/actions/calendar.ts, lib/actions/chat.ts
Stores calendar and drawing context messages with the tool role.

Chat message identity

Layer / File(s) Summary
Generate missing chat message IDs
lib/actions/chat-db.ts
Assigns UUIDs to messages without IDs before deduplication and upsert processing.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • QueueLab/QCX#689: Touches the same calendar-context message persistence path and its synthetic message roles.

Poem

A bunny hops where new jobs grow,
While tool-tagged messages softly flow.
Missing IDs now bloom with cheer,
UUID carrots for every hare.
The database smiles: “All paths are clear!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the PR’s main theme: schema/constraint cleanup across migrations and message role fixes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

@CLAassistant

Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.


ngoiyaeric seems not to be a GitHub user. You need a GitHub account to be able to sign the CLA. If you have already a GitHub account, please add the email address used for this commit to your account.
You have signed the CLA already but the status is still pending? Let us recheck it.

@vercel

vercel Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

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

Project Deployment Actions Updated (UTC)
qcx Error Error Jul 9, 2026 9:05pm

- msg.id can be string | undefined in the NewMessage type
- Use nullish coalescing (??) to generate a UUID fallback
- Fixes TypeScript build error: Type 'string | undefined' not assignable to 'string'
@ngoiyaeric ngoiyaeric merged commit fdec5be into QueueLab:main Jul 9, 2026
1 of 3 checks passed

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
lib/actions/chat-db.ts (1)

126-128: 🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Dedup suffix produces invalid UUIDs that will violate the uuid column constraint.

When a duplicate id is detected, the code appends -${Math.random().toString(36).substring(2, 8)} to the original UUID. This produces a string like 550e8400-e29b-41d4-a716-446655440000-abc123, which is not a valid UUID. PostgreSQL's uuid column type will reject this on insert, causing the entire transaction to fail.

This is pre-existing, but it's directly in the path of the line 123 change — any msg.id that is defined but duplicated (the "groupeId" scenario described in the comment at line 117) will hit this branch.

🔒️ Proposed fix: use `crypto.randomUUID()` for dedup collisions
       while (seenIds.has(id)) {
-        id = `${id}-${Math.random().toString(36).substring(2, 8)}`;
+        id = crypto.randomUUID();
       }

This ensures all generated IDs remain valid UUIDs. If you need to preserve a deterministic relationship to the original ID for debugging, consider a UUID v5 (namespace-based) approach instead, but a fresh random UUID is the simplest correct fix.

🤖 Prompt for 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.

In `@lib/actions/chat-db.ts` around lines 126 - 128, The deduplication logic in
chat-db.ts is producing invalid UUIDs by appending a random suffix to an
existing id inside the seenIds collision loop. Update the duplicate-id handling
in the chat insert path so the generated replacement remains a valid UUID, using
crypto.randomUUID() (or an equivalent UUID-safe approach) in the branch that
currently mutates id. Keep the fix localized to the id collision handling around
the msg.id/groupId flow so inserts into the uuid column do not fail.
lib/actions/calendar.ts (1)

92-102: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Avoid double-encoding calendar tool payloads.
app/actions.tsx re-stringifies every tool message before sending it to researcher, so persisting JSON.stringify(...) here turns the calendar note into escaped JSON on the next pass. Serialize this payload only once, or skip the second stringify for tool messages that are already text.

🤖 Prompt for 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.

In `@lib/actions/calendar.ts` around lines 92 - 102, The calendar tool payload is
being serialized twice: `createMessage` in `calendar.ts` stores `content` as an
already stringified JSON value, and `app/actions.tsx` stringifies `tool`
messages again before sending them to `researcher`. Update the
`calendarContextMessage` construction so the `tool` payload is stored in the
form expected by the downstream flow, or adjust the `tool` handling in
`app/actions.tsx` to avoid re-stringifying content that is already plain text.
Use the `calendarContextMessage` and `createMessage` path to locate the fix.
🤖 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 `@lib/actions/chat.ts`:
- Around line 235-240: The drawing-context tool payload is being stringified
twice between chat message creation and the researcher path. Update the
`newDrawingMessage` flow in `chat.ts` and the `tool` handling in
`app/actions.tsx` so the payload is serialized in only one place and passed
through unchanged everywhere else. Use the existing `DbNewMessage`,
`newDrawingMessage`, and `researcher` tool-message path to keep the stored
content as the original JSON string instead of an escaped string.

---

Outside diff comments:
In `@lib/actions/calendar.ts`:
- Around line 92-102: The calendar tool payload is being serialized twice:
`createMessage` in `calendar.ts` stores `content` as an already stringified JSON
value, and `app/actions.tsx` stringifies `tool` messages again before sending
them to `researcher`. Update the `calendarContextMessage` construction so the
`tool` payload is stored in the form expected by the downstream flow, or adjust
the `tool` handling in `app/actions.tsx` to avoid re-stringifying content that
is already plain text. Use the `calendarContextMessage` and `createMessage` path
to locate the fix.

In `@lib/actions/chat-db.ts`:
- Around line 126-128: The deduplication logic in chat-db.ts is producing
invalid UUIDs by appending a random suffix to an existing id inside the seenIds
collision loop. Update the duplicate-id handling in the chat insert path so the
generated replacement remains a valid UUID, using crypto.randomUUID() (or an
equivalent UUID-safe approach) in the branch that currently mutates id. Keep the
fix localized to the id collision handling around the msg.id/groupId flow so
inserts into the uuid column do not fail.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 578f63ef-858b-4cd9-afb9-18f7610f8dac

📥 Commits

Reviewing files that changed from the base of the PR and between 951886e and 759a7af.

📒 Files selected for processing (5)
  • drizzle/migrations/0007_create_prompt_generation_jobs.sql
  • drizzle/migrations/0008_allow_data_message_role.sql
  • lib/actions/calendar.ts
  • lib/actions/chat-db.ts
  • lib/actions/chat.ts
📜 Review details
⚠️ CI failures not shown inline (1)

Commit Status: Vercel – qcx: Vercel – qcx

Conclusion: failure

Authorization required to deploy.
🧰 Additional context used
🪛 Squawk (2.59.0)
drizzle/migrations/0008_allow_data_message_role.sql

[warning] 12-12: Using NOT VALID and VALIDATE CONSTRAINT in the same transaction will block all reads while the constraint is validated. Add constraint as NOT VALID in one transaction and VALIDATE CONSTRAINT in a separate transaction.

(constraint-missing-not-valid)

drizzle/migrations/0007_create_prompt_generation_jobs.sql

[warning] 17-17: Using NOT VALID and VALIDATE CONSTRAINT in the same transaction will block all reads while the constraint is validated. Add constraint as NOT VALID in one transaction and VALIDATE CONSTRAINT in a separate transaction.

(constraint-missing-not-valid)

🔇 Additional comments (5)
drizzle/migrations/0007_create_prompt_generation_jobs.sql (2)

4-13: LGTM!


15-17: 🗄️ Data Integrity & Integration | 💤 Low value

Squawk NOT VALID + VALIDATE warning is a false positive here.

The blocking concern only matters when validating a constraint against existing rows. Since prompt_generation_jobs is created empty in this same migration (Line 4), VALIDATE CONSTRAINT scans zero rows and completes instantly, so the two-step split provides no benefit and causes no read-blocking. No change required; you could optionally inline the FK in the CREATE TABLE to reduce noise, but leaving it consistent with your other migrations is fine.

Column and FK contract matches lib/db/schema.ts (promptGenerationJobs) and the users.id UUID target.

Source: Linters/SAST tools

lib/actions/chat-db.ts (2)

123-123: UUID fallback for missing message IDs is correct.

Generating a UUID before dedup tracking ensures all messages are uniformly handled by the seenIds set and the onConflictDoUpdate target. This properly addresses the undefined msg.id flow from the upstream caller in lib/actions/chat.ts.


123-123: 🔒 Security & Privacy

crypto.randomUUID() is fine here Bun exposes the Web Crypto API globally, so this bare reference works in the configured runtime.

			> Likely an incorrect or invalid review comment.
drizzle/migrations/0008_allow_data_message_role.sql (1)

8-12: 🩺 Stability & Availability

Verify the validation step is isolated from the add-constraint step.

The file uses NOT VALID and VALIDATE CONSTRAINT back-to-back, and the static-analysis hint says this can prolong locking if the migration runner wraps the file in a single transaction. Please confirm the execution model; otherwise split validation into a follow-up migration.

Source: Linters/SAST tools

Comment thread lib/actions/chat.ts
Comment on lines 235 to 240
const newDrawingMessage: DbNewMessage = {
userId: userId,
chatId: chatId,
role: 'data',
role: 'tool',
content: JSON.stringify(contextData),
createdAt: new Date(),

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- lib/actions/chat.ts (around lines 220-260) ---'
sed -n '220,260p' lib/actions/chat.ts

echo
echo '--- app/actions.tsx (around lines 360-410) ---'
sed -n '360,410p' app/actions.tsx

echo
echo '--- search for tool-message handling and JSON.stringify usage ---'
rg -n "role:\s*'tool'|role === 'tool'|JSON\.stringify\(msg\.content\)|content:\s*JSON\.stringify|CoreMessage|tool" lib app -g '!**/dist/**' -g '!**/build/**'

Repository: QueueLab/QCX

Length of output: 14446


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- lib/actions/chat.ts (around lines 200-220) ---'
sed -n '200,220p' lib/actions/chat.ts

echo
echo '--- app/actions.tsx (around lines 150-180) ---'
sed -n '150,180p' app/actions.tsx

echo
echo '--- app/actions.tsx (around lines 660-710) ---'
sed -n '660,710p' app/actions.tsx

echo
echo '--- lib/types/index.ts (around lines 55-80) ---'
sed -n '55,80p' lib/types/index.ts

Repository: QueueLab/QCX

Length of output: 4245


Avoid double-stringifying the drawing-context tool payload. app/actions.tsx:377-388 stringifies every tool message again before sending it to researcher, so the JSON string saved in lib/actions/chat.ts becomes an escaped string instead of the original payload. Keep the existing string as-is for this path, or stringify in one place only.

🤖 Prompt for 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.

In `@lib/actions/chat.ts` around lines 235 - 240, The drawing-context tool payload
is being stringified twice between chat message creation and the researcher
path. Update the `newDrawingMessage` flow in `chat.ts` and the `tool` handling
in `app/actions.tsx` so the payload is serialized in only one place and passed
through unchanged everywhere else. Use the existing `DbNewMessage`,
`newDrawingMessage`, and `researcher` tool-message path to keep the stored
content as the original JSON string instead of an escaped string.

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