v1.4.0 Phase 1: ORM/DB index sync + dashboard-dedup gap (#57) and UI consistency pass (#69) #107

Merged
claude-bot merged 2 commits from feat/v1.4.0-phase1 into main 2026-07-19 03:16:00 +00:00
Contributor

First phase of the v1.4.0 codebase-health milestone. Two independent findings, disjoint file sets.

#57 (F-18) — ORM index metadata sync + dashboard-dedup NULL gap

The drift was broader than the six indexes the audit enumeratedalembic revision --autogenerate also flagged mismatches on six further tables. The models now reproduce the live DB exactly, so autogenerate on a clean head yields a genuinely empty diff (verified against a live-schema Postgres).

  • Added model index metadata matching migration-only indexes: sent_alerts (location_id, nws_alert_id, expires), locations (nws_state, enabled), sent_forecasts (sent_date), and notification_deliveries (sent_alert_id + composite ix_notification_deliveries_due).
  • Reconciled a unique-constraint-vs-unique-index metadata mismatch on five more tables (ai_summary_policies, product_freshness, public_alert_pages, public_tokens, redirect_tokens): models now declare the named uq_* constraint + non-unique ix_* index that the DB actually has. Model metadata only — no schema change.
  • Migration 0024: partial unique index uq_sent_alerts_dashboard_dedup (nws_alert_id, location_id) WHERE channel_id IS NULL closes the dashboard-dedup NULL gap (Postgres NULL semantics left dashboard-only rows undeduped at the DB level), plus a (location_id, channel_id) hot-path index. Note: the SELECT-then-insert dedup is already race-safe under the scheduler's max_instances=1; the index formalizes the invariant and guards a future multi-instance deployment (a savepoint-based IntegrityError handler would be the follow-up if concurrency is ever introduced).
  • Added the missing alembic/script.py.mako — it was never committed, so alembic revision failed on any fresh clone; that's what blocked the empty-diff acceptance check in the first place.
  • env.py: documented the ALTER TYPE ADD VALUE single-transaction pitfall (0007) for future authors.
  • Postgres-tier test asserts the partial unique index rejects a duplicate dashboard row.

#69 (F-30) — UI consistency pass

Mechanical front-end cleanup: ✕ encoding fix, confirm() parity on removeSub, double-submit guards on five more handlers, standardized user-facing errors onto inline error divs (all ~22 alert() error sites, via a shared showInlineError helper — broader than the 7 the audit listed, fulfilling item 4's intent), aria-labels on icon-only remove buttons, defined-token fix for --color-accent, and deletion of the dead/unreachable afterSwap JSON block (an unescaped-innerHTML hazard if revived).

Testing (dev-server Postgres)

  • alembic revision --autogenerateempty diff
  • Migration 0024 applies on upgrade head
  • Bulk suite 658 passed; Postgres tier 4 passed (incl. the new dedup test).

Closes #57, #69

🤖 Generated with Claude Code

First phase of the v1.4.0 codebase-health milestone. Two independent findings, disjoint file sets. ## #57 (F-18) — ORM index metadata sync + dashboard-dedup NULL gap The drift was **broader than the six indexes the audit enumerated** — `alembic revision --autogenerate` also flagged mismatches on six further tables. The models now reproduce the live DB exactly, so **autogenerate on a clean head yields a genuinely empty diff** (verified against a live-schema Postgres). - Added model index metadata matching migration-only indexes: `sent_alerts` (location_id, nws_alert_id, expires), `locations` (nws_state, enabled), `sent_forecasts` (sent_date), and `notification_deliveries` (sent_alert_id + composite `ix_notification_deliveries_due`). - Reconciled a unique-constraint-vs-unique-index metadata mismatch on five more tables (`ai_summary_policies`, `product_freshness`, `public_alert_pages`, `public_tokens`, `redirect_tokens`): models now declare the named `uq_*` constraint + non-unique `ix_*` index that the DB actually has. **Model metadata only — no schema change.** - **Migration 0024**: partial unique index `uq_sent_alerts_dashboard_dedup (nws_alert_id, location_id) WHERE channel_id IS NULL` closes the dashboard-dedup NULL gap (Postgres NULL semantics left dashboard-only rows undeduped at the DB level), plus a `(location_id, channel_id)` hot-path index. Note: the SELECT-then-insert dedup is already race-safe under the scheduler's `max_instances=1`; the index formalizes the invariant and guards a future multi-instance deployment (a savepoint-based `IntegrityError` handler would be the follow-up if concurrency is ever introduced). - **Added the missing `alembic/script.py.mako`** — it was never committed, so `alembic revision` failed on any fresh clone; that's what blocked the empty-diff acceptance check in the first place. - `env.py`: documented the `ALTER TYPE ADD VALUE` single-transaction pitfall (0007) for future authors. - Postgres-tier test asserts the partial unique index rejects a duplicate dashboard row. ## #69 (F-30) — UI consistency pass Mechanical front-end cleanup: `✕`→`✕` encoding fix, `confirm()` parity on `removeSub`, double-submit guards on five more handlers, standardized user-facing errors onto inline error divs (all ~22 `alert()` error sites, via a shared `showInlineError` helper — broader than the 7 the audit listed, fulfilling item 4's intent), `aria-label`s on icon-only remove buttons, defined-token fix for `--color-accent`, and deletion of the dead/unreachable `afterSwap` JSON block (an unescaped-innerHTML hazard if revived). ## Testing (dev-server Postgres) - `alembic revision --autogenerate` → **empty diff** ✓ - Migration 0024 applies on `upgrade head` ✓ - Bulk suite **658 passed**; Postgres tier **4 passed** (incl. the new dedup test). Closes #57, #69 🤖 Generated with [Claude Code](https://claude.com/claude-code)
F-18: six live indexes existed only in migrations, and autogenerate would have
proposed dropping them (and more — the drift was broader than the six the audit
enumerated). Now the ORM metadata reproduces the live DB exactly and
`alembic revision --autogenerate` on a clean head yields an EMPTY diff.

- Model index metadata added to match existing migration-only indexes:
  sent_alerts (location_id, nws_alert_id, expires), locations (nws_state,
  enabled), sent_forecasts (sent_date), and notification_deliveries
  (sent_alert_id, plus the composite ix_notification_deliveries_due).
- Reconciled the unique-constraint-vs-unique-index drift on five further tables
  (ai_summary_policies, product_freshness, public_alert_pages, public_tokens,
  redirect_tokens): models now declare the named uq_* UniqueConstraint + a
  non-unique ix_* index, matching what the DB actually has. Model metadata only
  — no schema change.
- New migration 0024: partial unique index
  `uq_sent_alerts_dashboard_dedup (nws_alert_id, location_id) WHERE channel_id
  IS NULL` to close the dashboard-dedup NULL gap (Postgres NULL semantics left
  dashboard-only rows undeduped at the DB level), plus a composite
  (location_id, channel_id) index for the dedup hot path. The SELECT-then-insert
  dedup is safe today under the scheduler's max_instances=1; the index formalizes
  the invariant and guards future multi-instance deployments.
- Added the missing alembic/script.py.mako template — it was never committed, so
  `alembic revision` failed on any fresh clone; this is what blocked the
  empty-diff check.
- env.py: note the ALTER TYPE ADD VALUE single-transaction pitfall (0007).
- Postgres-tier test asserts the partial unique index rejects a duplicate
  dashboard row.

Verified on the dev-server Postgres: empty autogenerate diff, 0024 applies,
bulk suite 658 passed, postgres tier 4 passed.

Closes #57

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
UI consistency pass: encoding, confirm parity, submit guards, inline errors, a11y (#69)
All checks were successful
CI / test (pull_request) Successful in 4m35s
0df88b4e93
F-30 mechanical cleanup, one pass:
- Fix ✕ mojibake → ✕ (dashboard, channels, subs partial); standardize close glyphs.
- Add confirm() to removeSub for parity with removeLocationSub.
- Apply the existing disable-during-request guard to submitAddChannel,
  submitEditChannel, addSubscription, submitEditLocation, submitEditLocationSub.
- Standardize user-facing errors on inline error divs instead of alert() (all
  ~22 error sites converted via a shared showInlineError helper / existing
  containers; the one genuine success alert in testModel left as-is).
- aria-label on icon-only remove buttons (subs_list, location_subs_list).
- Replace undefined --color-accent with the defined --color-primary.
- Delete the dead, unreachable afterSwap JSON block in app.js (partials render
  HTML, never JSON — an unescaped-innerHTML hazard if revived).

Closes #69

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
claude-bot deleted branch feat/v1.4.0-phase1 2026-07-19 03:16:01 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
rbrooks/WeatherBot!107
No description provided.