fix(db): add missing FK constraints on graph_edges + notes (#179, #180) - #245

Closed
Jose-Gael-Cruz-Lopez wants to merge 15 commits into
mainfrom
fix/fk-integrity
Closed

fix(db): add missing FK constraints on graph_edges + notes (#179, #180)#245
Jose-Gael-Cruz-Lopez wants to merge 15 commits into
mainfrom
fix/fk-integrity

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jun 22, 2026

Copy link
Copy Markdown
Member

graph_edges.user_id, notes.user_id, and notes.course_id were bare TEXT columns with no REFERENCES, inconsistent with every sibling table in the learning schema. An edge/note could be written for a non-existent user, and deleting a course left dangling notes that list_notes surfaces but can't resolve.

Changes

  • backend/db/migration_fk_integrity.sql — new hand-applied migration:
    • deletes orphan rows first (so the ALTER TABLE can validate),
    • adds each FK behind the DO $$ … IF NOT EXISTS (SELECT 1 FROM pg_constraint …) guard already used in migration_gradebook.sql (Postgres has no ADD CONSTRAINT IF NOT EXISTS), so it is safely re-runnable.
  • backend/db/supabase_schema.sql — inline REFERENCES on all three columns so fresh databases are born with the FKs; comment block updated to note only the note_concepts.concept_node_id link stays soft.
  • backend/tests/test_fk_integrity_migration.py — drift guard: each constraint is guarded, orphans are cleaned before the ALTER, schema declares the inline references.

Verification

  • ruff check . clean; gated suite green (+3 new).
  • Orphan deletion is intentional: rows that violate the new FK are invalid data (no owning user, or a course that no longer exists).

Closes#179, #180.

Summary by CodeRabbit

  • Bug Fixes

    • Strengthened database integrity by enforcing foreign-key relationships for graph edges and notes to ensure valid user and course references.
    • Automatically removes orphaned records that would violate these relationships.
    • Added indexing to improve lookups by user on graph edges.
  • Tests

    • Added drift-guard tests that verify the migration safely applies foreign-key constraints and that orphan cleanup occurs before constraints are added.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 22, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitUpdated (UTC)
❌ Deployment failed
View logs
frontend75d3729Jun 22 2026, 03:55 AM

@coderabbitai

coderabbitaiBot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Jose-Gael-Cruz-Lopez, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 43 minutes and 55 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

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 credits.

🚦 How do rate 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 see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a71d73bd-17bd-4660-a650-0be10b29825e

📥 Commits

Reviewing files that changed from the base of the PR and between cc1ec58 and 4f8655e.

📒 Files selected for processing (3)
  • backend/db/migrations/0001_baseline_schema.sql
  • backend/db/migrations/0020_fk_integrity.sql
  • backend/tests/test_fk_integrity_migration.py
📝 Walkthrough

Walkthrough

Adds inline REFERENCES FK constraints to graph_edges.user_id, notes.user_id, and notes.course_id in the canonical schema. A new idempotent migration script purges orphan rows before conditionally adding each FK constraint via pg_constraint name guards. A new test module validates that the migration and schema remain consistent with each other.

Changes

FK Integrity for graph_edges and notes

Layer / File(s)Summary
Schema inline REFERENCES declarations
backend/db/supabase_schema.sql
graph_edges.user_id gains REFERENCES users(id) with a new supporting index; notes.user_id and notes.course_id gain REFERENCES users(id) and REFERENCES courses(id), converting three bare TEXT NOT NULL columns into hard FK columns with updated documentation.
Idempotent orphan-cleanup and conditional FK migration
backend/db/migration_fk_integrity.sql
New migration that deletes orphan rows in graph_edges and notes before each ALTER TABLE, then adds each FK constraint only when absent, guarded by pg_constraint name checks inside DO $$ ... $$ blocks.
Drift-guard tests
backend/tests/test_fk_integrity_migration.py
Three tests assert: each constraint name appears behind an IF NOT EXISTS pg_constraint guard in the migration; DELETE orphan statements precede their corresponding ALTER steps; and supabase_schema.sql declares the expected inline REFERENCES fragments.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

Poem

🐇 Hop hop, the orphan rows are gone,
No dangling edges left to mourn!
With REFERENCES neat and guards in place,
Each user_id finds its proper space.
The schema's tight, the migration's sound —
Foreign keys keep the data bound! 🌱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically summarizes the main change: adding missing FK constraints on three database columns across two tables, with linked issue references.
Description check✅ PassedThe description provides a clear summary, detailed changes breakdown, rationale, and verification steps, mostly following the repository template structure.
Linked Issues check✅ PassedThe PR fully implements the acceptance criteria from #179: adds FK constraint from graph_edges.user_id to users(id), cleans orphan rows via migration, and updates both migration and schema files. Changes also address the objectives for #180 (notes table FKs).
Out of Scope Changes check✅ PassedAll changes are directly scoped to enforcing FK integrity on the three identified columns. The index addition on graph_edges.user_id and test file are supporting/validation changes aligned with the stated objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/fk-integrity

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.

@coderabbitaicoderabbitaiBot 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 `@backend/db/supabase_schema.sql`:
- Around line 495-496: The comment explaining why note_concepts.concept_node_id
is intentionally not a hard FK states it's because graph_nodes uses TEXT ids
managed by application code, but this reasoning is inconsistent since
graph_edges.source_node_id and graph_edges.target_node_id also reference
graph_nodes(id) with TEXT ids yet have hard FKs defined. Update the comment at
lines 495-496 to clarify the actual rationale for not adding the FK constraint
to note_concepts.concept_node_id, such as whether concepts can exist before
their corresponding graph nodes are created or if there's a specific
application-level design consideration that necessitates this approach.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b17fdcab-4e71-4e4f-83a4-8009fa8e242a

📥 Commits

Reviewing files that changed from the base of the PR and between 4521305 and 75d3729.

📒 Files selected for processing (3)
  • backend/db/migration_fk_integrity.sql
  • backend/db/supabase_schema.sql
  • backend/tests/test_fk_integrity_migration.py

Comment on lines +495 to +496
-- links. Only the note_concepts.concept_node_id link is intentionally NOT a
-- hard FK, because graph_nodes uses TEXT ids managed by application code.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify the rationale for not adding FK to note_concepts.concept_node_id.

The comment states the link is "intentionally NOT a hard FK, because graph_nodes uses TEXT ids managed by application code." However, graph_edges.source_node_id and graph_edges.target_node_id (lines 98-99) both have hard FKs to graph_nodes(id), so the stated reason is inconsistent.

If the real reason is different (e.g., concepts can exist before graph nodes are created, or there's an application-level design consideration), please update the comment to explain the actual rationale.

🤖 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 `@backend/db/supabase_schema.sql` around lines 495 - 496, The comment
explaining why note_concepts.concept_node_id is intentionally not a hard FK
states it's because graph_nodes uses TEXT ids managed by application code, but
this reasoning is inconsistent since graph_edges.source_node_id and
graph_edges.target_node_id also reference graph_nodes(id) with TEXT ids yet have
hard FKs defined. Update the comment at lines 495-496 to clarify the actual
rationale for not adding the FK constraint to note_concepts.concept_node_id,
such as whether concepts can exist before their corresponding graph nodes are
created or if there's a specific application-level design consideration that
necessitates this approach.

Postgres does not auto-index the referencing side of a foreign key, and
sibling tables index this access path. Add the index in both the
migration and the canonical schema.
…he new FKs (#179, #180)
The FKs have no ON DELETE clause, so they default to RESTRICT (block
hard delete) rather than cascading cleanup. Correct the schema comment
that implied cleanup-on-delete, and document the real semantics at the
graph_edges/notes FK sites and in the migration header.
@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 24, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging4f8655eCommit Preview URL

Branch Preview URL
Jun 24 2026, 02:50 PM

main restructured db/ into ordered migrations applied by migrate.py, deleting
the flat migration_*.sql files. Move the FK-integrity DDL into
migrations/0020_fk_integrity.sql so existing databases get the graph_edges/notes
FK constraints. The DDL is already idempotent (pg_constraint-guarded ADD
CONSTRAINT, orphan DELETEs first, CREATE INDEX IF NOT EXISTS), so it is also a
no-op on fresh DBs that already have the inline FKs from 0001_baseline_schema.
…iles
supabase_schema.sql was deleted when main restructured db/. Assert the FK DDL
invariants against migrations/0020_fk_integrity.sql and the inline REFERENCES
against migrations/0001_baseline_schema.sql instead, preserving the original
verification intent (guarded ADD CONSTRAINT, orphan cleanup ordering, inline FKs
on fresh DBs). Also assert the idx_graph_edges_user_id index this PR adds.
@AndresL230

Copy link
Copy Markdown
Collaborator

Superseded by the DB modular redesign (#279). The graph_edges.user_id + notes FKs (#179/#180) ship in 0023/0025. Closing as obsolete (migration collision). Reopen if not fully covered by #279.

@AndresL230
AndresL230 deleted the fix/fk-integrity branch June 27, 2026 04:21
Sign up for freeto 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.

graph_edges.user_id has no FOREIGN KEY (orphan-row risk) — inconsistent with sibling tables

2 participants

@Jose-Gael-Cruz-Lopez@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(db): add missing FK constraints on graph_edges + notes (#179, #180) - #245

Closed
Jose-Gael-Cruz-Lopez wants to merge 15 commits into
mainfrom
fix/fk-integrity
Closed

fix(db): add missing FK constraints on graph_edges + notes (#179, #180)#245
Jose-Gael-Cruz-Lopez wants to merge 15 commits into
mainfrom
fix/fk-integrity

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jun 22, 2026

Copy link
Copy Markdown
Member

graph_edges.user_id, notes.user_id, and notes.course_id were bare TEXT columns with no REFERENCES, inconsistent with every sibling table in the learning schema. An edge/note could be written for a non-existent user, and deleting a course left dangling notes that list_notes surfaces but can't resolve.

Changes

  • backend/db/migration_fk_integrity.sql — new hand-applied migration:
    • deletes orphan rows first (so the ALTER TABLE can validate),
    • adds each FK behind the DO $$ … IF NOT EXISTS (SELECT 1 FROM pg_constraint …) guard already used in migration_gradebook.sql (Postgres has no ADD CONSTRAINT IF NOT EXISTS), so it is safely re-runnable.
  • backend/db/supabase_schema.sql — inline REFERENCES on all three columns so fresh databases are born with the FKs; comment block updated to note only the note_concepts.concept_node_id link stays soft.
  • backend/tests/test_fk_integrity_migration.py — drift guard: each constraint is guarded, orphans are cleaned before the ALTER, schema declares the inline references.

Verification

  • ruff check . clean; gated suite green (+3 new).
  • Orphan deletion is intentional: rows that violate the new FK are invalid data (no owning user, or a course that no longer exists).

Closes#179, #180.

Summary by CodeRabbit

  • Bug Fixes

    • Strengthened database integrity by enforcing foreign-key relationships for graph edges and notes to ensure valid user and course references.
    • Automatically removes orphaned records that would violate these relationships.
    • Added indexing to improve lookups by user on graph edges.
  • Tests

    • Added drift-guard tests that verify the migration safely applies foreign-key constraints and that orphan cleanup occurs before constraints are added.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 22, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitUpdated (UTC)
❌ Deployment failed
View logs
frontend75d3729Jun 22 2026, 03:55 AM

@coderabbitai

coderabbitaiBot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Jose-Gael-Cruz-Lopez, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 43 minutes and 55 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

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 credits.

🚦 How do rate 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 see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a71d73bd-17bd-4660-a650-0be10b29825e

📥 Commits

Reviewing files that changed from the base of the PR and between cc1ec58 and 4f8655e.

📒 Files selected for processing (3)
  • backend/db/migrations/0001_baseline_schema.sql
  • backend/db/migrations/0020_fk_integrity.sql
  • backend/tests/test_fk_integrity_migration.py
📝 Walkthrough

Walkthrough

Adds inline REFERENCES FK constraints to graph_edges.user_id, notes.user_id, and notes.course_id in the canonical schema. A new idempotent migration script purges orphan rows before conditionally adding each FK constraint via pg_constraint name guards. A new test module validates that the migration and schema remain consistent with each other.

Changes

FK Integrity for graph_edges and notes

Layer / File(s)Summary
Schema inline REFERENCES declarations
backend/db/supabase_schema.sql
graph_edges.user_id gains REFERENCES users(id) with a new supporting index; notes.user_id and notes.course_id gain REFERENCES users(id) and REFERENCES courses(id), converting three bare TEXT NOT NULL columns into hard FK columns with updated documentation.
Idempotent orphan-cleanup and conditional FK migration
backend/db/migration_fk_integrity.sql
New migration that deletes orphan rows in graph_edges and notes before each ALTER TABLE, then adds each FK constraint only when absent, guarded by pg_constraint name checks inside DO $$ ... $$ blocks.
Drift-guard tests
backend/tests/test_fk_integrity_migration.py
Three tests assert: each constraint name appears behind an IF NOT EXISTS pg_constraint guard in the migration; DELETE orphan statements precede their corresponding ALTER steps; and supabase_schema.sql declares the expected inline REFERENCES fragments.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

Poem

🐇 Hop hop, the orphan rows are gone,
No dangling edges left to mourn!
With REFERENCES neat and guards in place,
Each user_id finds its proper space.
The schema's tight, the migration's sound —
Foreign keys keep the data bound! 🌱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically summarizes the main change: adding missing FK constraints on three database columns across two tables, with linked issue references.
Description check✅ PassedThe description provides a clear summary, detailed changes breakdown, rationale, and verification steps, mostly following the repository template structure.
Linked Issues check✅ PassedThe PR fully implements the acceptance criteria from #179: adds FK constraint from graph_edges.user_id to users(id), cleans orphan rows via migration, and updates both migration and schema files. Changes also address the objectives for #180 (notes table FKs).
Out of Scope Changes check✅ PassedAll changes are directly scoped to enforcing FK integrity on the three identified columns. The index addition on graph_edges.user_id and test file are supporting/validation changes aligned with the stated objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/fk-integrity

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.

@coderabbitaicoderabbitaiBot 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 `@backend/db/supabase_schema.sql`:
- Around line 495-496: The comment explaining why note_concepts.concept_node_id
is intentionally not a hard FK states it's because graph_nodes uses TEXT ids
managed by application code, but this reasoning is inconsistent since
graph_edges.source_node_id and graph_edges.target_node_id also reference
graph_nodes(id) with TEXT ids yet have hard FKs defined. Update the comment at
lines 495-496 to clarify the actual rationale for not adding the FK constraint
to note_concepts.concept_node_id, such as whether concepts can exist before
their corresponding graph nodes are created or if there's a specific
application-level design consideration that necessitates this approach.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b17fdcab-4e71-4e4f-83a4-8009fa8e242a

📥 Commits

Reviewing files that changed from the base of the PR and between 4521305 and 75d3729.

📒 Files selected for processing (3)
  • backend/db/migration_fk_integrity.sql
  • backend/db/supabase_schema.sql
  • backend/tests/test_fk_integrity_migration.py

Comment on lines +495 to +496
-- links. Only the note_concepts.concept_node_id link is intentionally NOT a
-- hard FK, because graph_nodes uses TEXT ids managed by application code.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify the rationale for not adding FK to note_concepts.concept_node_id.

The comment states the link is "intentionally NOT a hard FK, because graph_nodes uses TEXT ids managed by application code." However, graph_edges.source_node_id and graph_edges.target_node_id (lines 98-99) both have hard FKs to graph_nodes(id), so the stated reason is inconsistent.

If the real reason is different (e.g., concepts can exist before graph nodes are created, or there's an application-level design consideration), please update the comment to explain the actual rationale.

🤖 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 `@backend/db/supabase_schema.sql` around lines 495 - 496, The comment
explaining why note_concepts.concept_node_id is intentionally not a hard FK
states it's because graph_nodes uses TEXT ids managed by application code, but
this reasoning is inconsistent since graph_edges.source_node_id and
graph_edges.target_node_id also reference graph_nodes(id) with TEXT ids yet have
hard FKs defined. Update the comment at lines 495-496 to clarify the actual
rationale for not adding the FK constraint to note_concepts.concept_node_id,
such as whether concepts can exist before their corresponding graph nodes are
created or if there's a specific application-level design consideration that
necessitates this approach.

Postgres does not auto-index the referencing side of a foreign key, and
sibling tables index this access path. Add the index in both the
migration and the canonical schema.
…he new FKs (#179, #180)
The FKs have no ON DELETE clause, so they default to RESTRICT (block
hard delete) rather than cascading cleanup. Correct the schema comment
that implied cleanup-on-delete, and document the real semantics at the
graph_edges/notes FK sites and in the migration header.
@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 24, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging4f8655eCommit Preview URL

Branch Preview URL
Jun 24 2026, 02:50 PM

main restructured db/ into ordered migrations applied by migrate.py, deleting
the flat migration_*.sql files. Move the FK-integrity DDL into
migrations/0020_fk_integrity.sql so existing databases get the graph_edges/notes
FK constraints. The DDL is already idempotent (pg_constraint-guarded ADD
CONSTRAINT, orphan DELETEs first, CREATE INDEX IF NOT EXISTS), so it is also a
no-op on fresh DBs that already have the inline FKs from 0001_baseline_schema.
…iles
supabase_schema.sql was deleted when main restructured db/. Assert the FK DDL
invariants against migrations/0020_fk_integrity.sql and the inline REFERENCES
against migrations/0001_baseline_schema.sql instead, preserving the original
verification intent (guarded ADD CONSTRAINT, orphan cleanup ordering, inline FKs
on fresh DBs). Also assert the idx_graph_edges_user_id index this PR adds.
@AndresL230

Copy link
Copy Markdown
Collaborator

Superseded by the DB modular redesign (#279). The graph_edges.user_id + notes FKs (#179/#180) ship in 0023/0025. Closing as obsolete (migration collision). Reopen if not fully covered by #279.

@AndresL230
AndresL230 deleted the fix/fk-integrity branch June 27, 2026 04:21
Sign up for freeto 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.

graph_edges.user_id has no FOREIGN KEY (orphan-row risk) — inconsistent with sibling tables

2 participants

@Jose-Gael-Cruz-Lopez@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(db): add missing FK constraints on graph_edges + notes (#179, #180) - #245

Closed
Jose-Gael-Cruz-Lopez wants to merge 15 commits into
mainfrom
fix/fk-integrity
Closed

fix(db): add missing FK constraints on graph_edges + notes (#179, #180)#245
Jose-Gael-Cruz-Lopez wants to merge 15 commits into
mainfrom
fix/fk-integrity

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jun 22, 2026

Copy link
Copy Markdown
Member

graph_edges.user_id, notes.user_id, and notes.course_id were bare TEXT columns with no REFERENCES, inconsistent with every sibling table in the learning schema. An edge/note could be written for a non-existent user, and deleting a course left dangling notes that list_notes surfaces but can't resolve.

Changes

  • backend/db/migration_fk_integrity.sql — new hand-applied migration:
    • deletes orphan rows first (so the ALTER TABLE can validate),
    • adds each FK behind the DO $$ … IF NOT EXISTS (SELECT 1 FROM pg_constraint …) guard already used in migration_gradebook.sql (Postgres has no ADD CONSTRAINT IF NOT EXISTS), so it is safely re-runnable.
  • backend/db/supabase_schema.sql — inline REFERENCES on all three columns so fresh databases are born with the FKs; comment block updated to note only the note_concepts.concept_node_id link stays soft.
  • backend/tests/test_fk_integrity_migration.py — drift guard: each constraint is guarded, orphans are cleaned before the ALTER, schema declares the inline references.

Verification

  • ruff check . clean; gated suite green (+3 new).
  • Orphan deletion is intentional: rows that violate the new FK are invalid data (no owning user, or a course that no longer exists).

Closes#179, #180.

Summary by CodeRabbit

  • Bug Fixes

    • Strengthened database integrity by enforcing foreign-key relationships for graph edges and notes to ensure valid user and course references.
    • Automatically removes orphaned records that would violate these relationships.
    • Added indexing to improve lookups by user on graph edges.
  • Tests

    • Added drift-guard tests that verify the migration safely applies foreign-key constraints and that orphan cleanup occurs before constraints are added.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 22, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitUpdated (UTC)
❌ Deployment failed
View logs
frontend75d3729Jun 22 2026, 03:55 AM

@coderabbitai

coderabbitaiBot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Jose-Gael-Cruz-Lopez, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 43 minutes and 55 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

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 credits.

🚦 How do rate 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 see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a71d73bd-17bd-4660-a650-0be10b29825e

📥 Commits

Reviewing files that changed from the base of the PR and between cc1ec58 and 4f8655e.

📒 Files selected for processing (3)
  • backend/db/migrations/0001_baseline_schema.sql
  • backend/db/migrations/0020_fk_integrity.sql
  • backend/tests/test_fk_integrity_migration.py
📝 Walkthrough

Walkthrough

Adds inline REFERENCES FK constraints to graph_edges.user_id, notes.user_id, and notes.course_id in the canonical schema. A new idempotent migration script purges orphan rows before conditionally adding each FK constraint via pg_constraint name guards. A new test module validates that the migration and schema remain consistent with each other.

Changes

FK Integrity for graph_edges and notes

Layer / File(s)Summary
Schema inline REFERENCES declarations
backend/db/supabase_schema.sql
graph_edges.user_id gains REFERENCES users(id) with a new supporting index; notes.user_id and notes.course_id gain REFERENCES users(id) and REFERENCES courses(id), converting three bare TEXT NOT NULL columns into hard FK columns with updated documentation.
Idempotent orphan-cleanup and conditional FK migration
backend/db/migration_fk_integrity.sql
New migration that deletes orphan rows in graph_edges and notes before each ALTER TABLE, then adds each FK constraint only when absent, guarded by pg_constraint name checks inside DO $$ ... $$ blocks.
Drift-guard tests
backend/tests/test_fk_integrity_migration.py
Three tests assert: each constraint name appears behind an IF NOT EXISTS pg_constraint guard in the migration; DELETE orphan statements precede their corresponding ALTER steps; and supabase_schema.sql declares the expected inline REFERENCES fragments.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

Poem

🐇 Hop hop, the orphan rows are gone,
No dangling edges left to mourn!
With REFERENCES neat and guards in place,
Each user_id finds its proper space.
The schema's tight, the migration's sound —
Foreign keys keep the data bound! 🌱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically summarizes the main change: adding missing FK constraints on three database columns across two tables, with linked issue references.
Description check✅ PassedThe description provides a clear summary, detailed changes breakdown, rationale, and verification steps, mostly following the repository template structure.
Linked Issues check✅ PassedThe PR fully implements the acceptance criteria from #179: adds FK constraint from graph_edges.user_id to users(id), cleans orphan rows via migration, and updates both migration and schema files. Changes also address the objectives for #180 (notes table FKs).
Out of Scope Changes check✅ PassedAll changes are directly scoped to enforcing FK integrity on the three identified columns. The index addition on graph_edges.user_id and test file are supporting/validation changes aligned with the stated objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/fk-integrity

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.

@coderabbitaicoderabbitaiBot 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 `@backend/db/supabase_schema.sql`:
- Around line 495-496: The comment explaining why note_concepts.concept_node_id
is intentionally not a hard FK states it's because graph_nodes uses TEXT ids
managed by application code, but this reasoning is inconsistent since
graph_edges.source_node_id and graph_edges.target_node_id also reference
graph_nodes(id) with TEXT ids yet have hard FKs defined. Update the comment at
lines 495-496 to clarify the actual rationale for not adding the FK constraint
to note_concepts.concept_node_id, such as whether concepts can exist before
their corresponding graph nodes are created or if there's a specific
application-level design consideration that necessitates this approach.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b17fdcab-4e71-4e4f-83a4-8009fa8e242a

📥 Commits

Reviewing files that changed from the base of the PR and between 4521305 and 75d3729.

📒 Files selected for processing (3)
  • backend/db/migration_fk_integrity.sql
  • backend/db/supabase_schema.sql
  • backend/tests/test_fk_integrity_migration.py

Comment on lines +495 to +496
-- links. Only the note_concepts.concept_node_id link is intentionally NOT a
-- hard FK, because graph_nodes uses TEXT ids managed by application code.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify the rationale for not adding FK to note_concepts.concept_node_id.

The comment states the link is "intentionally NOT a hard FK, because graph_nodes uses TEXT ids managed by application code." However, graph_edges.source_node_id and graph_edges.target_node_id (lines 98-99) both have hard FKs to graph_nodes(id), so the stated reason is inconsistent.

If the real reason is different (e.g., concepts can exist before graph nodes are created, or there's an application-level design consideration), please update the comment to explain the actual rationale.

🤖 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 `@backend/db/supabase_schema.sql` around lines 495 - 496, The comment
explaining why note_concepts.concept_node_id is intentionally not a hard FK
states it's because graph_nodes uses TEXT ids managed by application code, but
this reasoning is inconsistent since graph_edges.source_node_id and
graph_edges.target_node_id also reference graph_nodes(id) with TEXT ids yet have
hard FKs defined. Update the comment at lines 495-496 to clarify the actual
rationale for not adding the FK constraint to note_concepts.concept_node_id,
such as whether concepts can exist before their corresponding graph nodes are
created or if there's a specific application-level design consideration that
necessitates this approach.

Postgres does not auto-index the referencing side of a foreign key, and
sibling tables index this access path. Add the index in both the
migration and the canonical schema.
…he new FKs (#179, #180)
The FKs have no ON DELETE clause, so they default to RESTRICT (block
hard delete) rather than cascading cleanup. Correct the schema comment
that implied cleanup-on-delete, and document the real semantics at the
graph_edges/notes FK sites and in the migration header.
@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 24, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging4f8655eCommit Preview URL

Branch Preview URL
Jun 24 2026, 02:50 PM

main restructured db/ into ordered migrations applied by migrate.py, deleting
the flat migration_*.sql files. Move the FK-integrity DDL into
migrations/0020_fk_integrity.sql so existing databases get the graph_edges/notes
FK constraints. The DDL is already idempotent (pg_constraint-guarded ADD
CONSTRAINT, orphan DELETEs first, CREATE INDEX IF NOT EXISTS), so it is also a
no-op on fresh DBs that already have the inline FKs from 0001_baseline_schema.
…iles
supabase_schema.sql was deleted when main restructured db/. Assert the FK DDL
invariants against migrations/0020_fk_integrity.sql and the inline REFERENCES
against migrations/0001_baseline_schema.sql instead, preserving the original
verification intent (guarded ADD CONSTRAINT, orphan cleanup ordering, inline FKs
on fresh DBs). Also assert the idx_graph_edges_user_id index this PR adds.
@AndresL230

Copy link
Copy Markdown
Collaborator

Superseded by the DB modular redesign (#279). The graph_edges.user_id + notes FKs (#179/#180) ship in 0023/0025. Closing as obsolete (migration collision). Reopen if not fully covered by #279.

@AndresL230
AndresL230 deleted the fix/fk-integrity branch June 27, 2026 04:21
Sign up for freeto 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.

graph_edges.user_id has no FOREIGN KEY (orphan-row risk) — inconsistent with sibling tables

2 participants

@Jose-Gael-Cruz-Lopez@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(db): add missing FK constraints on graph_edges + notes (#179, #180) - #245

Closed
Jose-Gael-Cruz-Lopez wants to merge 15 commits into
mainfrom
fix/fk-integrity
Closed

fix(db): add missing FK constraints on graph_edges + notes (#179, #180)#245
Jose-Gael-Cruz-Lopez wants to merge 15 commits into
mainfrom
fix/fk-integrity

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jun 22, 2026

Copy link
Copy Markdown
Member

graph_edges.user_id, notes.user_id, and notes.course_id were bare TEXT columns with no REFERENCES, inconsistent with every sibling table in the learning schema. An edge/note could be written for a non-existent user, and deleting a course left dangling notes that list_notes surfaces but can't resolve.

Changes

  • backend/db/migration_fk_integrity.sql — new hand-applied migration:
    • deletes orphan rows first (so the ALTER TABLE can validate),
    • adds each FK behind the DO $$ … IF NOT EXISTS (SELECT 1 FROM pg_constraint …) guard already used in migration_gradebook.sql (Postgres has no ADD CONSTRAINT IF NOT EXISTS), so it is safely re-runnable.
  • backend/db/supabase_schema.sql — inline REFERENCES on all three columns so fresh databases are born with the FKs; comment block updated to note only the note_concepts.concept_node_id link stays soft.
  • backend/tests/test_fk_integrity_migration.py — drift guard: each constraint is guarded, orphans are cleaned before the ALTER, schema declares the inline references.

Verification

  • ruff check . clean; gated suite green (+3 new).
  • Orphan deletion is intentional: rows that violate the new FK are invalid data (no owning user, or a course that no longer exists).

Closes#179, #180.

Summary by CodeRabbit

  • Bug Fixes

    • Strengthened database integrity by enforcing foreign-key relationships for graph edges and notes to ensure valid user and course references.
    • Automatically removes orphaned records that would violate these relationships.
    • Added indexing to improve lookups by user on graph edges.
  • Tests

    • Added drift-guard tests that verify the migration safely applies foreign-key constraints and that orphan cleanup occurs before constraints are added.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 22, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitUpdated (UTC)
❌ Deployment failed
View logs
frontend75d3729Jun 22 2026, 03:55 AM

@coderabbitai

coderabbitaiBot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Jose-Gael-Cruz-Lopez, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 43 minutes and 55 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

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 credits.

🚦 How do rate 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 see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a71d73bd-17bd-4660-a650-0be10b29825e

📥 Commits

Reviewing files that changed from the base of the PR and between cc1ec58 and 4f8655e.

📒 Files selected for processing (3)
  • backend/db/migrations/0001_baseline_schema.sql
  • backend/db/migrations/0020_fk_integrity.sql
  • backend/tests/test_fk_integrity_migration.py
📝 Walkthrough

Walkthrough

Adds inline REFERENCES FK constraints to graph_edges.user_id, notes.user_id, and notes.course_id in the canonical schema. A new idempotent migration script purges orphan rows before conditionally adding each FK constraint via pg_constraint name guards. A new test module validates that the migration and schema remain consistent with each other.

Changes

FK Integrity for graph_edges and notes

Layer / File(s)Summary
Schema inline REFERENCES declarations
backend/db/supabase_schema.sql
graph_edges.user_id gains REFERENCES users(id) with a new supporting index; notes.user_id and notes.course_id gain REFERENCES users(id) and REFERENCES courses(id), converting three bare TEXT NOT NULL columns into hard FK columns with updated documentation.
Idempotent orphan-cleanup and conditional FK migration
backend/db/migration_fk_integrity.sql
New migration that deletes orphan rows in graph_edges and notes before each ALTER TABLE, then adds each FK constraint only when absent, guarded by pg_constraint name checks inside DO $$ ... $$ blocks.
Drift-guard tests
backend/tests/test_fk_integrity_migration.py
Three tests assert: each constraint name appears behind an IF NOT EXISTS pg_constraint guard in the migration; DELETE orphan statements precede their corresponding ALTER steps; and supabase_schema.sql declares the expected inline REFERENCES fragments.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

Poem

🐇 Hop hop, the orphan rows are gone,
No dangling edges left to mourn!
With REFERENCES neat and guards in place,
Each user_id finds its proper space.
The schema's tight, the migration's sound —
Foreign keys keep the data bound! 🌱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically summarizes the main change: adding missing FK constraints on three database columns across two tables, with linked issue references.
Description check✅ PassedThe description provides a clear summary, detailed changes breakdown, rationale, and verification steps, mostly following the repository template structure.
Linked Issues check✅ PassedThe PR fully implements the acceptance criteria from #179: adds FK constraint from graph_edges.user_id to users(id), cleans orphan rows via migration, and updates both migration and schema files. Changes also address the objectives for #180 (notes table FKs).
Out of Scope Changes check✅ PassedAll changes are directly scoped to enforcing FK integrity on the three identified columns. The index addition on graph_edges.user_id and test file are supporting/validation changes aligned with the stated objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/fk-integrity

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.

@coderabbitaicoderabbitaiBot 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 `@backend/db/supabase_schema.sql`:
- Around line 495-496: The comment explaining why note_concepts.concept_node_id
is intentionally not a hard FK states it's because graph_nodes uses TEXT ids
managed by application code, but this reasoning is inconsistent since
graph_edges.source_node_id and graph_edges.target_node_id also reference
graph_nodes(id) with TEXT ids yet have hard FKs defined. Update the comment at
lines 495-496 to clarify the actual rationale for not adding the FK constraint
to note_concepts.concept_node_id, such as whether concepts can exist before
their corresponding graph nodes are created or if there's a specific
application-level design consideration that necessitates this approach.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b17fdcab-4e71-4e4f-83a4-8009fa8e242a

📥 Commits

Reviewing files that changed from the base of the PR and between 4521305 and 75d3729.

📒 Files selected for processing (3)
  • backend/db/migration_fk_integrity.sql
  • backend/db/supabase_schema.sql
  • backend/tests/test_fk_integrity_migration.py

Comment on lines +495 to +496
-- links. Only the note_concepts.concept_node_id link is intentionally NOT a
-- hard FK, because graph_nodes uses TEXT ids managed by application code.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify the rationale for not adding FK to note_concepts.concept_node_id.

The comment states the link is "intentionally NOT a hard FK, because graph_nodes uses TEXT ids managed by application code." However, graph_edges.source_node_id and graph_edges.target_node_id (lines 98-99) both have hard FKs to graph_nodes(id), so the stated reason is inconsistent.

If the real reason is different (e.g., concepts can exist before graph nodes are created, or there's an application-level design consideration), please update the comment to explain the actual rationale.

🤖 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 `@backend/db/supabase_schema.sql` around lines 495 - 496, The comment
explaining why note_concepts.concept_node_id is intentionally not a hard FK
states it's because graph_nodes uses TEXT ids managed by application code, but
this reasoning is inconsistent since graph_edges.source_node_id and
graph_edges.target_node_id also reference graph_nodes(id) with TEXT ids yet have
hard FKs defined. Update the comment at lines 495-496 to clarify the actual
rationale for not adding the FK constraint to note_concepts.concept_node_id,
such as whether concepts can exist before their corresponding graph nodes are
created or if there's a specific application-level design consideration that
necessitates this approach.

Postgres does not auto-index the referencing side of a foreign key, and
sibling tables index this access path. Add the index in both the
migration and the canonical schema.
…he new FKs (#179, #180)
The FKs have no ON DELETE clause, so they default to RESTRICT (block
hard delete) rather than cascading cleanup. Correct the schema comment
that implied cleanup-on-delete, and document the real semantics at the
graph_edges/notes FK sites and in the migration header.
@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 24, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging4f8655eCommit Preview URL

Branch Preview URL
Jun 24 2026, 02:50 PM

main restructured db/ into ordered migrations applied by migrate.py, deleting
the flat migration_*.sql files. Move the FK-integrity DDL into
migrations/0020_fk_integrity.sql so existing databases get the graph_edges/notes
FK constraints. The DDL is already idempotent (pg_constraint-guarded ADD
CONSTRAINT, orphan DELETEs first, CREATE INDEX IF NOT EXISTS), so it is also a
no-op on fresh DBs that already have the inline FKs from 0001_baseline_schema.
…iles
supabase_schema.sql was deleted when main restructured db/. Assert the FK DDL
invariants against migrations/0020_fk_integrity.sql and the inline REFERENCES
against migrations/0001_baseline_schema.sql instead, preserving the original
verification intent (guarded ADD CONSTRAINT, orphan cleanup ordering, inline FKs
on fresh DBs). Also assert the idx_graph_edges_user_id index this PR adds.
@AndresL230

Copy link
Copy Markdown
Collaborator

Superseded by the DB modular redesign (#279). The graph_edges.user_id + notes FKs (#179/#180) ship in 0023/0025. Closing as obsolete (migration collision). Reopen if not fully covered by #279.

@AndresL230
AndresL230 deleted the fix/fk-integrity branch June 27, 2026 04:21
Sign up for freeto 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.

graph_edges.user_id has no FOREIGN KEY (orphan-row risk) — inconsistent with sibling tables

2 participants

@Jose-Gael-Cruz-Lopez@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(db): add missing FK constraints on graph_edges + notes (#179, #180) - #245

Closed
Jose-Gael-Cruz-Lopez wants to merge 15 commits into
mainfrom
fix/fk-integrity
Closed

fix(db): add missing FK constraints on graph_edges + notes (#179, #180)#245
Jose-Gael-Cruz-Lopez wants to merge 15 commits into
mainfrom
fix/fk-integrity

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jun 22, 2026

Copy link
Copy Markdown
Member

graph_edges.user_id, notes.user_id, and notes.course_id were bare TEXT columns with no REFERENCES, inconsistent with every sibling table in the learning schema. An edge/note could be written for a non-existent user, and deleting a course left dangling notes that list_notes surfaces but can't resolve.

Changes

  • backend/db/migration_fk_integrity.sql — new hand-applied migration:
    • deletes orphan rows first (so the ALTER TABLE can validate),
    • adds each FK behind the DO $$ … IF NOT EXISTS (SELECT 1 FROM pg_constraint …) guard already used in migration_gradebook.sql (Postgres has no ADD CONSTRAINT IF NOT EXISTS), so it is safely re-runnable.
  • backend/db/supabase_schema.sql — inline REFERENCES on all three columns so fresh databases are born with the FKs; comment block updated to note only the note_concepts.concept_node_id link stays soft.
  • backend/tests/test_fk_integrity_migration.py — drift guard: each constraint is guarded, orphans are cleaned before the ALTER, schema declares the inline references.

Verification

  • ruff check . clean; gated suite green (+3 new).
  • Orphan deletion is intentional: rows that violate the new FK are invalid data (no owning user, or a course that no longer exists).

Closes#179, #180.

Summary by CodeRabbit

  • Bug Fixes

    • Strengthened database integrity by enforcing foreign-key relationships for graph edges and notes to ensure valid user and course references.
    • Automatically removes orphaned records that would violate these relationships.
    • Added indexing to improve lookups by user on graph edges.
  • Tests

    • Added drift-guard tests that verify the migration safely applies foreign-key constraints and that orphan cleanup occurs before constraints are added.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 22, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitUpdated (UTC)
❌ Deployment failed
View logs
frontend75d3729Jun 22 2026, 03:55 AM

@coderabbitai

coderabbitaiBot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Jose-Gael-Cruz-Lopez, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 43 minutes and 55 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

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 credits.

🚦 How do rate 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 see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a71d73bd-17bd-4660-a650-0be10b29825e

📥 Commits

Reviewing files that changed from the base of the PR and between cc1ec58 and 4f8655e.

📒 Files selected for processing (3)
  • backend/db/migrations/0001_baseline_schema.sql
  • backend/db/migrations/0020_fk_integrity.sql
  • backend/tests/test_fk_integrity_migration.py
📝 Walkthrough

Walkthrough

Adds inline REFERENCES FK constraints to graph_edges.user_id, notes.user_id, and notes.course_id in the canonical schema. A new idempotent migration script purges orphan rows before conditionally adding each FK constraint via pg_constraint name guards. A new test module validates that the migration and schema remain consistent with each other.

Changes

FK Integrity for graph_edges and notes

Layer / File(s)Summary
Schema inline REFERENCES declarations
backend/db/supabase_schema.sql
graph_edges.user_id gains REFERENCES users(id) with a new supporting index; notes.user_id and notes.course_id gain REFERENCES users(id) and REFERENCES courses(id), converting three bare TEXT NOT NULL columns into hard FK columns with updated documentation.
Idempotent orphan-cleanup and conditional FK migration
backend/db/migration_fk_integrity.sql
New migration that deletes orphan rows in graph_edges and notes before each ALTER TABLE, then adds each FK constraint only when absent, guarded by pg_constraint name checks inside DO $$ ... $$ blocks.
Drift-guard tests
backend/tests/test_fk_integrity_migration.py
Three tests assert: each constraint name appears behind an IF NOT EXISTS pg_constraint guard in the migration; DELETE orphan statements precede their corresponding ALTER steps; and supabase_schema.sql declares the expected inline REFERENCES fragments.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

Poem

🐇 Hop hop, the orphan rows are gone,
No dangling edges left to mourn!
With REFERENCES neat and guards in place,
Each user_id finds its proper space.
The schema's tight, the migration's sound —
Foreign keys keep the data bound! 🌱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically summarizes the main change: adding missing FK constraints on three database columns across two tables, with linked issue references.
Description check✅ PassedThe description provides a clear summary, detailed changes breakdown, rationale, and verification steps, mostly following the repository template structure.
Linked Issues check✅ PassedThe PR fully implements the acceptance criteria from #179: adds FK constraint from graph_edges.user_id to users(id), cleans orphan rows via migration, and updates both migration and schema files. Changes also address the objectives for #180 (notes table FKs).
Out of Scope Changes check✅ PassedAll changes are directly scoped to enforcing FK integrity on the three identified columns. The index addition on graph_edges.user_id and test file are supporting/validation changes aligned with the stated objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/fk-integrity

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.

@coderabbitaicoderabbitaiBot 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 `@backend/db/supabase_schema.sql`:
- Around line 495-496: The comment explaining why note_concepts.concept_node_id
is intentionally not a hard FK states it's because graph_nodes uses TEXT ids
managed by application code, but this reasoning is inconsistent since
graph_edges.source_node_id and graph_edges.target_node_id also reference
graph_nodes(id) with TEXT ids yet have hard FKs defined. Update the comment at
lines 495-496 to clarify the actual rationale for not adding the FK constraint
to note_concepts.concept_node_id, such as whether concepts can exist before
their corresponding graph nodes are created or if there's a specific
application-level design consideration that necessitates this approach.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b17fdcab-4e71-4e4f-83a4-8009fa8e242a

📥 Commits

Reviewing files that changed from the base of the PR and between 4521305 and 75d3729.

📒 Files selected for processing (3)
  • backend/db/migration_fk_integrity.sql
  • backend/db/supabase_schema.sql
  • backend/tests/test_fk_integrity_migration.py

Comment on lines +495 to +496
-- links. Only the note_concepts.concept_node_id link is intentionally NOT a
-- hard FK, because graph_nodes uses TEXT ids managed by application code.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify the rationale for not adding FK to note_concepts.concept_node_id.

The comment states the link is "intentionally NOT a hard FK, because graph_nodes uses TEXT ids managed by application code." However, graph_edges.source_node_id and graph_edges.target_node_id (lines 98-99) both have hard FKs to graph_nodes(id), so the stated reason is inconsistent.

If the real reason is different (e.g., concepts can exist before graph nodes are created, or there's an application-level design consideration), please update the comment to explain the actual rationale.

🤖 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 `@backend/db/supabase_schema.sql` around lines 495 - 496, The comment
explaining why note_concepts.concept_node_id is intentionally not a hard FK
states it's because graph_nodes uses TEXT ids managed by application code, but
this reasoning is inconsistent since graph_edges.source_node_id and
graph_edges.target_node_id also reference graph_nodes(id) with TEXT ids yet have
hard FKs defined. Update the comment at lines 495-496 to clarify the actual
rationale for not adding the FK constraint to note_concepts.concept_node_id,
such as whether concepts can exist before their corresponding graph nodes are
created or if there's a specific application-level design consideration that
necessitates this approach.

Postgres does not auto-index the referencing side of a foreign key, and
sibling tables index this access path. Add the index in both the
migration and the canonical schema.
…he new FKs (#179, #180)
The FKs have no ON DELETE clause, so they default to RESTRICT (block
hard delete) rather than cascading cleanup. Correct the schema comment
that implied cleanup-on-delete, and document the real semantics at the
graph_edges/notes FK sites and in the migration header.
@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 24, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging4f8655eCommit Preview URL

Branch Preview URL
Jun 24 2026, 02:50 PM

main restructured db/ into ordered migrations applied by migrate.py, deleting
the flat migration_*.sql files. Move the FK-integrity DDL into
migrations/0020_fk_integrity.sql so existing databases get the graph_edges/notes
FK constraints. The DDL is already idempotent (pg_constraint-guarded ADD
CONSTRAINT, orphan DELETEs first, CREATE INDEX IF NOT EXISTS), so it is also a
no-op on fresh DBs that already have the inline FKs from 0001_baseline_schema.
…iles
supabase_schema.sql was deleted when main restructured db/. Assert the FK DDL
invariants against migrations/0020_fk_integrity.sql and the inline REFERENCES
against migrations/0001_baseline_schema.sql instead, preserving the original
verification intent (guarded ADD CONSTRAINT, orphan cleanup ordering, inline FKs
on fresh DBs). Also assert the idx_graph_edges_user_id index this PR adds.
@AndresL230

Copy link
Copy Markdown
Collaborator

Superseded by the DB modular redesign (#279). The graph_edges.user_id + notes FKs (#179/#180) ship in 0023/0025. Closing as obsolete (migration collision). Reopen if not fully covered by #279.

@AndresL230
AndresL230 deleted the fix/fk-integrity branch June 27, 2026 04:21
Sign up for freeto 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.

graph_edges.user_id has no FOREIGN KEY (orphan-row risk) — inconsistent with sibling tables

2 participants

@Jose-Gael-Cruz-Lopez@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(db): add missing FK constraints on graph_edges + notes (#179, #180) - #245

Closed
Jose-Gael-Cruz-Lopez wants to merge 15 commits into
mainfrom
fix/fk-integrity
Closed

fix(db): add missing FK constraints on graph_edges + notes (#179, #180)#245
Jose-Gael-Cruz-Lopez wants to merge 15 commits into
mainfrom
fix/fk-integrity

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jun 22, 2026

Copy link
Copy Markdown
Member

graph_edges.user_id, notes.user_id, and notes.course_id were bare TEXT columns with no REFERENCES, inconsistent with every sibling table in the learning schema. An edge/note could be written for a non-existent user, and deleting a course left dangling notes that list_notes surfaces but can't resolve.

Changes

  • backend/db/migration_fk_integrity.sql — new hand-applied migration:
    • deletes orphan rows first (so the ALTER TABLE can validate),
    • adds each FK behind the DO $$ … IF NOT EXISTS (SELECT 1 FROM pg_constraint …) guard already used in migration_gradebook.sql (Postgres has no ADD CONSTRAINT IF NOT EXISTS), so it is safely re-runnable.
  • backend/db/supabase_schema.sql — inline REFERENCES on all three columns so fresh databases are born with the FKs; comment block updated to note only the note_concepts.concept_node_id link stays soft.
  • backend/tests/test_fk_integrity_migration.py — drift guard: each constraint is guarded, orphans are cleaned before the ALTER, schema declares the inline references.

Verification

  • ruff check . clean; gated suite green (+3 new).
  • Orphan deletion is intentional: rows that violate the new FK are invalid data (no owning user, or a course that no longer exists).

Closes#179, #180.

Summary by CodeRabbit

  • Bug Fixes

    • Strengthened database integrity by enforcing foreign-key relationships for graph edges and notes to ensure valid user and course references.
    • Automatically removes orphaned records that would violate these relationships.
    • Added indexing to improve lookups by user on graph edges.
  • Tests

    • Added drift-guard tests that verify the migration safely applies foreign-key constraints and that orphan cleanup occurs before constraints are added.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 22, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitUpdated (UTC)
❌ Deployment failed
View logs
frontend75d3729Jun 22 2026, 03:55 AM

@coderabbitai

coderabbitaiBot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Jose-Gael-Cruz-Lopez, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 43 minutes and 55 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

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 credits.

🚦 How do rate 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 see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a71d73bd-17bd-4660-a650-0be10b29825e

📥 Commits

Reviewing files that changed from the base of the PR and between cc1ec58 and 4f8655e.

📒 Files selected for processing (3)
  • backend/db/migrations/0001_baseline_schema.sql
  • backend/db/migrations/0020_fk_integrity.sql
  • backend/tests/test_fk_integrity_migration.py
📝 Walkthrough

Walkthrough

Adds inline REFERENCES FK constraints to graph_edges.user_id, notes.user_id, and notes.course_id in the canonical schema. A new idempotent migration script purges orphan rows before conditionally adding each FK constraint via pg_constraint name guards. A new test module validates that the migration and schema remain consistent with each other.

Changes

FK Integrity for graph_edges and notes

Layer / File(s)Summary
Schema inline REFERENCES declarations
backend/db/supabase_schema.sql
graph_edges.user_id gains REFERENCES users(id) with a new supporting index; notes.user_id and notes.course_id gain REFERENCES users(id) and REFERENCES courses(id), converting three bare TEXT NOT NULL columns into hard FK columns with updated documentation.
Idempotent orphan-cleanup and conditional FK migration
backend/db/migration_fk_integrity.sql
New migration that deletes orphan rows in graph_edges and notes before each ALTER TABLE, then adds each FK constraint only when absent, guarded by pg_constraint name checks inside DO $$ ... $$ blocks.
Drift-guard tests
backend/tests/test_fk_integrity_migration.py
Three tests assert: each constraint name appears behind an IF NOT EXISTS pg_constraint guard in the migration; DELETE orphan statements precede their corresponding ALTER steps; and supabase_schema.sql declares the expected inline REFERENCES fragments.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

Poem

🐇 Hop hop, the orphan rows are gone,
No dangling edges left to mourn!
With REFERENCES neat and guards in place,
Each user_id finds its proper space.
The schema's tight, the migration's sound —
Foreign keys keep the data bound! 🌱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically summarizes the main change: adding missing FK constraints on three database columns across two tables, with linked issue references.
Description check✅ PassedThe description provides a clear summary, detailed changes breakdown, rationale, and verification steps, mostly following the repository template structure.
Linked Issues check✅ PassedThe PR fully implements the acceptance criteria from #179: adds FK constraint from graph_edges.user_id to users(id), cleans orphan rows via migration, and updates both migration and schema files. Changes also address the objectives for #180 (notes table FKs).
Out of Scope Changes check✅ PassedAll changes are directly scoped to enforcing FK integrity on the three identified columns. The index addition on graph_edges.user_id and test file are supporting/validation changes aligned with the stated objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/fk-integrity

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.

@coderabbitaicoderabbitaiBot 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 `@backend/db/supabase_schema.sql`:
- Around line 495-496: The comment explaining why note_concepts.concept_node_id
is intentionally not a hard FK states it's because graph_nodes uses TEXT ids
managed by application code, but this reasoning is inconsistent since
graph_edges.source_node_id and graph_edges.target_node_id also reference
graph_nodes(id) with TEXT ids yet have hard FKs defined. Update the comment at
lines 495-496 to clarify the actual rationale for not adding the FK constraint
to note_concepts.concept_node_id, such as whether concepts can exist before
their corresponding graph nodes are created or if there's a specific
application-level design consideration that necessitates this approach.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b17fdcab-4e71-4e4f-83a4-8009fa8e242a

📥 Commits

Reviewing files that changed from the base of the PR and between 4521305 and 75d3729.

📒 Files selected for processing (3)
  • backend/db/migration_fk_integrity.sql
  • backend/db/supabase_schema.sql
  • backend/tests/test_fk_integrity_migration.py

Comment on lines +495 to +496
-- links. Only the note_concepts.concept_node_id link is intentionally NOT a
-- hard FK, because graph_nodes uses TEXT ids managed by application code.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify the rationale for not adding FK to note_concepts.concept_node_id.

The comment states the link is "intentionally NOT a hard FK, because graph_nodes uses TEXT ids managed by application code." However, graph_edges.source_node_id and graph_edges.target_node_id (lines 98-99) both have hard FKs to graph_nodes(id), so the stated reason is inconsistent.

If the real reason is different (e.g., concepts can exist before graph nodes are created, or there's an application-level design consideration), please update the comment to explain the actual rationale.

🤖 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 `@backend/db/supabase_schema.sql` around lines 495 - 496, The comment
explaining why note_concepts.concept_node_id is intentionally not a hard FK
states it's because graph_nodes uses TEXT ids managed by application code, but
this reasoning is inconsistent since graph_edges.source_node_id and
graph_edges.target_node_id also reference graph_nodes(id) with TEXT ids yet have
hard FKs defined. Update the comment at lines 495-496 to clarify the actual
rationale for not adding the FK constraint to note_concepts.concept_node_id,
such as whether concepts can exist before their corresponding graph nodes are
created or if there's a specific application-level design consideration that
necessitates this approach.

Postgres does not auto-index the referencing side of a foreign key, and
sibling tables index this access path. Add the index in both the
migration and the canonical schema.
…he new FKs (#179, #180)
The FKs have no ON DELETE clause, so they default to RESTRICT (block
hard delete) rather than cascading cleanup. Correct the schema comment
that implied cleanup-on-delete, and document the real semantics at the
graph_edges/notes FK sites and in the migration header.
@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 24, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging4f8655eCommit Preview URL

Branch Preview URL
Jun 24 2026, 02:50 PM

main restructured db/ into ordered migrations applied by migrate.py, deleting
the flat migration_*.sql files. Move the FK-integrity DDL into
migrations/0020_fk_integrity.sql so existing databases get the graph_edges/notes
FK constraints. The DDL is already idempotent (pg_constraint-guarded ADD
CONSTRAINT, orphan DELETEs first, CREATE INDEX IF NOT EXISTS), so it is also a
no-op on fresh DBs that already have the inline FKs from 0001_baseline_schema.
…iles
supabase_schema.sql was deleted when main restructured db/. Assert the FK DDL
invariants against migrations/0020_fk_integrity.sql and the inline REFERENCES
against migrations/0001_baseline_schema.sql instead, preserving the original
verification intent (guarded ADD CONSTRAINT, orphan cleanup ordering, inline FKs
on fresh DBs). Also assert the idx_graph_edges_user_id index this PR adds.
@AndresL230

Copy link
Copy Markdown
Collaborator

Superseded by the DB modular redesign (#279). The graph_edges.user_id + notes FKs (#179/#180) ship in 0023/0025. Closing as obsolete (migration collision). Reopen if not fully covered by #279.

@AndresL230
AndresL230 deleted the fix/fk-integrity branch June 27, 2026 04:21
Sign up for freeto 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.

graph_edges.user_id has no FOREIGN KEY (orphan-row risk) — inconsistent with sibling tables

2 participants

@Jose-Gael-Cruz-Lopez@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(db): add missing FK constraints on graph_edges + notes (#179, #180) - #245

Closed
Jose-Gael-Cruz-Lopez wants to merge 15 commits into
mainfrom
fix/fk-integrity
Closed

fix(db): add missing FK constraints on graph_edges + notes (#179, #180)#245
Jose-Gael-Cruz-Lopez wants to merge 15 commits into
mainfrom
fix/fk-integrity

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jun 22, 2026

Copy link
Copy Markdown
Member

graph_edges.user_id, notes.user_id, and notes.course_id were bare TEXT columns with no REFERENCES, inconsistent with every sibling table in the learning schema. An edge/note could be written for a non-existent user, and deleting a course left dangling notes that list_notes surfaces but can't resolve.

Changes

  • backend/db/migration_fk_integrity.sql — new hand-applied migration:
    • deletes orphan rows first (so the ALTER TABLE can validate),
    • adds each FK behind the DO $$ … IF NOT EXISTS (SELECT 1 FROM pg_constraint …) guard already used in migration_gradebook.sql (Postgres has no ADD CONSTRAINT IF NOT EXISTS), so it is safely re-runnable.
  • backend/db/supabase_schema.sql — inline REFERENCES on all three columns so fresh databases are born with the FKs; comment block updated to note only the note_concepts.concept_node_id link stays soft.
  • backend/tests/test_fk_integrity_migration.py — drift guard: each constraint is guarded, orphans are cleaned before the ALTER, schema declares the inline references.

Verification

  • ruff check . clean; gated suite green (+3 new).
  • Orphan deletion is intentional: rows that violate the new FK are invalid data (no owning user, or a course that no longer exists).

Closes#179, #180.

Summary by CodeRabbit

  • Bug Fixes

    • Strengthened database integrity by enforcing foreign-key relationships for graph edges and notes to ensure valid user and course references.
    • Automatically removes orphaned records that would violate these relationships.
    • Added indexing to improve lookups by user on graph edges.
  • Tests

    • Added drift-guard tests that verify the migration safely applies foreign-key constraints and that orphan cleanup occurs before constraints are added.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 22, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitUpdated (UTC)
❌ Deployment failed
View logs
frontend75d3729Jun 22 2026, 03:55 AM

@coderabbitai

coderabbitaiBot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Jose-Gael-Cruz-Lopez, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 43 minutes and 55 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

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 credits.

🚦 How do rate 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 see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a71d73bd-17bd-4660-a650-0be10b29825e

📥 Commits

Reviewing files that changed from the base of the PR and between cc1ec58 and 4f8655e.

📒 Files selected for processing (3)
  • backend/db/migrations/0001_baseline_schema.sql
  • backend/db/migrations/0020_fk_integrity.sql
  • backend/tests/test_fk_integrity_migration.py
📝 Walkthrough

Walkthrough

Adds inline REFERENCES FK constraints to graph_edges.user_id, notes.user_id, and notes.course_id in the canonical schema. A new idempotent migration script purges orphan rows before conditionally adding each FK constraint via pg_constraint name guards. A new test module validates that the migration and schema remain consistent with each other.

Changes

FK Integrity for graph_edges and notes

Layer / File(s)Summary
Schema inline REFERENCES declarations
backend/db/supabase_schema.sql
graph_edges.user_id gains REFERENCES users(id) with a new supporting index; notes.user_id and notes.course_id gain REFERENCES users(id) and REFERENCES courses(id), converting three bare TEXT NOT NULL columns into hard FK columns with updated documentation.
Idempotent orphan-cleanup and conditional FK migration
backend/db/migration_fk_integrity.sql
New migration that deletes orphan rows in graph_edges and notes before each ALTER TABLE, then adds each FK constraint only when absent, guarded by pg_constraint name checks inside DO $$ ... $$ blocks.
Drift-guard tests
backend/tests/test_fk_integrity_migration.py
Three tests assert: each constraint name appears behind an IF NOT EXISTS pg_constraint guard in the migration; DELETE orphan statements precede their corresponding ALTER steps; and supabase_schema.sql declares the expected inline REFERENCES fragments.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

Poem

🐇 Hop hop, the orphan rows are gone,
No dangling edges left to mourn!
With REFERENCES neat and guards in place,
Each user_id finds its proper space.
The schema's tight, the migration's sound —
Foreign keys keep the data bound! 🌱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically summarizes the main change: adding missing FK constraints on three database columns across two tables, with linked issue references.
Description check✅ PassedThe description provides a clear summary, detailed changes breakdown, rationale, and verification steps, mostly following the repository template structure.
Linked Issues check✅ PassedThe PR fully implements the acceptance criteria from #179: adds FK constraint from graph_edges.user_id to users(id), cleans orphan rows via migration, and updates both migration and schema files. Changes also address the objectives for #180 (notes table FKs).
Out of Scope Changes check✅ PassedAll changes are directly scoped to enforcing FK integrity on the three identified columns. The index addition on graph_edges.user_id and test file are supporting/validation changes aligned with the stated objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/fk-integrity

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.

@coderabbitaicoderabbitaiBot 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 `@backend/db/supabase_schema.sql`:
- Around line 495-496: The comment explaining why note_concepts.concept_node_id
is intentionally not a hard FK states it's because graph_nodes uses TEXT ids
managed by application code, but this reasoning is inconsistent since
graph_edges.source_node_id and graph_edges.target_node_id also reference
graph_nodes(id) with TEXT ids yet have hard FKs defined. Update the comment at
lines 495-496 to clarify the actual rationale for not adding the FK constraint
to note_concepts.concept_node_id, such as whether concepts can exist before
their corresponding graph nodes are created or if there's a specific
application-level design consideration that necessitates this approach.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b17fdcab-4e71-4e4f-83a4-8009fa8e242a

📥 Commits

Reviewing files that changed from the base of the PR and between 4521305 and 75d3729.

📒 Files selected for processing (3)
  • backend/db/migration_fk_integrity.sql
  • backend/db/supabase_schema.sql
  • backend/tests/test_fk_integrity_migration.py

Comment on lines +495 to +496
-- links. Only the note_concepts.concept_node_id link is intentionally NOT a
-- hard FK, because graph_nodes uses TEXT ids managed by application code.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify the rationale for not adding FK to note_concepts.concept_node_id.

The comment states the link is "intentionally NOT a hard FK, because graph_nodes uses TEXT ids managed by application code." However, graph_edges.source_node_id and graph_edges.target_node_id (lines 98-99) both have hard FKs to graph_nodes(id), so the stated reason is inconsistent.

If the real reason is different (e.g., concepts can exist before graph nodes are created, or there's an application-level design consideration), please update the comment to explain the actual rationale.

🤖 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 `@backend/db/supabase_schema.sql` around lines 495 - 496, The comment
explaining why note_concepts.concept_node_id is intentionally not a hard FK
states it's because graph_nodes uses TEXT ids managed by application code, but
this reasoning is inconsistent since graph_edges.source_node_id and
graph_edges.target_node_id also reference graph_nodes(id) with TEXT ids yet have
hard FKs defined. Update the comment at lines 495-496 to clarify the actual
rationale for not adding the FK constraint to note_concepts.concept_node_id,
such as whether concepts can exist before their corresponding graph nodes are
created or if there's a specific application-level design consideration that
necessitates this approach.

Postgres does not auto-index the referencing side of a foreign key, and
sibling tables index this access path. Add the index in both the
migration and the canonical schema.
…he new FKs (#179, #180)
The FKs have no ON DELETE clause, so they default to RESTRICT (block
hard delete) rather than cascading cleanup. Correct the schema comment
that implied cleanup-on-delete, and document the real semantics at the
graph_edges/notes FK sites and in the migration header.
@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 24, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging4f8655eCommit Preview URL

Branch Preview URL
Jun 24 2026, 02:50 PM

main restructured db/ into ordered migrations applied by migrate.py, deleting
the flat migration_*.sql files. Move the FK-integrity DDL into
migrations/0020_fk_integrity.sql so existing databases get the graph_edges/notes
FK constraints. The DDL is already idempotent (pg_constraint-guarded ADD
CONSTRAINT, orphan DELETEs first, CREATE INDEX IF NOT EXISTS), so it is also a
no-op on fresh DBs that already have the inline FKs from 0001_baseline_schema.
…iles
supabase_schema.sql was deleted when main restructured db/. Assert the FK DDL
invariants against migrations/0020_fk_integrity.sql and the inline REFERENCES
against migrations/0001_baseline_schema.sql instead, preserving the original
verification intent (guarded ADD CONSTRAINT, orphan cleanup ordering, inline FKs
on fresh DBs). Also assert the idx_graph_edges_user_id index this PR adds.
@AndresL230

Copy link
Copy Markdown
Collaborator

Superseded by the DB modular redesign (#279). The graph_edges.user_id + notes FKs (#179/#180) ship in 0023/0025. Closing as obsolete (migration collision). Reopen if not fully covered by #279.

@AndresL230
AndresL230 deleted the fix/fk-integrity branch June 27, 2026 04:21
Sign up for freeto 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.

graph_edges.user_id has no FOREIGN KEY (orphan-row risk) — inconsistent with sibling tables

2 participants

@Jose-Gael-Cruz-Lopez@AndresL230
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(db): add missing FK constraints on graph_edges + notes (#179, #180) - #245

Closed
Jose-Gael-Cruz-Lopez wants to merge 15 commits into
mainfrom
fix/fk-integrity
Closed

fix(db): add missing FK constraints on graph_edges + notes (#179, #180)#245
Jose-Gael-Cruz-Lopez wants to merge 15 commits into
mainfrom
fix/fk-integrity

Conversation

@Jose-Gael-Cruz-Lopez

@Jose-Gael-Cruz-LopezJose-Gael-Cruz-Lopez commented Jun 22, 2026

Copy link
Copy Markdown
Member

graph_edges.user_id, notes.user_id, and notes.course_id were bare TEXT columns with no REFERENCES, inconsistent with every sibling table in the learning schema. An edge/note could be written for a non-existent user, and deleting a course left dangling notes that list_notes surfaces but can't resolve.

Changes

  • backend/db/migration_fk_integrity.sql — new hand-applied migration:
    • deletes orphan rows first (so the ALTER TABLE can validate),
    • adds each FK behind the DO $$ … IF NOT EXISTS (SELECT 1 FROM pg_constraint …) guard already used in migration_gradebook.sql (Postgres has no ADD CONSTRAINT IF NOT EXISTS), so it is safely re-runnable.
  • backend/db/supabase_schema.sql — inline REFERENCES on all three columns so fresh databases are born with the FKs; comment block updated to note only the note_concepts.concept_node_id link stays soft.
  • backend/tests/test_fk_integrity_migration.py — drift guard: each constraint is guarded, orphans are cleaned before the ALTER, schema declares the inline references.

Verification

  • ruff check . clean; gated suite green (+3 new).
  • Orphan deletion is intentional: rows that violate the new FK are invalid data (no owning user, or a course that no longer exists).

Closes#179, #180.

Summary by CodeRabbit

  • Bug Fixes

    • Strengthened database integrity by enforcing foreign-key relationships for graph edges and notes to ensure valid user and course references.
    • Automatically removes orphaned records that would violate these relationships.
    • Added indexing to improve lookups by user on graph edges.
  • Tests

    • Added drift-guard tests that verify the migration safely applies foreign-key constraints and that orphan cleanup occurs before constraints are added.

@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 22, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitUpdated (UTC)
❌ Deployment failed
View logs
frontend75d3729Jun 22 2026, 03:55 AM

@coderabbitai

coderabbitaiBot commented Jun 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@Jose-Gael-Cruz-Lopez, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 43 minutes and 55 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

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 credits.

🚦 How do rate 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 see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: a71d73bd-17bd-4660-a650-0be10b29825e

📥 Commits

Reviewing files that changed from the base of the PR and between cc1ec58 and 4f8655e.

📒 Files selected for processing (3)
  • backend/db/migrations/0001_baseline_schema.sql
  • backend/db/migrations/0020_fk_integrity.sql
  • backend/tests/test_fk_integrity_migration.py
📝 Walkthrough

Walkthrough

Adds inline REFERENCES FK constraints to graph_edges.user_id, notes.user_id, and notes.course_id in the canonical schema. A new idempotent migration script purges orphan rows before conditionally adding each FK constraint via pg_constraint name guards. A new test module validates that the migration and schema remain consistent with each other.

Changes

FK Integrity for graph_edges and notes

Layer / File(s)Summary
Schema inline REFERENCES declarations
backend/db/supabase_schema.sql
graph_edges.user_id gains REFERENCES users(id) with a new supporting index; notes.user_id and notes.course_id gain REFERENCES users(id) and REFERENCES courses(id), converting three bare TEXT NOT NULL columns into hard FK columns with updated documentation.
Idempotent orphan-cleanup and conditional FK migration
backend/db/migration_fk_integrity.sql
New migration that deletes orphan rows in graph_edges and notes before each ALTER TABLE, then adds each FK constraint only when absent, guarded by pg_constraint name checks inside DO $$ ... $$ blocks.
Drift-guard tests
backend/tests/test_fk_integrity_migration.py
Three tests assert: each constraint name appears behind an IF NOT EXISTS pg_constraint guard in the migration; DELETE orphan statements precede their corresponding ALTER steps; and supabase_schema.sql declares the expected inline REFERENCES fragments.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly related issues

Poem

🐇 Hop hop, the orphan rows are gone,
No dangling edges left to mourn!
With REFERENCES neat and guards in place,
Each user_id finds its proper space.
The schema's tight, the migration's sound —
Foreign keys keep the data bound! 🌱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check nameStatusExplanationResolution
Docstring Coverage⚠️ WarningDocstring coverage is 0.00% which is insufficient. The required threshold is 80.00%.Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check nameStatusExplanation
Title check✅ PassedThe title clearly and specifically summarizes the main change: adding missing FK constraints on three database columns across two tables, with linked issue references.
Description check✅ PassedThe description provides a clear summary, detailed changes breakdown, rationale, and verification steps, mostly following the repository template structure.
Linked Issues check✅ PassedThe PR fully implements the acceptance criteria from #179: adds FK constraint from graph_edges.user_id to users(id), cleans orphan rows via migration, and updates both migration and schema files. Changes also address the objectives for #180 (notes table FKs).
Out of Scope Changes check✅ PassedAll changes are directly scoped to enforcing FK integrity on the three identified columns. The index addition on graph_edges.user_id and test file are supporting/validation changes aligned with the stated objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/fk-integrity

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.

@coderabbitaicoderabbitaiBot 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 `@backend/db/supabase_schema.sql`:
- Around line 495-496: The comment explaining why note_concepts.concept_node_id
is intentionally not a hard FK states it's because graph_nodes uses TEXT ids
managed by application code, but this reasoning is inconsistent since
graph_edges.source_node_id and graph_edges.target_node_id also reference
graph_nodes(id) with TEXT ids yet have hard FKs defined. Update the comment at
lines 495-496 to clarify the actual rationale for not adding the FK constraint
to note_concepts.concept_node_id, such as whether concepts can exist before
their corresponding graph nodes are created or if there's a specific
application-level design consideration that necessitates this approach.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: b17fdcab-4e71-4e4f-83a4-8009fa8e242a

📥 Commits

Reviewing files that changed from the base of the PR and between 4521305 and 75d3729.

📒 Files selected for processing (3)
  • backend/db/migration_fk_integrity.sql
  • backend/db/supabase_schema.sql
  • backend/tests/test_fk_integrity_migration.py

Comment on lines +495 to +496
-- links. Only the note_concepts.concept_node_id link is intentionally NOT a
-- hard FK, because graph_nodes uses TEXT ids managed by application code.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Clarify the rationale for not adding FK to note_concepts.concept_node_id.

The comment states the link is "intentionally NOT a hard FK, because graph_nodes uses TEXT ids managed by application code." However, graph_edges.source_node_id and graph_edges.target_node_id (lines 98-99) both have hard FKs to graph_nodes(id), so the stated reason is inconsistent.

If the real reason is different (e.g., concepts can exist before graph nodes are created, or there's an application-level design consideration), please update the comment to explain the actual rationale.

🤖 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 `@backend/db/supabase_schema.sql` around lines 495 - 496, The comment
explaining why note_concepts.concept_node_id is intentionally not a hard FK
states it's because graph_nodes uses TEXT ids managed by application code, but
this reasoning is inconsistent since graph_edges.source_node_id and
graph_edges.target_node_id also reference graph_nodes(id) with TEXT ids yet have
hard FKs defined. Update the comment at lines 495-496 to clarify the actual
rationale for not adding the FK constraint to note_concepts.concept_node_id,
such as whether concepts can exist before their corresponding graph nodes are
created or if there's a specific application-level design consideration that
necessitates this approach.

Postgres does not auto-index the referencing side of a foreign key, and
sibling tables index this access path. Add the index in both the
migration and the canonical schema.
…he new FKs (#179, #180)
The FKs have no ON DELETE clause, so they default to RESTRICT (block
hard delete) rather than cascading cleanup. Correct the schema comment
that implied cleanup-on-delete, and document the real semantics at the
graph_edges/notes FK sites and in the migration header.
@cloudflare-workers-and-pages

cloudflare-workers-and-pagesBot commented Jun 24, 2026

Copy link
Copy Markdown

Deploying with Cloudflare Workers Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

StatusNameLatest CommitPreview URLUpdated (UTC)
✅ Deployment successful!
View logs
frontend-staging4f8655eCommit Preview URL

Branch Preview URL
Jun 24 2026, 02:50 PM

main restructured db/ into ordered migrations applied by migrate.py, deleting
the flat migration_*.sql files. Move the FK-integrity DDL into
migrations/0020_fk_integrity.sql so existing databases get the graph_edges/notes
FK constraints. The DDL is already idempotent (pg_constraint-guarded ADD
CONSTRAINT, orphan DELETEs first, CREATE INDEX IF NOT EXISTS), so it is also a
no-op on fresh DBs that already have the inline FKs from 0001_baseline_schema.
…iles
supabase_schema.sql was deleted when main restructured db/. Assert the FK DDL
invariants against migrations/0020_fk_integrity.sql and the inline REFERENCES
against migrations/0001_baseline_schema.sql instead, preserving the original
verification intent (guarded ADD CONSTRAINT, orphan cleanup ordering, inline FKs
on fresh DBs). Also assert the idx_graph_edges_user_id index this PR adds.
@AndresL230

Copy link
Copy Markdown
Collaborator

Superseded by the DB modular redesign (#279). The graph_edges.user_id + notes FKs (#179/#180) ship in 0023/0025. Closing as obsolete (migration collision). Reopen if not fully covered by #279.

@AndresL230
AndresL230 deleted the fix/fk-integrity branch June 27, 2026 04:21
Sign up for freeto 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.

graph_edges.user_id has no FOREIGN KEY (orphan-row risk) — inconsistent with sibling tables

2 participants

@Jose-Gael-Cruz-Lopez@AndresL230