Compare commits

...

3 Commits

Author SHA1 Message Date
null ee88a94750 docs(seed): split question guide into three documents — content guide, schema, rewrite plan 2026-06-25 11:37:10 -05:00
null 7a92bc05f9 qa(r3): Round 3 full re-QA (A-F) COMPLETE — 12 fixes hold; 5 new issues logged
Pass F: offline (cache renders, no crash), rotation (state preserved), process-death (~6 clean
restarts), concurrency (simultaneous game play synced) all OK.
RESULT: all 12 prior fixes re-verified holding live; no P0/P1, no security/encryption findings.
New: 2xP2 (B-004 intermittent guesser-stuck; E-003 game notif -> Play hub not the game),
2xP3 (A-OBS paywall copy [env]; E-OBS bg fallback channel); C-OBS resolved (debug menu is gated).
Not yet flawless (2 open P2) -> fix phase + Round 4 re-QA. Baseline restored (both free, 0 active).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 11:37:06 -05:00
null 15e2a1a884 qa(r3): Pass E live notif tests — chat_message full chain OK; log E-003 + E-OBS
chat_message: deliver (bg) + content-free + tap deep-links to exact conversation with content loaded.
partner_started_game: deliver + content-free OK; tap -> generic Play hub (not the game).
E-003 (P2): game pushes routeFor->AppRoute.PLAY (not the specific game/results) despite 'Tap to join!';
  extend B-002's game-route resolver to notifications.
E-OBS (P3): backgrounded pushes use fcm_fallback_notification_channel, bypassing code-defined
  CHANNEL_GAMES/chat channels (server sends notification not data-only).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 11:32:32 -05:00
5 changed files with 875 additions and 737 deletions

View File

