# Simulator Implementation Decision Register

**Revision:** working tree `0.1.0`  
**Decision date:** 26 August 2026  
**Applies to:** local provider-neutral simulator only  
**Policy/API/schema:** `sim-policy-2026-09-04.8` / `v1 + v2 pre-queue passenger binding` / `sim-schema-v2-prequeue-binding`

## Approved simulator boundary

The executable slice is fully synthetic. It does not call, scrape, emulate credentials for, or accept data from IRCTC, UIDAI, Railway, a payment provider, or a real passenger. Queue turns grant only an attempt. The fake PRS remains the sole authority for synthetic availability, fares, berth-segment allocation, booking results, and PT sold units.

The passenger UI cannot select baseline bypass mode or scenario faults. The first slice exposes queue-controlled mode only. A future matched experiment runner will own a scenario-scoped baseline bypass outside passenger routes.

## Functional responsibility map

| Boundary | Simulator owner | Production owner required before Phase 3/7 |
| --- | --- | --- |
| Product/fairness policy | Repository product maintainer | IRCTC/Railway product and policy authority |
| Queue/admission/permit | Queue module maintainer | Authorised platform and security owners |
| Synthetic identity/risk | Local profile fixture owner | Authorised identity, fraud, privacy owners |
| Catalogue/fare/inventory/allocation | Fake PRS module maintainer | CRIS/PRS and Railway fare owners |
| Synthetic payment/refund | Booking orchestration maintainer | Authorised payment, finance, reconciliation owners |
| Audit/metrics/support view | Observability module maintainer | Operations, security, privacy, support owners |
| Accessibility/content | Passenger UI maintainer | Accessibility, legal/content, user-research owners |

Named organisational approvals are outside this repository. They are mandatory for authorised shadow work and remain a hard block on global Phase 3 and PT-7.

## Versioned simulator decisions

- One opaque subject and passenger group has one active intent per opening group.
- An intent accepts one to three ranked train/class/quota preferences. TQ and PT are candidate preferences under one entitlement.
- AC and non-AC options cannot share an intent.
- The proposed production pre-queue opens 15 minutes before the applicable class opening. Every valid intent received before opening belongs to one equal-odds cohort; the two-minute preference cutoff freezes edits but does not close or tier that cohort.
- The pre-opening order is `SHA-256(seed | subject_ref | intent_id)`. The seed commitment is published in bootstrap data and the seed is revealed on opening.
- Late arrivals are designed to use a server sequence; the passenger UI currently demonstrates the pre-opening path only.
- Assignment uses the approved opportunity envelope and fresh fake-PRS availability. The queue does not mutate inventory.
- Offers last 60 seconds. Permits have a hard two-minute limit. The initial controller uses `C=3`, `R=2/s`, and zero reserve.
- Local permits are compact ES256 JWS tokens signed by an ephemeral P-256 key. Server state binds subject, intent, entry, event, run, route, class, quota, party, session hash, nonce, scope, fence, and expiry and enforces single terminal use.
- Money is bigint minor units internally and base-10 strings in JSON. Binary floating point is not used for fare calculation.
- PT uses the approved simulation defaults: cumulative confirmed passenger units, exact-ratio floor boundaries, per-passenger sequential tiers, and `3 × basic + Tatkal` cap basis. PT is personal-digital and confirmed-only.
- Quote, queue, offer, permit, payment-start, decline, unknown, and failure do not advance PT sold units. Only a confirmed fake-PRS allocation does.
- `UNKNOWN` marks the permit as reconciling and blocks a second attempt. This slice does not yet include the worker that converges the outcome.
- Audit events form an append-only SHA-256 integrity chain and use opaque actor references.

## Scenario seed strategy

The first scenario is `ac-opening-balanced-001` with seed:

```text
9a6c4f12f3d1d8af88db94c2a8ae0c370d91bfbb72a0d6f5f0cf843421d72911
```

Its public commitment is SHA-256 of that UTF-8 seed. Future scenarios must carry a distinct identifier, seed, policy/configuration hash, catalogue fixture, workload, and failure schedule.

## Stable reason-code catalogue

Reason codes are defined in `src/lib/contracts.ts`. The implemented boundary covers eligibility, channel, route/preference/passenger validation, PT consent and maximum, unknown availability, sale/offer/permit/quote expiry, scope and fence mismatch, replay, price change, payment decline/unknown, allocation failure, sold out, idempotency conflict, and malformed command. API errors return a stable code and user-safe message; audit events record the code.

## Data classification and retention baseline

