← HR Cluster

Changelog

Operational and technical updates to the HR Cluster platform.

Types of changes
Added New functionality or modules.
Updated Improvements or changes to existing functionality.
Core Major architectural, platform-wide, or operational changes.
Deprecated Features scheduled for removal.
Removed Features or functionality removed.
Fixed Bug fixes and issue resolution.
Security Security improvements or vulnerability fixes.

2026

Smaller polish

Documents review modal preview is capped so body, thumbs, metadata, and class list wrap into a single scroll region; the doc column drops the redundant "Verified: date (name)" line; delete-proof placement tightened; per-file × removal added on bundle thumbs in the public competence form.

Groups • recipient language resolved from contact for emails and PDFs

When a group has a designated contact, outbound emails and the PDFs they carry now render in that contact's preferred language instead of the manager's session language.

Locations • crew list actions inline on the table

Crew list actions are now exposed directly on the locations table, with sub-row buttons aligned on a single line so narrow viewports don't drop them onto a second row.

Clockwork • faster clock-in flow with progress feedback and pre-warmed GPS

GPS is now pre-warmed when the form opens and times out faster, narrowing the gap between submit and server response. A loading-progress toast bridges the submit-to-modal handoff so the screen never looks frozen, and the verify→handout continuation persists across reload via sessionStorage. Post-action URL cleanup is now consistent across every clock-in/out entry point, and the "Clocking out…" toast clears immediately when there is no follow-up return flow.

Analytics • consent-gated GA4 with sanitised paths

Google Analytics now loads only after the visitor accepts cookie consent, and tracked URLs are stripped of tokens, signing keys, and workspace slugs before being sent. A new hrcTrack() helper standardises custom events across modules so feature usage can be measured consistently.

Technical focus: GA4 • Consent gate • Path sanitisation

Public form uploads • mobile camera multi-shot fix

Adding a second photo from a phone camera no longer wipes the first. The file queue now accumulates across change events and submits via fetch + FormData whenever items are pending, so multi-attachment uploads complete in a single round-trip and survive iOS Safari's per-tap input reset.

Outbound emails and PDFs render in the recipient's language end-to-end

Every outbound email and the PDFs they carry now resolve language per recipient. Coverage includes group-contact emails, food-allergies summaries, and shift emails sent from the Clockwork QR pipeline. Saving a user profile no longer writes the actor's session language into users.preferred_language, closing a long-running source of mis-localised mail. The email template loader now lives inside the with_lang() boundary so async sends pick up the resolved language even when no admin is in session.

Technical focus: resolve_recipient_lang() • with_lang() boundary • preferred_language default empty

Competence • multi-file proof bundles with per-class verification

Competence proof can now be uploaded as a bundle of multiple files, so a renewal upload doesn't replace the previous evidence. Each class on a multi-class certificate verifies, rejects, and resets independently in the review modal. Actions stay scoped to the class you clicked, not the whole bundle. Verifying the last outstanding class auto-approves the linked pending bundle, and the review modal's preview, body, thumbs, metadata, and class list now scroll as one capped region instead of stacking off-screen.

Deploy • DB migration gate on app nodes

App-node post-receive hooks now refuse to rsync if ops/required_migration.txt names a migration that hasn't been applied to the database. Worker nodes are exempt by hostname so non-DB-touching deploys still flow. Structurally enforces DB-first deploy ordering, so app code can no longer reach the apps before the schema it depends on.

Technical focus: ops/required_migration.txt • Pre-deploy schema check

Clockwork • observational performance instrumentation

performance.mark() is now placed across the full clock-in/out timeline (form open, submit, modal visible, handout/return visible) so timing regressions can be caught from real-user metrics rather than hand-timing the flow.

Technical focus: performance.mark() • cw-* timing marks

Imports • shift CSV helper, column alignment, upload validation

