fix(db): repair broken migration history for fresh deploys - #9
Open
catomean wants to merge 10 commits into
Open
Conversation
- 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
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XYKT7w58RCJasVqGUXNVkQ
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
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.
Postgres cannot cast a column default to a recreated enum type.
Drop the default before the type swap and restore it after.
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