ADR 0115 — Persona-Routed Beta Walkthrough Scenarios
Status: Accepted
Date: 2026-06-26 UTC
Deciders: Kristerpher (operator) via #3855 design card
Scope: Raptor (beta_walkthrough.py), Antlers (WalkthroughScreenClient.tsx),
beta_walkthrough_progress, beta_walkthrough_paper_trades tables,
feature_flags.yaml
Context
The beta activation walkthrough serves all full-track testers a single frozen SPX Iron Condor scenario. The operator has defined three intent buckets that each require a different scenario and builder. Before implementation can start, eight design questions must be answered — flag topology, intent-bucket storage, builder strategy for Covered Call and Vertical Spread, route structure, fixture schema, and paper-trade table approach.
Decision
One new independent flag (FLAG_WALKTHROUGH_PERSONA_ROUTING) gates this
feature. It does not extend FLAG_BETA_WALKTHROUGH_SELF_SELECT. The two flags
compose via a matrix (see beta-walkthrough-persona-routing.md §3.Q1).
intent_bucket is stored as a dedicated nullable TEXT column on
beta_walkthrough_progress with a CHECK constraint
('income' | 'covered_call' | 'directional' | 'unknown'). It is not serialised
into persona_answers.
Route structure is uniform: /screen/2 and /screen/3 return scenario-specific
payloads driven by the stored intent_bucket. No new route segments.
Covered Call builder is a self-contained static guide in the walkthrough route
handler. It does not call strategy_templates_v2. The guide enforces otm_only
and min_dte inline.
Vertical Spread builder reuses /screen/simple/1 with a VS-specific payload
when intent_bucket = 'directional'. No new route. No new fixture file needed
(static inline data).
Paper trade table: the existing legs_json column stores all leg data for CC
and VS fills. A new strategy_type TEXT NOT NULL DEFAULT 'iron_condor' column tags
the shape. IC-specific named columns are left NULL for CC/VS fills; this is
documented and intentional.
Survey (screen 5) is uniform across all three paths. No branching. intent_bucket
is accessible via join to the progress row for offline analysis.
All resolutions are detailed in docs/architecture/beta-walkthrough-persona-routing.md.
Language choice rationale
This ADR does not introduce a new service. The feature extends an existing Raptor (Python / Flask) route file and an existing Antlers (TypeScript / Next.js) component. No language-tier classification is required.
Consequences
Positive
- Three distinct tester journeys from a single flag flip; no route proliferation.
intent_bucketcolumn is queryable for cohort analysis without JSON parsing.- No flag dependency on
strategy_templates_v2— walkthrough is self-contained. - VS path reuses existing, tested
screen/simple/1handler; minimal net-new code. - Single migration (Raptor 0049) adds two columns; easily rolled back.
Negative / risks
- IC-specific named columns (
short_call_strike, etc.) are NULL for CC/VS rows. Silent iflegs_jsonis not populated correctly. Mitigation:strategy_typemakes the shape explicit; implementing PR must validatelegs_jsonround-trip. - The directional path routes to
screen/simple/1, which is currently gated byFLAG_BETA_WALKTHROUGH_SELF_SELECT. If PERSONA_ROUTING is ON and SELF_SELECT is OFF, the directional path uses an inline guide block on/screen/2instead. Feature-developer must handle both sub-cases. - Content (CC fixture, door-screen copy, scenario_copy) is not yet authored (#3857). Implementation must not block; placeholder strings are acceptable in the first dispatch as long as #3857 closes before the first tester cohort.
Neutral
- Survey remains uniform; any intent-aware branching in the survey is deferred to a future card.
strategy_templates_v2remains independent; the CC walkthrough guide is a simplified subset of its compliance rules, not a call to it.
Alternatives considered
Alt A — extend persona_answers JSONB to store intent_bucket
Rejected: persona_answers is NULL for testers who chose the self-select track.
Reading a routing key from a JSON blob on every hot-path request is fragile and
slower than a column lookup. A dedicated column with a CHECK constraint is safer
and more explicit.
Alt B — scenario-prefixed routes (/screen/cc/2, /screen/ic/2)
Rejected: adds route proliferation and requires frontend URL-routing logic.
The frontend already renders whatever the API returns. Uniform route + payload
branching by intent_bucket is simpler and keeps the frontend dumb.
Alt C — route CC through strategy_templates_v2
Rejected: creates a flag dependency. If FLAG_STRATEGY_TEMPLATES_V2 is OFF
(which it is by default), the CC walkthrough path would silently break. The
walkthrough must be self-contained.
Alt D — new table per strategy type
Rejected: over-engineered for a pre-launch beta exercise. legs_json + strategy_type
on the existing table is sufficient.
Security / GDPR checklist
- PII collected:
intent_bucketis a three-value enum — not PII. Existing PII on the progress row (tester_email) is unchanged. - Retention period: 30-day post-program purge (same policy as all
beta_walkthrough_*data; no new retention concern). - Deletion on DSR:
intent_bucketandstrategy_typeare deleted as part of thebeta_walkthrough_progress/beta_walkthrough_paper_tradesrow purge. No additional DSR path required. - Audit trail:
updated_aton the progress row advances on every screen POST.intent_bucketandstrategy_typeare operator-readable in the table. Not a financial or permission state change; no separate audit log entry needed. - Stored credentials: none. Walkthrough token hashed (SHA-256) at rest. Raw token never stored.
- Breach notification path: inherits from Raptor's existing breach-notification procedure. No new PII surface.
- Secrets location + rotation: no secrets introduced by this feature. Existing
BETA_TOKEN_SECRETunchanged. - Kill-switch:
FLAG_WALKTHROUGH_PERSONA_ROUTINGOFF restores IC-only behaviour instantly without migration or rollback.
Revisit when
- When the beta program ends and
beta_walkthrough_*tables are purged; the scenarios can be archived or ported to onboarding. - When
strategy_templates_v2reaches GA; the CC walkthrough guide could then delegate to the live validator instead of running inline checks. - If a fourth intent bucket is needed; extend the CHECK constraint and add a registry entry.