Repository navigation
Conversation
…mands The policy and user-association qualification commands counted saved policies through the UserPolicy model. Both run before every v2 upgrade, so once the model takes a new table name they would query a table the database does not have yet. The policy qualification counts only while its revision is pending, when the table is still named user_policies, so it names that table. The user-association qualification counts on every run, so it counts the table under whichever name the database has: user_policy_associations once renamed, otherwise user_policies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The saved-policy table now follows the naming of the other user-association tables. Revision fa7fa1035444 was autogenerated from the renamed model. Its one documented post-generation correction replaces the generated create-and-drop pair, which would have deleted every saved policy, with an in-place rename; each constraint named after the table is dropped under its old name and created under the generated name, and the generated changes to the two convention-named indexes and the three foreign keys that refer to the table are kept around the rename. The composite listing indexes keep their ix_user_policies_* names, as the other association tables keep their short index names. The privilege declarations and the head revision constant use the new name. Dual-write's saved-policy copy goes through the model and follows it. Unchanged: the route /v2/user-policies, the user_policy_id columns, legacy_user_policy_mappings and the v1 Cloud SQL table user_policies. Not included: a temporary view named user_policies for the release still running during the deploy. A view is not autogenerated, so it needs a human migration decision; a strict expected-failure test records the gap. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- The revision test finds fa7fa1035444 by its ID instead of through the head constant, so a later revision no longer makes it read the wrong file, and it checks the revision's parent through the script directory. - The lifecycle test checks each privilege of the probe role on its own (has_table_privilege with a list is true when any one is held), and checks grants and delete cascades after the downgrade as well as after the upgrade. - The test for the missing user_policies view now stores a saved policy and upgrades to head before reading it under the old name, and only a missing relation counts as the expected failure, so an empty database or a failure while setting up no longer satisfies it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The merge table and the rule for Stage 10 mirror rows name the table by its current name. The rollback and schema compatibility sections no longer say head lets earlier releases run: until a view under the old name exists, a release from before the rename fails every statement on saved policies, from the schema upgrade until the new release takes traffic, and after a rollback until the rename is downgraded. They also say that downgrading the rename alone keeps every row, and that the Stage 12A compatibility test now maps the renamed table and no longer proves the parent release runs against head. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## v2plan/1f-121-repoint-saved-items #4010 +/- ##
=====================================================================
- Coverage 87.95% 87.95% -0.01%
=====================================================================
Files 259 259
Lines 16169 16174 +5
Branches 2636 2636
=====================================================================
+ Hits 14222 14226 +4
- Misses 1275 1276 +1
Partials 672 672 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #4008
Summary
user_policiesis renamed touser_policy_associations, matchinguser_household_associations,user_simulation_associationsanduser_report_associations. Its primary key, unique constraint, three check constraints, two foreign keys and two convention-named indexes take the new name; the foreign keys fromlegacy_user_policy_mappingsanduser_report_associationsfollow the table.ix_user_policies_country_user_created_id,ix_user_policies_country_policyandix_user_policies_country_legacy_policy. The sibling association tables keep short names for the same indexes (ix_user_households_*,ix_user_simulations_*,ix_user_reports_*), so the short names are the existing convention.privileges.py) andV2_ALEMBIC_HEAD_REVISIONuse the new name. The Stage 10 saved-policy dual-write copy goes through the model and follows it.user_policies, so it names that table. The user-association qualification counts on every run, so it countsuser_policy_associationswhen that table exists anduser_policiesotherwise. Their JSON evidence keeps the keyuser_policies./v2/user-policies, theuser_policy_idcolumns,legacy_user_policy_mappings, and the v1 Cloud SQL tableuser_policies.Migration notes
policyuser-association routes keep their contracts; only the table behind them is renamed. The generated migration contracts do not change.user_policiestable is untouched.fa7fa1035444(parentfbb7488ebe8a).The migration correction, and why it is within the migration rules
The revision was generated with
uv run alembic -c alembic-v2.ini revision --autogenerate. Autogeneration cannot detect a rename: it created an emptyuser_policy_associationstable and droppeduser_policies, which would delete every saved policy, and its copies of the three composite indexes would have collided with the old table's indexes of the same names. The repository's Alembic rules allow a minimal, documented, tested post-generation correction of an objectively incorrect autogenerated operation. The revision has one such correction in each direction:op.rename_tablereplaces the create-and-drop pair.user_policy_associationsis dropped under its old name and created under the generated name with the generated definition, using Alembic operations only (noop.execute, no data operations).The correction is documented in the revision and in
docs/engineering/skills/alembic-migrations.md("API v2 saved-policy table rename"). The v2 Postgres lifecycle test stores a saved policy with a v1 mapping and a report that names it, then checks that the rows, references, delete cascades, a table grant (each privilege separately) and every constraint and index name are right after the upgrade and after the downgrade. Rebuilding the primary key, unique and check constraints takes a brief lock on the table during the upgrade.Left out: the temporary
user_policiesviewThe plan for this rename includes a view named
user_policiesover the renamed table, so that the release still serving during a deploy keeps working. It is not in this PR. Alembic does not autogenerate views, and creating one would be a hand-written schema operation, which the migration rules leave to a human decision.test_previous_release_still_finds_user_policiesrecords the gap as a strict expected failure: it stores a saved policy, upgrades to head and reads it under the old name. It starts failing, as a reminder to remove the marker, once a view exists.Without the view, a Public API release from before this revision fails every statement it makes on saved policies, because it names
user_policies:deploy-cloud-run-candidatetakes; in staging, until the staging deploy)./v2/user-policiesroutes, user-report reads that join saved policies, the sign-in merge that moves an unauthenticated user's saved policies, and Stage 10 saved-policy dual-write copies (DB_WRITE_POLICYiscloud_sqltoday, so these are off).If a view is added later, the runtime role also needs grants on it. That means a
privileges.pyentry, and a change to the privilege reconciliation, whose table query reads only ordinary and partitioned tables (relkind IN ('r', 'p')), so it would treat a declared view as missing and grant nothing.The Stage 12A document (
docs/migration/stage-12a-user-login.md) now says this in its rollback and schema compatibility sections, and uses the new table name for current behavior.Environment variables
None. The change reads and adds no environment variables, and touches no workflow, image or infrastructure.
What this PR waits for
user_policiesview described above: merge without it and accept the gap during the deploy, or add the view (with its grants) first.Testing
Each commit was written test-first:
0fda92e3f(qualification commands):test_saved_policies_are_counted_under_their_name_after_the_renamefailed first (assert 0 == 3: the renamed table was never counted). Then both qualification unit files passed (16) and the qualification integration test passed against disposable PostgreSQL. The two tests for the pending case pin the old name; they passed before and after.b91cebc80(the rename): on a fresh disposable PostgreSQL 17 database, 11 unit tests failed first (KeyError onuser_policy_associations, the missing revision, the privilege manifest, the recorded saved-policy SQL) and 4 lifecycle tests failed (NoSuchTableError public.user_policy_associations,user_policiesstill present). Then:alembic -c alembic-v2.ini checkreported no new operations;reconcile_v2_privileges.py apply --create-missing-rolesandverifypassed, then the identity, comparison-run, privilege and saved-policy mirroring integration tests (27 passed);make testset without coverage passed (3,398).dcf53c748(review follow-up, tests):has_table_privilege(role, table, 'SELECT, UPDATE')returned true for a role holding only SELECT, so each privilege is now checked on its own.ON DELETE SET NULLremoved from the downgrade's report foreign key, the new downgrade cascade check failed.459b03111(review follow-up, document only): the Stage 12A document.ruff format --check .,ruff check .,uv run --frozen --extra dev mypy,python scripts/run_quality_guards.py;python scripts/export_migration_contracts.pyleft the generated files unchanged.No inline
# noqasuppressions are added. A rules fixture (rules/fixtures/api.v2.runtime-privileges-declared/reads_good.py) changes its docstring to the new table name, so this PR requests a review fromcore-developers.🤖 Generated with Claude Code