# BoardSim — Frontend API Specification ## Overview The frontend communicates with the backend via REST/HTTP or WebSocket endpoints. The backend is a FastAPI server running at a configurable base URL (default: `http://localhost:8000` for local dev, or `https://-board-sim-env.hf.space` for production). **Key Principle**: Frontend and backend are fully decoupled. The frontend only needs to know these endpoints; it does not import any backend code. --- ## 1. REST Endpoints ### `POST /reset` **Purpose**: Start a new game episode. **Request Body**: ```json { "seed": 42, "episode_id": "optional-uuid-string" } ``` **Response** (200 OK): ```json { "observation": { "state": { "round": 1, "revenue": 2000000.0, "burn_rate": 1200000.0, "runway_months": 14.0, "product_readiness": 0.45, "market_share": 0.08, "team_morale": 0.70, "investor_confidence": 0.65, "regulatory_risk": 0.20, "profitability_score": 0.0, "trust": { "CTO": 0.5, "CFO": 0.5, "Investor Rep": 0.5, "Independent": 0.5 }, "trust_history": [ { "round": 0, "CTO": 0.5, "CFO": 0.5, "Investor Rep": 0.5, "Independent": 0.5 } ], "history": [], "done_reason": null, "winning_decision": null }, "event": "New Competitor Entry — A larger competitor enters your core market with aggressive pricing and threatens your customer base.", "options": [ "cut_prices", "double_down_on_quality", "form_strategic_partnership" ], "npc_statements": [ { "role": "CTO", "statement": "From an operational standpoint, the trade-offs here are clear. I'm voting double_down_on_quality.", "vote": "double_down_on_quality", "confidence": 0.78 }, { "role": "CFO", "statement": "From a fiduciary standpoint, only one of these is defensible. I'm voting cut_prices.", "vote": "cut_prices", "confidence": 0.66 }, { "role": "Investor Rep", "statement": "We were not funded to play it safe. I'm voting form_strategic_partnership.", "vote": "form_strategic_partnership", "confidence": 0.70 }, { "role": "Independent", "statement": "Long-term reputation outlasts any single quarter. I'm voting double_down_on_quality.", "vote": "double_down_on_quality", "confidence": 0.59 } ], "round": 1 }, "done": false, "info": { "episode_id": "uuid-string", "seed": 42 } } ``` --- ### `POST /step` **Purpose**: Submit the agent's decision for the current round. **Request Body**: ```json { "action": { "decision": "double_down_on_quality", "coalition_pitch": "Investing in product quality protects long-term reputation and reduces operational risk while preserving margin discipline." } } ``` **Response** (200 OK): ```json { "observation": { "state": { "round": 2, "revenue": 2000000.0, "burn_rate": 900000.0, "runway_months": 18.5, "product_readiness": 0.45, "market_share": 0.08, "team_morale": 0.65, "investor_confidence": 0.60, "regulatory_risk": 0.20, "profitability_score": 12.34, "trust": { "CTO": 0.65, "CFO": 0.70, "Investor Rep": 0.40, "Independent": 0.55 }, "trust_history": [ { "round": 0, "CTO": 0.5, "CFO": 0.5, "Investor Rep": 0.5, "Independent": 0.5 }, { "round": 1, "CTO": 0.65, "CFO": 0.70, "Investor Rep": 0.40, "Independent": 0.55 } ], "history": [ { "round": 1, "event_title": "Round 1 — Series-B runway crunch", "agent_decision": "cut_costs", "winning_decision": "cut_costs", "reward": 1.25, "profitability_before": 0.0, "profitability_after": 12.34 } ], "done_reason": null, "winning_decision": "cut_costs" }, "event": "Round 2 — Enterprise contract w/ source-code escrow\nDescription: A Fortune 500 enterprise wants to sign a $5M contract but demands source code escrow.", "options": [ "accept_deal", "negotiate_terms", "reject_deal" ], "npc_statements": [ { "role": "CTO", "statement": "...", "vote": "...", "confidence": 0.XX } ], "round": 2 }, "reward": 1.25, "done": false, "info": { "round": 2, "winning_decision": "cut_costs", "winning_vote_tally": { "cut_costs": 4.2, "raise_capital": 1.3, "reduce_scope": 0.5 }, "pitch_scores": { "CTO": 0.0, "CFO": 0.0, "Investor Rep": 0.0, "Independent": 0.0 } } } ``` --- ### `GET /health` **Purpose**: Health check. Confirms backend is running. **Response** (200 OK): ```json { "status": "healthy" } ``` --- ### `GET /docs` **Purpose**: Auto-generated Swagger/OpenAPI documentation. Use for development reference. **Location**: `http://localhost:8000/docs` (or on HF Space at `/docs`) --- ## 2. WebSocket Streaming (Optional, Advanced) If you want real-time streaming during training or multi-agent play: ### `WebSocket /ws` **Purpose**: Bi-directional message streaming (not required for single-agent frontend). Connection example: ```javascript const ws = new WebSocket("ws://localhost:8000/ws"); ws.onmessage = (event) => { const message = JSON.parse(event.data); console.log(message); // e.g., { "type": "step", "observation": {...} } }; ``` *(Details omitted if not used for initial frontend.)* --- ## 3. Data Models Reference ### `BoardSimObservation` (returned by `/reset` and `/step`) ```javascript { state: { round: number, // 1-indexed: 1..10 revenue: number, // in dollars burn_rate: number, // monthly spend in dollars runway_months: number, // months until bankruptcy product_readiness: float (0..1), market_share: float (0..1), team_morale: float (0..1), investor_confidence: float (0..1), regulatory_risk: float (0..1), profitability_score: number, trust: { // per NPC, 0..1 "CTO": 0.5, "CFO": 0.5, "Investor Rep": 0.5, "Independent": 0.5 }, trust_history: Array, // per-round trust snapshots history: Array, // past decisions & outcomes done_reason: string | null, // e.g., "bankruptcy", "acquisition", "ipo", null winning_decision: string | null }, event: string, // event title + description options: [string, string, string], // 3 valid decision strings for this round npc_statements: [ { role: "CTO" | "CFO" | "Investor Rep" | "Independent", statement: string, vote: string (one of options), confidence: float (0..1) }, // ... one per NPC role (4 total) ], round: number } ``` ### `BoardSimAction` (sent to `/step`) ```javascript { decision: string, // must be one of observation.options coalition_pitch: string | null // optional persuasion attempt (unused in v1) } ``` --- ## 4. Error Responses ### 422 Unprocessable Entity Invalid action format or decision not in options. **Response**: ```json { "detail": [ { "loc": ["body", "action", "decision"], "msg": "value is not a valid enumeration member", "type": "type_error.enum" } ] } ``` ### 400 Bad Request Malformed JSON or missing required fields. --- ## 5. Frontend Integration Checklist - [ ] **Initialize**: On app load, call `POST /reset` to get initial observation. - [ ] **Display State**: Render `observation.state` as metrics (revenue, runway, morale, trust, etc.). - [ ] **Display Event**: Show `observation.event` (crisis title + description). - [ ] **Display NPCs**: Render 4 NPC cards with their `statement`, `vote`, and `confidence`. - [ ] **Render Decision Options**: Display 3 buttons (or cards) for each string in `observation.options`. - [ ] **Handle User Click**: On decision click, POST `/step` with the selected `decision`. - [ ] **Update UI**: Parse response observation and repeat from "Display State". - [ ] **Terminal State**: If `done` is true, show final metrics and `done_reason` (e.g., "Bankruptcy", "IPO"). - [ ] **Optional Coalition Pitch**: Text input for `coalition_pitch` (future extension; safe to leave blank for v1). --- ## 6. Backend Base URL Configuration For local development: ``` http://localhost:8000 ``` For HF Space deployment (after `openenv push`): ``` https://-board-sim-env.hf.space ``` **Frontend environment variable** (optional): ``` REACT_APP_API_BASE_URL=http://localhost:8000 // or REACT_APP_API_BASE_URL=https://-board-sim-env.hf.space ``` --- ## 7. Example Frontend Workflow ```javascript // 1. Reset const resetRes = await fetch(`${API_BASE}/reset`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ seed: 42 }) }); const { observation, done, info } = await resetRes.json(); // 2. Render observation displayState(observation.state); displayNPCStatements(observation.npc_statements); displayDecisionButtons(observation.options); // 3. User clicks decision const decision = "cut_costs"; // from button click const stepRes = await fetch(`${API_BASE}/step`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: { decision, coalition_pitch: "" } }) }); const { observation: nextObs, reward, done: nextDone } = await stepRes.json(); // 4. Repeat or show results if (nextDone) { displayEndgameScreen(nextObs.state, nextObs.state.done_reason); } else { displayState(nextObs.state); // ... repeat } ``` --- ## 8. No Backend Imports in Frontend ✅ **OK**: `fetch("http://localhost:8000/reset")` ❌ **NOT OK**: `import { BoardSimEnvironment } from "backend"` The frontend is a standalone web app. All communication is via HTTP/WebSocket.