gabriel / musehub public
0075_fix_proposal_snapshot_commit_naming.py python
86 lines 3.3 KB
Raw
sha256:8a1389ae62d0a688d5e027763249bd8faedfc39ab00857d65da6620d1ab8a689 Merge 'feat/9a-4-f7-overseer-provenance' into 'dev' — propo… Human 14 hours ago
1 """Fix musehub_proposals from/to_snapshot_id naming bug -- add from/to_commit_id
2
3 musehub#144 review of proposal #9 traced a real naming bug back to #0049
4 ("proposal_snapshot_anchors"): that migration's own docstring says it
5 "captures the HEAD commit ID of each branch" but named the columns
6 from_snapshot_id/to_snapshot_id. Every row ever written to these columns
7 holds a commit ID, never a Muse Snapshot (manifest) ID -- confirmed via
8 musehub_proposals.create_proposal, which has always populated them from
9 branch.head_commit_id.
10
11 Fix: add from_commit_id/to_commit_id (String(128), nullable), backfill them
12 from the existing from_snapshot_id/to_snapshot_id values (which already hold
13 commit IDs for every existing row), then correct from_snapshot_id/
14 to_snapshot_id in place to hold the real snapshot_id looked up from each
15 referenced commit. Existing rows whose commit_id no longer resolves in
16 musehub_commits (e.g. GC'd/unreachable history) are left with a null
17 snapshot_id rather than a guessed value.
18
19 Revision ID: 0075
20 Revises: 0074
21 """
22 from __future__ import annotations
23
24 from typing import Sequence, Union
25
26 from alembic import op
27 import sqlalchemy as sa
28
29
30 # revision identifiers, used by Alembic.
31 revision: str = '0075'
32 down_revision: Union[str, None] = '0074'
33 branch_labels: Union[str, Sequence[str], None] = None
34 depends_on: Union[str, Sequence[str], None] = None
35
36
37 def upgrade() -> None:
38 op.add_column('musehub_proposals', sa.Column('from_commit_id', sa.String(length=128), nullable=True))
39 op.add_column('musehub_proposals', sa.Column('to_commit_id', sa.String(length=128), nullable=True))
40
41 # Backfill the new, correctly-named columns from the existing (mislabeled)
42 # columns -- every existing row's from_snapshot_id/to_snapshot_id is
43 # already a commit ID.
44 op.execute(
45 "UPDATE musehub_proposals "
46 "SET from_commit_id = from_snapshot_id, to_commit_id = to_snapshot_id"
47 )
48
49 # Now correct from_snapshot_id/to_snapshot_id in place to hold the real
50 # snapshot_id of the commit they reference. Rows whose commit no longer
51 # resolves (unreachable/GC'd history) fall back to NULL rather than a
52 # stale commit ID masquerading as a snapshot ID.
53 op.execute(
54 "UPDATE musehub_proposals p "
55 "SET from_snapshot_id = c.snapshot_id "
56 "FROM musehub_commits c "
57 "WHERE c.commit_id = p.from_commit_id"
58 )
59 op.execute(
60 "UPDATE musehub_proposals p "
61 "SET from_snapshot_id = NULL "
62 "WHERE p.from_commit_id IS NOT NULL "
63 "AND NOT EXISTS (SELECT 1 FROM musehub_commits c WHERE c.commit_id = p.from_commit_id)"
64 )
65 op.execute(
66 "UPDATE musehub_proposals p "
67 "SET to_snapshot_id = c.snapshot_id "
68 "FROM musehub_commits c "
69 "WHERE c.commit_id = p.to_commit_id"
70 )
71 op.execute(
72 "UPDATE musehub_proposals p "
73 "SET to_snapshot_id = NULL "
74 "WHERE p.to_commit_id IS NOT NULL "
75 "AND NOT EXISTS (SELECT 1 FROM musehub_commits c WHERE c.commit_id = p.to_commit_id)"
76 )
77
78
79 def downgrade() -> None:
80 # Restore the pre-fix semantics: from/to_snapshot_id hold the commit ID.
81 op.execute(
82 "UPDATE musehub_proposals "
83 "SET from_snapshot_id = from_commit_id, to_snapshot_id = to_commit_id"
84 )
85 op.drop_column('musehub_proposals', 'to_commit_id')
86 op.drop_column('musehub_proposals', 'from_commit_id')
File History 1 commit
sha256:8a1389ae62d0a688d5e027763249bd8faedfc39ab00857d65da6620d1ab8a689 Merge 'feat/9a-4-f7-overseer-provenance' into 'dev' — propo… Human 14 hours ago