Skip to content

Fix migration bootstrap failures on fresh Postgres - #995

Merged
northdpole merged 4 commits into
OWASP:mainfrom
DevPatils:994-fix-migration-bootstrap-bugs
Aug 7, 2026
Merged

Fix migration bootstrap failures on fresh Postgres#995
northdpole merged 4 commits into
OWASP:mainfrom
DevPatils:994-fix-migration-bootstrap-bugs

Conversation

@DevPatils

@DevPatils DevPatils commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Fixes #994flask db upgrade fails when run against a genuinely fresh,
empty Postgres database, because no migration anywhere creates the
document_metadata column that the Node and CRE models require.

Note on scope: #994 originally reported three bugs. Two of them
(the uq_pair constraint collision, and the two unmerged migration
heads) were already fixed upstream independently before this PR was
ready — see a55e380 (constraint fix) and c7d8e9f0a1b2 (which merges
both heads while adding pgvector support). This PR delivers the one
remaining bug: the missing document_metadata column.

The bug

application/database/db.py defines document_metadata as a real
column on both Node and CRE (mapped via metadata_json), but no
migration anywhere creates it. A fresh migration run crashes the first
time any code queries it:

The fix

Adds migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py,
which creates the column on both tables — defensively checking whether
it already exists first, since production likely has it applied
out-of-band already (a common way this kind of drift survives — someone
patches the live schema directly without the change ever making it back
into a migration file).

Test plan

  • Dropped/recreated a fresh local Postgres database (pgvector-enabled)
  • Ran flask db upgrade heads end-to-end from empty — completes cleanly
  • flask db heads shows exactly 1 head (b5ac48010165)
  • Confirmed via \d cre / \d node in psql — document_metadata
    present on both tables
  • black --check passes on the new migration file

Verification

Before Fix:

Migration.Bug.mp4

After Fix :

after.fix.mp4

@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3ce1cdc8-9c70-4054-aff1-cbaa1b79eb69

📥 Commits

Reviewing files that changed from the base of the PR and between d6e7cf8 and f069870.

📒 Files selected for processing (1)
  • migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py

Summary by CodeRabbit

  • Bug Fixes
    • Added support for storing document metadata across applicable records.
    • Improved database upgrade reliability by safely handling installations where metadata fields already exist.
    • Preserved existing records and metadata during schema updates.
    • Ensured document metadata remains available for supported processing and display workflows.

Walkthrough

The migration conditionally adds nullable JSON document_metadata columns to node and cre. Its downgrade operation does not remove either column.

Changes

Migration bootstrap fixes

Layer / File(s) Summary
Document metadata column migration
migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py
Adds revision metadata, checks each table independently before adding document_metadata, and preserves the columns during downgrade.

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

Possibly related PRs

  • OWASP/OpenCRE#1006: Revises the same document_metadata migration with equivalent idempotent column checks and downgrade preservation.

Suggested reviewers: northdpole

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the migration bootstrap failure fixed by this pull request.
Description check ✅ Passed The description explains the missing-column defect, the migration fix, scope limits, and validation results.
Linked Issues check ✅ Passed The migration adds guarded document_metadata columns to both required tables, addressing the remaining defect in issue #994.
Out of Scope Changes check ✅ Passed The pull request contains only the migration needed to add document_metadata columns and preserve existing data.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 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
`@migrations/versions/0115af8843b4_add_missing_document_metadata_column_to_.py`:
- Around line 37-42: Update the migration’s upgrade/downgrade ownership handling
so columns detected as pre-existing and skipped by upgrade are not dropped
during downgrade. Persist which document_metadata columns this revision created,
then make downgrade drop only those owned columns while preserving out-of-band
columns and their data.
- Around line 20-34: Update upgrade() to preserve existing metadata_json values
whenever document_metadata is absent: before or while adding the new column on
each affected table, rename or copy the populated metadata_json data into
document_metadata so ORM and backend reads retain it. Keep the existing
defensive checks for already-present document_metadata, and add a migration test
covering a schema with populated metadata_json and no document_metadata.

In `@migrations/versions/7bf4eac76958_add_rule_id_column.py`:
- Line 28: Align the constraint name used by the cre_node_links ORM model in
application/database/db.py with the migration’s uq_node_pair name, or
consistently rename both sides to another shared name. Ensure the persisted
schema and ORM metadata use exactly the same constraint identifier.
🪄 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: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: be6a969e-dde3-42ac-b2ee-995942a3b7dc

📥 Commits

