Skip to content

Let saved reforms and households point at a new v2 policy or household - #4005

Draft
anth-volk wants to merge 3 commits into
masterfrom
v2plan/1f-121-repoint-saved-items
Draft

anth-volk wants to merge 3 commits into
masterfrom
v2plan/1f-121-repoint-saved-items

Conversation

@anth-volk

Copy link
Copy Markdown
Collaborator

Fixes #4004

Summary

  • PATCH /v2/user-policies/{association_id} accepts policy_id, and PATCH /v2/user-households/{association_id} accepts household_id. A saved reform or household can then point at a newly created v2 policy or household while keeping its ID, owner, name and creation time, so reports that link to a saved reform keep working after it is edited.
  • Each request still loads the saved item only for the caller and the requested country (another user's item returns 404). The new policy or household must exist (404) and be in the same country (400); the composite foreign keys enforce the country rule in the database as well.
  • Both references supplied. If policy_id is supplied alone, it clears legacy_policy_id, because the v1 policy no longer describes what is saved; the same applies to household_id and legacy_household_id. If both are supplied, the v1 reference must exist in v1 (422) and both are stored; the v2 reference must equal the v2 copy of that v1 record when one exists (400 otherwise). These are the same rules creation already applies. A legacy_*_id supplied alone behaves as before: the v2 reference is re-resolved from the copy mapping, or cleared.
  • Saved policies maintained by the v1 saved-policy mirror still return 409 on PATCH, including a repoint.
  • Neither reference can be set to null (422).

Migration notes

  • Route groups covered: policy and household (user associations). The response contracts are unchanged; only the PATCH request bodies gain one optional field each, and the generated migration contracts do not change.
  • What stays on the Flask/v1 path: nothing changes there. Dual-write and the v1 saved-policy mirror are unaffected.
  • User-visible change: none until the app sends the new fields, which it does only after it moves to the v2 routes.

Environment variables

None. The change reads no environment variables and touches no workflow, image or infrastructure.

What this PR waits for

Only review. It has no schema change, no migration and no deployment dependency. It is the bottom of a stack of Public API PRs for the v2 metadata, regions and saved-items work; the PRs above it build on this branch.

Testing

Each commit was written test-first:

  • 79dd19602 (saved policies): 9 tests failed first (policy_id refused as an extra field); then the four user-association test files passed (44), and tests/unit/v2, tests/contract and tests/unit/test_migration_contract_artifacts.py passed (1,060).
  • e3d176fbb (saved households): 8 tests failed first (household_id refused); then 70 passed in the four files, 1,065 in the broad suite. Against a disposable PostgreSQL 17 database: alembic -c alembic-v2.ini upgrade head, reconcile_v2_privileges.py apply --create-missing-roles and verify, then tests/integration/test_alembic_v2_lifecycle.py, test_v2_user_identity_persistence.py and test_v2_privilege_reconciliation.py (25 passed, services running as the runtime role).
  • 083820742 (review follow-up, tests only): pins the PATCH failure statuses for households. It passed immediately, because the shared failure mapping already returns them; with the conflict status temporarily changed to 409, two cases failed.
  • On every commit: ruff format --check ., ruff check ., uv run --frozen --extra dev mypy, python scripts/run_quality_guards.py; python scripts/export_migration_contracts.py left the generated files unchanged.

No inline # noqa suppressions are added.

🤖 Generated with Claude Code

anth-volk and others added 3 commits October 22, 2026 02:10
PATCH /v2/user-policies/{id} now accepts policy_id. Supplied alone, it
points the saved policy at that v2 policy and clears legacy_policy_id,
which no longer describes the saved content. Supplied with
legacy_policy_id, both are stored, and policy_id must be the v2 copy of
that v1 policy when one exists, exactly as creation decides. The policy
must exist in the row's country. The row keeps its ID, owner, name and
creation time, so an edited reform keeps the ID that reports link to.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PATCH /v2/user-households/{id} now accepts household_id, with the same
rules as a saved policy's policy_id: supplied alone, it points the saved
household at that v2 household and clears legacy_household_id; supplied
with legacy_household_id, both are stored, and household_id must be the
v2 copy of that v1 household when one exists. The household must exist
in the row's country, and the row keeps its ID, owner, name and creation
time. Without this, editing a saved household changed its ID.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A household repoint can now fail with a reference conflict, and the
household route tests sent only DELETE requests through the failure
mapping. Send a repointing PATCH through it and pin each refusal's status:
400 for a country or reference conflict, 404 for a missing household or
saved household, and 422 for a missing v1 household.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 21, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.56098% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 87.95%. Comparing base (fc3ee33) to head (0838207).
⚠️ Report is 41 commits behind head on master.

Files with missing lines Patch % Lines
.../v2/user_households/database_connectors/updates.py 80.00% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           master    #4005       +/-   ##
===========================================
+ Coverage   50.12%   87.95%   +37.82%     
===========================================
  Files         243      259       +16     
  Lines       14726    16169     +1443     
  Branches     2343     2636      +293     
===========================================
+ Hits         7382    14222     +6840     
+ Misses       6721     1275     -5446     
- Partials      623      672       +49     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

This branch was successfully deployed

1 active deployment
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.

Let a saved reform or household point at a new v2 policy or household without changing its ID

1 participant