| Data | Classification | Local retention decision |
| --- | --- | --- |
| Synthetic profile and passenger ordinal | Non-sensitive test data | Reset with scenario; never substitute real data |
| Opaque intent/entry/attempt/support references | Operational pseudonymous | Active scenario plus test evidence only |
| Permit token/session hash | Security-sensitive synthetic | Memory only; expires with lease/restart |
| Quote/fare/audit | Synthetic financial/operational | Scenario evidence; policy/version stamped |
| Aggregate metrics | Non-identifying | Evidence report duration |
| Real Aadhaar/VID/OTP/biometric/password/CVV/UPI PIN/payment credentials | Prohibited | Reject; never log, persist, tokenize, or accept |

The SQL foundation revokes public table access. A production-shaped adapter must add encrypted storage, role-specific grants, managed key rotation, retention jobs, deletion workflows, and access review before hosted personal data is considered.

## Threat and recovery baseline

| Threat/failure | Implemented control | Remaining Phase 2 work |
| --- | --- | --- |
| Duplicate join/confirm | Durable-shape idempotency key + request hash; replay returns original result | PostgreSQL transaction adapter and cross-process test |
| Queue stuffing/multiple preferences | One entitlement and one active assignment model | Device/network/account cluster rules and appeals |
| Permit forgery/sharing/replay | ES256 signature, complete scope, session hash, nonce, fence, expiry, server status | Managed key rotation, revocation store, adversarial corpus |
| Refresh/request-volume advantage | Priority independent of polling; server-directed jitter | Redis atomic scripts and burst comparison |
| Berth double allocation | Per-segment occupancy invariant and deterministic transaction model | Serializable PostgreSQL concurrency test |
| Payment/booking unknown | Reconciliation state blocks retry | Reconciliation worker, age alert, terminal policy |
| Stale PRS health | Domain policy requires pause | Fault-controlled health adapter and controller reduction |
| Hot-store/controller loss | Fence field and fail-closed design | Redis failover, lease sweep, stale-leader chaos test |
| Prohibited data leakage | Synthetic-only controls and redacted operator view | Automated prohibited-data scan and privacy review |
| UI/timer exclusion | Native controls, labels, live status, visible focus, reflow/reduced motion CSS | Screen-reader, alternative-input, zoom, slow-network usability evidence |

## Unresolved production semantics

Every question in `PREMIUM_TATKAL_RESEARCH_AND_IMPLEMENTATION.md` section 4 remains owned by an authorised CRIS/PRS/Railway policy authority and blocks PT-7. In particular: pricing denominator/scope, small-quota boundary, cross-slab party treatment, cancellation effects, segment/distance scope, cap basis, rounding/GST order, quote binding/lifetime, and enabled train/class/routes are simulation configuration—not production claims.

## V2 Phase A product and catalogue decisions

- The independent fictional passenger product name is **RailSaanjh**. It uses no official logo, wordmark, trade dress, endorsement, or live-integration claim.
- `/` is now the general train-booking homepage and General (`GN`) is the default quota. The V1 passenger simulator remains runnable at `/v1` until later V2 phases reach parity.
- `BookingQuotaCode` remains `GN | TQ | PT`, while V2 queue admission is narrowed to `QueueQuotaCode` (`TQ`). The V1 `ProtectedQuotaCode`/`QuotaCode` types remain compatibility aliases for the preserved simulator only.
- Phase A uses 12 familiar public station names/codes and nine fictional daily train services. Train identities, schedules, fares, availability, and outcomes are visibly synthetic.
- Passenger train runs are derived relative to the selected date. Intermediate after-midnight boarding derives the originating service date before generating the run reference.
- Catalogue availability is a deterministic projection of immutable run, segment, class, and quota inputs. It is display-only and cannot reserve or decrement fake-PRS inventory.
- General and Premium Tatkal selections work without creating a queue. TQ alone explains and enters the conditional waiting-room boundary; PT remains searchable at opening time and requires explicit dynamic-fare consent plus a maximum party fare.
- The V1 scenario seed is unchanged. Phase A has no queue simulation seed; its deterministic catalogue projection is versioned as `demo-catalogue-2026-08-26.a`.
- Hosted persistence remains owned by V2 Phase B and must be selected before public queue/session deployment. This does not block the stateless Phase A catalogue/search surface.

## V2 Phase B session, simulation, and persistence decisions

