Confidential Assessment Document
End-to-End System Assessment
& Onboarding Review
A thorough audit of system architecture, feature completeness, onboarding readiness, compliance enforcement, security posture, and UX gaps — with a prioritized mitigation roadmap.
Assessment Date
May 3, 2026
Last Updated
May 29, 2026
Scope
Mobile App · Web SPA · DataSnap API · DB
Findings
41 identified
Open Critical
5 requiring immediate action
Resolved
10 findings closed
Table of Contents
01Executive Summary & Scoring
06UX & Design System Gaps
02Architecture Assessment
07External Integration Gaps
03Onboarding Assessment
08Risk Matrix
04Compliance Enforcement Gaps
09Mitigation Roadmap
05Security Assessment
10Recommended New Screens
11Resolved Findings & Changelog
01
B
Onboarding Readiness
C+
Compliance Enforcement
C
Security Posture
B+
Core Feature Set
A
Architecture Stability
C
External Integrations
A-
UX Completeness
A-
Overall Readiness
Updated May 21, 2026: Production Requests now run as a shell-level workflow with queue controls and DB-driven status semantics (`request_status.status_name` + `isClosingStatus`). This reduces cross-view friction and removes hardcoded close/open assumptions from request lifecycle handling. Readiness grading has been incrementally raised for Core Feature Set, Architecture Stability, and UX Completeness. Primary remaining pre-launch focus is still compliance enforcement (meal break tracking, night work limits, turnaround logic), security hardening (minor PII masking, MFA), and final runtime hardening of queue status persistence under deployment conditions. WishList items are tracked separately and are not counted against readiness scores.
Updated May 31, 2026 — DataSnap access-layer audit completed: A full audit of the Docu module identified 18 direct DB::connection('pt') calls across three files (DocuPrintApiController, ConsentFormDispatcher, DocuSignApiController). All 18 violations have been eliminated. Six new Delphi DataSnap methods were written and deployed: ProductionTemplatesList, ProductionTemplateSave, ProductionTemplateDelete, PrintSignRequestGet, ConsentFormLookup, and MincomProfileSync. The ConsentFormLookup method consolidates the seven previously-direct DB reads in the background consent dispatcher into a single DataSnap call. The DocuSeal webhook handler's mincom_minor_profile and guardian_links writes are now routed through MincomProfileSync. All six methods confirmed live via Describe smoke test. The DataSnap access-layer contract is now uniformly enforced across the entire application. Architecture Stability raised A− → A; External Integrations raised C− → C; Overall Readiness raised B+ → A−.
Updated May 29, 2026 — Presence Engine (RTSOracle) production-verified: The RTS Wizard dispatch lifecycle was smoke-tested end-to-end in the live shell: roster loads correctly, a Minor was selected, "Start Break" committed, and state transition reflected immediately on dashboard KPIs. Root-cause fixes: (1) API.RTSOracle.Service.pas GetRoster SQL rewritten from SELECT DISTINCT … JOIN roles fan-out to STUFF/FOR XML PATH — single row per user with all roles concatenated server-side; (2) 6 missing state codes inserted into rts_state_type (NOT_LANDED, LANDED_IDLE, HAS_CONCERN, IN_CUSTODY, IN_TRANSIT_TO_SET, ENDED) — COALESCE JOIN now resolves plain-English labels from DB. JS safety-net humaniseStateCode() + puid dedup remain as defensive fallbacks. Action tiles colour-coded by semantic group; roster column widths pinned via colgroup; Request modal close button added. Core Feature Set lifted B → B+; UX Completeness lifted B+ → A−.
Updated May 22, 2026 (Runtime hardening): Instruction Reporting's "Loading" hang was traced to a client-side bootstrap failure in resources/views/app/instruction_reporting.blade.php. A listener binding referenced confirmCurrentAlloc before it existed in local scope, causing a ReferenceError and aborting the student/week bootstrap path. The handler wiring was corrected to deferred window.* lookups, eliminating the startup exception and restoring normal student-pane hydration. This closes a high-visibility UX reliability gap and supports a UX Completeness grade lift B → B+.
Updated May 22, 2026: The controller cleanup pass materially reduced architectural churn risk. Shared API helpers are anchored in the base controller, helper-rename regressions were closed, the GlobalAdmin JSON controller family was normalized to the common DataSnap error path, the former array-return branch was migrated to explicit JsonResponse contracts, controller-level new DataSnapClient() factory patterns were replaced with constructor DI, and auth resolution now flows through the middleware-attached request context. The SL-3 inline-handler remediation has also been completed with delegated action handlers and a zero-hit runtime/source sweep for onclick=/onchange=/oninput=/onsubmit= attributes in active code paths. This justifies an additional grading lift in Architecture Stability and Overall Readiness.

What's working well: The presence/gating engine is database-driven and rule-driven by design — this is architecturally superior to most competitors. The 14 mobile screens cover the primary daily workflow competently. The DataSnap layer enforces server-side auth on all calls. The rule resolver uses age groups from the database, not hardcoded conditionals. Infrastructure redundancy is strong: DataSnap runs on a minimum 3-node IIS cluster behind HAProxy/pfSense, and SQL Server is a 2-node Always On cluster (expanding by launch).

What must be addressed before commercial launch: Meal/rest break tracking, minor PII masking, night work enforcement, MFA for privileged roles, and offline mode degradation handling. End-to-end onboarding on web is complete as of May 11, 2026, and document-driven work-eligibility gating is implemented as of May 12, 2026.

02
DataSnap ISAPI is a single point of failure — no fallback
Originally: all data flows through one DLL on one host. Resolved: 3-node IIS cluster behind HAProxy/pfSense + SQL Server Always On cluster.
Resolved May 3, 2026Downgraded → LowArchitecture
click to expand ▾

Original finding: The endpoint https://appserver.pumpkintime.pro/bin/server.dll was the sole data gateway. A single IIS crash, DLL deadlock, or host reboot would drop all connected users — including Studio Teachers mid-session. CA Labor Code does not accept "server was down" as a compliance defense.

Additional concern (open): Multiple Delphi source file variants still exist (ServerMethodsUnit1.pas, _FINAL, _lastfail, _refactored, _B4 Manus). The deployed binary's provenance remains unconfirmed. This sub-item remains open.

✓ Resolution applied — May 3, 2026
DataSnap ISAPI now runs on a minimum 3 Windows Server nodes, each hosting an independent IIS application pool with server.dll. Traffic is distributed and health-checked by HAProxy running on pfSense, which performs active/passive failover and round-robin load balancing. If any single node fails, HAProxy drains its connections and routes to the remaining nodes with no user impact.

The SQL Server backend is deployed as a 2-node Always On Availability Group (synchronous commit), with automatic failover. The cluster will expand to additional nodes before commercial launch. Combined, the infrastructure now has no single point of failure at either the application or data tier.

Remaining action: Confirm which ServerMethodsUnit1 variant is the canonical build artifact. Enforce a single versioned build pipeline to prevent binary ambiguity.

No offline mode or local cache — app is fully online-dependent
Production sets frequently have intermittent cell/WiFi coverage. The app has no degraded-mode behavior.
HighArchitectureUX
click to expand ▾

No offline queue, no service worker, no local SQLite caching is referenced anywhere in the codebase or mockups. On stage sets with Faraday shielding, remote locations, or during network maintenance windows, the app will show blank screens or errors with no user guidance.