Reviewing files that changed from the base of the PR and between a55e380 and dfa5de6.

📒 Files selected for processing (3)
  • migrations/versions/0115af8843b4_add_missing_document_metadata_column_to_.py
  • migrations/versions/7bf4eac76958_add_rule_id_column.py
  • migrations/versions/a0c5734926c5_merge_divergent_embedding_metadata_heads.py

Comment on lines +20 to +34
def upgrade():
# Defensive: some environments (e.g. production) already have this column
# applied out-of-band without a corresponding migration ever being
# committed, so this must not assume a clean "column doesn't exist" state.
inspector = inspect(op.get_bind())
node_columns = {c["name"] for c in inspector.get_columns("node")}
cre_columns = {c["name"] for c in inspector.get_columns("cre")}

if "document_metadata" not in node_columns:
with op.batch_alter_table("node", schema=None) as batch_op:
batch_op.add_column(sa.Column("document_metadata", sa.JSON(), nullable=True))

if "document_metadata" not in cre_columns:
with op.batch_alter_table("cre", schema=None) as batch_op:
batch_op.add_column(sa.Column("document_metadata", sa.JSON(), nullable=True))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== migrations relevant files =="
fd -a '0115af8843b4_add_missing_document_metadata_column_to_\.py|metadata_json|document_metadata' . | sed 's#^\./##' | head -200

echo
echo "== migration file outline =="
if [ -f "migrations/versions/0115af8843b4_add_missing_document_metadata_column_to_.py" ]; then
  wc -l migrations/versions/0115af8843b4_add_missing_document_metadata_column_to_.py
  sed -n '1,220p' migrations/versions/0115af8843b4_add_missing_document_metadata_column_to_.py
fi

echo
echo "== benchmark import parity relevant lines =="
if [ -f "scripts/benchmark_import_parity.py" ]; then
  sed -n '80,130p' scripts/benchmark_import_parity.py
fi

echo
echo "== search metadata_json/document_metadata usages =="
rg -n "document_metadata|metadata_json|metadata_json" . --glob '!*.pyc' --glob '!.git/**' | head -500

Repository: OWASP/OpenCRE

Length of output: 8113


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import json
from pathlib import Path

migration = Path("migrations/versions/0115af8843b4_add_missing_document_metadata_column_to_.py").read_text()
benchmark = Path("scripts/benchmark_import_parity.py").read_text()
backend = Path("application/database/db.py").read_text()

checks = {
    "migration_adds_without_rename_or_copy": {
        "add_column_node": 'batch_op.add_column(sa.Column("document_metadata", sa.JSON(), nullable=True))' in migration,
        "upgrade_prefers_exact_document_metadata_name": '"document_metadata" not in node_columns' in migration and '"document_metadata" not in cre_columns' in migration,
        "no_metadata_json_rename": "metadata_json" in migration and "rename_column" in migration,
        "no_metadata_json_copy": ("metadata_json" not in migration) or ("copy" not in migration.lower() and "rename" not in migration.lower()),
    },
    "benchmark_selects_document_metadata_when_present": {
        "_document_metadata_select": (
            'if "document_metadata" in cols:' in benchmark
            and 'return "document_metadata"' in benchmark
            and 'if "metadata_json" in cols:' in benchmark
            and 'return "metadata_json AS document_metadata"' in benchmark
        ),
        "canonicalized_result_key": '"document_metadata": _json_canonical(r["document_metadata"])' in benchmark,
    },
    "backend_orm_and_reads_use_document_metadata": {
        "orm_column_maps_to_meta_json_name": 'sqla.Column("document_metadata", sqla.JSON, nullable=True)' in backend and 'metadata_json = sqla.Column' in backend,
        "read_prefers_document_metadata_ORM_attr": ".metadata_json" in backend,
    },
}

for group, claims in checks.items():
    print("\n".join(f"{group}/{k}: {json.dumps(v)}" for k, v in claims.items()))
PY

Repository: OWASP/OpenCRE

Length of output: 770


Preserve existing metadata_json before adding document_metadata.

This migration only checks for document_metadata, so a schema that has populated metadata_json will get a new empty document_metadata column. Because the ORM, backend reads, and benchmark parity select prefer document_metadata, existing metadata becomes invisible after migration. Rename or copy metadata_json values into document_metadata when document_metadata is absent, and add a migration test for that path.

