# Scheduling Enterprise-Readiness Audit

**Assessment date:** 2026-09-02
**Repositories:** `buyerkiosk-web`, `buyerkiosk-team`, `buyerkiosk-live-flutter`
**Branches:** `fix/scheduling-integrity`
**Normal bases:** Web `master`; Team `main`; Live `master`

## Executive verdict

**FAIL CLOSED — not yet approved for unrestricted enterprise rollout.**

The review confirmed and fixed material defects in shift persistence, overnight and DST handling, publication visibility, concurrent assignment safety, payroll export integrity, AI authorization, API destination validation, and mobile pending-request consistency. Focused scheduling suites are strong, authenticated Team and Live navigation works against dev2, and deterministic cross-app checks confirmed draft/published/unpublished behavior.

The deterministic Web lifecycle, payroll, AI, DST/boundary, Team production realtime, and a fresh 21-row pending-request traversal have controlled dev2 evidence with exact cleanup. The pending-pagination technical gate now passes: final collector regressions reject duplicate identities, repeated cursors, and incomplete or excessive traversal counts; the E2E suite passed **10/10**; and a fresh hash-bound independent review matched all Web, Live, and E2E manifests with no security or logic findings. Local repository packaging is complete, including initialization and an initial verified commit for the E2E harness. Explicit disposition of unrelated repository-wide baseline failures and any remote push/PR workflow remain open.

Passing tests do not prove the module is bug-free.

## Confirmed fixes

### Shift persistence and concurrency

- Manual create, update, assignment, AI assignment, Open Shift claims, approved swaps, and recurrence paths use a shared database-scoped employee-assignment advisory lock.
- Multi-employee locks are acquired in sorted order and released after the protected transaction.
- Manager updates distinguish omitted fields from explicit employee clearing.
- Open Shift notes can be set, retained, and explicitly cleared with `notes: null`.
- Overnight Open Shift creation and editing roll an end time earlier than or equal to start into the following store-local day.

### Timezones, DST, and duration

- Store timezone is authoritative for AI scheduling.
- Solver intervals use real instants rather than naive local comparisons.
- Repeated-hour fall-back coverage compares elapsed shift minutes with availability duration, preventing a one-minute local window from covering a real one-hour fold.
- Overnight and spring-forward/fall-back solver cases are covered.
- Web REST and Team realtime duration calculations use canonical timestamps, include seconds, and round consistently.
- Team and Live consume canonical backend duration fields where available.

### Publication visibility

- Employee REST and realtime contracts carry explicit publication state.
- Team filters non-published rows fail-closed.
- Realtime events without authoritative publication state trigger REST reconciliation instead of silently mutating employee-visible state.
- Web and Team share a byte-identical publication fixture matching the real `Shift::jsonSerialize()` payload.

### Payroll integrity

- Exported rounded hours reconcile with exported pay.
- Export finalization, status mutation, and audit persistence are atomic.
- `Idempotency-Key` is required from the client and retained across response-loss retries.
- Existing keys are resolved before mutable provider, timesheet, employee, rule, or CSV reads.
- Replays rebuild the original CSV from durable scalar-row snapshots and verify the persisted SHA-256.
- Reusing a key for a different request fails closed.
- Historical employee/rate/rule snapshots remain available after later source mutation.
- Native export is gated to explicitly authoritative scheduling-provider stores.

### AI scheduling integrity

- Anonymous and guest-session requests receive JSON `401 UNAUTHORIZED` before store lookup or controller execution.
- AI apply and ordinary scheduling writes share the same employee-assignment serialization boundary.
- AI active-job migration remediation is restart-safe and deterministic.
- Suggestion, active-job, and payroll-integrity migrations use independently guarded DDL operations so partial rollout can be safely resumed.

### Live manager app