Shift CSV imports get a helper card that shows the expected format, with column headers now matching the form labels exactly so renaming a field in the form doesn't silently break the import. Upload validation enforces is_uploaded_file() on the temp path before any further processing.

Technical focus: is_uploaded_file • Header validation

Billing • workspace plans split from operational add-ons

The billing page separates the workspace plan tiers (Core / Pro / Enterprise) from operational add-ons such as Ops and module-level entitlements, so it's clear at a glance which line items are subscription tiers versus per-feature opt-ins.

Locations • crew list actions surfaced from the modal

Each location modal now exposes the crew list actions directly, so you can pull a roster or message attached crew without leaving the page.

Food & allergies • async request emails, clearer copy

Sending out request emails no longer blocks the page response. Requests are queued and dispatched asynchronously through the worker. The email copy now spells out that the worker should review and confirm even when they have no allergies to declare.

Food & allergies • related and overlapping crew in summary PDF

The summary PDF now includes a person-centric section listing related and overlapping crew with shared allergens or diet markers. Names of unrelated workers are never paired with their dietary data; the section is anonymised for GDPR. The detailed PDF's group filter now correctly includes subgroup members, and the section reads group membership from current account_users, not affiliation history.

Items • bulk return, bulk transfer, and clock-out return capture

Issued items can now be returned or transferred in bulk from the Open page, and Clockwork's clock-out flow captures returns alongside the session itself. Pool items configured with require_identifier_on_issue are supported in handout, and max_quantity is enforced for concurrent issuance across multiple workers. Closes a race that could let a finite pool go over its cap.

Public changelog API now exposes module taxonomy and technical-focus metadata

GET /api/public/changelog now returns a `modules` array (locked taxonomy of 20 keys) and `technical_focus` (string or null) on every entry. Existing fields unchanged — purely additive. ETag bumped from v2 to v3 so cached clients revalidate cleanly.

Technical focus: ETag v2 → v3 • Locked module taxonomy • CORS allowlist

Public changelog redesigned as an operational technical surface

Outcome-first titles, type and module pills, optional technical-focus footer line on infrastructure and security entries. Module taxonomy spans 20 keys (Workforce, Clockwork, Infrastructure, Security…). The page is now English across all locales for terminology stability; surrounding workspace UI stays localized.

Smaller polish across users, imports, and platform switcher

Employee preselected as default worker type on user import; phone and rate formatted in import preview; alert bullets indented; age shown inline beside DOB on user detail; phone country-code stripping unified across forms; platform switcher hidden when an account has only one platform enabled.

Contract upload form prefills validity dates from project

Selecting a project on the contract upload form now prefills valid_from and valid_to, mirroring the existing behaviour of the new-contract form.

Submission deduplication unified across signup, public form, and admin paths

Double-submit dedup is now applied consistently to every submission entry point. The food-allergies group column reads current group state, and invoice reference is now properly optional rather than silently flagging a submission as incomplete.

Transactional emails resolve language per recipient

All outbound mail — not just contracts — now picks language from the recipient's preference plus the account default, instead of the sending actor's session language. Closes a long-standing class of mis-localised notifications.

Technical focus: resolve_recipient_lang() • Per-recipient language resolution

Form interaction model unified across the workspace

Native browser confirm and alert dialogs replaced with in-app modals (Escape closes, focus returns to the trigger). Every form shows a submit loading state, and submitter values are preserved when validation fails.

Security hardening pass across POST handlers, PIN entry, and signing tokens

Enforced CSRF on every POST endpoint, added per-user PIN throttling, and started logging IP plus user-agent on contract-signing and password-reset tokens. Mail attachments now fail closed when the file is missing, and elevated access is cleared automatically when a user is demoted.

Technical focus: CSRF • PIN throttling • Per-token IP/UA audit

Form submissions grouped by workflow state, reviewed in a dedicated modal

Submissions are bucketed by workflow stage (needs review, approved, rejected) instead of raw lifecycle states. Each opens in its own review modal, keeping context off the main list.