Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions E2E_USER_STORIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ task that found it, so the row can be traced.
| REG-07 | P2 | Level selection does not persist once the selector is hidden/disabled. | "Fix level selections persisting when selector is disabled/hidden" | todo |
| REG-08 | P1 | An authority with a **relative** `login_url` still reaches a usable login page. | Found 2026-07-30, this suite | **blocked** — currently worked around in `seed.ts`; ts-client resolves a relative `login_url` against the authority host **without its port**, so any non-443 deployment dead-ends. Needs a ts-client/init fix before a spec can assert the good behaviour. |
| REG-09 | P1 | Concurrent `POST /bookings` do not 500. | **[PPT-2642](https://acaprojects.atlassian.net/browse/PPT-2642)** | **fixed at source, not yet in the image this suite runs against.** Root cause was in pg-orm: crystal-db clears a connection's transaction flag only *after* the COMMIT it issues, so a COMMIT that fails — the serialization failures a booking burst produces — leaves it set, and the one place connections return to the pool could not see it. The connection went back to the pool with its `BEGIN` still open; reads kept working inside the orphaned transaction while every write returned 500 until restart. Fixed in [pg-orm#19](https://github.com/spider-gazelle/pg-orm/pull/19) (v2.2.4) and picked up by staff-api in #386. Measured with images differing only by that version: control stranded a connection in 3 of 4 bursts and ended unable to write; fixed, 0 of 11 and still writable. **`e2e/stack` pulls `placeos/staff-api:latest`, which is a release image — so this suite keeps running the old behaviour until the next platform release.** **Corrected 2026-09-14: this suite now DOES burst concurrent bookings.** The line that used to sit here — "harmless for now, nothing here bursts concurrent bookings" — stopped being true when the group-invite specs (VIS-03/VIS-04) landed: a group invite posts the container and every member at once, which is exactly the burst. Measured with `e2e/support/repro/vis-b6-burst.ts` (no browser, 4 concurrent POSTs x 12 rounds): 13x 201, 29x 400, 6x 500, clean in the early rounds and degrading after. Before a rollback-and-retry was added to the visitor flows the group specs failed about one run in three; with it, 10 of 10 runs clean and the recovery fired twice. That retry is a **workaround living in `e2e/support/visitor/visitor.flows.ts`**, to be deleted once the staff-api image carries pg-orm v2.2.4 — it is also why the suite being green must not be read as REG-09 being gone from the image. Reproducers: `e2e/support/repro/reg09-concurrent-bookings.ts` and `vis-b6-burst.ts`. |
| REG-10 | P1 | The booking form does not discard user input while it is still initialising. | **[PPT-2643](https://acaprojects.atlassian.net/browse/PPT-2643)** | **blocked** — the form is rebuilt when async init completes and restores defaults (title, All Day, Require locker), silently dropping anything typed before that. A real user can hit this; they would just see their title or options revert. `bookDeskViaUI` converges on the state to work around it, which means **the suite no longer detects it** — hence this row. Investigated 2026-08-05 against #478 (`a0360486`): **the bug is still live**, established by reading the code rather than by running this suite, and fixed in **PR #479**. `newForm`'s protected branch is never taken by the flows — the current user is restored from cache ~50ms after bootstrap, while `NewDeskFlowComponent.ngOnInit` calls `loadForm` then `newForm` only after org init plus a 300ms settle — and `loadForm` had no capture at all. The shipped e2e suite meanwhile is stable at 6 consecutive full runs, 8/8, `--retries=0`, which is precisely the problem: **it passes either way**. Removing the block could not be shown to be safe *or* unsafe from here: the race needs initialisation to be slow relative to typing, and this machine wins it every time. Two failed attempts to prove otherwise, both recorded so nobody repeats them: (1) a synthetic "type during init, assert it survives" spec passed with *and* without the fix, even with the API responses held to widen the window; (2) removing the block appeared to prove the bug survived — it did not. That red was a Playwright **strict-mode violation**, not a reverted value: opening the desk-select modal puts a second "All Day" checkbox in the DOM (`desk-filters`, bound to the same field), so an unscoped locator matched two elements and threw, with both checked. Scoping the locator to `desk-flow-form` then broke it a second way, because `setCheckbox` silently returns when its locator matches nothing, turning a narrower scope into a no-op and a genuinely invalid form. Both experiments were reverted. **The block stays and this row stays blocked even once #479 lands** — not because the app is unfixed, but because this suite cannot tell either way on fast hardware. The guard for REG-10 is the unit specs in `libs/bookings/src/test/booking-form.service.spec.ts`; unblocking this row needs artificial slowness (throttled CPU), not another e2e attempt. |
| REG-10 | P1 | The booking form does not discard user input while it is still initialising. | **[PPT-2643](https://acaprojects.atlassian.net/browse/PPT-2643)** | **partial** — `desk-initialisation.spec.ts` (DESK-21) covers the desk form. It holds the 300 ms settle timer of the desk flow, so its edits to the title and "Require locker" come before the form reset on any hardware. It fails when the replay of the "Require locker" edit is removed. No spec covers the other forms, and the flows below still converge on the form state. History: the form is rebuilt when async init completes and restores defaults (title, All Day, Require locker), silently dropping anything typed before that. A real user can hit this; they would just see their title or options revert. `bookDeskViaUI` converges on the state to work around it, which means **the suite no longer detects it** — hence this row. Investigated 2026-08-05 against #478 (`a0360486`): **the bug is still live**, established by reading the code rather than by running this suite, and fixed in **PR #479**. `newForm`'s protected branch is never taken by the flows — the current user is restored from cache ~50ms after bootstrap, while `NewDeskFlowComponent.ngOnInit` calls `loadForm` then `newForm` only after org init plus a 300ms settle — and `loadForm` had no capture at all. The shipped e2e suite meanwhile is stable at 6 consecutive full runs, 8/8, `--retries=0`, which is precisely the problem: **it passes either way**. Removing the block could not be shown to be safe *or* unsafe from here: the race needs initialisation to be slow relative to typing, and this machine wins it every time. Two failed attempts to prove otherwise, both recorded so nobody repeats them: (1) a synthetic "type during init, assert it survives" spec passed with *and* without the fix, even with the API responses held to widen the window; (2) removing the block appeared to prove the bug survived — it did not. That red was a Playwright **strict-mode violation**, not a reverted value: opening the desk-select modal puts a second "All Day" checkbox in the DOM (`desk-filters`, bound to the same field), so an unscoped locator matched two elements and threw, with both checked. Scoping the locator to `desk-flow-form` then broke it a second way, because `setCheckbox` silently returns when its locator matches nothing, turning a narrower scope into a no-op and a genuinely invalid form. Both experiments were reverted. **The block stays and this row stays blocked even once #479 lands** — not because the app is unfixed, but because this suite cannot tell either way on fast hardware. The guard for REG-10 is the unit specs in `libs/bookings/src/test/booking-form.service.spec.ts`; unblocking this row needs artificial slowness (throttled CPU), not another e2e attempt. |

## 4. Platform & configuration

Expand Down Expand Up @@ -327,7 +327,7 @@ Config gaps caused several production incidents, and they are invisible to UI sp
or through `use_bookings`?** Nobody has answered it. If the answer is "the calendar", the
green rows in §1b guard a path those customers never take. This needs a product answer, not more
specs.
- **Nine rows are blocked on product fixes, not on test effort** (REG-08, REG-09, REG-10,
- **Eight rows are blocked on product fixes, not on test effort** (REG-08, REG-09,
VIS-15, ROOM-11, ROOM-13, ROOM-14, ROOM-21, ROOM-22). All were found by this suite. Leaving them visible here is the point — a blocked row is coverage
information, a deleted row is not.
- **One row is blocked by the environment rather than by a bug** (AUTH-E2E-08), and it says
Expand Down Expand Up @@ -355,8 +355,9 @@ Config gaps caused several production incidents, and they are invisible to UI sp
`reg09-client-abort.ts` (which ruled out client disconnects) and `vis-b6-burst.ts` (the same
burst with no browser at all, which is what proved the visitor 400s were REG-09 rather than a
form bug).
- **REG-10 is invisible to this suite by design.** `bookDeskViaUI` converges on the form state,
so nothing here will catch it regressing. PPT-2643's fix landed in #478; whether it is
- **REG-10 is invisible to the booking flows of this suite by design.** `bookDeskViaUI` converges
on the form state, so no flow will catch it regressing. Only `desk-initialisation.spec.ts`
(DESK-21) detects it, and only for the desk form: it holds the settle timer of the desk flow. PPT-2643's fix landed in #478; whether it is
*complete* is genuinely unresolved, and the honest summary is that this suite cannot answer it
on hardware this fast. Deleting the converging block passes serially and fails about one full
parallel run in three, on the symptom rather than on the mechanism.
Expand Down
25 changes: 14 additions & 11 deletions apps/outlook-addin/src/app/desks/desk-form.component.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import {
Component,
inject,
Injector,
OnInit,
} from '@angular/core';
import { Component, computed, inject, Injector, OnInit } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { FormField } from '@angular/forms/signals';
import { MatCheckboxModule } from '@angular/material/checkbox';
import { MatFormFieldModule } from '@angular/material/form-field';
import { MatInputModule } from '@angular/material/input';
import { AssetListFieldComponent } from '@placeos/assets';
import { BookingFormService, DeskListFieldComponent } from '@placeos/bookings';
import {
BookingFormService,
DeskListFieldComponent,
requiresLocker,
} from '@placeos/bookings';
import {
AsyncHandler,
Desk,
Expand Down Expand Up @@ -67,7 +66,10 @@ import {
name="start-time"
[ngModel]="model().date"
(ngModelChange)="
model.update((m) => ({ ...m, date: $event }))
model.update((m) => ({
...m,
date: $event,
}))
"
[ngModelOptions]="{ standalone: true }"
></a-time-field>
Expand Down Expand Up @@ -96,9 +98,7 @@ import {
<div class="flex items-center space-x-2">
<div class="w-1/3 flex-1">
<mat-checkbox
[ngModel]="
!!model().secondary_resource
"
[ngModel]="requires_locker()"
(ngModelChange)="
model.update((m) => ({
...m,
Expand Down Expand Up @@ -176,6 +176,9 @@ export class DeskBookingFormComponent extends AsyncHandler implements OnInit {
'desks.can_book_lockers',
false,
);
public readonly requires_locker = computed(() =>
requiresLocker(this.model().secondary_resource),
);

public ngOnInit() {
this._service.setOptions({ type: 'desk' });
Expand Down
Loading
Loading