- Schedule View opens the actual weekly schedule route.
- Narrow-iPhone dashboard cards no longer overflow.
- API build overrides accept only HTTPS BuyerKiosk production/dev2 hosts; arbitrary and plaintext destinations fall back safely.
- Pending requests use a real server-owned signed cursor contract with canonical Base64URL encoding, deterministic timestamp plus composite `(type, requestId)` ordering, and binding to store/filter/sort state.
- Live drains server pages, rejects repeated cursors, malformed/unknown rows, negative/changing/impossible totals, inconsistent terminal metadata, and incomplete traversal, and uses generation plus immutable filter/sort/cursor snapshots so stale requests cannot overwrite newer state.
- Pending-request identity, selection, deduplication, and typed actions use composite `(type, requestId)` identity; ambiguous numeric-only actions fail closed.
- Batch results require an exact, unique processed/failed ID partition; malformed or incomplete responses fail closed and optimistic changes roll back.
- `retryable: false` is terminal even for unknown error codes.
- The exposed but incomplete punch-edit action is disabled rather than pretending to succeed.

## Verification evidence

### Web/backend

- Scheduling + MobileScheduling plus focused Staff Chat authorization run: **1,792 tests, 6,994 assertions passed**, with **2 skipped** and **5 existing PHPUnit deprecations**.
- Current MobileScheduling verification after pagination: **376 tests, 1,342 assertions passed**, with **3 PHPUnit deprecations**; all six scoped PHP implementation/test files are syntax-clean, and scoped PHPStan on the three implementation files reports **no errors**.
- Solver suite: **68 passed**.
- Payroll-focused suite: **103 tests, 475 assertions passed**.
- AI route/security/controller suite: **59 tests, 308 assertions passed**; one existing PHPUnit deprecation.
- Migration-focused checks: **6 tests / 68 assertions** and **4 tests / 59 assertions passed**.
- Shared publication contract checks passed, including complete serializer equality.
- Final independent backend rereview passed with no security concerns or logic errors; its primary focused command passed **79 tests / 654 assertions** and confirmed template replacement and active AI application cannot mutate published Team-visible rows.
- The current `phpstan-scheduling.neon` run remains non-green with **103 unrelated/baseline diagnostics** outside the Team realtime scheduling publisher paths; the only Staff Chat diagnostics are pre-existing `empty()` findings at lines 390 and 1301, not the token path. No diagnostic was reported in the new Ably publisher, Redis sequence provider, mobile manager wiring, or scheduling controller publication code.
- `git diff --check`: clean.

The complete Web baseline remains non-green from broader existing/environment-dependent failures, including PosDataCatchup integration tests. The default full PHPStan scan also remains unhealthy with **190 file errors across 51 files**; the narrower scheduling configuration still reports **103 unrelated/baseline diagnostics**, so PHPUnit, PHP syntax checks, changed-file diagnostic review, live E2E, and independent review are the applicable clean gates for this change set.

### Team Flutter

- Focused scheduling run: **131 tests passed**.
- Publication/realtime/duration suites: **49 passed**, with a contract/entity rerun of **23 passed**.
- Post-remediation realtime/provider/screen/entity/service verification: **104 tests passed**, including store-switch reauthorization, renewable tokens, connected-state confirmation, cached-wrapper teardown, connection timeout, disposal safety, sequence-gap reconciliation, cross-store dedup isolation, redacted revocation, and `schedule.resync` parsing/reconciliation.
- The final shared-connection remediation validates scope before every Chat subscription entry, cancels old-store listeners before new-store HTTP, fully serializes `connect`/`ensureConnected`/`disconnect`, and prevents an awaited `leaveChannel()` from clearing newer channel state or recreating an old-store subscription. Its focused command passed **71 tests** and broader selected realtime/scheduling/chat command passed **148 tests**. The bounded final independent rereview passed with no security concerns or logic errors; its focused race regression and full `chat_notifier_test.dart` run passed **1** and **45** tests respectively.
- Targeted Dart analysis: **no issues**.
- `git diff --check`: clean.

The complete Team baseline still contains approximately forty auth/FCM and other pre-existing failures outside this scheduling change set.

### Live Flutter

- Focused pending-request batch/provider suite: **47 passed**.
- Current selected scheduling verification after real pagination and scoped formatting: **766 passed, 2 skipped**.
- Broad scheduling suite, including core, data, providers, screens, widgets, integration, routing, and API-host coverage: **1,287 passed, 2 skipped**.
- EmployeePicker and RequestCard regressions: **39 focused tests passed**; scheduling-widget suite: **250 passed**.
- API-host, route, dashboard-layout, and focused provider tests passed.
- Targeted pagination-path analysis exits successfully with no errors or warnings and **27 info-only** `use_null_aware_elements` style findings; scoped formatting reports zero changed files.
- `git diff --check`: clean.