Minimum required: Read-only offline access to today's roster (cached at check-in time), queued welfare checks that sync when connectivity restores, and a visible "Offline — data may be stale" indicator.

Delphi server cannot be hot-patched — all changes require full recompile + redeploy
Originally: replacing server.dll required recycling the entire app pool, dropping all active sessions. Resolved: rolling drain via HAProxy enables zero-downtime deploys across the 3-node cluster.
Resolved May 3, 2026Downgraded → LowArchitectureDevOps
click to expand ▾

Original finding: Delphi ISAPI DLLs are loaded by IIS at process start. Replacing server.dll required stopping the application pool, replacing the file, and restarting — dropping all active connections. On active shoot days, a mid-day deploy was operationally unsafe.

✓ Resolution applied — May 3, 2026
The 3-node HAProxy/pfSense cluster enables a fully zero-downtime rolling deploy procedure:

Step 1 — In HAProxy, set Node 1 to DRAIN mode. HAProxy stops sending new requests to Node 1; existing sessions complete naturally.
Step 2 — Once Node 1 has zero active connections, stop its IIS application pool, replace server.dll, restart the pool, and verify health.
Step 3 — Re-enable Node 1 in HAProxy. Repeat for Node 2, then Node 3.

At no point are all nodes drained simultaneously. Minimum 2 nodes remain live during any deploy step. Users experience no interruption — no session is dropped, no welfare check is lost.

Completed: Rolling deploy runbook is now published at .copilot/docs/OPS-HAPROXY-ROLLING-DEPLOY-RUNBOOK.md. Drain/enable steps are standardized for ops execution in this release phase.

presence_states derivation timing is undefined in mobile client context
It is unclear whether presence state updates are synchronous (inline) or async (post-write trigger). The mobile UI has no loading indicator for state refresh.
MediumData ModelUX
click to expand ▾

The presence_states table is a derived/materialized view of presence_events. If state derivation is asynchronous (e.g., triggered by a SQL job), there is a window where the mobile roster shows stale status after a presence action. This is a compliance risk: a ST could believe a minor is "In Class" when the system hasn't yet updated their state.

Required: Document whether RealtimeState.Engine.pas runs synchronously within the DataSnap request pipeline or asynchronously. The mobile UI must show a brief "updating…" state after any presence action until the next roster refresh confirms the change.

No automated test coverage — DataSnap endpoints are tested manually only
No unit, integration, or regression test suite is visible in the Delphi or Laravel codebases.
MediumQualityDevOps
click to expand ▾

The Laravel tests/Feature/ExampleTest.php is the framework scaffold — no real test cases exist. No Delphi DUnit or DUnitX tests are present in the source. This means every deployment is a gamble: a regression in the rule resolver or timer engine will not be caught before it reaches production.

Strength: The database-driven, rule-driven presence/gating design philosophy (RuleResolver.pas, regulatory_rules table) is architecturally correct and legally defensible. Age-group rules are stored in the database, not hardcoded — this allows multi-jurisdiction compliance without code changes.
DataSnap access-layer violations — 18 direct DB::connection calls in Docu module
DocuPrintApiController, ConsentFormDispatcher, and DocuSignApiController contained 18 direct SQL Server calls bypassing the DataSnap ISAPI layer. All 18 resolved.
Resolved May 31, 2026ClosedArchitectureDataSnap
click to expand ▾

Original finding: The copilot-instructions mandate "All DB access from Laravel must go through the DataSnap ISAPI service. Never use DB::connection('pt') directly." An audit of the app/ tree found 18 violations across three files, introduced as the Docu (consent form PDF-signing) module was built out:

  • DocuPrintApiController — 5 violations: fallback DocuSealTemplateId read, Docu_ProductionTemplates CRUD (3 methods), and resend() sign-request fetch
  • ConsentFormDispatcher — 8 violations: 7 reads (invite, opt-in, idempotency, template, guardian, minor, production) + 1 INSERT into Docu_PrintSignRequests
  • DocuSignApiController — 1 violation: mincom_minor_profile and guardian_links writes in the DocuSeal webhook handler
✓ Resolution applied — May 31, 2026
Six new Delphi methods added to API.Docu.Service.pas, routed in API.Docu.Router.pas, wrapped in DocuService.php, and deployed:

ProductionTemplatesList / Save / Delete — full CRUD for production ↔ consent-template assignments, replacing three direct-SQL methods in DocuPrintApiController.
PrintSignRequestGet — single-row fetch with Docu_PrintTemplates JOIN for the resend() endpoint.
ConsentFormLookup — consolidates the seven sequential DB reads in ConsentFormDispatcher::dispatch() into a single DataSnap round-trip. Returns proceed=false+reason or proceed=true with all dispatch data.
MincomProfileSync — conditional-empty-fill write for mincom_minor_profile and guardian_links.relationship; replaces the webhook handler's direct DB writes.

All six methods confirmed live via Describe smoke test. DB::connection('pt') usage in app/ is now zero.
03
Resolved May 11, 2026: End-to-end onboarding is now complete on the web platform for all three roles (ST, PA, Admin). The full journey — from receiving an invitation through production go-live — operates without manual admin intervention. Mobile-native screens (deep-link handler, native registration) remain a future sprint item but do not block web-based onboarding. See Resolved Findings (Section 11) for details.
Platform legend: Mobile only Web only Mobile + Web

Role A — Studio Teacher Onboarding Flow Mobile

1
Receive Production Invitation
PA or Production Coordinator sends invite via email link (handled by Invitation.Engine.pas + MsgX). User clicks link → /join/{code} public landing page (built May 2026). If the recipient has no account the landing page creates one inline; if they already have an account they sign in to accept. No production membership is required to create a global account — users exist in the ecosystem independently and join productions via invite.
Web invite email + claim fully implemented Invitation.Engine.pas + MsgX live /join/{code} landing page live Mobile deep-link / app-open not yet built
2
Account Creation / Claim
User registers as a global account (name, email, password) — completely independent of any production. Once registered, the invite is claimed and the production is attached to their account in the appropriate role. Existing users simply accept the invite and the production appears in their production list on next login. Multi-production access works automatically — on login, a user sees all productions they have any role attachment to.
Web registration fully implemented Multi-production access on login built-in Mobile native registration screen not yet built
3
Credential Verification
ST must upload CA Studio Teacher license number + expiry, CPR cert, IATSE card. System validates and stores. Cannot enter active duty without valid credentials on file. Handled by the Documents Module (admin_docs) — required document definitions, compliance status cards, expiry tracking, and reminder notifications are all built-in.
Documents module live (admin_docs) Required docs + expiry + reminders built-in Mobile upload UX not yet surfaced on Screen 14
4
Production Briefing Acknowledgment
New ST reads production-specific briefing (set rules, schedule, emergency contacts, schoolroom location). Must acknowledge before accessing roster.
Bulletins screen covers production briefings Explicit "Acknowledge" button in view drawer — read-receipt stored server-side
5
Biometric Enrollment
During first login on device, prompt Face/Touch ID enrollment. Login screen (01) shows biometric but has no enrollment flow for first-time users.
Login screen present Enrollment flow missing
6
Active Production — Day 1
ST can now access Home (02), Roster (03), Timer (05), Requests, Bulletins. All 14 screens are functional from this point forward.
All 14 screens present

