Skip to content

fix: drop the chats.model_id column default (model provenance, chat#1956) - #56

Merged
sweetmantech merged 1 commit into
mainfrom
fix/drop-chats-model-id-default
Aug 13, 2026
Merged

fix: drop the chats.model_id column default (model provenance, chat#1956)#56
sweetmantech merged 1 commit into
mainfrom
fix/drop-chats-model-id-default

Conversation

@sweetmantech

@sweetmantech sweetmantech commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Closes the database row of recoupable/chat#1956.

What

One migration: alter table public.chats alter column model_id drop default; plus a column comment stating the new semantics — NULL means "not recorded"; a value present is a value some writer chose.

Why

The 'anthropic/claude-haiku-4.5' default fabricated model provenance: any writer omitting model_id produced a row indistinguishable from a deliberate model choice. Reproduced 2026-08-12 with four POST /api/chat/runs calls on four different explicit models — all four chat rows read the default while usage_events billed the real models (evidence table in recoupable/chat#1956).

Merge sequencing — DO NOT merge before recoupable/api#830

Hard dependency: api#830 makes every chats writer (headless + interactive) set model_id explicitly. Dropping the default first would leave interactive chats with NULL while the UI still expects a value. After api#830 is live, this default has zero remaining writers.

No backfill, deliberately

Historical fabricated rows can't be told apart from genuinely-defaulted interactive rows — rewriting in either direction fabricates provenance the other way. Date-fence instead: rows before api#830's deploy are untrustworthy; usage_events remains the record for completed runs.

The read path is already NULL-safe (chat.model_id ?? DEFAULT_MODEL_ID, api/lib/chat/handleChatWorkflowStream.ts:93).

🤖 Generated with Claude Code


Summary by cubic

Drops the public.chats.model_id column default to stop fabricating model provenance. Previously the column defaulted to 'anthropic/claude-haiku-4.5' when omitted; now it has no default and NULL means "not recorded."

  • Merge only after fix(chat): write the resolved model to chats.model_id at provision time api#830 lands; otherwise interactive chats would write NULL while the UI still expects a value.
  • No backfill; treat rows created before api#830’s deploy as untrustworthy for provenance and rely on usage_events for historical records.
  • No read-path changes required; existing code falls back when model_id is NULL.

Written for commit c22fe2c. Summary will update on new commits.

Review in cubic

…956)

The default fabricated provenance: writers that omitted model_id got a
plausible real model id instead of an honest NULL. After recoupable/api#830
every writer sets the field explicitly, so the default has zero remaining
writers. No backfill — pre-fix rows can't be classified retroactively;
fence by date, usage_events stays the historical record.

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

cursor Bot commented Aug 12, 2026

Copy link
Copy Markdown

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.

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The migration removes the default from public.chats.model_id. It documents that writers must set the value explicitly and that NULL means the model was not recorded. Existing rows are not backfilled.

Changes

Chat model provenance

Layer / File(s) Summary
Drop model_id default and document provenance
supabase/migrations/20260812230000_drop_chats_model_id_default.sql
The migration removes the fabricated default from public.chats.model_id, performs no historical backfill, and updates the column comment to document explicit writer provenance and NULL semantics.

Estimated code review effort: 2 (Simple) | ~10 minutes

Mergeability Score: 🟡 Moderate · up to c22fe

The migration removes the database default so omitted model IDs become NULL, but its schema comment overstates the reliability of historical non-NULL values, and deploying before the dependent writer rollout could leave interactive chats without a model ID. Merge should wait for the writer rollout and either correct the historical-data wording or explicitly accept that bounded risk.

Possibly related issues

  • recoupable/chat#1956 — The migration removes the chats.model_id default and documents NULL provenance semantics described by the issue.
🚥 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 clearly and concisely describes the main change: removing the default from chats.model_id.
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
  • Commit unit tests in branch fix/drop-chats-model-id-default

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.

@supabase

supabase Bot commented Aug 12, 2026

Copy link
Copy Markdown

Updates to Preview Branch (fix/drop-chats-model-id-default) ↗︎

Deployments Status Updated
Database Wed, 12 Aug 2026 22:22:13 UTC
Services Wed, 12 Aug 2026 22:22:13 UTC
APIs Wed, 12 Aug 2026 22:22:13 UTC

Tasks are run on every commit but only new migration files are pushed.
Close and reopen this PR if you want to apply changes from existing seed or migration files.

Tasks Status Updated
Configurations Wed, 12 Aug 2026 22:22:20 UTC
Migrations Wed, 12 Aug 2026 22:22:22 UTC
Seeding Wed, 12 Aug 2026 22:22:24 UTC
Edge Functions Wed, 12 Aug 2026 22:22:24 UTC

View logs for this Workflow Run ↗︎.
Learn more about Supabase for Git ↗︎.

@coderabbitai coderabbitai 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.

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 `@supabase/migrations/20260812230000_drop_chats_model_id_default.sql`:
- Around line 30-31: Update the column comment on public.chats.model_id so its
provenance guarantee applies only to rows created after the default was removed,
while explicitly acknowledging that historical non-NULL values may have been
generated by the former database default. Preserve the existing NULL semantics
and avoid claiming that every present value was chosen by a writer.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1862f9e7-71ab-4f26-9595-f0ea25755e99

📥 Commits

Reviewing files that changed from the base of the PR and between 0744caa and c22fe2c.

📒 Files selected for processing (1)
  • supabase/migrations/20260812230000_drop_chats_model_id_default.sql

Comment on lines +30 to +31
comment on column public.chats.model_id is
'Model recorded at provision time by the writer (chat#1956). NULL means "not recorded" — never defaulted at the database layer, so a value present is a value some writer chose.';

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Scope the provenance guarantee to new rows.

Existing rows remain unchanged. Lines 18-22 state that historical non-NULL values can be fabricated by the former database default. The current comment says every present value was chosen by a writer. That statement is false for some historical rows and can cause consumers to treat fabricated provenance as recorded provenance.

Proposed fix
 comment on column public.chats.model_id is
-  'Model recorded at provision time by the writer (chat#1956). NULL means "not recorded" — never defaulted at the database layer, so a value present is a value some writer chose.';
+  'For rows written after the explicit-writer rollout, this is the model recorded by the writer (chat#1956). NULL means "not recorded". Historical non-NULL values can reflect the former database default.';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
comment on column public.chats.model_id is
'Model recorded at provision time by the writer (chat#1956). NULL means "not recorded" — never defaulted at the database layer, so a value present is a value some writer chose.';
comment on column public.chats.model_id is
'For rows written after the explicit-writer rollout, this is the model recorded by the writer (chat#1956). NULL means "not recorded". Historical non-NULL values can reflect the former database default.';
🤖 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 `@supabase/migrations/20260812230000_drop_chats_model_id_default.sql` around
lines 30 - 31, Update the column comment on public.chats.model_id so its
provenance guarantee applies only to rows created after the default was removed,
while explicitly acknowledging that historical non-NULL values may have been
generated by the former database default. Preserve the existing NULL semantics
and avoid claiming that every present value was chosen by a writer.

@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

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="supabase/migrations/20260812230000_drop_chats_model_id_default.sql">

<violation number="1" location="supabase/migrations/20260812230000_drop_chats_model_id_default.sql:31">
P2: The persisted column comment makes a historical claim that is false: rows created before the `api#830` cutover can still contain the old database default even though no application writer chose it. Qualify the invariant with the cutover and mark historical rows untrustworthy, otherwise schema consumers may treat fabricated model IDs as provenance.</violation>
</file>

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

Re-trigger cubic

alter column model_id drop default;

comment on column public.chats.model_id is
'Model recorded at provision time by the writer (chat#1956). NULL means "not recorded" — never defaulted at the database layer, so a value present is a value some writer chose.';

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: The persisted column comment makes a historical claim that is false: rows created before the api#830 cutover can still contain the old database default even though no application writer chose it. Qualify the invariant with the cutover and mark historical rows untrustworthy, otherwise schema consumers may treat fabricated model IDs as provenance.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At supabase/migrations/20260812230000_drop_chats_model_id_default.sql, line 31:

<comment>The persisted column comment makes a historical claim that is false: rows created before the `api#830` cutover can still contain the old database default even though no application writer chose it. Qualify the invariant with the cutover and mark historical rows untrustworthy, otherwise schema consumers may treat fabricated model IDs as provenance.</comment>

<file context>
@@ -0,0 +1,31 @@
+  alter column model_id drop default;
+
+comment on column public.chats.model_id is
+  'Model recorded at provision time by the writer (chat#1956). NULL means "not recorded" — never defaulted at the database layer, so a value present is a value some writer chose.';
</file context>
Suggested change
'Model recorded at provision time by the writer (chat#1956). NULL means "not recorded" — never defaulted at the database layer, so a value present is a value some writer chose.';
'Model recorded at provision time by the writer (chat#1956). For rows written after the api#830 cutover, NULL means "not recorded"; a value present is a value some writer chose. Rows created before that cutover may contain the old database default and are untrustworthy.';

@sweetmantech

Copy link
Copy Markdown
Contributor Author

Verified on the Supabase branch deployment (vujowgbteeazchsjhohs)

The branch ran this PR's migration on top of the full prior chain — supabase_migrations.schema_migrations shows 20260812230000 drop_chats_model_id_default applied cleanly after 20260806230000 create_credit_grants.

Check Expected Observed on branch
Column default gone information_schema.columnscolumn_default: NULL
Column comment the "NULL means not recorded" semantics col_description returns the full comment verbatim
Insert without model_id NULL — honest "not recorded", not a fabricated model model_id: NULL
Insert with model_id value kept moonshotai/kimi-k3

(Behavioral checks used throwaway account/session/chat rows on the branch, deleted after.)

Merge gate satisfied: recoupable/api#830 is merged and live on Production (fd18f18e, deployed 2026-08-13T02:28:56Z, status success) — every chats writer now sets model_id explicitly, so this default has zero remaining writers.

Reminder for whoever merges: this repo has no CI apply step — after merge the migration still needs to be applied to the main project.

🤖 Generated with Claude Code

@sweetmantech
sweetmantech merged commit c95273f into main Aug 13, 2026
3 checks passed
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