# TenantSetupScreen — full review & replacement notes

**Scope.** The uploaded `TenantSetupScreen.tsx.txt` was read in full: 3,106 lines. The accompanying `TenantSetupScreen.tsx` is a single-file replacement for `src/screens/auth/TenantSetupScreen.tsx`. Line references marked **original** refer to the uploaded file, not the replacement. This is a static review plus a mocked test pass, not a production certification.

## Executive assessment

The screen combines tenant creation, plan selection, Razorpay checkout, ad lifecycle, haptics, theme token derivation, and a pooled 2D game. The broad feature set is useful, but three boundaries needed particular care: **money**, **simulation**, and **server trust**. The replacement preserves the existing screen, exported `PlanItem` shape, package imports, relative service/store imports, API paths and payload keys. It changes the internal implementation and visual treatment, and it deliberately does not introduce another application file or package.

The most serious issue cannot be repaired honestly in this file: the reward endpoint receives the score and target from the client. A modified client can submit arbitrary values. The server must establish eligibility independently and make reward grants idempotent. Likewise, payment reconciliation and idempotency must be durable on the server. Neither is made secure by the replacement.

## Entire-file map

| Original lines | Area | Analysis |
|---|---|---|
| 1–95 | Imports, `PlanItem`, hard-coded plans, ad-native-module guard | The public type, default screen and service boundaries must remain stable. Prices are static UI values; `/payment/plans` was used only for `test_mode`, not to refresh plan prices. The native ad guard depends on a particular module name. |
| 96–241 | Motion and haptics | Repeated loading haptics accumulated references to completed timeout handles and doubled Android vibration; system vibration settings were overridden. The replacement keeps the visual pulse, bounds timeout references, and respects system settings. |
| 245–280 | Theme tokens | Lexical string ordering is not brightness. Appending hex alpha to an arbitrary theme string can produce an invalid React Native color. The replacement parses supported colors, computes relative luminance and uses `rgba` where possible. |
| 282–425 | Entity types, fixed screen geometry, slot blueprint, briefing badges | `Dimensions.get('screen')` is frozen at module load; fixed typed slots have limited capacity. The replacement measures the modal and preserves slot types, skipping a roll rather than changing its type. |
| 428–581 | Rendered game arena | The Aegis scale is centered on the base view but the hitbox was anchored at its unscaled left. Visual and collision edges diverged. The replacement applies the half-width translation correction and passes measured geometry to the arena. |
| 583–1111 | Game state, ads, launch, timers, spawning, movement, collisions | Ad failures and duplicate taps could stall/duplicate launches. Native visual timelines and collision calculations disagreed for gold; Chrono did not affect existing hazards. Background elapsed time caused jumps. The replacement uses a single simulation clock, ad guards, explicit pause and continuous collision tests. |
| 1113–1816 | Modal, HUD, ready/game-over/win overlays | Android back was blocked during play with no explicit escape. The replacement adds pause, exit, a Chrono countdown and directional controls, and softens reward language to indicate verification. |
| 1819–2075 | Stepper, unused inline error, feature and plan cards | A null-only `InlineError` was dead code. The replacement removes it, preserves the shared `ErrorBox`, improves plan radio labels and selected/disabled state, and uses less monospaced body copy. |
| 2077–2311 | Store state, registration, reward claim, checkout | State initialized from `employee` could go stale. Checkout was unlocked while the gateway was open. Registration/reward/verification could be repeated after a successful server response followed by refresh failure. The replacement derives the step from the store and guards in-flight actions and in-session retries. |
| 2313–2661 | Main screen, links and calls to action | Copy contradicted itself on billing, assumed security properties and used decorative UI. The policy link lacked a rejection handler. The replacement uses restrained copy/layout, keeps an offline/error state and catches link failure. |
| 2663–3106 | Styles | Theme alpha concatenation, heavy shadows, large decorative orbs and extensive monospaced text reduced robustness and readability. The replacement uses safe color handling, clearer hierarchy, bounded content width and lighter surfaces. |

## Findings and dispositions

### 1. Client-reported reward is not an authoritative proof — critical, external
**Original:** 2197–2211. The reward request sends `plan`, `score` and `target_score` supplied by the app. A client can alter all three. **Replacement:** preserves that exact API contract because no backend change was provided, and labels the win as a submission for verification. **Still required:** server-controlled challenge/eligibility, replay protection, rate limiting, server-derived target, and idempotent entitlement grants. A checksum in the same client would not solve this.