🤖 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 `@migrations/versions/0115af8843b4_add_missing_document_metadata_column_to_.py`
around lines 20 - 34, Update upgrade() to preserve existing metadata_json values
whenever document_metadata is absent: before or while adding the new column on
each affected table, rename or copy the populated metadata_json data into
document_metadata so ORM and backend reads retain it. Keep the existing
defensive checks for already-present document_metadata, and add a migration test
covering a schema with populated metadata_json and no document_metadata.

Comment on lines +37 to +42
def downgrade():
with op.batch_alter_table("cre", schema=None) as batch_op:
batch_op.drop_column("document_metadata")

with op.batch_alter_table("node", schema=None) as batch_op:
batch_op.drop_column("document_metadata")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Do not unconditionally drop columns that this revision may not own.

Because upgrade() skips out-of-band columns, downgrade() can delete an existing document_metadata column and its data. Persist column ownership or make downgrade non-destructive for pre-existing columns.

🤖 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 `@migrations/versions/0115af8843b4_add_missing_document_metadata_column_to_.py`
around lines 37 - 42, Update the migration’s upgrade/downgrade ownership
handling so columns detected as pre-existing and skipped by upgrade are not
dropped during downgrade. Persist which document_metadata columns this revision
created, then make downgrade drop only those owned columns while preserving
out-of-band columns and their data.

with op.batch_alter_table("cre_node_links", schema=None) as batch_op:
batch_op.drop_constraint("uq_cre_node_link_pair", type_="unique")
batch_op.create_unique_constraint("uq_pair", ["cre", "node"])
batch_op.create_unique_constraint("uq_node_pair", ["cre", "node"])

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

Align the ORM constraint name with this migration.

The upgrade now creates uq_node_pair, but application/database/db.py still declares the cre_node_links constraint as uq_cre_node_link_pair. Update the model (or choose a different migration name) so the persisted schema and ORM metadata agree; otherwise Alembic autogeneration/schema checks can report drift after upgrade.

🤖 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 `@migrations/versions/7bf4eac76958_add_rule_id_column.py` at line 28, Align the
constraint name used by the cre_node_links ORM model in
application/database/db.py with the migration’s uq_node_pair name, or
consistently rename both sides to another shared name. Ensure the persisted
schema and ORM metadata use exactly the same constraint identifier.

@DevPatils DevPatils closed this Jul 24, 2026
@DevPatils
DevPatils force-pushed the 994-fix-migration-bootstrap-bugs branch from dfa5de6 to a55e380 Compare July 24, 2026 16:54
@DevPatils DevPatils reopened this Jul 25, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 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
`@migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py`:
- Around line 37-42: Make the migration’s downgrade non-destructive for
pre-existing columns: update upgrade() to persist whether each document_metadata
column was created by this migration, then have downgrade() consult that
ownership state and drop only migration-created columns. If ownership cannot be
persisted reliably, remove the unconditional drop behavior from downgrade()
rather than risking deletion of existing columns.
- Around line 24-34: Update the migration’s Node and CRE column handling to
preserve existing metadata_json values: when only metadata_json exists, rename
it to document_metadata (or copy its values before removing it), and when both
columns exist, transfer metadata_json values into document_metadata without
overwriting valid document_metadata data. Keep the migration safe when
document_metadata already exists and apply the same logic to both tables.
🪄 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: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4fed044a-9ea5-4e8c-a825-59580e1f37d6

📥 Commits

Reviewing files that changed from the base of the PR and between dfa5de6 and 0f0ad7e.

📒 Files selected for processing (1)
  • migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py

Comment on lines +24 to +34
inspector = inspect(op.get_bind())
node_columns = {c["name"] for c in inspector.get_columns("node")}
cre_columns = {c["name"] for c in inspector.get_columns("cre")}

if "document_metadata" not in node_columns:
with op.batch_alter_table("node", schema=None) as batch_op:
batch_op.add_column(sa.Column("document_metadata", sa.JSON(), nullable=True))

if "document_metadata" not in cre_columns:
with op.batch_alter_table("cre", schema=None) as batch_op:
batch_op.add_column(sa.Column("document_metadata", sa.JSON(), nullable=True))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repo files matching migration/scripts/db =="
git ls-files | rg '(^migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_|(^scripts/benchmark_import_parity\.py$|^application/database/db\.py$))' || true

echo
echo "== migration file =="
sed -n '1,140p' migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py

echo
echo "== scripts/benchmark_import_parity.py relevant =="
sed -n '1,180p' scripts/benchmark_import_parity.py

echo
echo "== application/database/db.py relevant =="
sed -n '1,180p' application/database/db.py