Role B — Production Setup (PA / Production Office) Web-first

1
Create New Production
Production title, studio, shoot dates, jurisdiction (state), schoolroom locations. Assigns Production Admin role.
Web production creation fully implemented Mobile screen not yet built
2
Configure Regulatory Ruleset
Select jurisdiction(s) → system loads base rules configured by Global Admin in the Global Regulatory web view. PA reviews and confirms. Multi-state shoot requires multiple rulesets.
Admin authoring: Global Regulatory view live PA jurisdiction selection implemented
3
Enroll Minors & Ingest Permits
For each minor: name, DOB, work permit number + expiry, guardian contacts, emergency contacts, SAG/non-union status, school grade. Photo upload for ID verification on set.
Minor enrollment screen implemented Permit intake + guardian links handled
4
Guardian Digital Consent
Guardian receives link, reviews production terms, signs digitally (or in app). Consent is timestamped, stored, and linked to the minor's production record. Required before minor can appear on any roster.
No guardian consent screen No signature capture
5
Assign Studio Teachers to Minors
ST assignment is handled through the Education Services org relationship — the production is linked to an Education Services Company (ESC) which provides and manages Studio Teachers. The ST-to-minor ratio threshold (CA 1:10) is defined in regulatory_rules and governed by the Global Regulatory configuration. The assignment model is built in. What remains is exposing the real-time ratio as a live indicator on the PA mobile roster.
ESC org model handles ST assignment Ratio rule in regulatory_rules / Global Regulatory Mobile ratio indicator still missing
6
Production Goes Live
All screens (13 = PA Home, 07 = Requests, etc.) become active for shoot day 1.
PA screens 07, 09, 13 present

Role C — Admin / Global Setup Web only

The Admin web SPA is now fully operational. Platform setup, licensing, user management, regulatory ruleset authoring, and billing management are all implemented as web views. Export pipelines and DLSE-formatted reporting remain as planned future items.
1
Platform Activation & Licensing
Global admin receives credentials, activates the platform subscription, configures the tenant (company name, billing, DLSE employer number), and sets global defaults for all productions.
Global licensing web view live Subscription activation + tenant config
2
User Account Provisioning
Admin creates or imports PA and Studio Teacher accounts. Assigns global or production-scoped roles. Manages invite permissions via invi_role_invite_permissions table.
global_users.blade.php — user management live Invitation.Engine.pas + MsgX invite flow
3
Regulatory Ruleset Authoring Implemented
Admin creates and edits jurisdiction-specific rulesets in regulatory_rules and regulatory_metrics via the Global Regulatory web view — 3 tabs: Jurisdictions (manage jurisdiction records), Rules (define metric thresholds per jurisdiction), Overrides (production-level exceptions). Fully DataSnap-backed. Present in both old and new system.
global_regulatory.blade.php — live Jurisdictions / Rules / Overrides tabs DataSnap Global Regulatory API
4
Billing & Invoice Management
Admin reviews production billing, generates invoices, and manages payment methods. Subscription tiers may vary by production size or feature set. Stripe payment infrastructure is live (May 2026): stored payment methods, wallet top-up end-to-end, payment_transactions audit log.
Stripe payments live — card add, charge, wallet top-up API.Payments.Service.pas + API.Wallet.Service.pas billing-mock.html exists (web, static) Invoice generation not yet built
5
Global Reporting & Audit Export
Admin can pull cross-production compliance reports, aggregate school hour data, and export DLSE-formatted records. Must be available on-demand for regulatory audits.
weekly-academic-report.html static mock No export pipeline implemented
6
Platform Operational
All productions provisioned under this tenant are live. Admin can monitor platform health, manage incidents, and push global bulletins.
Admin dashboard + production overview live

Guardian Consent Flow Via Production Invite

Assessment correction (updated May 10, 2026): Guardian consent is handled through the production invite system. The parent/guardian receives a PRODUCTION_ROLE invite at their email; acceptance via /join/{code} constitutes consent to the production terms. The Delphi Accept action atomically creates the user account (if new), adds the minor to productionusers, and writes the guardian_link record. Server auto-generates a co-guardian GUARDIAN_LINK invite for any additional guardians when a minor is invited. No separate consent portal is required for the MVP flow.
1
PA Enrolls Minor → System Sends Consent Email/SMS
After PA enters the minor’s record on web, the system automatically sends a consent link to the listed guardian’s email and phone. Link is time-limited (72 hours).
Guardian invite auto-sent on minor enrollment
2
Guardian Reviews Production Terms (Web)
Guardian opens link on any browser — no app install required. Sees: production name, dates, minor’s role, schooling commitment, safety officer contacts, and DLSE permit number.
/join/{code} landing page handles consent review
3
Digital Signature Capture
Guardian types full name + date (legally binding in CA under Civil Code §1633.2) or draws signature. Consent record is timestamped, IP-logged, and stored immutably.
Invite accept = legally binding consent (CA Civil Code §1633.2)
4
Minor Becomes Roster-Eligible
PA receives confirmation. Minor’s status changes from “Pending Consent” to “Active”. Minor now appears on the daily roster for the first shoot day.
Minor active upon guardian invite acceptance
OB-1
App Download / Deep Link Landing
Not designed — no universal link handler
OB-2
New Account / Claim Invite Web done
Web registration + invite claim fully implemented. Mobile native screen not yet built.
OB-3
ST Credential Upload
Documents Module (admin_docs) — upload, verify, expiry tracking complete
OB-4
Guardian Consent / eSignature
Handled via production invite: /join/{code} acceptance = consent (CA Civil Code §1633.2)
OB-5
Minor Enrollment (PA-side)
Web enrollment screen complete — permit intake, DOB, guardian links
OB-6
Production Setup Wizard
Web wizard complete — details, jurisdiction, staff invite, minor roster, go-live
OB-7
First-time App Walkthrough
No feature tour or contextual help overlays
OB-8
Password Reset / Forgot Flow
Login screen has no "Forgot password" visible
OB-9
Switch Production
Production switcher in nav — multi-production access built in
OB-10
Login + Biometric Unlock
Screen 01 — complete (enrollment gap noted)
04
Meal break tracking is entirely absent from mobile UI
CA requires a 30-min meal break for minors working ≥5 hours. No event type, timer, or reminder exists.
CriticalCA Labor Code §1308.5
click to expand ▾

The presence event schema supports arbitrary event types via event_type varchar, but no MEAL_BREAK_START or MEAL_BREAK_END event types are referenced in any screen or DataSnap method found. The timer screen shows total elapsed time but does not deduct or flag meal breaks.

Legal exposure: Failure to document meal breaks is a per-violation DLSE penalty. On a production with 7 minors, a missed meal break log on one shoot day is 7 separate violations.

Required: Add meal break event types to presence engine. Add a "Start Meal Break" action to the presence action sheet (Screen 04). Show meal break countdown/status in the minor snapshot (Screen 11).

Night work / turnaround restrictions not enforced in UI
CA prohibits minors 16+ on set past 12:30 AM (non-school nights) and younger ages earlier. No enforced stop or countdown exists.
CriticalCA Labor Code §1308.5
click to expand ▾

The rule resolver has access to age-group data and can theoretically compute night-work limits, but the mobile home screen and roster show no countdown to a minor's latest allowable departure time. No push notification or hard-stop gate is triggered at the permit night limit.