### 2. Checkout re-entry and recovery — high
**Original:** 2231–2279, especially 2252–2256. `loading` was cleared while `RazorpayCheckout.open` was pending, allowing a second order and plan changes. **Replacement:** a synchronous ref lock covers the entire action; selection remains disabled while checkout or in-session confirmation is pending. A returned payment receipt is kept in memory; failed verification can retry the same receipt, and after verification succeeds only account refresh is retried. The guard remains until the store reports an active plan. An incomplete receipt enters a review state instead of opening another order. **Boundary:** this state is in memory only. Process death, a lost response, server idempotency, signature verification, refunds and final settlement require backend reconciliation. The server order amount is authoritative; for an INR amount different from the displayed static price, the replacement asks the user to confirm it.

### 3. Registration/reward refresh failure — high
**Original:** 2178–2194 and 2203–2222. After a successful write, a failed `refreshMe` left the normal CTA available to repeat the write. **Replacement:** remembers a successful registration or reward submission in this mounted session and retries refresh instead; reward controls remain locked until active status is observed. A lost write response or app restart is still a server idempotency problem.

### 4. Paddle geometry — high
**Original:** 540–555, 681–684, 933–937 and 970–971. Base width is 92 px. At 1.28× the visible view expands by 25.76 px around its center, or 12.88 px on each side. The original collision bounds instead used the unscaled left edge and the full scaled width to the right; the laser was offset too. **Replacement:** the logical paddle position is the visible left edge, translation includes `(effectiveWidth - baseWidth) / 2`, and clamps, lasers and collisions use that same left edge and effective width.

### 5. Movement and collision divergence — high
**Original:** 883–895 versus 984–996. Gold's rendered X trajectory used sine easing while collision X was linear; 32 ms point samples could also skip fast intersections. **Replacement:** a shared position function supplies the drawn coordinates and collision positions; a swept relative-motion AABB test considers travel between ticks. Bomb easing remains quadratic; gold uses the matching sine curve. This is a JS-driven approximately 30 Hz loop, not the original fully native-timed visual motion. Device profiling is still required.

### 6. Chrono didn't slow active hazards — medium
**Original:** 824 and 853–858; 1055–1059. Only newly spawned hazards got a longer duration, despite text saying all falling hazards slow down. **Replacement:** each hazard's progress is advanced at reduced speed while the effect is active, including existing hazards, and the HUD shows remaining seconds. Simulation time freezes during pause.

### 7. Pool type corruption — high
**Original:** 842–850. When no slot for the requested kind was free, any inactive slot was used and its fixed `kind` became the actual spawn. This could create later-wave hazards early or replace a reward with a different item. **Replacement:** if the requested typed slot is busy, the spawn is skipped. This preserves roll semantics but may reduce spawn frequency under load.

### 8. Backgrounding, back and exit — high
**Original:** 924–1125. `Date.now()` advanced while backgrounded and the hardware back button was blocked during play. **Replacement:** backgrounding pauses; foregrounding requires explicit resume. The game clock does not advance while paused, and a pause overlay exposes resume/exit. An 80 ms maximum step avoids a large jump after a foreground stall, trading real-time accuracy for simulation stability. The back button pauses during play and closes otherwise.

### 9. Viewport geometry — medium
**Original:** 322–328. Screen dimensions and paddle height were fixed at import time, ignoring modal layout changes and safe areas. **Replacement:** modal `onLayout` measures the play area, repositions slots on resize, clears lasers and pauses during an active resize; the paddle and HUD account for insets. Test orientation, split screen and foldables on real devices.

### 10. Theme color and contrast — medium
**Original:** 248–265, 2801. For example, a dark red `#d01111` is lexically greater than `#cccccc`, while light `#c7d2fe` is not. Alpha suffixes are invalid for `rgba(...)` and already-alpha colors. **Replacement:** parses common hex and RGB(A) values, uses React Native `processColor` as a fallback, chooses black or white button text using relative luminance, and builds valid `rgba` strings. Unrecognized platform colors are retained unchanged; real light/dark themes and alpha-composited surfaces still need visual/contrast testing.

### 11. Interstitial concurrency/rejections — medium
**Original:** 738–747, 773–809. Start had no synchronous in-flight guard, and rejected SDK calls could abort start or create unhandled rejections. **Replacement:** prevents duplicate launches, catches ad failures, cancels stale launches after close, and guards delayed post-game ads. Actual native module linking, fill, timing, policies and SDK behavior require device tests.

