Switches `vitest.config.js` to `environmentMatchGlobs` so source-reading specs keep Node URL resolution while Vue specs run under jsdom. Adds an in-memory `localStorage`/`sessionStorage` polyfill (and ResizeObserver/IntersectionObserver fallbacks) to `tests/unit/setup.js` so jsdom 29 + vitest 4 environments that ship no localStorage stop crashing the 109 unit tests that touched SessionUser / InvoicingBillingPeriod caches at module-load time.
Test result: 1343/1343 fast + 1688/1688 serial pass (was 1195/1304 on master). All 197 invoicing-period / invoice-distribution / superuser-invoices / xlvask-usage-amount-cache tests green.
## Summary
- fall back to the existing scoped `RELEASE_MANAGER_GATE_TOKEN` when
`SERVER_UPDATE_TOKEN` is absent
- record the exact frontend SHA through the release-gate endpoint, then
independently read it back
- preserve the legacy dedicated-token path when it is configured
- carry the scoped credential and exact run-attempt build ID through
normal releases, rollback recovery, and restore-on-failure
## Dependency
Depends on backend PR copenhagentruckwash/api#342 being merged and
deployed before this PR is merged.
## Verification
- focused release-gate updater test: 1 passed
- direct exact-SHA update/readback execution passed
- ESLint passed for changed JavaScript/tests
- Prettier passed for both workflows and changed JavaScript/tests
- Node syntax and `git diff --check` passed
The existing broader cPanel release test is also updated; the local
cached dependency set cannot collect that file because `jszip` is
absent, so protected CI remains the full-suite authority.
## Summary
- preserve complete snapshot item payloads during inline edits and
reject partial text-field payloads
- force snapshot refreshes after parent/mutation changes with one
bounded recovery retry
- make legacy tree-action fallback create, confirm, and apply a fresh
compatible preview
- keep collection labeling localized and report the correct changed
count
## Verification
- focused object-tree and snapshot suites: 30 tests passed
- focused ESLint and `git diff --check` clean
- production build and selected-customer mocked Playwright flow passed
before final review fixes
- App Store Readiness and Qodana green on exact head; Automated Tests in
progress
- independent QA and reviewer gates: GO
Resolves all inline review threads on the current head.
Separate Pleno login email from e-conomic invoice email, make the login-email update flow authoritative and cache-safe, clear stale economic profile state, and add focused frontend coverage.
## What changed
- restore the desktop invoicing-period category groups as a vertical
right-hand rail
- use a 3:1 content-to-navigation split at desktop widths with
responsive stacking below 1024px
- size the inline month selector to the available content width with a
readable 32rem cap
- adapt the invoice review workspace to the narrower content region
- add unit contracts and browser geometry coverage for desktop, tablet,
and mobile
## Why
The grouped period categories had moved into a wide horizontal row,
leaving unused space on the right and constraining the period content.
The inline month picker was also too small to read comfortably.
## User impact
Superusers again see period categories in the right rail on desktop.
Compact layouts retain responsive navigation, and the review/object-tree
content avoids field wrapping when the main region becomes narrow.
## Validation
- 60/60 focused unit tests
- 30/30 full Chromium desktop invoicing-period browser tests
- 9/9 priority browser tests across Chromium desktop, tablet, and mobile
- ESLint
- production Vite build
- focused Prettier checks
- git diff check
Add a persistent recursive expand/collapse control above collected invoices and keep order-item quantity and price in the canonical editable field layout.
Unify truckwash.dk Kundeoprettelse and QR traffic on the shared customer page, add protected registration UX, and complete the limited-backoffice demo flow.
Adds a safe Bird Control Plane configuration surface with read-only health, credential redaction, protected capability switches, canonical workspace support, and focused frontend coverage.
Required CI passed on head cc04117d6b against master 3de5215b5e.
## Summary
Updates the QR login view to consume the short-lived, one-time employee
login grants created by approved Pleno Control Plane
Conversations/Suggestions actions.
- reads generated grants from the URL fragment
- scrubs the bearer from the address bar before exchange
- exchanges the grant for a normal session token, then uses the existing
secure session-storage path
- preserves legacy token QR links
- validates exact URL origin and removes raw credential/QR logging
- prevents repeated scanner exchange attempts while one is in progress
## Visual change previews
No layout or styling changes. The visible flow changes only after
opening or scanning a grant:
- Before: one-time grant links were rejected as unknown QR content.
- After: the existing loader appears during exchange; invalid/expired
grants use the existing localized error dialog; successful grants
redirect through the existing login path.
## Verification
- focused Vitest: 2 passed
- focused ESLint: passed
- production Vite build: passed (existing chunk-size warning only)
- `git diff --check`: passed
## Dependency
Pair with copenhagentruckwash/api (one-time limited-backoffice login
grants) and merge after that backend PR. Required by
copenhagentruckwash/pleno-control-plane#1.
Ensure department-scoped self-serve settings load and save safely across route transitions, document the API contract, and cover stale in-flight state.
Keep the iOS status bar outside the Capacitor web view and replace startup geolocation watching with silent permission checks plus an explicit location action.
Verified by full unit, App Store readiness, Qodana, production build, Capacitor sync, and Playwright mobile suites.
## What changed
- add `NSLocationAlwaysAndWhenInUseUsageDescription` to the iOS app
- localize the purpose string in Danish and English
- enforce the key in mobile permission validation and App Store
readiness tests
## Why
App Store Connect accepted builds 1 and 2 but emitted `ITMS-90683`,
stating that the final app references APIs requiring this purpose
string. Shipping a corrected binary avoids submitting a candidate with a
known Apple delivery warning.
## Validation
- `node scripts/mobile/check-permissions.mjs`
- `node scripts/mobile/validate-app-store.mjs --strict`
- `vitest run tests/unit/app-store-product-readiness.spec.js` (5 tests)
- Prettier check for changed JS files
- `git diff --check`
The production FTPS deploy now authenticates, but lftp exits when cPanel
returns 550 File exists for pre-created deployment directories. Use lftp
mkdir -p -f for the archive and activation directories so retries remain
idempotent while subsequent upload operations still surface real access
failures.\n\nVerification:\n- vitest tests/unit/cpanel-deploy.spec.js
(26 passed)\n- eslint scripts/release/cpanel-deploy-lib.mjs
tests/unit/cpanel-deploy.spec.js\n- git diff --check
Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
## Summary
- feed the generated lftp command script directly over stdin
- avoid reopening `/dev/stdin`, which fails on the production hosted
runner
- keep FTP credentials out of process arguments
## Verification
- `vitest run tests/unit/cpanel-deploy.spec.js` (26/26)
- ESLint on changed files
- `git diff --check`
Supersedes the failed production release run 29948809036.
## Summary
- Complete the frontend contract for hardened backend account deletion
(#319).
- For passwordless accounts, request a fresh deletion-specific WebAuthn
challenge and submit its serialized assertion.
- Reuse the existing passkey assertion serializer instead of duplicating
WebAuthn conversion logic.
- Accept the durable `manual_review` backend state while presenting the
existing safe failure copy.
## Verification
- Account deletion unit tests: 9/9.
- Focused ESLint passed for all four changed files.
- Node syntax checks and `git diff --check` passed.
Backend rollout flags remain default-off; this UI is inert until #319
schema checks and explicit API enablement are completed.
## Summary
- Preserve lftp stdout/stderr when the process exits non-zero.
- Surface bounded, whitespace-normalized diagnostics through the deploy
error.
- Redact FTPS host, username, password, path, URL userinfo, and encoded
secret forms.
## Verification
- `vitest run tests/unit/cpanel-deploy.spec.js` (25/25)
- `node --check scripts/release/cpanel-deploy-lib.mjs`
- `git diff --check`
This is the prerequisite diagnostic repair for failed Frontend Release
run 29854900889. Production was not switched during that failure.
Require explicit mobile-v* tags or manual dispatch, gate exact tested master SHAs, and default Google Play production submissions to an initial 1% in-progress rollout.
## Summary
- remove the release packager's undeclared dependency on host `zip` and
`unzip` executables
- create and round-trip validate ZIP artifacts in Node with explicit
paths, permissions, timestamps, CRC checks, and resource limits
- preserve the existing archive filename, checksum, inventory, and
top-level `dist/` contract
## Root cause
After the prebuilt-dist integrity repair passed on master, Frontend
Release reached packaging and failed with `spawn zip ENOENT` on the
self-hosted runner. The workflow never installed or checked either
archive executable.
## Verification
- focused release/deployment tests: 57/57 passed
- packager tests: 9/9 passed, including empty `PATH`, cross-timezone
determinism, exact archive entries, permissions, and oversized
sparse-file rejection
- real production build: 735 files packaged successfully with an empty
`PATH`
- repeated real packaging produced byte-identical archives
- Info-ZIP test/list/checksum validation passed
- extraction under `umask 077`: every directory is `0755`; all 735 files
extracted
- extracted inventory exactly matches the source inventory
- ESLint and Prettier passed
Co-authored-by: Jeppe Bundgaard <jb@truckwash.dk>
## What changed
- adds production iOS identity, localized storefront metadata, native
privacy declarations, App Store-safe artwork, and account-deletion UX
- mirrors the live Danish Google Play title, short description, and long
description in the App Store metadata source
- generates Android launcher/store icons from the opaque iOS marketing
master so both platforms use the same white background
- adds guarded GitHub Actions workflows for storefront readiness,
credential health, signed TestFlight uploads, and App Store candidate
preparation
- adds pinned Fastlane configuration with a committed dependency lock,
release manifest tooling, and an operational App Store runbook
- preserves the upstream iOS safe-area implementation while retaining
opaque App Store icon assets
## Why
The repository previously supported development-signed device bundles
but had no production App Store identity, reproducible storefront source
of truth, or protected signed-release pipeline. Apple also requires
in-app account deletion for apps that support account creation. The
Android icon master was transparent, which rendered as black on dark
store/device surfaces.
## Impact
Automation remains fail-closed behind
`APP_STORE_AUTOMATION_ENABLED=false`. No build can upload to TestFlight
or change App Store metadata until the switch is deliberately enabled
after merge and the remaining release gates are satisfied.
## Validation
- focused App Store, iOS icon, and cross-platform icon-background tests
pass
- every generated Android store/launcher icon is opaque with pure-white
corners; iOS marketing artwork is checked the same way
- Android icon drift check passes for all 19 generated files
- production Vite build and the broader focused release checks completed
successfully
- storefront metadata is valid; only the two expected screenshot-set
warnings remain
- App Store Readiness is green at head `4445fecc`
- Apple Distribution certificate and App Store profile were
independently verified for `HP3FJ4GVL7.io.truckwash.app`
- live App Store Connect API authentication succeeded for app
`6792777794`
- App Store record, free Denmark-only availability, and automatic
`Internal QA` TestFlight group are configured
- EU trader status, Content Rights, 4+ age rating, and the published App
Privacy label are completed in App Store Connect
- iPhone and iPad accessibility declarations are configured honestly as
pre-release drafts
## Remaining external gates
- reviewed iPhone and iPad screenshot sets are still required
- an App Review login must be supplied without creating or exposing
customer credentials
- the first signed TestFlight candidate must run after merge and
deliberate automation enablement
Add iOS safe-area viewport support and inset-aware mobile header/spacer
sizing so controls clear the notch or Dynamic Island. Desktop and tablet
headers remain unchanged. Includes focused unit and mobile browser
regression coverage.
## What changed
- load customer product rules for the booking customer and keep
selection fail-closed while they resolve
- prevent restricted products, add-ons, pickup, and wash certificates
from remaining selected or being submitted
- improve product-grid containment, selected add-on styling, unavailable
messaging, and tablet/mobile rendering
- preserve booking details when the API rejects stale restricted items
- add unit and Playwright coverage for loading, retry, exact
restrictions, automatic products, layout, and stale backend recovery
## Why
The customer booking flow could use stale or unrelated POS customer-rule
state, expose restricted items, and lose useful form state after a
server rejection. The desktop product area also overflowed and switched
to an inconsistent compact layout after selection.
## Validation
- desktop booking Playwright suite: 12 passed
- mobile booking Playwright suite: 4 passed, 8 expected desktop-only
skips
- relevant unit tests: 35 passed
- ESLint and i18n checks
- production Vite build
## Related backend PR
The coordinated API PR enforces the same rule at the write boundary.
Align restricted desktop and mobile POS controls, scope the Danish primary registration label to POS, and preserve generic registration wording across shared surfaces and other locales.