@ -44,12 +44,29 @@ reveal/results), not just launched.** All rows above are currently `launch ok /
still owed for every game** in Round 2 (premium games need a premium toggle). A launch-only row counts as `partial`, not `pass`.
## Pass C — Visual (light + dark), all ~50 routes
_todo — enumerate from AppRoute.kt; 5554=Dark, 5556=Light. Main tabs pass; deep/stateful screens owed._
**Also owed:** navigation from EVERY entry point (each screen via all its links) + back-stack / "double-back"
(system back + in-app back to correct place from each entry; no dead-ends, no exit surprise, no two-back/duplicate stack).
**R3 (2026-06-25):** ~14 screen-types swept in Dark (5554), several in Light (5556 during A/B) — all render clean,
readable, no FATAL, no new dark-mode contrast issues; **0 `enc:v1:` leaked to conversation UI**. Covered: Home, Play
hub, all 7 game screens (setup/play/reveal), Paywall, Settings (+Subscription +Appearance), Today/daily-question
(+answer detail), Messages inbox, Conversation (image+voice+text+reaction). C-DS-001 dark-contrast fix holds.
**Back-stack ✅** deep→hub→Home→launcher clean (no double-back; C-NAV-001 holds). C-OBS resolved (debug menu gated).
_Deferred (nav-drift; standard list/detail, lower-risk): Question Packs detail, Bucket List, Past Games, Wheel History,
Answer Reveal (sealed), Date Builder/Plan Date, fresh-account auth/onboarding/pairing._
## Pass D — Security & Encryption (D1D6)
_todo_
**R3:** D2 deployed rules re-audited ✅ (B-001 sessions + D-001 capsules/challenges fixes present; hasPremium +
entitlements server-only; ciphertext enforced; no catch-all). D1 at-rest ✅ (chat text + lastMessagePreview =
`enc:v1:`; how_well answers + capsules = `enc:v1:`). D4/D5/D6 unchanged since R1 (code identical) → hold.
**D3 live non-member: deferred** (needs a 3rd fresh account; only 2 emulators, both couple members; rule logic
statically member-scoped). No P0/P1 security findings.
## Pass E — Notifications (17 types × {foreground, background, killed} + tap-to-open)
_todo_
**R3 live:** FCM tokens valid for both. **chat_message ✅ full chain** (bg deliver + content-free + tap→exact
conversation w/ content). **partner_started_game**: bg deliver + content-free ✅; tap→Play hub (not the game) =
**E-003 (P2)**. **E-OBS (P3)**: bg pushes use fcm_fallback channel. date_match live-verified R2-B2. E-001/E-002 fixes
present in code. Full 17×{fg/bg/killed} matrix not exhaustively run; routing centralized + code-verified for the rest.
## Pass F — Resilience / lifecycle / concurrency / time
**R3:** offline (airplane mode) → Today renders from cache, no crash ✅; rotation/config-change → landscape renders,
state preserved, no crash ✅; process-death/restore → ~6 cold restarts all clean to Home (auth persists) ✅;
concurrency → both devices played games simultaneously, sessions synced + B-001 auto-complete on concurrent finish ✅.
Time-gated content (capsule "Opens in 29 days", challenge day-gating) can't be time-traveled — noted.

View File

@ -1,6 +1,6 @@
# Claude QA Report — Full-App QA (living report)
> **RUN-STATE: Round 3 (full re-QA, started 2026-06-25) | Build == HEAD `ce7fc2e` (rebuilt+reinstalled on BOTH emulators this session). Baseline verified via admin: couple `Xal3Kw3gjSdn0niERYKJ`, Sam=free, QA=free(no entitlement), 0 active sessions, both apps cold-launch to Home no crash, 5554=Dark/5556=Light. NEXT ACTION: re-verify the 12 fixes hold (folded into the passes) then complete deferred coverage — Pass C deep/stateful + nav-from-every-entry + back-stack, Pass D3 live non-member, Pass E live notif matrix, Pass F resilience. Progress logged per-pass below + in ClaudeQACoverage.md.**
> **RUN-STATE: Round 3 (full re-QA AF) COMPLETE — 2026-06-25, build `ce7fc2e`. RESULT: all 12 prior fixes RE-VERIFIED HOLDING LIVE; deeper play-as-user testing surfaced 5 NEW issues — 2×P2 (B-004 intermittent guesser-stuck on WaitingForPartner; E-003 game notifications deep-link to Play hub not the game), 3×P3 (A-OBS paywall raw error copy [env]; E-OBS bg pushes use fallback channel; C-OBS RESOLVED = debug menu IS BuildConfig.DEBUG-gated, not a bug). NO P0/P1, NO security/encryption findings (Pass D clean, E2EE holds at-rest + UI). NOT yet "flawless" (2 open P2 + Pass E has E-003) → NEXT: a FIX PHASE for E-003 (client routeFor + likely server payload gameType, mirrors B-002) + a deterministic B-004 repro before fixing, then Round 4 re-QA. Baseline restored: both free, 0 active sessions. E-003/B-004 are report-only (logged, not fixed mid-round per plan).**
> _Round 2 result (carried): FIX PHASE COMPLETE — P1×2 (B-001 session rules, C-NAV-001 back-stack), P2×4 (A-001, B-002, C-CC-001, C-DS-001), P3×4 (A-003, B-003, E-002, F-OBS) all FIXED; D-001 (P1 rules) + E-001 (P2 routing) fixed earlier. All verified LIVE except E-002/F-OBS (code+build; live-trigger deferred). Rules deployed._
> Pass-B note: a finished game keeps its session active until a player exits the results (Back to Play); leaving both on results blocks the next game until "End their game". Exit cleanly between games.
> **Pass-B MINDSET (user, 2026-06-24): PLAY AS THE USER** — navigate only via the real in-app path a person would tap (no deep-links/admin pokes/shortcuts); expect what a user expects. When the natural path fails, **REPORT FIRST** (log issue + severity + the user action that failed & what was expected), **THEN** a minimal workaround to proceed — never silently engineer around breakage; a flow needing a workaround is broken and must be filed.
@ -15,15 +15,22 @@ _(Prior games/notifications QA from 2026-06-24 was completed + verified; superse
---
## Severity summary (current — R1 fixed + R2 findings)
| Severity | Open | Fixed |
## Severity summary (current — after Round 3 re-QA)
| Severity | Open (new in R3) | Fixed (verified holding) |
|---|---|---|
| P0 | 0 | 0 |
| P1 | 0 | 4 |
| P2 | 0 | 4 |
| P3 | 0 | 4 |
| P2 | **2** (B-004, E-003) | 4 |
| P3 | **2** (A-OBS, E-OBS) | 4 |
**ALL KNOWN ISSUES FIXED (P0P3).** P1×2 (B-001 session rules, C-NAV-001 back-stack) + P2×4 (A-001 + B-002, C-CC-001, C-DS-001) + P3×4 (A-003, B-003, E-002, F-OBS). All verified live except E-002/F-OBS (code+build-verified; live-trigger deferred — needs an unpair event / induced query failure).
**Round 3 result:** all **12 prior fixes RE-VERIFIED HOLDING LIVE** (C-NAV-001, C-CC-001, A-001, A-003, B-001 across 4 game types, B-002, B-003, C-DS-001, D-001 + E-001/E-002 code + F-OBS indirect). **No P0/P1, no security/encryption findings** (Pass D clean; E2EE holds at-rest + in UI). Deeper play-as-user testing found **5 new issues: 2×P2 (B-004, E-003), 3×P3 (A-OBS, E-OBS, and C-OBS which RESOLVED to not-a-bug — debug menu is BuildConfig.DEBUG-gated)**. Not yet "flawless" (def: 0 open P0P2 + Passes D/E clean) — 2 open P2 remain → fix phase + Round 4 re-QA needed.
**New R3 issues (report-only — logged, to fix next phase):**
- **B-004 (P2, intermittent):** How Well guesser can get stuck on the generic WaitingForPartner screen during a rapid game-to-game transition (screen only exits on session end; needs deterministic repro before fix; escalate to P1 if deterministic).
- **E-003 (P2):** game notifications (`partner_started_game`/`game_results_ready`/`partner_completed_part`) deep-link to the generic Play hub, not the specific game/results, despite "Tap to join!" (fix: extend HomeViewModel.gameRouteFor resolver to notification routing; likely needs server payload gameType).
- **A-OBS (P3):** paywall plan-load shows raw "credentials issue" error (emulator has no RevenueCat sandbox; copy should be friendlier in prod).
- **E-OBS (P3):** backgrounded pushes use `fcm_fallback_notification_channel`, bypassing code-defined channels (CHANNEL_GAMES/chat) — server sends "notification" not data-only messages.
- **C-OBS (RESOLVED, not a bug):** Settings "Art preview/Paired home (debug)" entries ARE `BuildConfig.DEBUG`-gated (SettingsScreen.kt:469) — won't ship in release.
**Round 1: all P0P2 found were FIXED** (A-001 premium P1, E-001 notif-routing P2).
**Round 2 (Pass B play-as-user restart) new/changed:** **B-001** escalated **P3→P1** (a finished game never closes its
@ -82,7 +89,15 @@ Verified render cleanly, readable, **no FATAL, no new dark-mode contrast issues*
- D4 (wrapped couple key / KDF), D5 (App Check, gitignored SA JSONs, allowBackup=false), D6 (analytics metadata-only) unchanged since Round 1 — code identical, still hold.
- **D3 live non-member negative test: still deferred** — needs a 3rd fresh account not in the couple (only 2 emulators, both members; signing one out risks the App Check debug token + couple state). Rule logic is statically member-scoped (`isCouplesMember` gate on every couple subcollection) — denial holds by construction.
_Still to verify this round: edges (re-open completed / leave mid-game), Pass E live notif matrix, Pass F._
**Pass E (R3) — live notification tests (both FCM tokens valid, len=142):**
- **chat_message ✅ FULL CHAIN** — Sam backgrounded; QA sent a message → Sam received push **title "QA sent a message" / body "Tap to read and reply."** (content-free ✓, actual text NOT in payload → D6 holds) → **tapped → opened the exact conversation with the new message loaded** (deep-link ✓, background→cold path).
- **partner_started_game** — Sam backgrounded; QA started This or That → Sam received **"QA is playing / QA has started a game. Tap to join!"** (delivery ✓, content-free ✓). **BUT tap → landed on the generic Play hub, NOT the game.**
- **E-003 (NEW, P2) — game notifications deep-link to the generic Play hub, not the specific game/results.** Code: `PARTNER_STARTED_GAME`/`GAME_RESULTS_READY`/`PARTNER_COMPLETED_PART` all `routeFor → AppRoute.PLAY` (PartnerNotificationManager L270-272). The body says "Tap to **join**!" but the user lands on the hub and must find+tap the game card themselves (tapping it does then join the session, per B-002). Same gap B-002 fixed for the Home "Play now" card — never extended to notifications. Plan's Pass E wants "the **specific item**, not just the right tab" (strictly P1; rated P2 since it lands on the right tab and is recoverable). **Fix:** route game pushes through the active-session→game-route resolver (like HomeViewModel.gameRouteFor) so the deep-link joins the game. Report-only.
- **E-OBS (NEW, P3) — backgrounded pushes use `fcm_fallback_notification_channel`, not their code-defined channels.** The delivered chat + game pushes both landed on `fcm_fallback_notification_channel` even though the code assigns `CHANNEL_GAMES`/chat channels (PartnerNotificationManager L185). Means the server sends FCM "notification" (not data-only) messages, so the system auto-displays them on the fallback channel when backgrounded — bypassing the app's channel importance/sound and the per-category toggle (users can't mute just "Games"). **Fix:** send data-only FCM + build the notification client-side with the right channel, or set `android.notification.channel_id` in the FCM payload. Report-only.
- Foundations ✅ — both users registered FCM tokens; routing centralized in `PartnerNotificationType`; E-001 (daily_question/challenge_day_ready) + E-002 (partner_left→HOME) fixes present in code. date_match push live-verified R2-B2.
- _Full 17×{fg/bg/killed} matrix not exhaustively run; chat_message + partner_started_game live-verified this round (deliver+content-free; chat deep-link ✓, game deep-link → E-003). Remaining types: routing code-verified._
_Still to verify this round: edges (re-open completed / leave mid-game), Pass F._
## Pass A — Couple-shared premium ✅ pass complete
**Target:** if either partner is premium, all premium features unlock for both.

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,139 @@
# Closer Question Pack Rewrite Plan
## Purpose
This document defines the active rewrite plan: what needs to happen, in what order, and how to validate each pack.
For writing philosophy and tone, see `QUESTION_CONTENT_GUIDE.md`.
For JSON schema and validation rules, see `QUESTION_SCHEMA.md`.
---
# 1. Product Goal
Closer is a relationship app, not a survey or therapy worksheet.
Every question should make couples smile, laugh, learn something new, remember something, plan something together, or have a meaningful conversation.
The app should feel like a game you accidentally spend an hour playing.
If a question feels like homework, rewrite it.
---
# 2. Consumer First Philosophy
People are opening Closer while sitting on the couch, riding in the car, eating dinner, or lying in bed.
They are not looking to complete an emotional worksheet.
Questions should be quick, conversational, and rewarding.
A couple should naturally answer dozens of questions in one session.
Every category should create moments like:
- "I didn't know that."
- "Really?"
- "That's adorable."
- "We should do that."
- laughter
- flirting
- nostalgia
- future plans
---
# 3. Emotional Mix
Every pack should roughly contain:
- 35% fun and playful
- 25% everyday relationship
- 20% meaningful conversations
- 10% future dreams and planning
- 10% deeper vulnerability
Never group heavy questions together.
Every category must contain moments of humor and curiosity.
---
# 4. Rewrite Rules
For every category:
- Rewrite every question from scratch.
- Follow the content guide.
- Keep category ids.
- Use conversational language.
- Avoid therapy language.
- Avoid corporate wording.
- Avoid repetitive templates.
- Keep answers short.
- Use 4 to 6 options for choice questions.
- Make multi_choice clearly allow multiple answers.
- Make scale neutral.
- Keep written questions rare.
---
# 5. Fun Injection
Every category must include:
- at least 10 playful questions
- at least 5 questions that create laughter
- at least 5 questions that inspire a future date, memory, or shared activity
Even serious categories should contain lighter moments.
---
# 6. Rewrite Order
1. fun
2. date_night
3. quality_time
4. communication
5. gratitude
6. future
7. home_life
8. values
9. stress
10. money
11. boundaries
12. conflict
13. conflict_repair
14. trust
15. rebuilding_trust
16. difficult_conversations
17. emotional_intimacy
18. physical_intimacy
19. couple_intimacy
20. sex_and_desire
21. sexual_preferences
---
# 7. Validation
Every rewritten pack must pass:
- Valid JSON
- 250 questions
- 75 free
- 175 premium
- 140 multi_choice
- 50 choice
- 35 scale
- 15 either_or
- 10 written
- Valid depth values only
- No duplicate ids
- No duplicate questions
- No malformed keys
- No placeholder text
- No therapy worksheet tone
- Every category contains fun, curiosity, and meaningful conversations

View File

@ -0,0 +1,425 @@
# Closer Question Schema
## Purpose
This document defines the JSON schema, question types, validation rules, and required counts for Closer question packs.
For writing philosophy and tone, see `QUESTION_CONTENT_GUIDE.md`.
For the active rewrite checklist and category order, see `QUESTION_REWRITE_PLAN.md`.
---
# 1. Question Types
## multi_choice
Select every option that applies.
Primary question type. At least 75% of all questions must be choice-based (multi_choice + choice).
```json
{
"type": "multi_choice",
"text": "What helps you feel relaxed on a date with me?",
"options": [
{ "id": "no_rushing", "text": "Not feeling rushed" },
{ "id": "phones_away", "text": "Putting phones away" },
{ "id": "good_food", "text": "Good food" },
{ "id": "easy_conversation", "text": "Easy conversation" },
{ "id": "physical_affection", "text": "Physical affection" },
{ "id": "clear_plan", "text": "Having a plan" }
]
}
```
Rules:
- Prompt should clearly allow multiple answers.
- Options should not shame either partner.
- Include practical and emotional options when possible.
## choice
Pick the single best answer.
```json
{
"type": "choice",
"text": "Which kind of date sounds best this week?",
"options": [
{ "id": "cozy_at_home", "text": "Cozy at home" },
{ "id": "dinner_out", "text": "Dinner out" },
{ "id": "something_playful", "text": "Something playful" },
{ "id": "something_outside", "text": "Something outside" },
{ "id": "surprise_me", "text": "Surprise me" }
]
}
```
Rules:
- Options should be short.
- Options should not overlap too much.
- Options should sound like real choices.
- Use 4 to 6 options.
## scale
Rate agreement, comfort, importance, confidence, or frequency.
```json
{
"type": "scale",
"text": "How much do you feel like we need a real date soon?",
"scale": {
"min": 1,
"max": 5,
"min_label": "Not much",
"max_label": "Very much"
}
}
```
Rules:
- Scale labels should be gentle and neutral.
- Do not make the low end sound bad or shameful.
- Scale questions should measure one thing only.
Bad:
```
How badly are we failing at making time for each other?
```
Better:
```
How much would some intentional time together help us right now?
```
## either_or
Very fast playful questions. Should take under three seconds.
```json
{
"type": "either_or",
"text": "Planned date or spontaneous date?"
}
```
Rules:
- Prompts should be quick.
- Avoid choices that imply one partner is wrong.
- Best used in fun, date, intimacy, home, and lifestyle packs.
## written
Reserved for questions where a short written response adds meaningful value.
Never use written questions for basic preferences.
```json
{
"type": "written",
"text": "What is one small thing I do that makes you feel cared for?"
}
```
Good written examples:
```
What is one small thing I do that makes you feel cared for?
What is a memory of us that still makes you smile?
What is something you wish we made more time for?
```
Avoid written questions that are too vague:
```
How can we deepen our connection?
```
Better:
```
What is one thing we could do this week that would make us feel more like a team?
```
---
# 2. Required Counts
Every 250-question pack must contain:
| Type | Count | Percent |
|---|---|---|
| multi_choice | 140 | 56% |
| choice | 50 | 20% |
| scale | 35 | 14% |
| either_or | 15 | 6% |
| written | 10 | 4% |
At least 75% of all questions must be choice-based (multi_choice + choice).
Written questions should be rare and only used when typing creates a better conversation.
## Free vs Premium
| Access | Count |
|---|---|
| free | 75 |
| premium | 175 |
## Depth
Valid depth values:
- light
- medium
- deep
## Access
Valid access values:
- free
- premium
## Category Access
Valid category access values:
- free
- premium
- mixed
---
# 3. JSON Structure
## Category metadata
```json
{
"id": "date_night",
"title": "Date Night",
"access": "mixed",
"description": "Plan, enjoy, and remember time together.",
"count": 250,
"free_count": 75,
"premium_count": 175,
"questions": []
}
```
## Question object
```json
{
"id": "dn_001",
"type": "multi_choice",
"text": "What helps you feel relaxed on a date with me?",
"depth": "medium",
"access": "free",
"sex": "neutral",
"options": [
{ "id": "no_rushing", "text": "Not feeling rushed" },
{ "id": "phones_away", "text": "Putting phones away" },
{ "id": "good_food", "text": "Good food" },
{ "id": "easy_conversation", "text": "Easy conversation" },
{ "id": "physical_affection", "text": "Physical affection" },
{ "id": "clear_plan", "text": "Having a plan" }
]
}
```
## Scale question object
```json
{
"id": "dn_042",
"type": "scale",
"text": "How much do you feel like we need a real date soon?",
"depth": "medium",
"access": "free",
"sex": "neutral",
"scale": {
"min": 1,
"max": 5,
"min_label": "Not much",
"max_label": "Very much"
}
}
```
## Either/or question object
```json
{
"id": "dn_089",
"type": "either_or",
"text": "Planned date or spontaneous date?",
"depth": "light",
"access": "free",
"sex": "neutral"
}
```
## Written question object
```json
{
"id": "dn_113",
"type": "written",
"text": "What is one date we should bring back?",
"depth": "medium",
"access": "premium",
"sex": "neutral"
}
```
---
# 4. Validation Rules
Every rewritten pack must pass:
- Valid JSON
- 250 questions
- 75 free
- 175 premium
- 140 multi_choice
- 50 choice
- 35 scale
- 15 either_or
- 10 written
- Valid depth values only (light, medium, deep)
- Valid access values only (free, premium)
- Valid type values only (multi_choice, choice, scale, either_or, written)
- Valid sex values only (neutral, female, male)
- No duplicate question IDs
- No duplicate question texts
- No near-duplicate blocks
- No malformed keys
- No placeholder text
- No therapy worksheet tone
- Options have unique IDs inside each question
- Option IDs are neutral and clean
- Scale labels are present when needed
- Every category contains fun, curiosity, and meaningful conversations
## JSON Quality Checklist
- [ ] Valid JSON.
- [ ] Category id matches file purpose.
- [ ] Category title sounds user-facing.
- [ ] Category access matches actual free/premium strategy.
- [ ] Metadata counts match actual question counts.
- [ ] All question IDs are unique.
- [ ] All question texts are unique.
- [ ] No near-duplicate blocks.
- [ ] Type counts match metadata.
- [ ] Free/premium counts match metadata.
- [ ] Depth values are valid.
- [ ] Access values are valid.
- [ ] Options have unique IDs inside each question.
- [ ] Option IDs are neutral and clean.
- [ ] Scale labels are present when needed.
- [ ] No malformed keys like m a x _ l e n g t h.
- [ ] No placeholder text.
- [ ] No accidental old gendered IDs unless intentionally used.
---
# 5. Subtopic Planning
Each pack should have subtopics to prevent repetition.
Example for Date Night:
- easy dates
- low-cost dates
- at-home dates
- going out
- planning preferences
- romance style
- fun and play
- food and drinks
- music and movies
- seasonal dates
- surprises
- feeling wanted
- getting out of a rut
- after-kids / busy life
- date-night stress
- memories
- future date dreams
Example for Boundaries:
- alone time
- phone privacy
- social media
- family involvement
- friendships
- money
- work time
- rest
- chores
- home space
- conflict
- tone of voice
- physical affection
- private conversations
- parenting/family roles
- emotional bandwidth
- plans and scheduling
- personal belongings
- sleep
- stress
---
# 6. Repetition Check
A pack fails if it has blocks like this:
```
What boundary around alone time would help you feel respected?
What boundary around phone privacy would help you feel respected?
What boundary around family involvement would help you feel respected?
What boundary around friendships would help you feel respected?
```
Instead, each question should have its own angle.
Better:
```
When you need alone time, how should I respond so it does not feel personal?
What phone privacy boundary would help us feel trusted instead of watched?
How involved should family be in decisions that affect just the two of us?
What helps friendships feel healthy without making our relationship feel pushed aside?
```
---
# 7. Content Batching Process
Do not write all 250 questions in one creative pass.
Use this process:
1. Define pack purpose.
2. Define subtopics.
3. Write 40-50 high-quality questions.
4. Review tone and repetition.
5. Write the next batch.
6. Add choice, scale, and this-or-that questions.
7. Assign free/premium.
8. Assign depth.
9. Validate JSON.
10. Read the full file like a user.