First Login¶
Purpose¶
First login is a stateful onboarding flow that doubles as character creation. A new account does not yet have a Player record; the flow runs an AI-driven dialogue against a security guard at the Callisto Colony shipyard, evaluates the player's persuasion against rarity-tier thresholds, and on success creates the Player row with a starter ship, credits, and initial state. The flow is resumable, has a guaranteed-safe failure path (escape pod), and works fully offline (manual provider fallback).
Inputs¶
The state machine reads:
- The player's User row (auth identity, locale).
- PlayerFirstLoginState row (if any) — has the player completed onboarding before?
- FirstLoginSession row (if any) — in-progress session for resume.
- ShipPresentationOptions — the 3+ ships offered this attempt.
- ShipRarityConfig table — admin-tunable spawn probability + persuasion thresholds.
- DialogueExchange rows for this session — full back-and-forth history.
- AI provider chain (same as ARIA dialogue) for response evaluation.
- The player's free-form text input.
The system fires on:
- GET /api/v1/first-login/status — bootstraps or resumes a session.
- POST /api/v1/first-login/session — start a new session.
- POST /api/v1/first-login/dialogue — submit the player's next response.
- POST /api/v1/first-login/claim — finalize the ship claim once persuasion succeeds.
- POST /api/v1/first-login/abandon — accept the escape-pod fallback.
Process¶
┌──────────────────────┐
│ App launch (auth) │
└──────────┬───────────┘
│
┌──────────▼──────────────┐
│ should_show_first_login │
│ (PlayerFirstLoginState │
│ null or completed=False)│
└──────────┬──────────────┘
no → go to game (skip flow)
yes → enter state machine
│
┌──────────────▼──────────────┐
STATE 1 → │ WELCOME / NARRATIVE OPEN │
│ (shipyard backdrop, cat) │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
STATE 2 → │ SHIP PRESENTATION │
│ - generate 3+ ships, rolled│
│ against ShipRarityConfig │
│ - persist ShipPresentation │
│ Options │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
STATE 3 → │ SHIP CHOICE │
│ player picks claimed ship │
│ → record_player_ship_claim │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
STATE 4 → │ TUTORIAL / DIALOGUE LOOP │
│ guard probes 4 topics: │
│ • identity_verification │
│ • arrival_details │
│ • ship_knowledge │
│ • situational_awareness │
│ per turn: │
│ - generate_guard_question │
│ - record_player_answer │
│ - _analyze_player_response│
│ (persuasiveness, conf., │
│ consistency, skill, │
│ inconsistencies, key │
│ info, believability) │
│ cat_mention_bonus: +0.15 │
└──────────────┬──────────────┘
│
┌──────────────▼──────────────┐
STATE 5 → │ EVALUATE OUTCOME │
│ _evaluate_dialogue_outcome │
│ compare aggregate │
│ persuasion vs rarity │
│ threshold (weak/avg/strong)│
└──┬───────────────────┬──────┘
│ pass │ fail (after retries)
▼ ▼
┌─────────────────┐ ┌──────────────────────┐
│ STATE 6: SPAWN │ │ STATE 6': ESCAPE POD │
│ - create Player │ │ auto_approve_escape │
│ - create Ship │ │ _pod (always passes) │
│ - set credits │ │ - 500 credits │
│ per table │ │ - negotiation=weak │
│ - sector=1 │ └──────────┬───────────┘
│ - mark first │ │
│ _login.compl. │ │
└────────┬────────┘ │
│ │
└───────────┬───────────┘
▼
┌──────────────────────────────┐
│ STATE 7: COMPLETE │
│ complete_first_login() │
│ - PlayerFirstLoginState │
│ persisted │
│ - finalize Ship + Player │
│ - emit notification │
└──────────────────────────────┘
Ship-claim difficulty matrix¶
DEFAULT_SHIP_CONFIGS from the service:
| Ship | Tier | Spawn % | Base credits | Weak / Avg / Strong threshold |
|---|---|---|---|---|
| Escape Pod | 1 | 100 | 1,000 | 0.30 / 0.30 / 0.30 |
| Light Freighter | 2 | 50 | 2,500 | 0.40 / 0.35 / 0.30 |
| Scout Ship | 3 | 25 | 2,000 | 0.55 / 0.50 / 0.45 |
| Fast Courier | 3 | 20 | 3,000 | 0.55 / 0.50 / 0.45 |
| Cargo Hauler | 4 | 10 | 5,000 | 0.65 / 0.55 / 0.50 |
| Defender | 5 | 5 | 7,000 | 0.75 / 0.65 / 0.60 |
| Colony Ship | 6 | 3 | 10,000 | 0.80 / 0.70 / 0.65 |
| Carrier | 7 | 1 | 15,000 | 0.85 / 0.75 / 0.70 |
Threshold selected based on the AI's negotiation_skill classification (weak / average / strong) of the player's dialogue.
Persistence (resume)¶
FirstLoginSession.idis bound to theUser.- Each
DialogueExchangeis persisted as it happens; reload mid-flow returns the full history. - Re-opening the app at any state replays from the last persisted state.
Skip / abandon¶
- Authenticated player whose
PlayerFirstLoginState.completed = Trueis routed to the main game; the flow does not run. - A player who explicitly abandons calls
auto_approve_escape_pod— the safest outcome (escape pod + 500 credits +negotiation_skill = weak).
AI evaluation¶
The evaluator (_analyze_player_response) returns:
{
persuasiveness: float (0..1),
confidence_level: enum,
consistency: float (0..1),
negotiation_skill: weak | average | strong,
inconsistencies: [],
key_information: { player_name?, claimed_ship?, ... },
overall_believability: float (0..1)
}
The provider chain matches the ARIA dialogue chain: primary → secondary → manual rule-based provider. The manual provider replicates the same response shape using pattern-matching, so first login works fully offline.
Cat easter egg¶
If the player mentions the orange cat from the opening narrative, the manual provider awards a flat +0.15 persuasion bonus. The bonus is applied regardless of the active provider (the rule-based detector runs in parallel). The detection is stored on the session for medal / achievement use later.
Outputs / state changes¶
On success path:
- Player row created (sector_id=1, current_sector_id=1, default turns=1000, max_turns=1000, military_rank="Recruit", personal_reputation=0, aria_consciousness_level=1, aria_bonus_multiplier=1.0).
- Player.credits set to claimed-ship's base credits (1,000–15,000).
- Ship row created via ship_specifications_seeder and assigned as Player.current_ship.
- PlayerFirstLoginState.completed = True, with negotiation outcome / cat-boost flags.
- FirstLoginSession.outcome = success.
On escape-pod path:
- Player row created with Escape Pod ship, 500 credits, negotiation_skill = weak.
- All other defaults identical.
Always:
- DialogueExchange rows record the full conversation.
- ShipPresentationOptions records which ships were shown.
- notification event fires on completion.
Invariants¶
- A
Usercannot finish first login twice — oncePlayerFirstLoginState.completed = True, the flow refuses to start a new session. - The escape-pod outcome always succeeds — there is no failure mode that leaves the player without a ship.
- Every dialogue evaluation persists before a state advance — interruption does not lose history.
- Spawn (state 6) is a single transaction: Player, Ship, and FirstLoginState change together.
- AI provider failure never blocks the flow — manual fallback is always available.
Player.first_login.completed = Truemeans no side-effects are pending.- Initial sector is always Sector 1 of Terran Space (the canonical Earth-area starter region).
Failure modes¶
| Mode | Target handling |
|---|---|
| AI provider down | Manual fallback evaluates dialogue; flow proceeds normally. |
| Player closes tab mid-flow | State persisted at every transition; resume picks up where they left off. |
| Player insists on a ship they can't justify | After N attempts (default 3), flow auto-routes to escape pod with explicit notice. |
| Spawn transaction fails | Whole transaction rolls back; player can retry; session not marked complete. |
| Duplicate user (race) | Unique constraint on Player.user_id rejects the second insert; second request returns the first Player row. |
| Provider returns malformed analysis | Default to negotiation_skill = average and persuasiveness = 0.5; do not crash. |
| Ship spec missing in seeder | Fail spawn; surface clear admin-facing error; player retries with a different ship. |
| Locale unsupported | Default to English; cat-detection still works (English-only patterns + locale-specific aliases). |
Source map¶
| Concern | Path (target) |
|---|---|
| Orchestrator | services/gameserver/src/services/first_login_service.py |
| AI dialogue helpers | services/gameserver/src/services/ai_dialogue_service.py |
| Multi-provider AI | services/gameserver/src/services/ai_provider_service.py |
| Manual fallback | services/gameserver/src/services/enhanced_manual_provider.py |
| Guard personalities | services/gameserver/src/utils/guard_personalities.py |
| Ship specifications seeder | services/gameserver/src/core/ship_specifications_seeder.py |
| First-login models | services/gameserver/src/models/first_login.py |
| API routes | services/gameserver/src/api/routes/first_login.py |
| Admin tools | services/gameserver/src/api/routes/admin_first_login.py |
| Client component | services/player-client/src/pages/FirstLogin/* |
Related¶
- DATA_MODELS:
../DATA_MODELS/player.md,models/first_login.py. - FEATURES:
../FEATURES/gameplay/first-login.md. - SYSTEMS: aria-dialogue.md, turn-regeneration.md.
- REST API:
first_loginroutes auto-published at<api-host>/docs.