- Each browser receives one signed, HTTP-only `rs_demo_session` cookie containing only an opaque session reference and expiry. The server stores a distinct opaque subject; duplicate tabs restore it and separate browsers do not share it.
- Protected selection creates a server-validated V2 intent before queue registration. One non-terminal queue is restored per demo session; reset and leave affect only that session.
- `tatkal-moderate-001` uses a deterministic subject/intent/run seed, 280–520 synthetic attempts ahead, a 35–55 second target, and one five-second PRS-latency slowdown. Video mode targets 12–15 seconds and is deployment-configured.
- `/tatkal-fair-access` explains the 15-minute proposed production concept using dated public evidence, an explicitly synthetic request-volume model, and a deterministic privacy-safe audit fixture. The executable V2 queue remains a condensed opening-surge simulation; it does not claim to run a live 15-minute sale clock or contact a randomness beacon.
- Progress is a pure projection of server time, immutable run inputs, the versioned slowdown window, and deterministic abandonment. Polling and refresh do not mutate elapsed time or improve priority.
- Fast-forward shifts only the current synthetic run’s virtual clock, permanently discloses acceleration, audits `DEMO_TIME_ACCELERATED`, and still requires `WAITING -> OFFERED -> ADMITTED`.
- V2 admission uses the shared configured `C=3`, `R=2/s`, safety reserve, controller fence, 60-second offer lease, and hard two-minute session-bound signed permit scope. Queue activity never changes fake-PRS inventory or Premium Tatkal sold units.
- The selected public hosted adapter is an Upstash-compatible Redis REST single-store fallback with a short distributed transaction lock. It is enabled by `TQM_STATE_DRIVER=redis`, `TQM_REDIS_REST_URL`, and `TQM_REDIS_REST_TOKEN`. Local memory and atomic file adapters support tests and restart evidence; in-memory public deployment remains prohibited.
- Production requires separate `TQM_SESSION_SECRET` and preferably `TQM_PERMIT_SECRET`. Demo controls are off in production unless `TQM_DEMO_CONTROLS_ENABLED=true`.

## V2 Phase C booking, ticket, and retrieval decisions

- General and Premium Tatkal create server-backed booking drafts and bypass queue registration. TQ booking drafts are materialised only from an `ADMITTED` Phase B queue entry and re-verify the stored signed, session-bound permit at quote and commit. A sold-out TQ attempt may prefill a matching PT draft as a convenience, but it is not a prerequisite for choosing PT.
- Five saved passenger fixtures are unselected by default. Public payloads accept only their opaque fixture references; strict schemas reject names, contact details, government identifiers, card data, UPI credentials, OTPs, and unexpected fields.
- Exact fake-PRS fare consent binds the selected fixture references, all-in minor-unit total, quote ID, inventory version, pricing version, and 90-second expiry. Any changed binding requires a fresh quote and fresh affirmative consent.
- Demo UPI, demo card, and demo net banking are labels only. Public clients cannot choose the outcome or submit a credential; the versioned scenario stored on the booking draft drives confirmed, RAC, waitlist, decline, unknown, sold-out, or refund-pending behavior.
- Phase C inventory is route-segment-aware and held in the same atomic durable adapter boundary as session, queue, quote, idempotency, result, and audit state. Confirmed allocation selects non-overlapping synthetic berths; RAC and waitlist never claim a berth; PT remains confirmed-only and advances its synthetic sold-unit counter only after confirmed allocation.
- Booking command replay returns the original durable outcome. A payment `UNKNOWN` result blocks every different booking key until reconciliation; this implementation intentionally does not pretend that the unresolved payment converges.
- My Trips and generated PNR-like references are scoped to the anonymous browser session. `DMO260001` is the single documented public fixture for judging retrieval. Every ticket view says “Not valid for travel.”
- Reset removes only the current session’s active drafts, quotes, and trip index. Committed fake-PRS allocation and audit effects remain immutable, while their result becomes unreachable through the reset session; this avoids changing another tester’s shared inventory view.

## V2 Phase D hardening and evidence decisions

- The passenger shell exposes one main landmark, a keyboard skip link, durable desktop navigation, and a functional mobile menu. The incomplete language selector is removed until reviewed Hindi content exists.
- Passenger copy uses a 12-pixel minimum rendered text size at the implementation’s default scale, keeps 44-pixel practical interaction targets, and reflows to a 320 CSS-pixel viewport without document-level horizontal scrolling.
- Reduced-motion preference and the explicit low-data mode suppress nonessential animation; low-data mode also hides synthetic activity-feed detail without changing queue state or polling authority.
- Queue announcements are limited to material state and health changes. Offer focus moves to the claim action; booking outcomes and form errors receive focus without extending any server lease.
- Offline queue status retains the last server-authoritative display, states its age, and reconnects automatically to the same queue reference. The browser never calculates or mutates priority.
- The preserved V1 and aggregate operator routes remain directly addressable for evidence, but neither is linked from primary passenger navigation. Public disclosure now describes the complete Phase A–D working boundary and all mocked dependencies.
- Automated Chromium checks run against the production build and cover public navigation, a complete General booking, PNR retrieval, keyboard entry, 390-pixel mobile navigation, 320-pixel reflow, reduced motion, and offline queue restoration.

