It was never used. The repository lives on a self-hosted Gitea, which
Vercel's git integration cannot connect to at all — so the documented
route amounted to "mirror to GitHub first", and nobody did.
vercel.json is gone, and with it the branch in next.config.ts that
switched off `output: "standalone"` when the VERCEL variable was
present. That branch was the only functional trace; everything else was
documentation and comments describing a second deployment path that did
not exist.
DEPLOYMENT.md loses its "two supported ways" framing and the whole
Vercel section — about fifty lines. Several statements next to it were
stale for a different reason and are corrected in the same pass: the
outbound-firewall table still listed Supabase's pooler (the database is
a container now, nothing leaves the server), the prerequisites still
demanded an existing Supabase project, and the .env table still asked
for a pooler connection string instead of the two new passwords.
The nightly job is described as what it is — a container in
docker-compose.yml — rather than as a replacement for Vercel Cron.
Migrations keep their references: two comments from July mention Vercel
Cron, and they describe what was true when they were written.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The one document describing the schema, docs/data-model.md, predates two
rebuilds. It names divisions/departments/teams and a positions table that
no longer exist, describes Supabase auth with an anon key and a service
role that were removed, and puts the policy count at 58 when it is 21.
Anyone reading it to understand the data would have been misled on every
count.
docs/datenkatalog.md replaces it, and was not typed up from memory: the
columns, defaults, keys and check constraints were read out of
information_schema and pg_catalog on the running database. Fifteen
tables, 142 columns, ten enum types, 21 policies. Where a rule appears in
prose, the constraint it comes from is named next to it.
Some of it only became visible by asking the database rather than the
migrations. generate_company_email and the is_hr_admin pair are still
defined but nothing calls them any more. Position numbers look like a
six followed by seven digits because the generator builds them that way,
not because anything enforces it — the column requires only uniqueness.
monthly_salary_gross is dead weight kept in case old rows hold data.
Three claims I drafted were wrong and the database said so: the position
number format, the event trigger's name (ensure_rls, the function behind
it is rls_auto_enable), and which tables deviate from the plain
is_hr_user() policy.
The old document keeps a pointer at the top instead of being deleted —
it is linked from the security review, and a stale document that says so
is more useful than a dead link.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Data model
- employee_assignments records org placement over time (valid_from/valid_to),
written by a trigger on `employees` rather than inside each RPC: ~70
`update employees` statements spread over fifteen migrations mean per-call
bookkeeping would miss paths today and again with every future RPC. A
partial unique index enforces the one-open-interval invariant the trigger
relies on when closing the current row.
- The Organigramm gains a Stichtag (default today). Membership comes from
entry/exit/karenz, past placement from the new history, future placement
projected from pending_org_changes. Placements predating the migration are
backfilled with today's values and flagged as such in the UI, since
employee_history only ever stored free text and cannot be reconstructed.
Correctness
- Reports and exports silently truncated at PostgREST's 1000-row cap
(db.max_rows); employee_history is already past it at ~800 staff. Every
whole-table read now pages explicitly.
- XLSX date cells were a day early: ExcelJS converts a Date to an Excel
serial straight off getTime(), so a Date built at local midnight lands on
the previous day's serial in any positive-offset zone.
- Date handling is pinned to Europe/Vienna throughout, and date-only strings
are formatted without a Date round-trip. The dashboard's YTD window was
built by round-tripping a local Date through toISOString(), which shifted
it a day early and dropped 31 December entirely.
- Export routes parsed measure/group/split/eventType with unchecked `as`
casts, so an unknown value reached column headers as `undefined` and the
Content-Disposition filename. Parsed against the label maps now, with the
filename slugged as a backstop.
- toXlsx keyed columns by header text, silently dropping the second of any
two columns sharing a name — split columns take their header from data.
- The org chart tree walks had no cycle guard; nothing in the schema forbids
a manager_id cycle, and one would hang the tab rather than misreport.
- The login page reflected ?error= verbatim, letting anyone put arbitrary
text on the real sign-in screen; messages are looked up by code now.
- React Flow needs elementsSelectable on, or it sets pointer-events:none on
the whole node and the expand control stops responding.
UI
- Mobile: the shell was unusable below lg — a fixed 236px margin pushed
content off-screen with no mobile navigation at all. The sidebar is now a
drawer, dvh replaces vh, safe-area insets are honoured, inputs are 16px so
iOS stops zooming on focus, and form grids stack.
- Org chart nodes redesigned: per-kind accent stripes and icons, vacant
roles called out, expand control moved to the bottom edge carrying the
child count.
- Pagination is windowed; it previously rendered one link per page (54 for
the employee list, unbounded for the audit log).
- Positions page reduced to open positions with a single "Besetzen" action.
- The employee Organisation tab links into the org chart focused on that
person, reusing the chart's existing search-match highlighting.
Also included, uncommitted until now
- Dependants, HR notes, academic titles, split address fields, position
validity and role/employment fields, with their migrations and UI.
- Docker/compose deployment setup, data-model and security-review docs.