echo
echo "== search metadata_json/document_metadata references =="
rg -n '"?metadata_json"?\b|"?document_metadata"?\b' scripts application migrations -S || true

Repository: OWASP/OpenCRE

Length of output: 17258


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== search older migrations for document_metadata/metadata_json creation == =="
rg -n 'document_metadata|metadata_json|add_column.*metadata|create_table.*metadata' migrations scripts -S || true

echo
echo "== inspect alembic version/creates/migrations references =="
sed -n '1,140p' scripts/prod-docker-entrypoint.sh || true

echo
echo "== deterministic migration behavior probe =="
python3 - <<'PY'
def upgrade_columns_after_migration(existing_node, existing_cre):
    node_columns = set(existing_node)
    cre_columns = set(existing_cre)

    if "document_metadata" not in node_columns:
        node_columns = node_columns | {"document_metadata"}
    if "document_metadata" not in cre_columns:
        cre_columns = cre_columns | {"document_metadata"}

    return {"node": sorted(node_columns), "cre": sorted(cre_columns)}

cases = [
    (["metadata_json"], []),
    (["metadata_json"], ["metadata_json"]),
    (["document_metadata", "metadata_json"], ["document_metadata", "metadata_json"]),
]
for node, cre in cases:
    result = upgrade_columns_after_migration(node, cre)
    print({"existing_node": node, "existing_cre": cre}, "=>", result)
PY

echo
echo "== behavioral probe: data accessible from ORM-backed column if only legacy column is present? =="
python3 - <<'PY'
legacy_schema = {"document_metadata": None}
ORM_reads_column = legacy_schema.get("metadata_json")
print({"migration_result_schema": legacy_schema, "orm_attribute_value_from_document_metadata": ORM_reads_column})
PY

Repository: OWASP/OpenCRE

Length of output: 3951


Preserve legacy metadata_json values before setting document_metadata.

The ORM for Node and CRE reads metadata_json mapped to the SQL column document_metadata, and benchmark parity also supports schemas that have metadata_json instead. This migration only adds an empty document_metadata column when metadata_json exists, so existing metadata becomes inaccessible. Rename/copy metadata_json to document_metadata for both tables, and handle the case where both columns already exist.

🤖 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 `@migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py`
around lines 24 - 34, Update the migration’s Node and CRE column handling to
preserve existing metadata_json values: when only metadata_json exists, rename
it to document_metadata (or copy its values before removing it), and when both
columns exist, transfer metadata_json values into document_metadata without
overwriting valid document_metadata data. Keep the migration safe when
document_metadata already exists and apply the same logic to both tables.

Comment thread migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py Outdated
@northdpole

Copy link
Copy Markdown
Collaborator

Thanks for chasing down #994. Please land this before #1006 (that PR should rebase on top of this one).

Fixes needed before merge

  1. Black — CI fails on migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py (would reformat). Run black on that file and push.
  2. PR body is stale — bugs 1–2 from Bug: flask db upgrade fails on a fresh Postgres database (3 migration bugs) #994 are already upstream; this branch only ships the remaining document_metadata migration. Please update the description so reviewers see the actual scope.
  3. Typo in title — “Postgress” → “Postgres” (optional but nice).

Once black is green, this is next in the migration queue; #1006 will rebase after it merges.

@DevPatils

Copy link
Copy Markdown
Contributor Author

Thanks for chasing down #994. Please land this before #1006 (that PR should rebase on top of this one).

Fixes needed before merge

  1. Black — CI fails on migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py (would reformat). Run black on that file and push.
  2. PR body is stale — bugs 1–2 from Bug: flask db upgrade fails on a fresh Postgres database (3 migration bugs) #994 are already upstream; this branch only ships the remaining document_metadata migration. Please update the description so reviewers see the actual scope.
  3. Typo in title — “Postgress” → “Postgres” (optional but nice).

Once black is green, this is next in the migration queue; #1006 will rebase after it merges.

Thanks for chasing down #994. Please land this before #1006 (that PR should rebase on top of this one).

Fixes needed before merge

  1. Black — CI fails on migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py (would reformat). Run black on that file and push.
  2. PR body is stale — bugs 1–2 from Bug: flask db upgrade fails on a fresh Postgres database (3 migration bugs) #994 are already upstream; this branch only ships the remaining document_metadata migration. Please update the description so reviewers see the actual scope.
  3. Typo in title — “Postgress” → “Postgres” (optional but nice).

Once black is green, this is next in the migration queue; #1006 will rebase after it merges.

Fixing