## V2 General connecting-journey decisions

- Connecting search is General-quota only and performs one same-station transfer with a 90-minute to 12-hour factual layover. The first boarding is on the selected calendar date; the onward run may depart after midnight.
- Timetable, confirmed-availability, and fare calculation are separate provider interfaces. This branch keeps the historical DataMeet timetable and deterministic synthetic commercial data; it does not scrape IRCTC, Indian Railways, or NTES.
- Only `AVAILABLE` options that can confirm the full 1–4 passenger party are eligible. RAC, waitlist, stale, unknown, sold-out, and partial-capacity combinations are excluded. Same-class variants rank before mixed-class fallbacks.
- Results are Pareto-checked and capped at ten while retaining labelled representatives for earliest arrival, shortest journey, lowest fare, seat headroom, and useful transfer buffer. No missed-connection probability is shown because authoritative historical delay data is not available.
- The client submits only an exact opaque `itineraryRef` plus route, date, and party size. The server recomputes the selected train/class pair before intent, quote, and commit; client-supplied train, fare, class, or seat values are not accepted.
- The mock commits both segment inventories in one simulator transaction immediately before one demo payment. Failure on either segment allocates neither; success creates two synthetic PNRs under one `connectingJourneyRef`. This atomic behavior is explicitly a simulator convenience, while IRCTC connecting journeys use linked PNRs: https://contents.irctc.co.in/en/Scilent%20feature%20of%20connecting%20train.pdf.
- A later authorized enquiry adapter should target IRCTC TIES for schedules, trains between stations, cluster stations, fares, and accommodation availability; TIES does not authorize booking: https://contents.irctc.co.in/en/TIES_Policy.pdf. Real booking remains a separate authorized B2C integration phase: https://contents.irctc.co.in/en/B2C_PSP_Policy_other_than_Startup_MSME.pdf.

## Tatkal pre-queue verified passenger binding decisions

- Only Tatkal (`TQ`) changes sequence: service selection, exact saved-party selection and synthetic verification, fair-access consent, then queue registration. General, Premium Tatkal, and General connecting journeys keep their established flows.
- Adding a passenger requires a unique `DEMO-PAX-*` reference and the fixed simulation OTP `123456`; no SMS is sent. Aadhaar-like 12-digit input is rejected. The reference is normalized and HMAC-derived with `TQM_IDENTITY_SECRET`, then both the reference and OTP are discarded. Public passenger payloads expose verification status, method, timestamp, and a safe suffix hint, never the private token or consent inputs.
- Self, guardian-linked minor, and alternate synthetic verification are supported. A guardian-linked minor must queue with the verified adult guardian. Changes to name, age, or gender invalidate verification unless a non-released entitlement exists, in which case the profile edit is rejected.
- Exact service scope is catalogue/train run, journey date, boarding/alighting pair, class, and Tatkal quota. A sorted party fingerprint is copied to intent, queue entry, entitlement claim, signed permit, quote, booking attempt/result, and matching Premium Tatkal fallback.
- Queue registration atomically claims the exact-service scope for both the requesting subject and every private traveller identity token. Only the same subject, exact service, and identical party restores an active queue. Other-account, different-party, and partial-overlap conflicts return stable generic reason codes without disclosing the conflicting queue.
- Leave, offer/permit expiry, sold out, and payment decline release indices. Re-entry creates a new queue at the back and is audited as `REENTERED`. Confirmed, RAC, and waitlist consume the entitlement; unknown-payment and refund-pending outcomes retain it as held.
- Broader route/date/opening-group counts are risk-analysis metrics only and never decide blocking. Registration attempts store hashed subjects, hashed service/route scopes, party fingerprints, outcomes, and timestamps; the operator endpoint exposes aggregates only.
- State schema version 3 and Redis namespace `railsaanjh:v2:phase-b:v3` invalidate nonterminal legacy queues without party bindings while retaining committed booking results and allocations.
- This is a synthetic fairness proposal, not a statement that passenger identity verification is currently mandatory for Tatkal. A production implementation requires an authorised agency-specific identity token and formal privacy, appeals, retention, and regulatory review.