### 12. Haptics, accessibility, and presentation — medium
**Original:** 79, 112–168, 182–237, 1934–1969, 2332–2334 and 2663 onwards. Android received duplicate explicit vibration and system settings were ignored; the loading timeout array grew. `InlineError` always had a null message. Multiple claims and visual treatments were unverified or overly decorative. **Replacement:** respects Android haptic settings, bounds timer references, removes dead error UI and the ambient blobs, uses more readable body type and quieter surfaces, adds plan radio semantics, pause/exit and directional buttons, and removes the conflicting auto-renew/SSL claims. This is not a complete accessibility audit: real screen-reader play, reduced motion, touch target sizing, localization and color contrast still need verification.

### 13. Product & scaling concerns — external/needs validation
The static enterprise plan target is **2,500,000 points**, versus **14,900** for monthly (about 168×). Completion time, fairness, slot saturation and the practical value of this optional challenge need playtesting. Plan prices/descriptions remain hard-coded to preserve the existing contract; the live `/payment/plans` response shape was not supplied. The replacement prompts if an INR order differs from the displayed plan price, but does not implement a new remote pricing schema. Ads may have platform/policy constraints independent of correctness.

## Review loops — subjective, not a certification

| Pass | Change | Review estimate |
|---|---|---|
| Baseline | Original, before fixes | 5.2 / 10 |
| 1 — establish the contract | Preserved imports/endpoints; removed dead UI and unsafe theme assumptions | 6.6 / 10 |
| 2 — align the simulation | Shared clock/geometry, swept collisions, pause, resized arena, correct typed pool and Chrono | 7.6 / 10 |
| 3 — protect money | Synchronous lock, receipt retry, refresh-only recovery, order amount confirmation and clearer copy | 8.4 / 10 |
| 4 — stress the seams | Strict mock type-check and behavioral harness; accessibility and interaction refinements | 8.8 / 10 |
| Target | Real integration, device/performance, server verification, policy and accessibility tests | 10 / 10 target; **not yet verified** |

Ratings are editorial estimates, not measured QA scores. Claiming 10/10 without the actual app, backend, SDK configurations and devices would be misleading.

## What was verified here

- **TypeScript:** `tsc --noEmit --strict`, with `noUnusedLocals` and `noUnusedParameters`, React Native 0.86.3 declarations and mocked declarations for the project imports and other SDKs. This checks syntax and the mocked interface, **not** compatibility with the user's exact dependency versions or actual declarations.
- **Pure math:** 11 assertions exercise color parsing/alpha/luminance and the actual swept collision helper, including a fast crossing and two moving bodies.
- **Mocked component behavior:** same-render plan selection followed by payment; double-tap order lock; plan lock while the gateway is open; retry of the same receipt after verification failure; refresh-only retry after successful verification; store activation; double-tap registration and refresh-only recovery; store hydration; shared ad initialization, duplicate start guard, rejected and stale-ad recovery; background pause, explicit resume and exit; mismatched receipt review; and cancellation of a changed INR order amount.
- **Review website:** desktop and 390 px mobile browser interaction checks covered the plan selector, game, findings filter, hitbox slider, before/after tabs, source search/copy/download and horizontal overflow. An automated axe-core pass reported no violations in the checked default and expanded/game-open desktop states; this does not establish complete accessibility and does not test the native screen.
- **Not verified here:** real React Native rendering, actual imports/dependency versions, Android/iOS SDKs, device rotation/safe areas, screen readers, frame rate/battery use, real Razorpay test/live checkout, ad delivery, backend permissions and signature checks, score security, crash recovery, policy compliance or enterprise completion time.

The browser phone/game on the review site is an illustrative web adaptation. It does not run the React Native component, contact the backend, take payments or grant rewards.

## Integration checklist

1. Back up the original and replace **only** `src/screens/auth/TenantSetupScreen.tsx` with the supplied file.
2. Run your project's own TypeScript/lint/build and resolve any mismatch against your **actual** existing service/store/package signatures; those files were not supplied here.
3. On iOS and Android, test new and hydrated tenants, offline and failed refresh states, plan selection, cancellation, test/live gateway return, failed verification and confirmation retry. Confirm price/currency and every backend response shape.
4. On-device, test safe areas, rotation, background/resume, hardware back, interruption by ads, haptic settings, screen readers, touch controls and performance with crowded pools.
5. On the server, verify Razorpay signatures and authorization, reconcile by order/payment ID, make writes and reward grants idempotent, derive target/eligibility server-side, and prevent score forgery and replay. Persist pending-payment recovery outside this screen if required.
6. Confirm plan terms, prices and game economics with the product owner; load them from a trusted API when its actual contract is available.

No backend or other app file is changed by this deliverable. Those server-side and integration checks remain essential before production use.