## Authenticated dev2 evidence

Environment: isolated non-production `dev2`, store `ou00`, timezone `America/Chicago`.

### Passed

- Team iOS scheduling navigation.
- Live iOS scheduling navigation.
- Live route into the weekly schedule.
- Web marker cleanup.
- Web-created overnight Open Shift database read-back:
  - unassigned employee;
  - UTC `03:00 → 07:00`;
  - exactly four elapsed hours.
- Live manager edit:
  - Open Shift visible as `22:00 → 02:00`, `4h`;
  - assigned through the real app;
  - database read-back confirmed assignment;
  - cleared back to Open Shift;
  - database read-back confirmed `employeeId = null`.
- Team cross-app visibility through authoritative refresh:
  - assigned draft hidden;
  - published overnight shift visible as `10:00 PM – 2:00 AM`, `4h`;
  - unpublished shift hidden again.

### Passed after deterministic continuation

- The complete Web lifecycle passed with deterministic browser primitives: create, overnight edit, assign, explicit unassign, publish, unpublish, soft delete, API/database read-back, and exact cleanup.
- Spring-forward, fall-back, overnight, split-shift, week-boundary, and month-boundary cases passed across Web, Live, Team, API, and database surfaces with seven exactly cleaned fixtures.
- Payroll live export, same-key response-loss replay, immutable audit read-back, and exact cleanup passed after exact-store `ou00` migration.
- Math AI generation reached `OPTIMAL`, applied one isolated assignment atomically while retaining draft publication state, persisted matching job/suggestion provenance, and exactly cleaned all artifacts. A discovered job-linkage defect was repaired under RED/GREEN coverage.
- Pending-request single and batch terminal errors, malformed batch isolation, canonical codes, raw compatibility messages, and multilingual machine classification passed direct dev2 plus focused tests.
- Genuine pending-request pagination passed final controlled dev2 validation in `E2E-PENDING-PAGINATION-20260902-H`: 21 exact isolated `ou00` rows were traversed across three server pages at limit 7, stable server total and traversed count were 21, every exact ID was found, the cursor exhausted, dry-run cleanup matched exactly 21, applied cleanup deleted all 21, and zero remained.
- Team production realtime passed controlled dev2 validation in `E2E-TEAM-REALTIME-20260902-G`: an active-assignment-scoped token granted only `subscribe` on `kiosk_ou00`, draft creation did not leak during a recorded 15-second event window, authoritative publication converged over Ably, a published edit emitted a redacted `shift.deleted` visibility revocation without exposing draft details, per-channel sequence advanced monotonically from `66` to `67`, REST agreed with the revocation, draft deletion did not leak during a second recorded 15-second window, and exact shift/publish/audit cleanup verified zero rows.

### Remaining or blocked

- Natural-language Midscene action planning remains unreliable with `Replanned 20 times, exceeding the limit`; successful gates use deterministic browser primitives instead.
- Production Team realtime implementation, live convergence, and the final independent fail-closed security/logic rereview pass; Team scope is closed.
- Pending-request broad selected tests and the fresh 21-row live traversal pass. Explicitly authorized final remediation closed duplicate-identity, repeated-cursor, incomplete-terminal-count, excessive-total, and fabricated runner-metadata findings under RED/GREEN tests. The final hash-bound independent review matched all three scoped manifests and found no security concerns or logic errors; this technical gate is closed.
- Backend cursor pagination currently reduces response size but not source-query workload: all four pending sources are still gathered and hydrated before in-memory sorting and slicing. Query-level scalability is a disclosed follow-up, not a passed claim.

Evidence index:

