| src | ||
| .gitignore | ||
| index.html | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
| vite.config.ts | ||
Approve, Please
A "Papers, Please"-inspired bank change approval simulator. You are an RTB (Run The Bank) Change Approver. BTB developers submit change requests. You approve or deny based on policy, risk, and regulatory requirements. Your decisions have consequences — incidents, blocked releases, and a family that depends on your salary.
Built for a 6-hour hackathon. Stack: React + TypeScript + Vite. Zero backend, zero database. All game state in useReducer.
Quick Start
npm install
npm run dev
Open http://localhost:5173 in a browser.
Production Build
npm run build
npm run preview
Static files land in dist/.
How to Play
| Key | Action |
|---|---|
A |
APPROVE change request |
D |
DENY change request |
I |
INSPECT attachments (reveals violations) |
R |
Rulebook (policies + market holidays) |
N |
Next (advance to next CR or end of day) |
Enter |
Dismiss faxes, overlays, advance screens |
Esc |
Close modals |
Game Rules
- 60-second day timer. Process as many CRs as you can before the shift ends.
- £100 per correct decision. Wrong decisions earn nothing.
- £250 daily household bills. Rent, school, groceries. If you can't pay, family health drops.
- Family of three. Keiko (spouse), Ren (son, 11), Yuki (daughter, 7). All family dead = game over.
- Rules accumulate each day. Friday high-risk restrictions, exchange co-ordination, regulatory freeze, segregation of duties.
- Inspect before you decide. Violations only appear after you click INSPECT.
- BTB will spam you. Developers, release managers, and directors message your inbox during review.
- Consequences play out in real time. Citations, incident banners, faxes — all arrive in your inbox after a decision.
Project Structure
src/
├── main.tsx Entry point
├── App.tsx Main component — game loop, keyboard, screen routing
├── App.css All styles (~1100 lines)
├── types.ts TypeScript interfaces
├── scenarios.ts Game content — 9 handcrafted CRs, 6 rules, holidays, newspapers, filler templates
├── useGameState.ts Reducer + side effects (timer, spam, consequence queue)
└── components/
├── TopBar.tsx Date, freeze badge, money, timer
├── ChangeRequestPanel.tsx CR form with signoffs, violations, inspect
├── InboxPanel.tsx Teams-style message feed
├── CitationOverlay.tsx Paper printout citations/faxes
├── IncidentBanner.tsx Red P1 banner
├── RulebookModal.tsx Policies + holiday calendar table
└── InspectModal.tsx Attachment review findings
Adding Content
New Change Request (handcrafted)
Add an entry to _SCENARIOS_RAW in src/scenarios.ts:
{
id: "S10", dayIndex: 2, // dayIndex: 0=Dec1, 1=Dec5, 2=Dec12, 3=Dec13, 4=Dec20
changeRequest: {
id: "CR-5000",
title: "Your change title",
service: "Service Name",
riskLevel: "low", // "low" | "medium" | "high"
plan: "Implementation details...",
testPlan: "Test evidence...", // null = triggers violation
backoutPlan: "Rollback plan...", // null = triggers violation
// ...
},
inspectFindings: [...],
activeRuleIds: ["window_check", "backout_required", ...],
timeLimit: 45,
approve: { score: 10, cascade: cleanApproveCascade("Service Name") },
deny: { score: -5, cascade: [...] },
correctOutcome: "approve", // "approve" | "deny" | "ambiguous"
}
New Rule
Add to RULES array in src/scenarios.ts. The check function receives the change request + calendar state and returns a RuleCheckResult. Reference the activeRuleIds in the scenarios where it should fire.
Filler CR Templates
Edit FILLER_TEMPLATES in src/scenarios.ts. Template format:
{ title: "Update {service} ...", risk: "low", testPlan: true, backoutPlan: true, correctOutcome: "approve", plan: "..." }
{service} and {lib} are replaced at random from FILLER_SERVICES and FILLER_LIBS.
Known Gaps
- No sound effects (stamp thump, fax whine, incident siren)
- No localStorage persistence (refresh = lose progress)
- Background tab throttling affects
setTimeoutconsequence delivery - No responsive layout (<768px)
- No test suite configured
License
Hackathon prototype — do whatever you want with it.