@DevPatils DevPatils changed the title Fix migration bootstrap failures on fresh Postgress Fix migration bootstrap failures on fresh Postgres Aug 6, 2026
@DevPatils DevPatils changed the title Fix migration bootstrap failures on fresh Postgres Fix migration bootstrap failures on fresh Postgress Aug 6, 2026
@DevPatils DevPatils changed the title Fix migration bootstrap failures on fresh Postgress Fix migration bootstrap failures on fresh Postgres Aug 6, 2026
@DevPatils

Copy link
Copy Markdown
Contributor Author

@northdpole check it once all the issues are resolved.

@northdpole

Copy link
Copy Markdown
Collaborator

Black looks fixed — thanks. Please rebase onto latest main (~67 commits behind) before we merge.

Reminder: this should land before #1006 (which will then rebase on top of this).

Bugs 1 and 2 from OWASP#994 were already fixed upstream (uq_pair rename
removal in a55e380, heads merged via c7d8e9f0a1b2's pgvector migration).
This delivers the remaining fix: no migration anywhere created the
document_metadata column that Node and CRE models require, causing a
crash the moment any code queries it on a freshly-migrated database.
Defensively checks for existing column first, since production may
already have it applied out-of-band.

Verified end-to-end on a fresh Postgres database (with pgvector):
full migration chain completes, single head, document_metadata present
on both tables.

Addresses OWASP#994
@DevPatils
DevPatils force-pushed the 994-fix-migration-bootstrap-bugs branch from 6b643fa to d6e7cf8 Compare August 6, 2026 15:10
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@DevPatils

DevPatils commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Black looks fixed — thanks. Please rebase onto latest main (~67 commits behind) before we merge.

Reminder: this should land before #1006 (which will then rebase on top of this).

Thanks for the review! All three addressed:

  • Black: fixed and verified with black --check (passes clean).
  • Title: updated from "Postgress" to "Postgres".
  • PR description: rewritten to reflect the actual current scope. Since bugs 1 and 2 from Bug: flask db upgrade fails on a fresh Postgres database (3 migration bugs) #994 landed upstream independently while this was in review, this PR now only ships the remaining fix: the missing document_metadata column migration.
  • Rebase: done, on top of latest main (including the new pgvector, users/resource-selection, artifact-ingest, harvester-checkpoint, and Module B migrations that landed in between). No conflicts. Also had to repoint this migration's down_revision from the old tip to the new one (d4e5f6a7b8c9) so it builds on the actual current head instead of creating a fresh fork.

Re-verified end-to-end on a genuinely fresh Postgres database (pgvector-enabled) after the rebase: full migration chain (16 revisions) runs clean from empty, single head, document_metadata present on both node and cre.

Please have a look @northdpole !!!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py (1)

42-47: 🗄️ Data Integrity & Integration | 🔴 Critical

Do not drop pre-existing columns during downgrade.

When upgrade() finds an existing document_metadata column, it skips creation. downgrade() still drops that column unconditionally from both tables. A rollback can therefore delete existing application data.

Persist column ownership during upgrade() and drop only columns created by this revision. If ownership cannot be persisted reliably, make downgrade() non-destructive. This repeats the unresolved prior review finding.

🤖 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 `@migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py`
around lines 42 - 47, Make the migration’s upgrade/downgrade behavior
ownership-aware: update upgrade() to persist whether this revision created each
document_metadata column on cre and node, then have downgrade() drop only
columns recorded as created by this revision and retain pre-existing columns. If
creation ownership cannot be persisted reliably, make downgrade()
non-destructive instead of unconditionally dropping document_metadata.
🤖 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
`@migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py`:
- Around line 25-39: Update the migration logic for the node and cre tables to
reconcile metadata_json and document_metadata: rename metadata_json to
document_metadata when the legacy column is the only one present, and when both
exist copy legacy values only where document_metadata is null or empty without
overwriting valid values. Apply the same handling independently in both table
branches.

---

Duplicate comments:
In
`@migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py`:
- Around line 42-47: Make the migration’s upgrade/downgrade behavior
ownership-aware: update upgrade() to persist whether this revision created each
document_metadata column on cre and node, then have downgrade() drop only
columns recorded as created by this revision and retain pre-existing columns. If
creation ownership cannot be persisted reliably, make downgrade()
non-destructive instead of unconditionally dropping document_metadata.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: CHILL

Plan: Pro Plus

Run ID: b87493d2-2d3e-4502-bdda-1450ffeea9ab

📥 Commits

Reviewing files that changed from the base of the PR and between 4e167b5 and d6e7cf8.

