Skip to content

fix(db): repair broken migration history for fresh deploys - #9

Open
catomean wants to merge 10 commits into
masterfrom
claude/codebase-review-optimize-pso1cx
Open

fix(db): repair broken migration history for fresh deploys#9
catomean wants to merge 10 commits into
masterfrom
claude/codebase-review-optimize-pso1cx

Conversation

@catomean

Copy link
Copy Markdown
Collaborator
  • 20260223_simplify_roles_admin_only failed on any fresh database:
    Postgres cannot cast a column default to a recreated enum type.
    Drop the default before the type swap and restore it after.
  • Add catch-up migration for Incident.mediationMinutes and the
    SystemConfig table, which existed in schema.prisma but were never
    captured in a migration (drift from a past db push). IF NOT EXISTS
    guards keep it safe on databases that already have them.

Verified: prisma migrate deploy succeeds on a fresh database and
prisma migrate diff reports no drift afterwards.

Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Claude-Session: https://claude.ai/code/session_01XYKT7w58RCJasVqGUXNVkQ

claude added 10 commits July 16, 2026 17:03
- 20260223_simplify_roles_admin_only failed on any fresh database:
  Postgres cannot cast a column default to a recreated enum type.
  Drop the default before the type swap and restore it after.
- Add catch-up migration for Incident.mediationMinutes and the
  SystemConfig table, which existed in schema.prisma but were never
  captured in a migration (drift from a past db push). IF NOT EXISTS
  guards keep it safe on databases that already have them.

Verified: prisma migrate deploy succeeds on a fresh database and
prisma migrate diff reports no drift afterwards.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYKT7w58RCJasVqGUXNVkQ
Approving a request previously only flipped status to APPROVED while the
UI showed success — the resident stayed in the old unit and the captured
target unit was discarded. Now, when a request names a target unit and
the resident has an active placement, approval executes the transfer in
one transaction: atomic PENDING claim, capacity check (first free
non-ROOM spot), server-side blocking-conflict check, old placement ended
as TRANSFERRED, new placement created with computed compatibility
scores + pairwise assessments, spot and unit statuses updated, request
marked COMPLETED, audit-logged as TRANSFER. Requests without a target
unit stay approval-only and now link staff to the resident page to
complete the move.

Transfer-requests page: resident and unit references are real links,
COMPLETED gets its own tab, APPROVED cards show a follow-up link, the
duplicate full-table query is gone, and the review controls use the
design-system .input/.btn classes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYKT7w58RCJasVqGUXNVkQ
Lead each card with what the placement decision needs: fit score with
tier label (score-legend tooltip), occupancy, top strengths, blocking
concerns, high-risk warning and safeguard notes. Unit history, the
historical success box, the per-roommate breakdown and minor concerns
move behind a native <details> expander. Also drop a stray tracked
screenshot and ignore future nav-*.png captures.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYKT7w58RCJasVqGUXNVkQ
- Preferences round-trip: roommate-preference answers (age range,
  cultural preference, free-text wishes) are now serialized in a stable
  labeled format, parsed back into the form on revisit, and legacy
  free-form values are preserved in the wishes field instead of dropped.
- Dashboard shows the resident's open transfer request (status card
  linking to /portal/transfer), mirroring the reports card.
- Rating scales get emoji end anchors, a word label for the selected
  value and per-option aria-labels instead of bare 1-5 numbers.
- Emergency guidance unified across footer, report and help pages:
  112 first everywhere, then Polizei 117 / Rettungsdienst 144, AOZ
  emergency vs general line clearly separated.
- Tone fixes: portal error copy now uses du form; preferences success
  message says the AOZ team reviews changes.
- Login page: simpler copy plus hints telling residents (du form) their
  code is on the welcome letter and staff where codes come from.
- Sticky safe-area submit bars on the report and create-chore forms.
- Removed dead PORTAL_LABELS.login/.landing blocks (unused register flow).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYKT7w58RCJasVqGUXNVkQ
- Matching page previously fired ~5 queries per candidate unit (unit
  metrics 4x sequential + success rate 1x), saturating the Prisma pool
  on every load. New calculateUnitMetricsBatch computes metrics for all
  units in 4 parallel queries (accepting pre-loaded unit rows), and
  getSimilarPlacementSuccessRateBatch buckets one envelope query per
  score set. Existing per-unit exports keep identical return shapes and
  delegate to the batch versions.
- Analytics page: dropped an unused full resident-table fetch and
  narrowed all five remaining queries to the fields actually rendered.
- Housing detail: unplaced-resident candidates narrowed to the
  compatibility-relevant columns.
- Bounded the residents/housing list queries with the existing
  QUERY_LIMITS.pageList cap (counts remain exact).