- `../buyerkiosk-e2e/SCHEDULING_E2E_EVIDENCE.md`
- `../buyerkiosk-e2e/midscene_run/output/scheduling-shift-lifecycle-db-evidence-20260831-K.json`
- `../buyerkiosk-e2e/midscene_run/output/scheduling-cross-app-deterministic-evidence-20260831-L.json`
- `../buyerkiosk-e2e/midscene_run/output/scheduling-live-manager-deterministic-evidence-20260831-M.json`
- `../buyerkiosk-e2e/midscene_run/output/E2E-PAYROLL-20260901-B-evidence.json`
- `../buyerkiosk-e2e/midscene_run/output/E2E-AI-20260901-C-evidence.json`
- `../buyerkiosk-e2e/midscene_run/output/E2E-PENDING-20260901-A-evidence.json`
- `../buyerkiosk-e2e/midscene_run/output/E2E-PENDING-PAGINATION-20260902-H-evidence.json`
- `../buyerkiosk-e2e/midscene_run/output/E2E-TEAM-REALTIME-20260902-G-evidence.json`

## Cleanup and data safety

- Every fixture used a unique `E2E-*` marker.
- `buyerkiosk-e2e/scripts/cleanup-e2e-shifts.php` is dry-run by default, allows only `ou00`, rejects wildcard/unsafe markers, and refuses counts above its explicit maximum.
- The cleanup script was integration-tested against a disposable marked row.
- Two stale fixtures from earlier interrupted runs were removed with exact markers.
- Final read-only sweep confirmed **zero `E2E-*` scheduling rows** in `ou00`.
- Credentials were never inserted into Midscene prompts, reports, or this audit.
- The user-owned `buyerkiosk-live-flutter/coverage/lcov.info` deletion was preserved.

## Final independent-review verdict

### Web: PASS in branch and deterministic dev2 lifecycle

The independent fail-closed Web reviews found no blocking security or logic defects in microsecond revision tokens, restart-safe payroll duplicate quarantine, authorization, repeated-hour handling, payroll snapshots/replay/finalization, assignment locks, migration guards, or the overnight time-picker remediation. The final exact-store runner review also passed: strict parsing, pre-execution mixed/global rejection, one-time target preflight including empty migrations, nonzero failure exits, legacy untargeted no-op behavior, and defensive apply-time guards were verified with **7 tests / 13 assertions**, lint, and diff checks.

A deterministic dev2 lifecycle run exposed a UI boundary defect: the shared Syncfusion create/edit time picker limited endpoint choices to 8 AM–10 PM and coerced a requested 2 AM overnight end to 3 AM before timezone conversion. The outgoing request and database both contained `08:00Z` instead of the correct `07:00Z`. The branch removes those same-day picker bounds while retaining 15-minute increments, display format, and smart defaults. The strengthened contract regression rejects any `min:` or `max:` property and verifies the retained step/format. Verification passed **4 tests / 14 assertions**, the store-timezone Jest suite passed **12/12**, and the broad scheduling suite passed **1,500 tests / 6,019 assertions** with 2 skipped and 2 deprecations. Independent review found no security or logic defects.

Dev2 directly exposes this local working tree through ngrok; no deployment step was required. Exact-store migrations were applied only to `ou00`, with `pc00` read-back confirming that the control store was unchanged. Payroll export/replay/audit and AI generation/application/provenance were then rerun live and passed.

### Team: PASS

The final independent fail-closed Team review found no blocking security or logic defects. The initial `build()` path suppresses Riverpod's automatic `AsyncData` transition when its bounded result is already older than current requested demand; the originating `provider.future` still settles, state remains loading, and the yielded covering reconciliation publishes the current schedule.

The reviewer independently approved initial-build suppression, bounded reconciliation, stale-result guards, loading/error recovery, and nullable contracts. Verification passed **313 scheduling/domain/widget tests**, the provider suite passed **35/35**, the initial-build race regression passed **50/50 stress repetitions**, targeted Dart analysis reported no issues, and the diff check is clean.

## Remaining release gates

1. Record explicit disposition of unrelated full-repository baseline failures and the Task Engine stale-worker registry accumulation.
2. Decide whether to push the four local repositories and open pull requests.

Until these gates are complete, the correct classification is **product acceptance paths substantially verified across deterministic live and automated evidence, but unrestricted enterprise rollout and release packaging remain fail-closed**.