📒 Files selected for processing (1)
  • migrations/versions/b5ac48010165_add_missing_document_metadata_column_to_.py

upgrade() is defensive (only adds document_metadata where missing), so
downgrade() unconditionally dropping the column could destroy real data
on any environment where the column pre-existed before this migration
ran (e.g. production, applied out-of-band). There's no reliable way to
distinguish 'this migration added it' from 'it already existed', so the
safe choice is a no-op downgrade.

Verified: upgrade -> downgrade -> upgrade cycle on a fresh database
confirms the column survives the downgrade step correctly.
@DevPatils

Copy link
Copy Markdown
Contributor Author

@northdpole all the coderabbit reviews are fixed, please review it .

@northdpole northdpole left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Review — #995 fresh-Postgres document_metadata migration

CI green after re-running jobs stuck/cancelled in yesterday’s Actions outage. Scope is correctly narrowed to the remaining #994 bug (column missing on fresh migrate).

Looks good

  • Defensive inspect + add-only-if-missing (safe for prod drift)
  • sa.JSON() nullable on cre / node
  • Downgrade intentionally no-op — right call given out-of-band prod column risk
  • down_revision = d4e5f6a7b8c9 is a real head on main (Module B tables)

Non-blocking / follow-up

  1. main still has two Alembic heads (d4e5f6a7b8c9 and ab12cd34ef56). This PR extends the d4e5 line; after merge you’ll still want a merge revision (or rebase) so flask db upgrade without heads stays single-head.
  2. Branch is ~14 commits behind main — rebase before merge if convenient.
  3. #1006 should rebase on top of this once landed (don’t ship two competing document_metadata migrations).

Approving.

@northdpole

Copy link
Copy Markdown
Collaborator

CI recovered after the Actions outage (reran stuck Lint/Test). Ready from the review side aside from the optional rebase / dual-head follow-up noted in the approval.

@northdpole
northdpole merged commit a8c2436 into OWASP:main Aug 7, 2026
8 of 13 checks passed
@DevPatils

Copy link
Copy Markdown
Contributor Author

Review — #995 fresh-Postgres document_metadata migration

CI green after re-running jobs stuck/cancelled in yesterday’s Actions outage. Scope is correctly narrowed to the remaining #994 bug (column missing on fresh migrate).

Looks good

  • Defensive inspect + add-only-if-missing (safe for prod drift)
  • sa.JSON() nullable on cre / node
  • Downgrade intentionally no-op — right call given out-of-band prod column risk
  • down_revision = d4e5f6a7b8c9 is a real head on main (Module B tables)

Non-blocking / follow-up

  1. main still has two Alembic heads (d4e5f6a7b8c9 and ab12cd34ef56). This PR extends the d4e5 line; after merge you’ll still want a merge revision (or rebase) so flask db upgrade without heads stays single-head.
  2. Branch is ~14 commits behind main — rebase before merge if convenient.
  3. Fix SQLite migration compatibility and idempotency issues #1006 should rebase on top of this once landed (don’t ship two competing document_metadata migrations).

Approving.

Thanks so much for the thorough review, and glad the defensive
upgrade/no-op downgrade approach checked out!

One small correction on the follow-up note: I checked ab12cd34ef56
directly against a clean checkout of upstream main using Alembic's
ScriptDirectory.get_heads() (no DB involved, just parsing the migration
files) and it currently comes back with a single head:

northdpole added a commit to Bornunique911/OpenCRE that referenced this pull request Aug 9, 2026
Keep UniqueConstraint inside create_table for SQLite, drop the custom
_migration_tracking helper (schema clash risk), restore the OWASP#995
document_metadata no-op downgrade contract, and limit embedding_vec
downgrade to SQLite only.
Bornunique911 pushed a commit to Bornunique911/OpenCRE that referenced this pull request Aug 10, 2026
Keep UniqueConstraint inside create_table for SQLite, drop the custom
_migration_tracking helper (schema clash risk), restore the OWASP#995
document_metadata no-op downgrade contract, and limit embedding_vec
downgrade to SQLite only.
Bornunique911 pushed a commit to Bornunique911/OpenCRE that referenced this pull request Aug 15, 2026
Keep UniqueConstraint inside create_table for SQLite, drop the custom
_migration_tracking helper (schema clash risk), restore the OWASP#995
document_metadata no-op downgrade contract, and limit embedding_vec
downgrade to SQLite only.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: flask db upgrade fails on a fresh Postgres database (3 migration bugs)

2 participants