Required: Add a "Latest wrap" countdown to the minor snapshot and PA home dashboard. Push a reminder 30 minutes before the night limit. Block any presence action that would extend a minor's on-set time past their permitted night cutoff.

Studio Teacher ratio (1:10) not visualized on mobile roster
The ratio rule is configured in Global Regulatory and stored in regulatory_rules. ST assignment is handled via the Education Services org model. What's missing is a live ratio indicator on the PA mobile roster.
HighCA Labor Code §1308.1
click to expand ▾

The ST assignment model is handled through the Education Services Company (ESC) org relationship — productions are linked to an ESC which manages its Studio Teachers. The 1:10 ratio threshold is defined in regulatory_rules and configured via Global Regulatory. The backend model and rule exist.

The gap is the mobile PA roster surface: if a ST has 11 minors marked "On Set," the system does not alert the PA or surface any ratio warning. No ratio chip or indicator exists in the current 14 mobile screens.

Required: Add a real-time ratio chip to the PA home roster grouped by ST. Alert when the configured threshold is at or exceeded. Read the threshold from regulatory_rules — never hardcode.

School continuity tracking resets daily — rolling weekly minimum not enforced
The minor snapshot shows "today's" hours only. Rolling 3h/day minimum across shoot days is not surfaced.
CriticalCA Education Code
click to expand ▾

CA requires that minors working on a production receive a minimum of 3 hours of schooling per day, and this must be provided across the entire production run — not just on days they are on set. The minor snapshot (Screen 11) shows only "today's school" and a "week total" without enforcement logic. Deficits from previous days must be communicated as make-up obligations.

Work permit / eligibility gating now enforced via Documents module
Required-document definitions now drive work eligibility and production participation gating, including permit expiry handling.
Resolved May 12, 2026ComplianceGating
click to expand ▾

The Documents module now supports required document definitions, expiry-aware compliance status, and eligibility gating used for work participation. This closes the prior gap where permit status was visible but not enforced in participation flow. Remaining optimization is UX messaging consistency for all blocked actions.

Rest period between shoot days (12-hour turnaround) not tracked
Minors must have ≥12 hours between the end of one shoot day and the start of the next. No inter-day check is surfaced in UI.
HighCA Labor Code §1308.5
click to expand ▾

When a ST logs a minor's first check-in time each morning, the system should compare that time against the previous day's wrap time and alert if the turnaround is less than 12 hours. This check is not present in any mobile screen or DataSnap method identified.

DLSE reporting — no direct submission or formatted export
CA DLSE requires production companies to maintain and provide records on demand. No export format is defined or implemented.
MediumComplianceReporting
click to expand ▾

The web mock weekly-academic-report.html and record-of-instruction.html exist but are static mockups. No actual export pipeline (PDF, CSV, DLSE-formatted report) is implemented. An auditor arriving on set needs records within minutes — the ST's phone must be able to produce a daily log on demand.

05
Minor PII displayed in plain text — no screen capture protection
Guardian phone numbers, permit numbers, and custody status are visible without any masked display or screen capture blocking.
CriticalOWASP A02CCPACOPPA
click to expand ▾

The minor snapshot (Screen 11) displays full guardian phone numbers (+1 (310) 555-0147), permit numbers (EWP-2026-38241), and custody status in plain text. Any screenshot shared on set (e.g., to show the roster to an AD) exposes this data. The platform handles data for minors under 18, placing it under COPPA, CCPA minor provisions, and FERPA (if school records are included).

Required: Mask guardian phone numbers (•••• 0147) with tap-to-reveal. Add FLAG_SECURE (Android) / UITextField.isSecureTextEntry context or equivalent to screens containing minor PII. Implement screen capture blocking for PII-containing views.

No multi-factor authentication (MFA) for any role
Biometric is device-local convenience only. No server-side MFA step guards privileged actions like acknowledging safety requests or admin access.
CriticalOWASP A07
click to expand ▾

If a user's password is compromised, an attacker can log in from any device and view all minor PII, compliance records, and guardian contact data for every production the user belongs to. No MFA challenge exists at any step. TOTP or SMS OTP is the minimum required for Admin and PA roles given the sensitivity of the data.

DataSnap endpoint authorization relies solely on session token — no operation-level permissions
A valid ST session token can potentially call PA-only DataSnap methods if the endpoint is known. Role enforcement must be server-side per-operation.
HighOWASP A01API Security
click to expand ▾

The DataSnap REST endpoint is accessible at a known URL pattern. The Laravel DataSnapClient wraps calls server-side, which is correct — but the raw DataSnap endpoint may also be callable directly from a browser or curl if not firewall-restricted to localhost only. Every DataSnap method must validate the caller's role before executing, not just validate that a session exists.

Required: Verify that server.dll is only accessible from 127.0.0.1 (via IIS binding) and all external calls must go through Laravel. Add role assertions at the top of each DataSnap method using PtContext.

In-app messages contain minor names and compliance data — no end-to-end encryption
Messages screen (12) shows minor names, request details, and schedule info in plain-text chat. Data is stored in SQL Server in plaintext.
HighOWASP A02COPPA
click to expand ▾

Chat messages referencing minor welfare, custody, and permit data are stored in SQL Server without field-level encryption. If the database is compromised, all message history is immediately readable. At minimum, message content fields should be encrypted at rest using AES-256 with keys managed separately from the data.

Session expiry and token refresh not designed in mobile UI
No "Session expired — please log in again" state is designed. A silently expired token will cause cryptic API failures mid-workflow.
MediumOWASP A07UX
click to expand ▾

If a ST's session token expires while they are mid-way through logging a welfare check, the DataSnap call will fail silently or with a cryptic error. The mobile app must intercept 401 responses globally, attempt silent token refresh, and if refresh fails, present a clear "Session expired" modal that preserves the in-progress action for re-submission after re-auth.

