# R2-B blockers — pre-existing defects discovered by the spike The R2-B email + per-voter approval round-trip cannot complete the end-to-end DB-backed proof until both of the following pre-existing bugs are fixed. Both are OUT OF SCOPE for R2-B per the task brief. ## Blocker 1: model relationship bug — `MealPlan.votes` **File:** `backend/app/models/__init__.py:201` ```python class MealPlan(Base): ... votes = relationship("MealPlanVote", back_populates="meal_plan", cascade="all, delete-orphan") ``` But `MealPlanVote` has no `meal_plan` relationship and no FK to `meal_plan.id` — only to `meal_plan_item.id` (line 235). **Symptom:** `sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship MealPlan.votes` fires the moment ANY mapper is configured (i.e. on the first ORM use of any model). Blocks every flow, not just the vote flow. **Repro (with schema in place):** ```bash DATABASE_URL='postgresql://...' python -c " from app import models from sqlalchemy.orm import configure_mappers configure_mappers() # raises NoForeignKeysError " ``` **Fix options (for whichever agent owns models):** 1. Remove `MealPlan.votes` (votes are reachable via `MealPlan.items[*].votes`). 2. Add `meal_plan_id` FK to `MealPlanVote` and a back-ref. Requires migration. 3. Specify `primaryjoin="MealPlan.id == foreign(remote(MealPlanVote.meal_plan_item_id))"` via `MealPlanItem` — viewonly only. Option 1 is least invasive and matches existing usage in `meals.py` (no code reads `MealPlan.votes`). ## Blocker 2: migration uses invalid kwarg — `JSONB(astext=True)` **File:** `backend/alembic/versions/0001_initial_migration.py:101` ```python sa.Column('ingredients', postgresql.JSONB(astext=True), nullable=False), ``` `astext` is not a valid `JSONB.__init__` kwarg in SQLAlchemy 2.x. **Symptom:** `alembic upgrade head` fails with: `TypeError: JSON.__init__() got an unexpected keyword argument 'astext'`. **Impact:** `backend/tests/test_alembic.py::test_alembic_upgrade_head_roundtrip` fails. `backend/tests/conftest.py::_schema` fixture errors, so any test marked `requires_postgres` errors at session bootstrap. **Fix:** drop the kwarg. `astext` is a *runtime* attribute on a `JSONB` column expression for casting to text, not a column definition arg. ```python sa.Column('ingredients', postgresql.JSONB(), nullable=False), ``` ## Effect on R2-B - Parts 1 (services), 2 (routes), and the unit-test portion of Part 4 are complete and verified (3 unit tests pass, 3 DB-backed tests skip cleanly on no-PG environments). - Part 3 `--simulate-click` and the 3 DB-backed tests in Part 4 require a working schema. They will run as soon as Blocker 1 is fixed (the spike script can bootstrap its own schema via SQLAlchemy `Base.metadata.create_all` once the relationship resolves). ## Sandbox cleanup note While diagnosing Blocker 1, this agent ran `Base.metadata.create_all` against `mealplanner-db-1` (192.168.144.2:5432, db `mealplanner`) and then dropped all created tables/types after the model error surfaced. Final state: only `alembic_version` (matches pre-spike state).