- Indexes for real query patterns: Incident(housingUnitId, category,
  date), Placement(compatibilityScore), CompatibilityAssessment
  (comparedWithId), with a hand-authored migration matching prisma
  migrate diff output.
- Deleted vercel.json (dead artifact from the pre-Hetzner deploy).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYKT7w58RCJasVqGUXNVkQ
- Creating an incident now lands on the new incident's detail page with
  a success toast (was: bounced back to the list), so mediation time,
  follow-ups and resolution are one step away.
- Incident list rows: unit, reporter and subject are real links to
  housing/resident pages (row restructured to avoid nested anchors,
  title link preserved).
- PageHeader is now the h1 SSOT on every detail/create page (residents
  new + detail, incident detail, maintenance detail, check-in), with a
  new additive 'leading' slot so avatars/category emoji survive the
  conversion.
- Honest two-step indicator on the new-resident form: step 2 is labeled
  as the matching stage and a hint explains it starts after saving.
- Maintenance: assignee inputs get a datalist of known assignees plus a
  one-click 'Mir zuweisen' button on list and detail; detail priority
  pill now uses the shared .chip classes.
- Medical-documentation strings moved into the labels SSOT; placements
  overdue indicator uses the status-warning token instead of a raw
  palette class.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYKT7w58RCJasVqGUXNVkQ
… after review

Findings from an adversarial review of the branch diff:

- TransferActions showed the 'complete the transfer' link after a DENIAL
  (deny also returns executed:false); success UI now distinguishes deny,
  approve-only (wires in the previously-orphaned successApprovedOnly
  label) and executed outcomes.
- A pending request naming a target unit became permanently stuck when
  the resident's placement had ended: the transaction threw and rolled
  back to PENDING on every attempt. It now commits the APPROVED claim
  and reports executed:false instead. Also prefers the resident's
  current active placement when the one recorded on the request ended.
- Falsy-zero bug: a legitimate 0% fit score posted apartmentFitScore=100
  to placeResident ('|| 100' changed to '?? 100').
- MatchCard classified blocking concerns by German substring matching;
  now uses the shared isBlockingConcern SSOT, and apartment-level
  BLOCKING conflict reasons stay visible on the card face instead of
  hiding inside the details expander (the disabled button had no
  visible justification). Dropped the misleading details count, guarded
  a potential null deref, composed the score-legend tooltip from the
  existing matrix legend labels instead of a duplicate literal.
- transferPlacement (manual staff transfer) enforced no blocking-conflict
  check while placeResident and transfer approval both refuse hard
  constraints; the same guard now applies to all three paths.
- Preference parser now decodes the legacy single-line '. '-joined format
  (raw codes) instead of dumping it into the wishes field, and switches
  to free-text continuation mode so wishes lines that look like
  structured lines are never mis-promoted.
- 'Anfrage wurde bereits bearbeitet' literal centralized as
  ERROR_MESSAGES.TRANSFER_ALREADY_PROCESSED (the error classifier
  substring-matched on it).
- README documents that the daily notification cron must be scheduled on
  the host (the deleted vercel.json was its only scheduler).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYKT7w58RCJasVqGUXNVkQ
…le specs

The e2e suite had 41 failures that predate this branch (verified: the
identical failure set reproduces on master). Root causes and fixes:

Real app bugs:
- /housing rendered an EMPTY primary CTA link: it referenced the
  nonexistent PAGE_TITLES.newHousing key (untyped Record lookup), so the
  anchor had no accessible name. Now uses ACTION_LABELS.newHousing.
- role="tab" links without a role="tablist" parent on the incidents,
  maintenance and transfer-requests pages (axe critical).
- Algorithm page tabs pointed aria-controls at a tabpanel that was never
  rendered; content is now wrapped in the existing TabPanel.
- Unassociated form labels on the settings pilot-baseline inputs and the
  portal preferences selects/textarea (axe critical).
- Color-only inline link on portal roommates (1.37:1 contrast); now
  always underlined.
- Portal-housing a11y scan silently never scanned the page (placed
  resident gets redirected mid-scan); seed now marks an unplaced
  resident's preferences complete so the real browse page is scanned.

Spec drift (specs updated to current intended UI): sidebar nav was
replaced by the header megamenu; renamed headings/labels asserted via
their actual label-constant values ('Auswertung', 'Missions-KPIs',
'Neueste Platzierungen', new COMPLETED transfer tab, …); detail routes
stream through loading.tsx so 404s assert the rendered not-found page
instead of the HTTP status; matching specs updated for the redesigned
match cards and the intentional 'Platzierte Bewohner' what-if section.
Fragile locators that matched hidden mobile-DOM are scoped to the
desktop table/main landmarks.

Full suite: 192/192 passed. Jest: 2067 passed, 0 failures. No axe rules
weakened, no exclusions, no quarantined tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XYKT7w58RCJasVqGUXNVkQ
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.

2 participants