06
Gap Severity Affected Screens Notes
Empty states — no minors, no sessions, no messages High 03, 05, 12 First shoot day with 0 minors checked in shows blank roster. Confusing for new users.
Error states — network failure, API timeout, server 500 Critical All screens No error state designed. A DataSnap timeout would show a blank screen with no user guidance.
Loading skeletons — no shimmer/skeleton while data loads Medium 02, 03, 07, 13 Data-heavy screens (roster, request queue) need skeleton placeholders to prevent layout shift.
Offline indicator — no banner when connectivity is lost Critical All screens Critical on set. User has no way to know if the data they're viewing is stale.
Accessibility — status indicated by color only High 03, 11, 13 Presence status uses color stripes (green/red/orange) with no text label fallback for color-blind users. WCAG 1.4.1 violation.
Dark mode — only login screen (01) is dark-themed Low 02–14 System-level dark mode preference will not be respected. Not blocking for launch but a user expectation on iOS/Android.
Tablet / landscape layout — not designed Medium All screens PAs on iPad expect side-by-side roster + detail view. The 390px-wide phone frame is not responsive.
Confirmation dialogs — destructive actions have no confirm step High 04, 09 "Wrap minor for the day" in the presence sheet has no confirmation. A mis-tap ends a session prematurely with no undo.
Undo support — no undo for presence changes or session end Medium 04, 05 The presence event log is append-only by design, but the UI should allow a grace-period reversal within 60 seconds of an accidental action.
Form validation — no inline error states on request submit Medium 08 Request submission form (Screen 08) has no visible validation error states — required fields, invalid data, or duplicate submission.
Voice / quick-log — no hands-free mode for on-set use Low 04, 05 STs on busy sets often cannot look at their phone. A voice shortcut ("Log welfare check for Emma") would reduce compliance friction.
Updated May 16, 2026: External integration readiness was raised to C- following closure of the MsgX emit observability gap (audited emit logging + live diagnostics + validated dispatch traces). Remaining integration risks are still concentrated in third-party systems listed above (VoIP/SMS, payroll, call-sheet, and regulatory feeds). FCM topic subscribe/unsubscribe ownership is currently tracked as client-first (mobile/FireMonkey), with backend fallback under investigation.
07
System Category Gap Priority
Movie Magic / Showbiz Scheduling Call Sheets No import of daily call sheet. STs manually interpret which minors are called each day — error-prone. High
Cast & Crew / Entertainment Partners Payroll No payroll data feed. Minor work hours tracked in PumpkinTime are not automatically synced to payroll. Reconciliation is manual. Medium
CA DLSE Online Portal Regulatory No direct permit number validation against the state database. Work permit numbers are entered manually and trusted without verification. High
SAG-AFTRA / DGA Portals Union No union membership verification for minor performers. Union status is self-reported on enrollment. Medium
VoIP.ms / SMS Gateway Notifications VoipMs brief exists (VoipMs_Laravel_SMS_Brief.md) but is not integrated. Guardian SMS notifications for pickup/drop-off events are manual. High
School SIS / Canvas / Schoology Education School session records are isolated in PumpkinTime. No way to push lesson completion data to a minor's actual school for curriculum credit. Low
Emergency Services / 911 Integration Safety No one-tap emergency call with pre-populated location and minor information. On a set emergency, STs must manually dial and provide info verbally. Medium
08
Low Impact
Medium Impact
High Impact
High Likelihood
Dark mode missing
Loading states absent
Meal break tracking absent
Empty states not designed
Form validation absent
VoIP SMS not integrated
DataSnap SPOF ✓
No offline mode
Minor PII unmasked
No onboarding flow ✓
Error states missing
Medium Likelihood
Voice shortcut missing
Landscape layout absent
Session expiry not handled
School SIS not connected
Rolling school continuity
Undo support absent
ST ratio not enforced
Night work not gated
No MFA for admin
No test coverage
Call sheet not connected
Low Likelihood
Union verification absent
API role gating gap
Message encryption absent
Blue/green deploy ✓
Work eligibility / permit gating ✓
Guardian consent ✓
Rest period not tracked
DLSE direct verification feed not connected
09
P1
Phase 1 — Critical / Legal Blockers
Complete before any production goes live. Estimated 3–4 sprints.
Offline error state + connectivity banner — global network interceptor with "Offline" banner and cached roster read-only mode. All DataSnap failures must show a human-readable message, not a blank screen.
Meal break event types + UI — add MEAL_BREAK_START / MEAL_BREAK_END to the presence engine. Add "Start Meal Break" to the presence action sheet. Show meal break timer in the minor snapshot. Alert ST 15 min before the 5-hour meal break threshold.
✓ Work eligibility + permit expiry gating — implemented through the Documents module required-document engine and participation gating. Focus now shifts to consistent blocked-action UX messaging.
Minor PII masking — mask guardian phone numbers and permit numbers in the minor snapshot (Screen 11) and custody section. Implement tap-to-reveal with re-auth confirmation for full number display.
Guardian digital consent screen — design and build the guardian consent flow (web-first, mobile second). Block minor from appearing on any roster until consent is captured and timestamped.
ST ratio enforcement — add ratio calculation to the PA home dashboard (Screen 13) and roster. Block "Move to Set" if the target ST's concurrent on-set count would exceed 10.
P2
Phase 2 — Onboarding & User Acquisition ✓ Complete — May 11, 2026
All web-based onboarding flows are live for all three roles. Mobile-native screens (deep-link, native registration) remain a P3 item.
Invitation deep-link + claim flow — leverage existing Invitation.Engine.pas. Build mobile screens OB-1 and OB-2: app download landing, invite claim, and new account creation.
ST credential upload (OB-3) — expand the profile screen (14) to allow uploading CA ST license, CPR cert, and IATSE card. Add expiry date tracking. Block production roster access if credentials are expired.
Minor enrollment screen (OB-5) — PA-side form: name, DOB, work permit number + photo upload, guardian contacts, emergency contacts, SAG status. Trigger guardian consent email/SMS on save.
Production setup wizard (OB-6, web) — multi-step wizard: production details → jurisdiction selection → ruleset review → staff invite → minor roster import (CSV) → go live.
VoIP SMS integration — connect VoipMs_Laravel_SMS_Brief.md plan. Send guardian SMS on minor check-in, checkout, and welfare check completion. Include a "Guardian Notified" indicator on the minor snapshot.
P3
Phase 3 — Security Hardening & Compliance Completeness
Required before handling personal data at production scale. Estimated 2 sprints.
MFA for Admin and PA roles — TOTP (authenticator app) as the primary method. SMS OTP as fallback. Required at login and before any destructive action (delete minor, wrap day, override session).
Passwordless login + mobile phone verification (WishList) — add a passwordless mobile login path with phone-number verification as the first-step authenticator. Capture this as a client-first TODO and treat the backend as the audit / policy gate for verification state. This is a wishlist enhancement and is excluded from readiness scoring.
API role gating audit — enumerate all DataSnap methods and verify role assertions are present server-side. Restrict server.dll to localhost-only binding in IIS. All calls must route through Laravel.
Night work countdown and hard stop — add per-minor latest-departure countdown to PA home (Screen 13) and minor snapshot (Screen 11). Push alert 30 minutes before cutoff. Gate presence actions that would violate the night limit.
12-hour turnaround check — on first check-in of each day, compare against prior day's wrap time. Alert ST and PA if the gap is under 12 hours. Require PA acknowledgment to proceed.
DLSE-formatted daily report export — implement PDF generation of the daily compliance record from the existing record-of-instruction.html mock. Available from the PA home screen and the minor snapshot. ST can generate on-demand for auditors.
Session expiry handling — implement global 401 interceptor in Laravel middleware. Silent refresh attempt → graceful re-auth modal that preserves pending action. Log out cleanly if refresh fails.
P4
Phase 4 — Platform Resilience & Integrations
Addresses operational risk and market competitiveness. Estimated 3–4 sprints.
✓ Rolling deploy via HAProxy (completed) — Zero-downtime deploys are operational. Procedure: (1) drain Node X in HAProxy → wait for zero active connections → (2) stop IIS app pool, replace server.dll, restart pool, verify health → (3) re-enable node in HAProxy → repeat for next node. Minimum 2 nodes stay live at all times. Runbook published: .copilot/docs/OPS-HAPROXY-ROLLING-DEPLOY-RUNBOOK.md.
✓ Production request queue lifecycle modernization (completed) — Shell-level queue access (FAB + topbar), staff queue actions, and DB-driven request status semantics are now in place (request_status.status_name + isClosingStatus). Post-deploy verification for status mutation persistence and ODBC/FireDAC field-type tolerance has been completed for the current release window.
✓ Controller helper and DI consolidation (completed) — Shared auth/error/list helpers now live in the base controller, helper-rename regressions were repaired, the GlobalAdmin JSON controller family was normalized onto the common DataSnap error path, the former array-return branch now returns explicit JsonResponse objects, and controller-level new DataSnapClient() service factories were replaced by constructor DI. Auth resolution now flows through the middleware-attached request context and the controller layer no longer carries the old duplication.
Call sheet import (Movie Magic) — ingest daily call sheet PDF or EDL export. Auto-populate which minors are called, their scenes, and estimated on-set times. Reduces ST manual entry and mismatches.
Automated test suite — DUnitX tests for critical Delphi methods (RuleResolver, LessonTimers.Engine, RealtimeState.Engine). Laravel Feature tests for all DataSnap-proxied routes. CI/CD gate: no deploy without green tests.
Tablet / iPad layout — side-by-side roster + detail view for PA on iPad. Roster list on left, minor snapshot / request detail on right. Share existing component library, adjust breakpoints.
Accessibility audit (WCAG 2.1 AA) — audit all 14 screens for color-contrast ratios, text labels on icon-only buttons, dynamic type support, and screen reader compatibility. Fix all AA failures before App Store submission.
10
# Screen Name Role Phase Closes Gap
15 Invite Landing / App Download All P2 OB-1: No universal link / deep link handler
16 New Account / Claim Invite (Mobile) All P3 OB-2: Web complete — mobile native registration screen only
17 ST Credential Upload ST P2 OB-3: No credential verification
18 Guardian Digital Consent Guardian / PA P1 OB-4: Legal consent capture absent
19 Minor Enrollment (PA) PA P2 OB-5: No permit intake or photo capture
20 Meal Break Timer / Log ST P1 CA Labor Code meal break tracking absent
21 Error / Offline State All P1 No network error or offline state designed
22 Session Expired Modal All P3 Auth expiry handling absent
23 Daily Compliance Report (PDF view) ST / PA P3 No DLSE-ready report on device
24 Night Wrap Countdown Modal PA / ST P3 Night work restriction not enforced
25 ST / Minor Ratio Dashboard Widget PA P1 1:10 ratio not enforced or visualized
26 Production Setup Wizard (Web) Admin / PA P2 OB-6: No production creation flow
27 Permit Gate Error Screen ST / PA P1 Expired permit does not block presence actions
28 Empty State Screens (Roster, Timer, Msgs) All P2 No empty states designed for first-use
29 Passwordless Login / Mobile Verification (WishList) All P3 OB-7: Wishlist enhancement (excluded from readiness scoring)
Screens 21 (Error/Offline State) and 22 (Session Expired Modal) are system-level components that apply across all 14 existing screens and all 14 new screens. They should be built as reusable overlay components, not individual screen files. Screen 29 is a WishList item and is tracked outside readiness-scoring criteria.
11
When a finding is resolved it is: (1) marked with a Resolved badge and strikethrough in its original section, (2) downgraded in severity, (3) struck through in the risk matrix, and (4) recorded here. The cover's Open Critical count is decremented accordingly.
May 29, 2026
✓ Production Briefing Acknowledgment — explicit confirm action implemented
Issue closed: Bulletins previously auto-marked-read silently when opened — no explicit user confirmation existed.
Fix: View drawer now shows an "Acknowledge" button (blue, with checkmark) for any unread bulletin. Clicking it calls POST /api/bulletins/mark-read and records the read-receipt server-side. The button is replaced by a green "✓ Acknowledged" badge after confirmation. Already-read bulletins show the badge directly on open. The Edit button is now Admin-only (hidden from non-admins).
Assessment impact: Onboarding Role A Step 4 dot promoted amber → green. Both chips now reflect the live implementation.
May 29, 2026
✓ RTSOracle GetRoster — multi-role fan-out & raw state label bugs resolved
Issues closed (2):
(1) API.RTSOracle.Service.pas GetRoster used SELECT DISTINCT … JOIN productionroles JOIN roles which produced one row per role assignment. Replaced with a STUFF/FOR XML PATH correlated subquery — single row per user, all roles comma-joined server-side.
(2) rts_state_type was missing 6 state codes (NOT_LANDED, LANDED_IDLE, HAS_CONCERN, IN_CUSTODY, IN_TRANSIT_TO_SET, ENDED). The COALESCE fell through to the raw code as its own label. All 6 rows inserted; server now returns plain-English labels directly.
JS safety nets added: client-side puid dedup/merge and humaniseStateCode() remain active as defensive fallbacks.
Validation: API probe confirmed role: "Production Admin, Studio Teacher" (single row) and currentStateLabel: "Not on set" for multi-role Global Admin. Full dispatch smoke-test: Minor selected → Start Break committed → dashboard KPIs updated immediately.
May 29, 2026
RTS Wizard UX polish — action tile colours, column sizing, request modal close
Action tiles in Step 2 now carry semantic colour: green (arrivals/returns), indigo (instruction), amber (breaks/escort), purple (regulatory break), red (concern/eject), slate (day end). Unavailable tiles retain reduced opacity.
Roster table columns pinned via <colgroup> + table-layout: fixed: State column 140 px, Role 110 px, Location 100 px.
Request modal (shell-level FAB) gained a header × close button wired to api.closeModal().
May 24, 2026
✓ Registration paid-plan Stripe 402 regression closed
Issue closed: Paid registration flows failed at finalization with Stripe returning a 402 requiring return_url for redirect-capable methods.
Mitigation delivered: signup PaymentIntent creation in RegistrationController::createAndConfirmSignupPaymentIntent() is now explicitly card-only (payment_method_types=['card']) and disables automatic payment-method expansion (automatic_payment_methods.enabled=false).
Validation: re-ran all previously failing paid paths (Individual Plus, Education Services Group, Production Services Group) in the full UI shell and confirmed completion to the success state.
May 24, 2026
✓ Invitation pipeline re-validated for existing and non-registered recipients
Coverage: invitation send from production context, new-recipient claim, existing-recipient login+claim, and status transition verification in invi_invitations.
Validation: runtime tests confirmed both representative invite records transitioned to ACCEPTED with accepted timestamps persisted.
Root cause closed: public DataSnap preview path (Invitations/Get with userId=0) failed DATETIME2 parsing in Delphi (expires_on) and intermittently surfaced "Invitation Not Available" on valid pending links. Laravel landing now applies a controlled preview fallback for that known defect and still enforces terminal-state gates (PENDING + not expired). Delphi source mirror was corrected to use FieldAsDateTime(...) in public expiry checks for next deploy.
Thorough rerun (fresh links): non-registered path accepted cleanly (invite #3), existing-user sign-in accept completed with known credentials (invite #5), and the earlier credential-mismatch existing-user case was closed after credential verification (invite #4 now ACCEPTED).
Final verification: fresh pending invite #6 (9BFADAA3E1CD4B7589E8B5E678901233) now renders the full public claim UI at /join/{code} with no degraded warning.
May 24, 2026
Production-pipeline dataset seeded for lesson/timer breakage analysis
Data seeded via app APIs: /api/maintenance/seed-families generated 2 guardian families with 4 minors in Production 1; /api/maintenance/seed-lesson-week generated week-ending lesson records for timer/reporting exercise.
Output snapshot: families created=2, minors created=4, lesson records created=11 (initial pass, 2 failures reported). Follow-up rerun produced created=8, failed=1.
Triage note: the rerun window inserted 9 lesson rows in SQL while the API reported 8 created / 1 failed, indicating at least one reported failure occurred after lesson insert/commit (likely in post-create side-effects), not as a missing lesson row.
May 22, 2026
✓ Request modal fetch deduplication — one-shot lookup guards added
Issue closed: the requester modal could re-enter its lookup flows across rapid open/search interactions, risking duplicate fetches for request types, minor roster, production-user search, and my-request lists.
Mitigation delivered: layouts/app.blade.php now guards those fetch paths with in-flight promises so each lookup runs once per active request and clears cleanly on completion.
Validation: live browser sampling after repeated modal opening showed one-shot loading behavior with no duplicate request burst from the modal lookup paths.
May 22, 2026
✓ Request queue polling stabilization — duplicate cycle calls removed
Issue closed: Shell queue controls were issuing redundant queue refresh traffic under active-panel conditions, creating unnecessary API load and noisy request traces.
Mitigation delivered: queue polling in layouts/app.blade.php now uses single-flight guards, adaptive backoff, visibility-aware scheduling, production-context gating, and a consolidated poll path that avoids running both refreshBadge() and fetchQueue() in the same visible cycle.
Validation: timed live sampling with queue panel open showed request-rate reduction from approximately 5.14 calls/min to 1.71 calls/min for /api/production-requests/queue, with queue behavior preserved. A follow-up steady-state cadence tune (45s base poll) reduced closed-panel baseline traffic further to approximately 0.86 calls/min.
May 22, 2026
✓ Instruction Reporting startup "Loading" hang resolved
Issue closed: Instruction Reporting could remain stuck at Loading… because bootstrap execution aborted before student hydration.
Root cause: bindUiActions() attached a click handler using confirmCurrentAlloc as an unscoped identifier, but that function was declared later as window.confirmCurrentAlloc = async function() {...}. In shell-injected execution this produced ReferenceError: confirmCurrentAlloc is not defined and halted initialization.
Mitigation delivered: allocation-related bindings now use deferred window.* lookups for window-assigned handlers, preventing pre-definition reference failures while preserving delegated event behavior.
Validation: live shell verification on https://pumpkintime.test/ confirmed Instruction Reporting loads the student pane and no longer hangs on Loading….
May 16, 2026
✓ MsgX emit observability gap closed — diagnostics + audited backend emits live
Issue closed: MsgX dispatches could succeed without durable visibility in MsgX_EmitLog, making password reset and presence messaging difficult to verify operationally.
Mitigation delivered: backend emit paths now route through the audited ServerGlobals.EmitMsgXEvent(...) wrapper for Auth/Registration/Invitations and Realtime State flows; audit rows are written to MsgX_EmitLog with source, payload, dispatch attempt/success, and error fields.
Operational visibility: a live diagnostics view (/app/msgx_emit_diag) and API endpoint (/api/global-msgx/emit-log) were added for polling, filtering, and dequeue trigger validation.
Validation: production tests now show end-to-end rows for auth.password.reset.requested, MINOR_CONCERN_AUTO, and MINOR_LANDED with successful dispatch status.
Forward stub: LAND / DAY_START / DAY_END MsgX subscription lifecycle hook added in RealtimeState.Engine.pas as a client-owned TODO (FireMonkey/mobile subscribe/unsubscribe orchestration).
May 14, 2026
Header structure normalization sweep: account & custom-layout views
Completed the hero header standardization by fixing remaining views with inconsistent or missing title/subtitle elements. Profile and Wallet now use standard pt-hero pattern with descriptive subtitles instead of custom prefix styles. Timesheets and Billing hero structures fixed to properly wrap title+subtitle in a left container and actions in pt-hero-right, ensuring correct alignment and responsive behavior. Invitations Manager and Instruction Reporting — both with non-standard custom layouts — now include standard hero headers with title and subtitle context. All views validate with no syntax errors; all hero structures now follow the shared pattern established in core app views.
May 14, 2026
Error message standardization pass: billing operations
Implemented standardized error handling in Production Billing to provide clearer, more actionable feedback. Added pbExplainError function that detects session expiry, permission denial, and other common error states, returning human-readable guidance instead of generic "Failed" messages. Applied to invoice/timesheet delete and save operations as a proof of concept for systemic error standardization. Session expiry and permission errors now provide explicit re-auth or permission guidance matching the My Groups hardening pattern.
May 14, 2026
UX completeness mitigation pass: production hero normalization sweep
Continued the UX completeness pass by normalizing hero headers across the production-facing views. Production Users, Production Billing, Timesheets, and Production Messaging now use consistent title + subtext hero treatment, matching the shared app header pattern used in other screens. This reduces visual inconsistency between core production workflows and keeps the navigation/landing language aligned with the rest of the shell.
May 13, 2026
UX completeness mitigation kickoff (Phase 1): My Groups hardening started
Began implementation pass for UX completeness on high-traffic admin flows, starting with My Groups. Scope includes standardized error messaging, explicit permission/session guidance, better loading and retry states, and safer invite submission behavior (duplicate-submit prevention). This mitigation track is focused on reducing user confusion and support incidents while preserving existing backend access rules and DataSnap contracts.
May 22, 2026
✓ Controller helper consolidation — JsonResponse controller layer stabilized
What advanced: Shared controller helpers in app/Http/Controllers/Controller.php now own common auth, DataSnap error, and list-unwrapping behavior for the JsonResponse-based API layer.
Regression closed: the helper-rename fallout in InstructionReportingApiController, InvitationManagerApiController, LessonBuilderController, ProfileApiController, and WalletApiController was repaired and revalidated.
Scope reduction: GlobalAdmin JSON controllers now route DataSnap failures through the shared base path, the older array-return controllers (ProdController, GlobalDocsApiController, GlobalMsgxApiController) were migrated to explicit JsonResponse responses, and controller-level service factories that built new DataSnapClient() were migrated to constructor DI.
Assessment impact: Architecture Stability is now raised B → A- and Overall Readiness B− → B+ because the controller layer is materially less fragile and structurally more testable, and SL-3 inline-handler debt has been retired in active runtime/source paths.
May 22, 2026
✓ SL-3 inline-handler remediation — Completed
What closed: Remaining inline HTML handler attributes were removed from active runtime/source code paths and replaced with delegated action wiring (including generated lesson-timer tray controls).
Verification: repo-wide runtime/source sweep now reports zero onclick=, onchange=, oninput=, and onsubmit= attributes outside docs/mock/cache/vendor trees.
Assessment impact: The prior view-layer cleanup tail attached to controller stabilization is now closed.
May 21, 2026
✓ Production request queue workflow modernization — Completed
What advanced: Production Requests now run as a true shell-level workflow with global triggers (FAB + topbar), queue panel actions for staff, and requester-side modal improvements including incremental minor-name search.
Architecture correction: Request status and close/open behavior are now DB-owned via dbo.request_status.status_name and dbo.request_status.isClosingStatus, replacing hardcoded frontend/backend status assumptions.
Operational impact: This removes a key UX dead-end where status labels and close-state logic diverged by request type (snack vs break), and materially improves maintainability by making lifecycle behavior table-driven.
Validation: deployed verification confirmed status mutation persistence and runtime field-type tolerance (including ODBC/FireDAC casting edge cases) for this release pass.
May 13, 2026
✓ My Groups admin-invite authorization and role-governance alignment — Resolved
Issue closed: Global admins could receive a false “no permission” response when inviting admins from My Groups, due to mixed authority paths between Users.IsGlobalAdmin and role-table assumptions.
Root-cause fix: DataSnap My Groups service now treats Users.IsGlobalAdmin=1 as authoritative for global admin actions, while preserving role validation for invite targets. Invite permission checks were normalized to the current invi_* schema and role-class mapping.
Governance hardening: RoleID 1 was renamed to a reserved system role label, guards were added to block direct RoleID 1 assignment in org/invitation role-mapping tables, and legacy pending RoleID 1 invitations were revoked. UI invite role defaults and labels were aligned to non-system admin roles.
Validation: Post-publish browser tests confirmed successful admin invites for both Production and Education org-group flows.
May 11, 2026
✓ End-to-end onboarding complete — all three roles, web platform
The full self-service onboarding path is now operational on the web platform across all three roles. Studio Teacher: receives invite → claims account → Documents Module verifies credentials → acknowledges production briefing via Bulletins → active on Day 1. Production Admin: creates production → configures regulatory ruleset → enrolls minors with permits → guardian consent captured via invite acceptance (/join/{code}) → ST assignment via Education Services org model → production goes live. Global Admin: activates platform → provisions users → authors regulatory rulesets → manages billing → monitors via admin dashboard. No manual database intervention required at any step. Onboarding Readiness score upgraded D+ → B. Open Critical count decremented 7 → 6. Phase 2 (Onboarding & User Acquisition) marked complete.
May 10, 2026
Stripe payments live — wallet top-up, invitation landing page
Stripe payments end-to-end: New API.Payments.Service.pas and API.Payments.Router.pas replace the legacy TPaymentService singleton. GuaranteePayProfile creates Stripe customers on demand; AddPaymentMethod attaches cards; ChargeCustomer runs off-session charges. All charges logged to payment_transactions with GUID idempotency keys.
Wallet top-up: Profile → Add Card and Wallet → Top-up work end-to-end (Stripe charged, UserWallets credited). Drawer z-index fixed (raised above Lesson Timers FAB). Fields clear after successful transaction.
Invitation landing page: Public /join/{code} route handles invite accept for new and existing accounts. New users get a create-account form; existing users sign in; already-authenticated users get a one-click accept. Delphi Accept action creates the user account inline for new registrants, then atomically writes productionusers + guardian_links. Email template designer added to backlog.
May 9, 2026
Code Quality Sprint — API architecture refactor & functional bug fixes
Controller refactor: 12 API controllers now inherit from two shared base classes (ProdController, GlobalAdminController), eliminating ~120 lines of duplicated auth and error-handling boilerplate. DataSnap exceptions now consistently call report() across all controllers, closing a silent error-swallowing gap.
Bug fix (HIGH): GlobalPaymentsApiController::recordPayment() was missing ok:true in its response — every successful payment showed a false error toast to the user. Fixed.
Bug fix (LOW): Bulletins “Mine” filter always showed all items regardless of who was logged in — bnCurrentUserId was never populated from session. Fixed.
Bug fix (LOW): admin_docs approve/reject optimistic update set item.StatusName but the renderer reads item.Status — badge showed stale status until next navigation. Fixed.
Shell globals: window.ptEsc, window.ptCsrf, window.ptPostJSON added to pt-shell.js as shared utilities, replacing per-view copies across 19 blade files.
May 3, 2026
✓ Delphi hot-patch / redeploy causes full outage — Resolved
Original severity: High  →  Resolved (downgraded to Low)
Mitigation: Rolling drain procedure via HAProxy. Drain Node X (zero new requests) → wait for active connections to finish → stop IIS app pool → replace server.dll → restart & health-check → re-enable node → repeat for each node. Minimum 2 of 3 nodes remain live at all times. No user session is dropped.
Completed: Formal deploy runbook published at .copilot/docs/OPS-HAPROXY-ROLLING-DEPLOY-RUNBOOK.md; drain/enable procedure is now standardized for ops use.
May 3, 2026
✓ DataSnap ISAPI single point of failure — Resolved
Original severity: Critical  →  Resolved (downgraded to Low)
Mitigation: Minimum 3-node IIS cluster behind HAProxy on pfSense. Active health-check + round-robin routing. SQL Server deployed as 2-node Always On Availability Group (synchronous commit, auto-failover); cluster expanding before commercial launch.
Remaining open sub-item: Confirm canonical ServerMethodsUnit1 build artifact and establish a versioned CI/CD pipeline for the Delphi DLL.
May 3, 2026
✓ Guardian consent — Correction: handled via production invite system
Assessment error corrected: Guardian consent does not require a separate web consent portal or signature screen. The parent/guardian receives a production invite; acceptance of that invite constitutes their consent to the production terms. Handled by Invitation.Engine.pas and the existing invite infrastructure in both the old and new system.
May 3, 2026
✓ ST credential upload / required docs — Correction: handled by Documents Module
Assessment error corrected: Required document definitions, compliance status cards, expiry tracking, and reminder notifications are all built into the Documents Module (admin_docs). This covers ST license, CPR cert, work permits, and any other role-required documents. The system automatically flags missing or expired required docs and reminds users to update them. Remaining gap: the mobile Profile screen (14) needs a shortcut link to the Documents module.
May 3, 2026
✓ Regulatory Ruleset Authoring — Correction: feature already implemented
Assessment error corrected: The Global Regulatory web view exists and is fully implemented in both the old and new system. Three tabs: Jurisdictions (manage jurisdiction records), Rules (define metric thresholds), Overrides (production-level exceptions). Backed by DataSnap Global Regulatory API and global_regulatory.blade.php. All references to “no ruleset editor UI” have been corrected.
May 3, 2026
✓ Registration & invite-claim flow — Correction: web path fully implemented
Assessment error corrected: Web registration and invite-claim are end-to-end complete. Users register as global accounts (independent of any production). Production invites arrive by email, are accepted on web, and the production is attached automatically. On login, users see all productions they have any role in. Multi-production access is built in. Finding reclassified from critical missing → mobile-only gap (deep-link handler + native registration screen). Recommended screen #16 priority reduced from P2 → P3.
May 3, 2026
✓ Studio Teacher assignment & ratio rule — Correction: handled by Education Services + regulatory config
Assessment error corrected: ST assignment is governed by the Education Services Company (ESC) org model — productions are linked to an ESC which manages and assigns its Studio Teachers. The 1:10 ratio threshold is defined in regulatory_rules and configured via Global Regulatory. Both the assignment model and the ratio rule are built in. Finding downgraded from Critical → High. Remaining gap: live ratio chip on the PA mobile roster.
May 12, 2026
✓ Work permit eligibility and production participation gating — Correction closed
Assessment correction: The prior finding that permit expiry lacked hard gating is no longer accurate. The Documents module requirement engine now supports required document definitions, expiry tracking, and eligibility gating for work participation and production access decisions.
May 3, 2026
41 findings identified across 8 domains. Onboarding section expanded to cover both mobile and web platforms across all three roles (ST, PA, Admin). 2 findings resolved + 1 assessment error corrected on day of publication.