Compare commits
2 Commits
157f080ee6
...
d7d97cc8a8
| Author | SHA1 | Date |
|---|---|---|
|
|
d7d97cc8a8 | |
|
|
75a644e1ba |
|
|
@ -102,3 +102,4 @@ revenucat_secret_api.md
|
|||
# Local ship-readiness working plan — operator doc, not repo history
|
||||
release_guide.md
|
||||
seed/questions/closer_question_guides_150_max.zip
|
||||
seed/questions/rebuilding_trust_BATCH1_NOTE.md
|
||||
|
|
|
|||
12
Future.md
12
Future.md
|
|
@ -113,6 +113,18 @@ Still open:
|
|||
- **`scheduledOutcomesReminder` scaling cap** — the daily cron scans `couples` with a flat `.limit(200)` and no pagination (`functions/src/couples/scheduledOutcomesReminder.ts:35`), so couples beyond the first 200 silently never get 30/60/90-day outcome nudges. Fine pre-launch; **before the userbase approaches ~200 couples**, paginate with the same `orderBy(__name__)+startAfter` page loop `assignDailyQuestion` already uses (see `assignDailyQuestion.ts` PAGE_SIZE pattern).
|
||||
- **Compose stability: adopt `ImmutableList` for `*UiState` list/set fields (codebase-wide).** Strong-skipping (K2 default) is on, and the Home refactor added `@Immutable` to the Home card models — but `List<>`/`Set<>` params still only reference-skip. Add the `kotlinx.collections.immutable` dependency (JetBrains, first-party) and switch `*UiState` list/set fields to `ImmutableList`/`ImmutableSet` (with `.toImmutableList()` at each VM construction site) so repeated card/list params become structurally skippable. **Do it consistently across all ~20 screens in one pass — not per-screen — to avoid a snowflake.** `ui/home/HomeModels.kt` is already partly there (`@Immutable` on `HomeAction`/`PendingActionCard`/`HomeCategorySummary`), so Home is the natural reference implementation.
|
||||
|
||||
- **Test `HomeViewModel.loadHome` (the honest follow-up to the Home decomposition).** The R31 Home split made
|
||||
Home navigable, extracted its pure action logic into `HomeActionMapper` (12 JVM tests) and added a render
|
||||
smoke — but deliberately did **not** touch `loadHome`, the ~180-line Firestore orchestration (5 parallel
|
||||
retention fetches + streak/outcome calc + error handling) that remains the **highest-risk, still-untested**
|
||||
code on the screen. The render-smoke exercises the render path, not `loadHome`'s data assembly. Real next
|
||||
priority: a `HomeViewModel` test with fake repos covering the parallel-fetch success/partial-failure paths.
|
||||
Needs the repo interfaces faked (no Hilt/Firebase) — its own follow-up, not a Home-UI change.
|
||||
- **De-duplicate `dueFollowUpDay` across Home / Settings / YourProgress (3 copies).** The 30/60/90-day
|
||||
outcome-reminder day math is implemented three times (now `HomeActionMapper.dueFollowUpDay`,
|
||||
`SettingsViewModel`, `YourProgressViewModel`), all on the API-safe `Instant.atZone().toLocalDate()` path
|
||||
since R31. Lift to one shared helper so the three can't drift; low-risk once `loadHome` has a test around it.
|
||||
|
||||
|
||||
Improvement & feature ideas surfaced while QA-testing as a consumer (each works today — none are defects).
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# Daily Single Choice Weekday System v7
|
||||
# Daily Single Choice Weekday System v8 — Importer-Aligned
|
||||
|
||||
This document defines the Closer daily weekday question pack.
|
||||
|
||||
|
|
@ -6,26 +6,67 @@ This is a special pack. It is not a normal mixed category pack.
|
|||
|
||||
## Pack Identity
|
||||
|
||||
Recommended pack id:
|
||||
Logical pack id:
|
||||
|
||||
```text
|
||||
daily_single_choice_weekly_v1
|
||||
```
|
||||
|
||||
Recommended future file name:
|
||||
Production category id:
|
||||
|
||||
```text
|
||||
daily_single_choice_weekly_v1.json
|
||||
daily_fun_mc
|
||||
```
|
||||
|
||||
Current compatibility file name:
|
||||
Current compatibility filename:
|
||||
|
||||
```text
|
||||
daily_fun_multiple_choice_v3.json
|
||||
```
|
||||
|
||||
Use the old file name only while the app code still expects it. The content inside the file must still be single choice.
|
||||
Possible future filename:
|
||||
|
||||
```text
|
||||
daily_single_choice_weekly_v1.json
|
||||
```
|
||||
|
||||
The logical pack id and the production category id are not interchangeable.
|
||||
|
||||
- `category.id` must remain `daily_fun_mc` while the current app/database contract uses that category.
|
||||
- every Daily question must use `"category_id": "daily_fun_mc"`
|
||||
- store `"pack_id": "daily_single_choice_weekly_v1"` inside category metadata
|
||||
- the compatibility filename does not supply category identity
|
||||
- the production file must include the full `category` object and full `questions` array
|
||||
|
||||
When editing the existing production pack, preserve its current display name, description, access, and icon unless the product owner explicitly changes them.
|
||||
|
||||
## Required production wrapper
|
||||
|
||||
```json
|
||||
{
|
||||
"category": {
|
||||
"id": "daily_fun_mc",
|
||||
"display_name": "Daily Fun",
|
||||
"description": "One quick couples-game question for each day, including wildcard surprise days.",
|
||||
"access": "mixed",
|
||||
"icon_name": "calendar_today",
|
||||
"metadata": {
|
||||
"pack_id": "daily_single_choice_weekly_v1",
|
||||
"question_type_policy": "single_choice_only",
|
||||
"total_questions": 511,
|
||||
"free_questions": 86,
|
||||
"premium_questions": 425
|
||||
}
|
||||
},
|
||||
"questions": []
|
||||
}
|
||||
```
|
||||
|
||||
The example documents the required shape. In the live file, preserve the existing category display fields and icon unless they are intentionally changed.
|
||||
|
||||
A flat document with `id`, `title`, `count`, and `questions` but no `category` object is not importable as the Daily category.
|
||||
|
||||
## Required Counts
|
||||
## Required Counts
|
||||
|
||||
This special daily pack uses:
|
||||
|
|
@ -700,6 +741,29 @@ If the app code still uses older mode tags, include the compatibility tag too, b
|
|||
|
||||
## Patch Discipline: Fix Only What Fails
|
||||
|
||||
|
||||
### Production-file protection
|
||||
|
||||
Patch discipline does not change the production file shape.
|
||||
|
||||
A document containing only:
|
||||
|
||||
```text
|
||||
id
|
||||
expected_text
|
||||
reasons
|
||||
replacement text
|
||||
replacement options
|
||||
```
|
||||
|
||||
is a patch manifest, not the Daily pack.
|
||||
|
||||
Never overwrite `daily_fun_multiple_choice_v3.json` with that manifest.
|
||||
|
||||
Apply the patch to the complete 511-question source, retain the full category object, validate the complete result, and ship the complete production JSON.
|
||||
|
||||
|
||||
|
||||
Daily pack updates must use patch discipline.
|
||||
|
||||
The writer must review the full pack, mark the failing question IDs, and then fix only those marked IDs.
|
||||
|
|
@ -824,7 +888,11 @@ These sources informed the daily fun rules. Do not copy their question lists. Us
|
|||
|
||||
Before shipping:
|
||||
|
||||
1. Run schema and count validation against the totals in Required Counts (511 · 86 free · 425 premium once the wildcard add-on is in; 511 single_choice).
|
||||
1. Confirm the file contains the full category object and all 511 questions.
|
||||
2. Confirm `category.id` and every `category_id` are `daily_fun_mc`.
|
||||
3. Confirm every depth is integer `1`, `2`, or `3`.
|
||||
4. Confirm every option set is mirrored into `answer_config.options`.
|
||||
5. Run schema and count validation against the totals in Required Counts (511 · 86 free · 425 premium once the wildcard add-on is in; 511 single_choice).
|
||||
2. Run duplicate question checks.
|
||||
3. Run duplicate option-list checks.
|
||||
4. Check repeated openers and repeated option text.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,44 @@
|
|||
# Importer and Question-Guide Reconciliation
|
||||
|
||||
## Final authority
|
||||
|
||||
Production content must match the importer and Room model that ship today.
|
||||
|
||||
The guides now separate:
|
||||
|
||||
1. **Current production schema** — required for content JSON now.
|
||||
2. **Future schema direction** — an engineering migration, not an authoring choice.
|
||||
|
||||
## Correct current production rules
|
||||
|
||||
| Aspect | Production rule |
|
||||
|---|---|
|
||||
| depth | integer `1`, `2`, or `3` |
|
||||
| pack wrapper | `{ "category": {...}, "questions": [...] }` |
|
||||
| scale settings | `answer_config` |
|
||||
| written settings | `answer_config` |
|
||||
| this-or-that | exactly two explicit options, mirrored into `answer_config.options` |
|
||||
| Daily category id | `daily_fun_mc` |
|
||||
| Daily logical pack id | `daily_single_choice_weekly_v1` in metadata |
|
||||
| Daily production file | complete 511-question pack, never a patch manifest |
|
||||
|
||||
## Future string-depth migration
|
||||
|
||||
String depth is still documented as a possible future direction, but it is clearly marked **not production-ready**.
|
||||
|
||||
It becomes valid only after coordinated updates to the importer, Room schema/entity, database migration path, readers, routing, validation, tests, and existing content.
|
||||
|
||||
## Catalog-wide build gate
|
||||
|
||||
A pack can be perfect by itself and still fail the catalog.
|
||||
|
||||
Before rebuilding the database, validate all production files together for:
|
||||
|
||||
- duplicate IDs
|
||||
- duplicate exact or normalized question text
|
||||
- blocked near-duplicates
|
||||
- unknown categories
|
||||
- partial packs or patch manifests under production filenames
|
||||
- values that will be coerced or silently ignored
|
||||
|
||||
The known `Comfort first or solutions first?` collision must be resolved in one category before the catalog can pass.
|
||||
|
|
@ -16,6 +16,27 @@ Only a documented product-specific special pack may exceed 150 questions. The cu
|
|||
|
||||
Written questions must remain rare. A normal pack may use **0 to 5 written questions by default**. More than 5 requires a documented pack-specific reason and explicit product-owner approval. Never use written questions to fill a count.
|
||||
|
||||
## Importer and Production Contract Authority
|
||||
|
||||
The active app importer and Room database contract are authoritative for production JSON.
|
||||
|
||||
Human-friendly authoring labels, batch notes, patch manifests, and planning files may help writers work, but they must never replace or override the production schema.
|
||||
|
||||
Current production rules:
|
||||
|
||||
- a production pack must use the top-level shape `{ "category": {...}, "questions": [...] }`
|
||||
- the `category` object is required and must include `id`, `display_name`, `description`, `access`, and `icon_name`
|
||||
- every question must include `id`, `category_id`, `type`, `text`, integer `depth`, `access`, and `tags`
|
||||
- `category_id` must exactly match `category.id`
|
||||
- `depth` must be an integer: `1` = light, `2` = medium, `3` = deep
|
||||
- `tags` must be present as an array; use at least one meaningful tag unless a documented exception exists
|
||||
- `sex` is optional and nullable for ordinary packs; require it only when a product feature actually filters or routes by sex
|
||||
- question-type-specific fields must match `QUESTION_SCHEMA.md`
|
||||
- never rely on the filename to create or repair a missing category
|
||||
- a batch file, continuation note, validation report, or patch manifest is not a production question pack
|
||||
|
||||
When a writing document conflicts with the importer, stop and correct the writing document. Do not ship compatibility-breaking JSON and do not defer the mismatch to a future importer change.
|
||||
|
||||
---
|
||||
|
||||
# 1. The Closer Voice
|
||||
|
|
@ -145,6 +166,38 @@ SET PREMIUM_VALUE=Deeper romance, planning preferences, rut-breaking, and memory
|
|||
|
||||
Use depth intentionally.
|
||||
|
||||
The writing concepts are Light, Medium, and Deep, but the production JSON field is numeric:
|
||||
|
||||
| Writing concept | Production `depth` |
|
||||
|---|---:|
|
||||
| Light | `1` |
|
||||
| Medium | `2` |
|
||||
| Deep | `3` |
|
||||
|
||||
Never write `"light"`, `"medium"`, or `"deep"` into a production question object. Those labels may appear only in planning notes, coverage maps, or human-readable review documents.
|
||||
|
||||
## Future depth migration — not production-ready
|
||||
|
||||
String depth values such as `"light"`, `"medium"`, and `"deep"` are a possible future schema direction.
|
||||
|
||||
They are **not valid production values today**.
|
||||
|
||||
Do not use string depth until one coordinated engineering change updates and verifies all of the following:
|
||||
|
||||
```text
|
||||
build_db.py
|
||||
Room entity and column type
|
||||
database migration or asset rebuild path
|
||||
all getInt()/integer readers
|
||||
depth routing and help text
|
||||
seed validation
|
||||
automated tests
|
||||
existing production JSON
|
||||
```
|
||||
|
||||
Until that migration is merged and deployed, production JSON must use integers `1`, `2`, or `3`.
|
||||
|
||||
|
||||
## Light
|
||||
|
||||
Light questions should be easy, fun, and low-pressure.
|
||||
|
|
@ -194,23 +247,42 @@ ECHO Sensitive questions should never pressure a partner to disclose, forgive, e
|
|||
|
||||
# 6. Question Types
|
||||
|
||||
Each question type should have a purpose.
|
||||
Every production example in this section matches the importer contract used today.
|
||||
|
||||
`sex` is omitted because it is optional for ordinary packs. Add it only when a documented feature needs it.
|
||||
|
||||
## written
|
||||
|
||||
Use for stories, memories, reflection, emotional honesty, and open-ended answers.
|
||||
Use written questions only when a choice-based format would lose the story, nuance, or personal explanation.
|
||||
|
||||
Written questions are the exception, not the default. Use them only when a choice-based format would lose the story, nuance, or personal explanation. A normal pack should contain 0 to 5 written questions unless a documented exception is approved.
|
||||
A normal pack should contain 0 to 5 written questions unless a documented exception is approved.
|
||||
|
||||
Good written examples:
|
||||
|
||||
```text
|
||||
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?
|
||||
```json
|
||||
{
|
||||
"id": "emotional_intimacy_001",
|
||||
"category_id": "emotional_intimacy",
|
||||
"type": "written",
|
||||
"text": "What is one recent moment when you felt especially close to me?",
|
||||
"depth": 2,
|
||||
"access": "free",
|
||||
"tags": ["recent_memory", "closeness", "emotional_intimacy"],
|
||||
"answer_config": {
|
||||
"max_length": 500
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Avoid written questions that are too vague:
|
||||
Rules:
|
||||
|
||||
```bat
|
||||
ECHO Use answer_config for written settings.
|
||||
ECHO max_length is required when a custom limit matters.
|
||||
ECHO Do not use a top-level answer object.
|
||||
ECHO Do not use written questions for basic preferences.
|
||||
ECHO Do not use written questions to fill the pack count.
|
||||
```
|
||||
|
||||
Avoid:
|
||||
|
||||
```text
|
||||
How can we deepen our connection?
|
||||
|
|
@ -226,31 +298,42 @@ What is one thing we could do this week that would make us feel more like a team
|
|||
|
||||
## single_choice
|
||||
|
||||
Use for preferences and simple decisions.
|
||||
|
||||
Good single-choice example:
|
||||
Use when the player should choose one best answer.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "date_night_001",
|
||||
"category_id": "date_night",
|
||||
"type": "single_choice",
|
||||
"text": "Which kind of date sounds best this week?",
|
||||
"depth": 1,
|
||||
"access": "free",
|
||||
"tags": ["date_preference", "planning", "date_night"],
|
||||
"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" }
|
||||
]
|
||||
{ "id": "something_outside", "text": "Something outside" }
|
||||
],
|
||||
"answer_config": {
|
||||
"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" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
```bat
|
||||
ECHO Options should be short.
|
||||
ECHO Options should not overlap too much.
|
||||
ECHO Options should sound like real choices.
|
||||
ECHO Avoid more than 6 options unless truly needed.
|
||||
ECHO Use 4 to 6 options by default.
|
||||
ECHO Options should be short and natural.
|
||||
ECHO Every option must directly answer the prompt.
|
||||
ECHO Options should be similar in effort and emotional weight.
|
||||
ECHO Mirror top-level options into answer_config.options exactly.
|
||||
```
|
||||
|
||||
---
|
||||
|
|
@ -259,48 +342,32 @@ ECHO Avoid more than 6 options unless truly needed.
|
|||
|
||||
Use when more than one answer can be true.
|
||||
|
||||
Good multi-choice example:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "date_night_002",
|
||||
"category_id": "date_night",
|
||||
"type": "multi_choice",
|
||||
"text": "What helps you feel relaxed on a date with me?",
|
||||
"depth": 1,
|
||||
"access": "free",
|
||||
"tags": ["comfort", "date_preferences", "date_night"],
|
||||
"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:
|
||||
|
||||
```bat
|
||||
ECHO Multi-choice prompts should say or imply that multiple answers are okay.
|
||||
ECHO Options should not shame either partner.
|
||||
ECHO Include practical and emotional options when possible.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## scale
|
||||
|
||||
Use for comfort level, closeness, energy, readiness, satisfaction, or frequency.
|
||||
|
||||
Good scale example:
|
||||
|
||||
```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"
|
||||
{ "id": "physical_affection", "text": "Physical affection" }
|
||||
],
|
||||
"answer_config": {
|
||||
"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" }
|
||||
],
|
||||
"min_selections": 1,
|
||||
"max_selections": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
|
@ -308,8 +375,43 @@ Good scale example:
|
|||
Rules:
|
||||
|
||||
```bat
|
||||
ECHO The prompt should clearly allow multiple answers.
|
||||
ECHO Options should not shame either partner.
|
||||
ECHO Options should not overlap excessively.
|
||||
ECHO Mirror top-level options into answer_config.options exactly.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## scale
|
||||
|
||||
Use for one measurable dimension such as comfort, closeness, energy, readiness, satisfaction, confidence, importance, or frequency.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "date_night_003",
|
||||
"category_id": "date_night",
|
||||
"type": "scale",
|
||||
"text": "How much would a real date help us feel connected right now?",
|
||||
"depth": 2,
|
||||
"access": "free",
|
||||
"tags": ["connection", "readiness", "date_night"],
|
||||
"answer_config": {
|
||||
"min": 1,
|
||||
"max": 5,
|
||||
"min_label": "Not much",
|
||||
"max_label": "A lot"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
```bat
|
||||
ECHO Put scale settings inside answer_config.
|
||||
ECHO Do not use a top-level scale object.
|
||||
ECHO min, max, min_label, and max_label are required for custom labels.
|
||||
ECHO Scale labels should be gentle and neutral.
|
||||
ECHO Do not make the low end sound bad or shameful.
|
||||
ECHO Scale questions should measure one thing only.
|
||||
```
|
||||
|
||||
|
|
@ -329,27 +431,42 @@ How much would some intentional time together help us right now?
|
|||
|
||||
## this_or_that
|
||||
|
||||
Use for fast, playful, low-pressure choices.
|
||||
Use for a fast two-option choice.
|
||||
|
||||
Good this-or-that examples:
|
||||
|
||||
```text
|
||||
Planned date or spontaneous date?
|
||||
Dress up or stay cozy?
|
||||
Cook together or order in?
|
||||
Sunset walk or late-night drive?
|
||||
```json
|
||||
{
|
||||
"id": "date_night_004",
|
||||
"category_id": "date_night",
|
||||
"type": "this_or_that",
|
||||
"text": "Planned date or spontaneous date?",
|
||||
"depth": 1,
|
||||
"access": "free",
|
||||
"tags": ["quick_choice", "date_style", "date_night"],
|
||||
"options": [
|
||||
{ "id": "planned_date", "text": "Planned date" },
|
||||
{ "id": "spontaneous_date", "text": "Spontaneous date" }
|
||||
],
|
||||
"answer_config": {
|
||||
"options": [
|
||||
{ "id": "planned_date", "text": "Planned date" },
|
||||
{ "id": "spontaneous_date", "text": "Spontaneous date" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
```bat
|
||||
ECHO This-or-that prompts should be quick.
|
||||
ECHO Avoid choices that imply one partner is wrong.
|
||||
ECHO Best used in fun, date, intimacy, home, and lifestyle packs.
|
||||
ECHO Supply exactly two options.
|
||||
ECHO Do not ship a bare this_or_that prompt with no options.
|
||||
ECHO Mirror top-level options into answer_config.options exactly.
|
||||
ECHO Keep both choices balanced and easy to understand.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
|
||||
# 7. Free vs Premium Strategy
|
||||
|
||||
Free questions should prove the app is worth using.
|
||||
|
|
@ -698,39 +815,180 @@ What tends to make date night harder for you to enjoy?
|
|||
|
||||
---
|
||||
|
||||
# 12. JSON Quality Rules
|
||||
# 12. Production JSON Contract and Quality Rules
|
||||
|
||||
Every JSON file should pass these checks.
|
||||
A JSON file is production-ready only when it is both valid content and a complete importable pack.
|
||||
|
||||
## 12.1 Required top-level shape
|
||||
|
||||
Every production pack must use:
|
||||
|
||||
```json
|
||||
{
|
||||
"category": {
|
||||
"id": "example_category",
|
||||
"display_name": "Example Category",
|
||||
"description": "User-facing description.",
|
||||
"access": "mixed",
|
||||
"icon_name": "favorite"
|
||||
},
|
||||
"questions": []
|
||||
}
|
||||
```
|
||||
|
||||
Required category fields:
|
||||
|
||||
```text
|
||||
id
|
||||
display_name
|
||||
description
|
||||
access
|
||||
icon_name
|
||||
```
|
||||
|
||||
Optional category fields such as `schema_version` and `metadata` may be included, but they do not replace the required fields.
|
||||
|
||||
Do not use the old simple-pack shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "example",
|
||||
"title": "Example",
|
||||
"count": 25,
|
||||
"questions": []
|
||||
}
|
||||
```
|
||||
|
||||
Do not rely on filename parsing to create a category. A missing `category` object is a hard production failure.
|
||||
|
||||
## 12.2 Required question fields
|
||||
|
||||
Every production question must include:
|
||||
|
||||
```text
|
||||
id
|
||||
category_id
|
||||
type
|
||||
text
|
||||
depth
|
||||
access
|
||||
tags
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
- `category_id` must exactly equal the enclosing `category.id`
|
||||
- `depth` must be the integer `1`, `2`, or `3`
|
||||
- `tags` must be an array and should contain at least one meaningful tag
|
||||
- `access` must be `free` or `premium`
|
||||
- `sex` is optional and nullable for ordinary category packs
|
||||
- `sex` becomes required only for a documented feature that routes or filters by it
|
||||
- choice options and `answer_config` must follow `QUESTION_SCHEMA.md`
|
||||
- top-level `options` and `answer_config.options` must match when both are required by the active schema
|
||||
|
||||
|
||||
## 12.2A Type-specific importer contract
|
||||
|
||||
The active importer reads:
|
||||
|
||||
| Type | Production source |
|
||||
|---|---|
|
||||
| `single_choice` | top-level `options`; mirror into `answer_config.options` |
|
||||
| `multi_choice` | top-level `options`; mirror into `answer_config.options` |
|
||||
| `this_or_that` | exactly two top-level `options`; mirror into `answer_config.options` |
|
||||
| `scale` | `answer_config.min`, `max`, `min_label`, `max_label` |
|
||||
| `written` | `answer_config`, including `max_length` when customized |
|
||||
|
||||
Do not place scale settings in a top-level `scale` object.
|
||||
|
||||
Do not place written settings in a top-level `answer` object.
|
||||
|
||||
Do not ship `this_or_that` without two explicit choices.
|
||||
|
||||
|
||||
## 12.3 Production pack versus work artifact
|
||||
|
||||
These are not production packs:
|
||||
|
||||
```text
|
||||
25-question batch files
|
||||
continuation notes
|
||||
coverage maps
|
||||
validation reports
|
||||
marked-fix lists
|
||||
patch manifests
|
||||
apply scripts
|
||||
review summaries
|
||||
```
|
||||
|
||||
A work artifact must never overwrite a production filename.
|
||||
|
||||
A partial batch may be saved only outside importer-scanned production directories and must be clearly labeled as work in progress. The last complete validated production pack stays in place until the full replacement is ready.
|
||||
|
||||
A patch manifest must:
|
||||
|
||||
- use a clearly non-production name such as `*.patch.json`
|
||||
- live outside any importer-scanned production directory
|
||||
- identify the complete source pack it applies to
|
||||
- be applied to the full source pack
|
||||
- produce a complete validated production JSON before shipping
|
||||
|
||||
A patch manifest is never itself renamed to the production filename.
|
||||
|
||||
## 12.4 Daily pack protection
|
||||
|
||||
`daily_fun_multiple_choice_v3.json` is the compatibility filename for the Daily Single-Choice Weekday System.
|
||||
|
||||
That production file must always contain the complete documented pack:
|
||||
|
||||
```text
|
||||
500 weekday questions
|
||||
11 wildcard questions
|
||||
511 total questions
|
||||
86 free
|
||||
425 premium
|
||||
511 single_choice
|
||||
```
|
||||
|
||||
A daily patch document containing only changed IDs is not the daily pack. Restore or retain the complete 511-question source, apply the patch to that source, then validate and ship the complete result.
|
||||
|
||||
## 12.5 Per-file hard checks
|
||||
|
||||
```bat
|
||||
ECHO [ ] Valid JSON.
|
||||
ECHO [ ] Category id matches file purpose.
|
||||
ECHO [ ] Category title sounds user-facing.
|
||||
ECHO [ ] Category access matches actual free/premium strategy.
|
||||
ECHO [ ] Top-level category object exists.
|
||||
ECHO [ ] Top-level questions array exists.
|
||||
ECHO [ ] Category contains id, display_name, description, access, and icon_name.
|
||||
ECHO [ ] Category id matches the file purpose.
|
||||
ECHO [ ] Category title and description are user-facing.
|
||||
ECHO [ ] Metadata counts match actual question counts.
|
||||
ECHO [ ] All question IDs are unique.
|
||||
ECHO [ ] All question texts are unique.
|
||||
ECHO [ ] No near-duplicate blocks.
|
||||
ECHO [ ] Every question has category_id matching category.id.
|
||||
ECHO [ ] Every question has integer depth 1, 2, or 3.
|
||||
ECHO [ ] Every question has an access value of free or premium.
|
||||
ECHO [ ] Every question has a tags array.
|
||||
ECHO [ ] All question IDs are unique inside the file.
|
||||
ECHO [ ] All question texts are unique inside the file.
|
||||
ECHO [ ] No near-duplicate blocks remain.
|
||||
ECHO [ ] Type counts match metadata.
|
||||
ECHO [ ] Free/premium counts match metadata.
|
||||
ECHO [ ] Depth values are valid.
|
||||
ECHO [ ] Access values are valid.
|
||||
ECHO [ ] Options have unique IDs inside each question.
|
||||
ECHO [ ] Option IDs are neutral and clean.
|
||||
ECHO [ ] Choice options mirror answer_config where required.
|
||||
ECHO [ ] Scale labels are present when needed.
|
||||
ECHO [ ] No malformed keys like m a x _ l e n g t h.
|
||||
ECHO [ ] Written answer limits are present when needed.
|
||||
ECHO [ ] No malformed keys.
|
||||
ECHO [ ] No placeholder text.
|
||||
ECHO [ ] No accidental old gendered IDs unless intentionally used.
|
||||
ECHO [ ] The file is a complete pack, not a batch or patch artifact.
|
||||
```
|
||||
|
||||
Recommended access values:
|
||||
Valid access values:
|
||||
|
||||
```text
|
||||
free
|
||||
premium
|
||||
```
|
||||
|
||||
Recommended category access values:
|
||||
Valid category access values:
|
||||
|
||||
```text
|
||||
free
|
||||
|
|
@ -738,15 +996,15 @@ premium
|
|||
mixed
|
||||
```
|
||||
|
||||
Recommended depth values:
|
||||
Valid production depth values:
|
||||
|
||||
```text
|
||||
light
|
||||
medium
|
||||
deep
|
||||
1
|
||||
2
|
||||
3
|
||||
```
|
||||
|
||||
Recommended question types:
|
||||
Valid question types:
|
||||
|
||||
```text
|
||||
written
|
||||
|
|
@ -756,7 +1014,41 @@ scale
|
|||
this_or_that
|
||||
```
|
||||
|
||||
---
|
||||
## 12.6 Catalog-wide hard gate
|
||||
|
||||
Per-file validation is not enough.
|
||||
|
||||
Before rebuilding `app.db` or shipping question content, validate every production pack together.
|
||||
|
||||
The full catalog must have:
|
||||
|
||||
- globally unique question IDs
|
||||
- globally unique exact question text
|
||||
- no normalized duplicate question text after case and whitespace normalization
|
||||
- no blocked near-duplicates across categories
|
||||
- no production filename containing a patch or partial batch
|
||||
- no category resolving to `unknown`
|
||||
- no schema mismatch that would coerce or zero a field
|
||||
|
||||
A duplicate in two different category files is still a hard failure. The importer may abort the entire build, not merely skip the duplicate question.
|
||||
|
||||
When the same idea belongs in two packs, rewrite each prompt around its category-specific purpose rather than copying the same text.
|
||||
|
||||
## 12.7 Catalog impact reporting
|
||||
|
||||
When replacing or trimming an existing pack, report:
|
||||
|
||||
```text
|
||||
old question count
|
||||
new question count
|
||||
net catalog change
|
||||
old free/premium counts
|
||||
new free/premium counts
|
||||
whether the change is intentional and approved
|
||||
```
|
||||
|
||||
A planned reduction from a legacy 250-question pack to a stronger 150-question pack is not a schema defect, but the live catalog shrink must be visible in the completion report.
|
||||
|
||||
|
||||
# 13. Pack Structure Recommendation
|
||||
|
||||
|
|
@ -792,6 +1084,23 @@ Question packs must not be written in one uninterrupted bulk-generation pass.
|
|||
|
||||
A normal category pack may contain up to 150 questions. Only a documented special product pack may exceed that limit. Regardless of final size, question creation must be divided into controlled batches so quality, continuity, counts, and repetition can be reviewed throughout the work.
|
||||
|
||||
## 14.0 Production Replacement Rule
|
||||
|
||||
Batching controls the writing process; it does not redefine the production file.
|
||||
|
||||
During a multi-batch rewrite:
|
||||
|
||||
1. Keep the last complete production pack intact.
|
||||
2. Draft and review each batch in a clearly labeled work artifact or controlled workspace.
|
||||
3. Never overwrite the production filename with only the completed batches so far.
|
||||
4. Never change the production file to a simple batch schema.
|
||||
5. Merge reviewed batches into the complete `{ "category": ..., "questions": [...] }` pack.
|
||||
6. Run per-file validation.
|
||||
7. Run the catalog-wide duplicate and import validation.
|
||||
8. Replace the production file only with the complete validated result.
|
||||
|
||||
A continuation note may say `Next ID: ...`; that does not make the partial JSON shippable.
|
||||
|
||||
## 14.1 Batch Size
|
||||
|
||||
Write exactly 25 new questions per batch.
|
||||
|
|
@ -1104,40 +1413,51 @@ Even an approved mass rewrite must still use 25-question batches.
|
|||
After all planned questions have been written or corrected:
|
||||
|
||||
1. Validate the complete JSON file.
|
||||
2. Confirm category metadata.
|
||||
3. Confirm all IDs.
|
||||
4. Confirm all IDs are unique.
|
||||
5. Confirm all question texts are unique.
|
||||
6. Confirm total question count.
|
||||
7. Confirm question-type distribution.
|
||||
8. Confirm free and premium distribution.
|
||||
9. Confirm depth distribution.
|
||||
10. Confirm targeting fields and tags.
|
||||
11. Run exact duplicate checks.
|
||||
12. Run near-duplicate checks.
|
||||
13. Run repeated-stem and repeated-option checks.
|
||||
14. Review subtopic coverage.
|
||||
15. Review emotional-depth spacing.
|
||||
16. Review mechanic distribution.
|
||||
17. Review free-to-premium progression.
|
||||
18. Read a representative sample from every section aloud.
|
||||
19. Mark every failure by ID and reason.
|
||||
20. Patch only failed IDs.
|
||||
21. Re-run every affected validation and review check.
|
||||
2. Confirm the top-level shape is `{ "category": ..., "questions": [...] }`.
|
||||
3. Confirm the category object contains every required field.
|
||||
4. Confirm every question has `category_id` matching `category.id`.
|
||||
5. Confirm every question uses integer depth `1`, `2`, or `3`.
|
||||
6. Confirm every question has a tags array.
|
||||
7. Confirm all IDs are unique inside the file.
|
||||
8. Confirm all question texts are unique inside the file.
|
||||
9. Confirm total question count.
|
||||
10. Confirm question-type distribution.
|
||||
11. Confirm free and premium distribution.
|
||||
12. Confirm targeting fields.
|
||||
13. Run exact duplicate checks inside the file.
|
||||
14. Run near-duplicate checks inside the file.
|
||||
15. Run repeated-stem and repeated-option checks.
|
||||
16. Review subtopic coverage.
|
||||
17. Review emotional-depth spacing.
|
||||
18. Review mechanic distribution.
|
||||
19. Review free-to-premium progression.
|
||||
20. Read a representative sample from every section aloud.
|
||||
21. Mark every failure by ID and reason.
|
||||
22. Patch only failed IDs.
|
||||
23. Re-run every affected validation and review check.
|
||||
24. Run the full-catalog global ID and question-text duplicate gate.
|
||||
25. Confirm no production filename contains a patch manifest or partial batch.
|
||||
26. Report the old count, new count, and net catalog change.
|
||||
|
||||
The pack may ship only when:
|
||||
|
||||
```bat
|
||||
ECHO The JSON parses.
|
||||
ECHO The production top-level shape is category plus questions.
|
||||
ECHO Required category fields exist.
|
||||
ECHO Every category_id matches category.id.
|
||||
ECHO Metadata counts match actual counts.
|
||||
ECHO IDs are unique.
|
||||
ECHO Question texts are unique.
|
||||
ECHO IDs are unique inside the pack and across the catalog.
|
||||
ECHO Question texts are unique inside the pack and across the catalog.
|
||||
ECHO Free/premium counts match.
|
||||
ECHO Type counts match.
|
||||
ECHO Depth values are valid.
|
||||
ECHO Depth values are integers 1, 2, or 3.
|
||||
ECHO Tags exist on every question.
|
||||
ECHO Required targeting fields are valid.
|
||||
ECHO The file is a complete pack, not a patch or partial batch.
|
||||
ECHO No unresolved hard failures remain.
|
||||
ECHO No unacceptable repetition remains.
|
||||
ECHO The full catalog import gate passes.
|
||||
ECHO The final sample sounds natural and enjoyable aloud.
|
||||
ECHO The tone guide was followed.
|
||||
```
|
||||
|
|
@ -1268,15 +1588,22 @@ When Claude rewrites a pack:
|
|||
|
||||
```bat
|
||||
ECHO Read the existing JSON first.
|
||||
ECHO Preserve required schema unless told otherwise.
|
||||
ECHO Preserve required counts unless told otherwise.
|
||||
ECHO Treat the active importer and Room contract as schema authority.
|
||||
ECHO Preserve the production shape: category object plus questions array.
|
||||
ECHO Use integer depth 1, 2, or 3.
|
||||
ECHO Add category_id and tags to every production question.
|
||||
ECHO Keep sex optional unless the pack-specific feature requires it.
|
||||
ECHO Preserve required counts unless an approved rewrite changes them.
|
||||
ECHO Never overwrite a production filename with a partial batch.
|
||||
ECHO Never overwrite a production filename with a patch manifest.
|
||||
ECHO Apply patches to the complete source pack and return the complete JSON.
|
||||
ECHO Improve wording, variety, and product feel.
|
||||
ECHO Do not create repetitive template blocks.
|
||||
ECHO Do not use weird phrasing.
|
||||
ECHO Do not overuse therapy language.
|
||||
ECHO Do not make sensitive prompts coercive.
|
||||
ECHO Validate the final JSON.
|
||||
ECHO Report count totals.
|
||||
ECHO Validate the final JSON per file and across the full catalog.
|
||||
ECHO Report old count, new count, and net catalog change.
|
||||
ECHO Report any schema changes.
|
||||
```
|
||||
|
||||
|
|
@ -1284,11 +1611,16 @@ Claude should not say a file is done unless:
|
|||
|
||||
```bat
|
||||
ECHO The JSON parses.
|
||||
ECHO The file is a complete production pack.
|
||||
ECHO The category object and questions array exist.
|
||||
ECHO Required category and question fields exist.
|
||||
ECHO The counts match.
|
||||
ECHO IDs are unique.
|
||||
ECHO Question texts are unique.
|
||||
ECHO Depth values are integers 1, 2, or 3.
|
||||
ECHO IDs are unique inside the pack and across the catalog.
|
||||
ECHO Question texts are unique inside the pack and across the catalog.
|
||||
ECHO Free/premium counts match.
|
||||
ECHO Type counts match.
|
||||
ECHO The catalog import gate passes.
|
||||
ECHO The tone guide was followed.
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,116 @@
|
|||
# Closer Question Guide Correction Summary
|
||||
|
||||
## Why this update was required
|
||||
|
||||
The writing documents and the active importer disagreed about production depth and file shape.
|
||||
|
||||
The old guide allowed human-readable depth strings such as `light`, `medium`, and `deep`. The active Room-backed importer expects an integer field, so string depth can be read as `0`.
|
||||
|
||||
The old workflow also did not clearly forbid a partial batch or patch manifest from replacing a production JSON filename.
|
||||
|
||||
## Correct production authority
|
||||
|
||||
Production packs now require:
|
||||
|
||||
```text
|
||||
top-level category object
|
||||
top-level questions array
|
||||
category.id
|
||||
category.display_name
|
||||
category.description
|
||||
category.access
|
||||
category.icon_name
|
||||
question.category_id
|
||||
question.type
|
||||
question.text
|
||||
question.depth as integer 1/2/3
|
||||
question.access
|
||||
question.tags array
|
||||
```
|
||||
|
||||
`sex` remains optional and nullable unless a documented feature specifically reads it.
|
||||
|
||||
## Work artifacts
|
||||
|
||||
The corrected guides explicitly classify these as non-production:
|
||||
|
||||
```text
|
||||
partial batches
|
||||
simple-pack batch JSON
|
||||
patch manifests
|
||||
validation reports
|
||||
coverage maps
|
||||
continuation notes
|
||||
apply scripts
|
||||
review summaries
|
||||
```
|
||||
|
||||
They must not overwrite production filenames or live in importer-scanned production locations.
|
||||
|
||||
## Daily pack rule
|
||||
|
||||
`daily_fun_multiple_choice_v3.json` must always remain the complete 511-question Daily Single-Choice Weekday System.
|
||||
|
||||
A JSON document containing only changed IDs is a patch manifest. It must be applied to the complete 511-question source; the resulting complete pack is the file that ships.
|
||||
|
||||
## Catalog-wide rule
|
||||
|
||||
Every production file must pass individually, then the complete catalog must pass together.
|
||||
|
||||
The catalog gate rejects:
|
||||
|
||||
- duplicate IDs across files
|
||||
- duplicate exact or normalized question text across files
|
||||
- blocked cross-category near-duplicates
|
||||
- unknown categories
|
||||
- partial or patch artifacts under production filenames
|
||||
- depth or other field coercion caused by schema mismatches
|
||||
|
||||
## Immediate file remediation implied by the corrected guide
|
||||
|
||||
### `rebuilding_trust.json`
|
||||
|
||||
Do not ship the 25-question Batch 1 artifact.
|
||||
|
||||
Rebuild or complete the production pack using:
|
||||
|
||||
- full `{ "category": ..., "questions": [...] }` shape
|
||||
- required category object
|
||||
- `category_id` on every question
|
||||
- integer depth `1`, `2`, or `3`
|
||||
- tags arrays
|
||||
- the approved final count and access/type distribution
|
||||
|
||||
Keep the last complete production version in place until the full replacement passes validation.
|
||||
|
||||
### `daily_fun_multiple_choice_v3.json`
|
||||
|
||||
Restore the complete 511-question source.
|
||||
|
||||
Apply the 17-ID patch to that complete source.
|
||||
|
||||
Ship the complete validated 511-question result, not the patch manifest.
|
||||
|
||||
### Cross-file duplicate
|
||||
|
||||
Reword one instance of:
|
||||
|
||||
```text
|
||||
Comfort first or solutions first?
|
||||
```
|
||||
|
||||
Do not allow identical question text in both Emotional Intimacy and Stress. Give each version a category-specific angle, then rerun the complete catalog gate.
|
||||
|
||||
## Catalog count changes
|
||||
|
||||
Intentional 250-to-150 pack reductions are allowed under the 150-question cap.
|
||||
|
||||
Every completion report must still show:
|
||||
|
||||
```text
|
||||
old pack count
|
||||
new pack count
|
||||
net catalog change
|
||||
free/premium change
|
||||
approval or product rationale
|
||||
```
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# Closer Question Quality Checklist v8
|
||||
# Closer Question Quality Checklist v10 — Importer-Aligned
|
||||
|
||||
**See also:** [QUESTION_CONTENT_GUIDE.md](QUESTION_CONTENT_GUIDE.md) | [QUESTION_SCHEMA.md](QUESTION_SCHEMA.md) | [QUESTION_REWRITE_PLAN.md](QUESTION_REWRITE_PLAN.md)
|
||||
|
||||
|
|
@ -31,6 +31,43 @@ Reject any question that contains or strongly resembles:
|
|||
|
||||
These are therapy worksheet patterns.
|
||||
|
||||
## Production File Hard Checks
|
||||
|
||||
Run these before tone or content review.
|
||||
|
||||
Reject the file immediately if any item fails:
|
||||
|
||||
- top-level `category` object is missing
|
||||
- top-level `questions` array is missing
|
||||
- category is missing `id`, `display_name`, `description`, `access`, or `icon_name`
|
||||
- any question is missing `id`, `category_id`, `type`, `text`, `depth`, `access`, or `tags`
|
||||
- any `category_id` does not exactly match `category.id`
|
||||
- any production `depth` is not the integer `1`, `2`, or `3`
|
||||
- any `tags` value is missing or is not an array
|
||||
- the file uses the old simple-pack shape
|
||||
- the file is a partial batch
|
||||
- the file is a patch manifest
|
||||
- the production filename was overwritten by a work artifact
|
||||
- the filename is being used as a substitute for a missing category
|
||||
|
||||
`sex` is optional and nullable for ordinary packs. Do not reject a normal pack merely because `sex` is absent. Require it only when a documented feature actually uses it for targeting.
|
||||
|
||||
|
||||
## Type-specific importer checks
|
||||
|
||||
Reject the question if:
|
||||
|
||||
- a `scale` stores its settings outside `answer_config`
|
||||
- a `written` question stores its settings outside `answer_config`
|
||||
- a `this_or_that` question has no options
|
||||
- a `this_or_that` question has anything other than two options
|
||||
- a choice question’s top-level options do not match `answer_config.options`
|
||||
- a production question uses string depth
|
||||
- a content note says “fix the importer later” while the production JSON remains incompatible
|
||||
|
||||
String depth is a future migration only. Until the code migration lands, valid production depth is integer `1`, `2`, or `3`.
|
||||
|
||||
|
||||
## Normal Pack Hard Checks
|
||||
|
||||
For every normal category pack, confirm before content review:
|
||||
|
|
@ -43,6 +80,7 @@ For every normal category pack, confirm before content review:
|
|||
* free and premium counts match the documented pack plan
|
||||
* all IDs and question texts are unique
|
||||
* no exact or near-duplicate blocks remain
|
||||
* old count, new count, and net catalog change are reported when replacing a legacy pack
|
||||
|
||||
## Daily Pack Hard Checks
|
||||
|
||||
|
|
@ -61,6 +99,12 @@ For the daily single choice weekday pack, confirm before content review:
|
|||
* no duplicate IDs
|
||||
* no duplicate question text
|
||||
* no duplicate exact option lists
|
||||
* the production file contains the full `category` object and all 511 questions
|
||||
* the production file is not a patch manifest containing only changed IDs
|
||||
* the category object exists and its id matches every `category_id`
|
||||
* the production category id remains `daily_fun_mc` unless app code and data are migrated
|
||||
* logical pack id `daily_single_choice_weekly_v1` belongs in metadata, not in place of the category object
|
||||
* every daily question uses integer depth and includes category_id, access, and tags
|
||||
|
||||
## Daily Fun Gate
|
||||
|
||||
|
|
@ -241,6 +285,28 @@ Better:
|
|||
Save me the best couch spot
|
||||
```
|
||||
|
||||
## Catalog-Wide Hard Gate
|
||||
|
||||
Per-file checks are not enough.
|
||||
|
||||
Before rebuilding `app.db` or shipping any question change, scan every production pack together.
|
||||
|
||||
Reject the entire content build if any of these remain:
|
||||
|
||||
- duplicate question ID across files
|
||||
- duplicate exact question text across files
|
||||
- duplicate question text after case and whitespace normalization
|
||||
- blocked near-duplicate prompts across related categories
|
||||
- a category resolving to `unknown`
|
||||
- a production filename containing a patch, partial batch, validation report, or other work artifact
|
||||
- a depth value that would be coerced or read as `0`
|
||||
- a question missing `category_id`
|
||||
- a question missing its tags array
|
||||
|
||||
A duplicate across two otherwise valid packs still fails the whole catalog.
|
||||
|
||||
When two categories need similar ideas, rewrite each prompt around its own category purpose instead of copying identical text.
|
||||
|
||||
## Patch Discipline Checks
|
||||
|
||||
Before updating a daily pack, confirm the workflow is patch mode.
|
||||
|
|
@ -253,10 +319,16 @@ Required:
|
|||
* only marked IDs are edited
|
||||
* passing IDs are left unchanged
|
||||
* metadata is preserved unless metadata failed
|
||||
* the patch manifest is stored outside importer-scanned production files
|
||||
* the complete source pack remains available
|
||||
* the patch is applied to the complete source pack before shipping
|
||||
* the result is a complete production pack, not a patch-only deliverable
|
||||
* the report lists marked count, patched count, and remaining flag count
|
||||
|
||||
Reject the update if it rewrites passing questions without a mass rewrite exception.
|
||||
|
||||
Also reject the update if a patch manifest or partial batch replaces the production JSON, even when the patch itself is valid JSON.
|
||||
|
||||
Mass rewrite exception requires:
|
||||
|
||||
* more than 60 percent of the weekday or pack fails
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
# QUESTION_REWRITE_PLAN.md
|
||||
# Closer Question Rewrite Plan v10 — Importer-Aligned
|
||||
|
||||
## Purpose
|
||||
|
||||
|
|
@ -8,6 +8,48 @@ The goal is not just clean JSON.
|
|||
|
||||
The goal is questions real couples want to answer.
|
||||
|
||||
|
||||
## Importer-aligned schema snapshot
|
||||
|
||||
Every production replacement must preserve:
|
||||
|
||||
```text
|
||||
top-level category object
|
||||
top-level questions array
|
||||
category_id on every question
|
||||
integer depth 1/2/3
|
||||
tags array on every question
|
||||
answer_config for scale settings
|
||||
answer_config for written settings
|
||||
two explicit options for this_or_that
|
||||
```
|
||||
|
||||
The future string-depth plan is not part of current production work.
|
||||
|
||||
If the intended future schema differs from the importer, create a separate engineering migration task. Do not make content agents bridge the mismatch by shipping incompatible JSON.
|
||||
|
||||
|
||||
## Production Replacement Rule
|
||||
|
||||
Writing in batches does not permit shipping a partial pack.
|
||||
|
||||
During a rewrite:
|
||||
|
||||
1. Keep the last complete production JSON in place.
|
||||
2. Draft each batch in a controlled workspace or clearly labeled WIP artifact.
|
||||
3. Do not save a 25-question batch under the production filename.
|
||||
4. Do not switch the production file to the old simple-pack shape.
|
||||
5. Merge reviewed batches into the full `{ "category": ..., "questions": [...] }` structure.
|
||||
6. Use integer depth `1`, `2`, or `3`.
|
||||
7. Include `category_id` and `tags` on every production question.
|
||||
8. Validate the complete pack.
|
||||
9. Validate the full catalog across every production file.
|
||||
10. Replace the production file only after both gates pass.
|
||||
|
||||
A continuation note such as `Next ID: rt_026` proves that work is incomplete. It is not permission to replace the live pack with the first 25 questions.
|
||||
|
||||
A patch manifest is also a work artifact. It must never replace the source pack.
|
||||
|
||||
## Standard Pack Workflow
|
||||
|
||||
For normal category packs of up to 150 questions:
|
||||
|
|
|
|||
|
|
@ -1,351 +1,464 @@
|
|||
# Closer Question Schema v7
|
||||
# Closer Question Schema v8 — Importer-Aligned
|
||||
|
||||
**See also:** [QUESTION_CONTENT_GUIDE.md](QUESTION_CONTENT_GUIDE.md) | [QUESTION_REWRITE_PLAN.md](QUESTION_REWRITE_PLAN.md) | [QUESTION_QUALITY_CHECKLIST.md](QUESTION_QUALITY_CHECKLIST.md)
|
||||
**See also:** [QUESTION_CONTENT_GUIDE.md](QUESTION_CONTENT_GUIDE.md) | [DAILY_SINGLE_CHOICE_WEEKDAY_SYSTEM.md](DAILY_SINGLE_CHOICE_WEEKDAY_SYSTEM.md) | [QUESTION_REWRITE_PLAN.md](QUESTION_REWRITE_PLAN.md) | [QUESTION_QUALITY_CHECKLIST.md](QUESTION_QUALITY_CHECKLIST.md)
|
||||
|
||||
## Purpose
|
||||
## Authority
|
||||
|
||||
This document defines the JSON schema, question types, validation rules, and required counts for Closer question packs.
|
||||
This document defines the JSON that imports correctly **today**.
|
||||
|
||||
For writing philosophy and tone, see `QUESTION_CONTENT_GUIDE.md`.
|
||||
The active `build_db.py` behavior and the Room storage contract are authoritative for production files.
|
||||
|
||||
For rewrite workflow and special pack exceptions, see `QUESTION_REWRITE_PLAN.md`.
|
||||
A future schema direction may be documented separately, but it must not appear in production examples until the importer, Room model, migrations, readers, tests, and existing content are updated together.
|
||||
|
||||
## Question Types
|
||||
## Production pack shape
|
||||
|
||||
Use these type names exactly:
|
||||
|
||||
* written
|
||||
* single_choice
|
||||
* multi_choice
|
||||
* scale
|
||||
* this_or_that
|
||||
|
||||
Do not rename them unless the app code is updated first.
|
||||
|
||||
## multi_choice
|
||||
|
||||
Select every option that applies.
|
||||
|
||||
Use 4 to 6 options.
|
||||
|
||||
Example:
|
||||
Every production pack must use:
|
||||
|
||||
```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" }
|
||||
]
|
||||
"category": {
|
||||
"id": "quality_time",
|
||||
"display_name": "Quality Time",
|
||||
"description": "Questions about being present and enjoying meaningful time together.",
|
||||
"access": "mixed",
|
||||
"icon_name": "schedule",
|
||||
"metadata": {}
|
||||
},
|
||||
"questions": []
|
||||
}
|
||||
```
|
||||
|
||||
Required top-level fields:
|
||||
|
||||
```text
|
||||
category
|
||||
questions
|
||||
```
|
||||
|
||||
Required category fields:
|
||||
|
||||
```text
|
||||
id
|
||||
display_name
|
||||
description
|
||||
access
|
||||
icon_name
|
||||
```
|
||||
|
||||
`metadata` is optional but recommended.
|
||||
|
||||
Do not use a flat or simple-pack wrapper such as:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "quality_time",
|
||||
"title": "Quality Time",
|
||||
"count": 25,
|
||||
"questions": []
|
||||
}
|
||||
```
|
||||
|
||||
The importer reads `data["category"]`. A missing category object can cause the file to resolve to `unknown`.
|
||||
|
||||
Do not rely on the filename to recover category identity.
|
||||
|
||||
## Required question fields
|
||||
|
||||
Every production question requires:
|
||||
|
||||
```text
|
||||
id
|
||||
category_id
|
||||
type
|
||||
text
|
||||
depth
|
||||
access
|
||||
tags
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
* Prompt should clearly allow multiple answers.
|
||||
* Options should not shame either partner.
|
||||
* Options should be practical, emotional, or playful when possible.
|
||||
* Options must not overlap too much.
|
||||
- `category_id` must exactly match `category.id`
|
||||
- `type` must be a supported type
|
||||
- `text` must be non-empty and catalog-unique
|
||||
- `depth` must be integer `1`, `2`, or `3`
|
||||
- `access` must be `free` or `premium`
|
||||
- `tags` must be an array
|
||||
- use at least one meaningful tag unless a documented product exception exists
|
||||
- `sex` is optional and nullable for ordinary packs
|
||||
- require `sex` only when a documented feature actually reads it
|
||||
|
||||
## single_choice
|
||||
## Depth: production values
|
||||
|
||||
Select one best answer.
|
||||
| Meaning | Production value |
|
||||
|---|---:|
|
||||
| Light | `1` |
|
||||
| Medium | `2` |
|
||||
| Deep | `3` |
|
||||
|
||||
Use 4 to 6 options.
|
||||
Production example:
|
||||
|
||||
Example:
|
||||
```json
|
||||
"depth": 2
|
||||
```
|
||||
|
||||
### Future depth migration — not production-ready
|
||||
|
||||
String values are a planned possibility:
|
||||
|
||||
```json
|
||||
"depth": "medium"
|
||||
```
|
||||
|
||||
Do **not** ship them today.
|
||||
|
||||
The current importer writes depth into an integer column, and Room reads the field as an integer. A string can be stored or coerced incorrectly and then read as `0`, breaking depth routing and help text.
|
||||
|
||||
String depth becomes valid only after a coordinated code and data migration updates:
|
||||
|
||||
```text
|
||||
build_db.py
|
||||
Room entity and schema
|
||||
database migration or asset rebuild path
|
||||
all integer depth readers
|
||||
depth routing and help text
|
||||
validation
|
||||
tests
|
||||
all production content
|
||||
```
|
||||
|
||||
Until that migration lands, integers are mandatory.
|
||||
|
||||
## Supported question types
|
||||
|
||||
Use these names exactly:
|
||||
|
||||
```text
|
||||
written
|
||||
single_choice
|
||||
multi_choice
|
||||
scale
|
||||
this_or_that
|
||||
```
|
||||
|
||||
## `single_choice`
|
||||
|
||||
Use when the player chooses one best answer.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "quality_time_001",
|
||||
"category_id": "quality_time",
|
||||
"type": "single_choice",
|
||||
"text": "Which kind of date sounds best this week?",
|
||||
"text": "Which low-key plan sounds best tonight?",
|
||||
"depth": 1,
|
||||
"access": "free",
|
||||
"tags": ["low_energy", "easy_plan", "quality_time"],
|
||||
"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" }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Rules:
|
||||
|
||||
* Options should be short.
|
||||
* Options should not overlap too much.
|
||||
* Options should sound like real choices.
|
||||
* Every option must directly answer the prompt.
|
||||
* Every option should be similar in weight.
|
||||
|
||||
## scale
|
||||
|
||||
Rate agreement, comfort, importance, confidence, or frequency.
|
||||
|
||||
Example:
|
||||
|
||||
```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"
|
||||
{ "id": "short_walk", "text": "A short walk" },
|
||||
{ "id": "one_episode", "text": "One episode together" },
|
||||
{ "id": "snack_and_talk", "text": "A snack and a talk" },
|
||||
{ "id": "music_on_the_couch", "text": "Music on the couch" }
|
||||
],
|
||||
"answer_config": {
|
||||
"options": [
|
||||
{ "id": "short_walk", "text": "A short walk" },
|
||||
{ "id": "one_episode", "text": "One episode together" },
|
||||
{ "id": "snack_and_talk", "text": "A snack and a talk" },
|
||||
{ "id": "music_on_the_couch", "text": "Music on the couch" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Rules:
|
||||
Requirements:
|
||||
|
||||
* Scale labels should be gentle and neutral.
|
||||
* Do not make the low end sound bad or shameful.
|
||||
* Scale questions should measure one thing only.
|
||||
- use 4 to 6 options by default
|
||||
- top-level `options` are required
|
||||
- `answer_config.options` must mirror top-level options exactly
|
||||
- every option must directly answer the prompt
|
||||
- options should be similar in effort, emotional weight, and intimacy
|
||||
|
||||
## this_or_that
|
||||
## `multi_choice`
|
||||
|
||||
Very fast playful questions.
|
||||
|
||||
Example:
|
||||
Use when more than one answer can be true.
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "this_or_that",
|
||||
"text": "Planned date or spontaneous date?"
|
||||
"id": "quality_time_002",
|
||||
"category_id": "quality_time",
|
||||
"type": "multi_choice",
|
||||
"text": "What helps time together feel easy to enjoy?",
|
||||
"depth": 1,
|
||||
"access": "free",
|
||||
"tags": ["presence", "comfort", "quality_time"],
|
||||
"options": [
|
||||
{ "id": "no_rushing", "text": "Not feeling rushed" },
|
||||
{ "id": "phones_away", "text": "Putting phones away" },
|
||||
{ "id": "easy_conversation", "text": "Easy conversation" },
|
||||
{ "id": "clear_plan", "text": "Knowing the general plan" }
|
||||
],
|
||||
"answer_config": {
|
||||
"options": [
|
||||
{ "id": "no_rushing", "text": "Not feeling rushed" },
|
||||
{ "id": "phones_away", "text": "Putting phones away" },
|
||||
{ "id": "easy_conversation", "text": "Easy conversation" },
|
||||
{ "id": "clear_plan", "text": "Knowing the general plan" }
|
||||
],
|
||||
"min_selections": 1,
|
||||
"max_selections": 3
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Rules:
|
||||
Requirements:
|
||||
|
||||
* Prompts should be quick.
|
||||
* Avoid choices that imply one partner is wrong.
|
||||
* Best used in fun, date, intimacy, home, and lifestyle packs.
|
||||
- use 4 to 6 options by default
|
||||
- top-level `options` are required
|
||||
- `answer_config.options` must mirror them exactly
|
||||
- selection bounds must be valid when included
|
||||
- options must not overlap excessively
|
||||
|
||||
## written
|
||||
## `scale`
|
||||
|
||||
Reserved for questions where a short written response adds meaningful value.
|
||||
|
||||
Example:
|
||||
The importer reads scale settings from `answer_config`.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "quality_time_003",
|
||||
"category_id": "quality_time",
|
||||
"type": "scale",
|
||||
"text": "How easy is it to be present when we finally get time together?",
|
||||
"depth": 2,
|
||||
"access": "free",
|
||||
"tags": ["presence", "attention", "quality_time"],
|
||||
"answer_config": {
|
||||
"min": 1,
|
||||
"max": 5,
|
||||
"min_label": "Not easy yet",
|
||||
"max_label": "Very easy"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Requirements:
|
||||
|
||||
- use `answer_config`
|
||||
- include `min`, `max`, `min_label`, and `max_label`
|
||||
- measure one thing only
|
||||
- keep both labels neutral and non-shaming
|
||||
|
||||
Do not put scale settings in a separate top-level object. The importer does not read custom scale labels from there.
|
||||
|
||||
## `written`
|
||||
|
||||
The importer reads written settings from `answer_config`.
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "quality_time_004",
|
||||
"category_id": "quality_time",
|
||||
"type": "written",
|
||||
"text": "What is one small thing I do that makes you feel cared for?",
|
||||
"answer": {
|
||||
"text": "What is one recent moment together that felt worth slowing down for?",
|
||||
"depth": 2,
|
||||
"access": "free",
|
||||
"tags": ["recent_memory", "presence", "quality_time"],
|
||||
"answer_config": {
|
||||
"max_length": 500
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Written questions require an `answer` configuration with `max_length`.
|
||||
Requirements:
|
||||
|
||||
Do not use written questions for basic preferences.
|
||||
- use `answer_config`
|
||||
- include `max_length` when a custom limit matters
|
||||
- do not use a separate top-level `answer` object
|
||||
- use written questions only when typing adds real value
|
||||
- normal packs should contain 0 to 5 written questions
|
||||
|
||||
## Standard Category Packs
|
||||
## `this_or_that`
|
||||
|
||||
A normal category pack may contain **up to 150 questions**. The maximum is not a required count. Smaller packs are valid when they provide stronger coverage with less repetition.
|
||||
A `this_or_that` question must contain its two choices.
|
||||
|
||||
For a full 150-question mixed pack, use this planning target:
|
||||
```json
|
||||
{
|
||||
"id": "quality_time_005",
|
||||
"category_id": "quality_time",
|
||||
"type": "this_or_that",
|
||||
"text": "Quiet time or playful time?",
|
||||
"depth": 1,
|
||||
"access": "free",
|
||||
"tags": ["quick_choice", "mood", "quality_time"],
|
||||
"options": [
|
||||
{ "id": "quiet_time", "text": "Quiet time" },
|
||||
{ "id": "playful_time", "text": "Playful time" }
|
||||
],
|
||||
"answer_config": {
|
||||
"options": [
|
||||
{ "id": "quiet_time", "text": "Quiet time" },
|
||||
{ "id": "playful_time", "text": "Playful time" }
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Requirements:
|
||||
|
||||
- exactly two top-level options
|
||||
- `answer_config.options` must mirror them exactly
|
||||
- never ship a bare prompt with no A/B choices
|
||||
- both choices should be balanced
|
||||
|
||||
## Standard category packs
|
||||
|
||||
A normal pack may contain up to 150 questions.
|
||||
|
||||
A full 150-question target is:
|
||||
|
||||
| Type | Count |
|
||||
|---|---:|
|
||||
| multi_choice | 90 |
|
||||
| single_choice | 30 |
|
||||
| scale | 15 |
|
||||
| this_or_that | 10 |
|
||||
| written | 5 |
|
||||
| `multi_choice` | 90 |
|
||||
| `single_choice` | 30 |
|
||||
| `scale` | 15 |
|
||||
| `this_or_that` | 10 |
|
||||
| `written` | 5 |
|
||||
|
||||
Free and premium split:
|
||||
|
||||
* 45 free
|
||||
* 105 premium
|
||||
|
||||
Normal-pack rules:
|
||||
|
||||
* total question count must be 150 or fewer
|
||||
* metadata counts must match the actual file
|
||||
* written questions should normally total 0 to 5
|
||||
* written questions are optional and must not be used as filler
|
||||
* more than 5 written questions requires a documented pack-specific exception
|
||||
* a smaller pack may use a different type mix when the mix is documented and fits the category
|
||||
|
||||
## Special Packs
|
||||
|
||||
Special product packs may exceed the normal 150-question maximum or override the standard type mix only when documented.
|
||||
|
||||
The daily single choice weekday pack is the current documented 511-question special exception.
|
||||
|
||||
## Daily Single Choice Weekday Pack
|
||||
|
||||
Pack id:
|
||||
Normal full-pack access target:
|
||||
|
||||
```text
|
||||
daily_single_choice_weekly_v1
|
||||
45 free
|
||||
105 premium
|
||||
```
|
||||
|
||||
Compatibility file name:
|
||||
The count is a ceiling, not a quota.
|
||||
|
||||
## Daily Single-Choice Weekday System
|
||||
|
||||
Compatibility filename:
|
||||
|
||||
```text
|
||||
daily_fun_multiple_choice_v3.json
|
||||
```
|
||||
|
||||
Target counts — weekday pack (frozen; do not change these 500):
|
||||
Production category id:
|
||||
|
||||
| Field | Count |
|
||||
|---|---:|
|
||||
| weekday total | 500 |
|
||||
| weekday free | 75 |
|
||||
| weekday premium | 425 |
|
||||
| single_choice | 500 |
|
||||
```text
|
||||
daily_fun_mc
|
||||
```
|
||||
|
||||
Plus the wildcard add-on (surprise days; see `DAILY_SINGLE_CHOICE_WEEKDAY_SYSTEM.md` → Wildcard Mode).
|
||||
The wildcard pool size must stay **coprime with 10** (the wildcard-day spacing), so 11, not 10/12:
|
||||
Logical pack id stored in metadata:
|
||||
|
||||
| Field | Count |
|
||||
|---|---:|
|
||||
| wildcard free | 11 |
|
||||
```text
|
||||
daily_single_choice_weekly_v1
|
||||
```
|
||||
|
||||
**New pack totals (weekday + wildcard):** 511 total · 86 free · 425 premium · 511 single_choice.
|
||||
|
||||
These counts do not apply to standard category packs.
|
||||
|
||||
## Daily Pack Metadata Example
|
||||
The production file must use the normal top-level wrapper:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "daily_single_choice_weekly_v1",
|
||||
"title": "Daily Single Choice",
|
||||
"access": "mixed",
|
||||
"description": "One weekday themed single choice question per day, plus wildcard surprise days.",
|
||||
"count": 511,
|
||||
"free_count": 86,
|
||||
"premium_count": 425,
|
||||
"question_type_policy": "single_choice_only",
|
||||
"review_policy": "weekday_batch_loop_required",
|
||||
"patch_policy": "fix_marked_ids_only",
|
||||
"mass_rewrite_policy": "requires_over_60_percent_shared_failure",
|
||||
"content_policy": "fun_first_daily_game_questions",
|
||||
"research_note": "Use relationship question research patterns, not copied prompts.",
|
||||
"category": {
|
||||
"id": "daily_fun_mc",
|
||||
"display_name": "Daily Fun",
|
||||
"description": "One quick couples-game question for each day, including wildcard surprise days.",
|
||||
"access": "mixed",
|
||||
"icon_name": "calendar_today",
|
||||
"metadata": {
|
||||
"pack_id": "daily_single_choice_weekly_v1",
|
||||
"question_type_policy": "single_choice_only",
|
||||
"total_questions": 511,
|
||||
"free_questions": 86,
|
||||
"premium_questions": 425
|
||||
}
|
||||
},
|
||||
"questions": []
|
||||
}
|
||||
```
|
||||
|
||||
If the compatibility filename `daily_fun_multiple_choice_v3.json` is used, the metadata should still make clear that the content is `single_choice` only.
|
||||
When editing the existing production file, preserve its current category display fields and icon unless the product owner explicitly changes them. The important contract is that the category object exists and its `id` matches every question’s `category_id`.
|
||||
|
||||
Required totals:
|
||||
|
||||
## Daily Content Metadata
|
||||
|
||||
Daily packs should include metadata that makes the content standard impossible to miss.
|
||||
|
||||
Recommended fields:
|
||||
|
||||
```json
|
||||
{
|
||||
"question_type_policy": "single_choice_only",
|
||||
"review_policy": "weekday_batch_loop_required",
|
||||
"patch_policy": "fix_marked_ids_only",
|
||||
"mass_rewrite_policy": "requires_over_60_percent_shared_failure",
|
||||
"content_policy": "fun_first_daily_game_questions",
|
||||
"research_note": "Use relationship question research patterns, not copied prompts."
|
||||
}
|
||||
```text
|
||||
500 weekday questions
|
||||
11 wildcard questions
|
||||
511 total questions
|
||||
86 free
|
||||
425 premium
|
||||
511 single_choice
|
||||
```
|
||||
|
||||
These fields are not a replacement for validation. They document the intended behavior so future rewrites do not quietly turn the pack into chores, therapy, or generic wellness sludge.
|
||||
Every daily question requires:
|
||||
|
||||
## Daily Question Object Example
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "daily_monday_001",
|
||||
"type": "single_choice",
|
||||
"text": "What tiny date move sounds best tonight?",
|
||||
"depth": "light",
|
||||
"access": "free",
|
||||
"sex": "neutral",
|
||||
"tags": ["daily_monday_mood_check"],
|
||||
"options": [
|
||||
{ "id": "dessert_couch", "text": "Dessert on the couch" },
|
||||
{ "id": "kitchen_dance", "text": "A two-song kitchen dance" },
|
||||
{ "id": "snack_walk", "text": "A short walk with snacks" },
|
||||
{ "id": "movie_pick", "text": "A ridiculous movie pick" }
|
||||
]
|
||||
}
|
||||
```text
|
||||
category_id: daily_fun_mc
|
||||
integer depth
|
||||
access
|
||||
tags
|
||||
top-level options
|
||||
mirrored answer_config.options
|
||||
```
|
||||
|
||||
## Daily Single Choice Content Rules
|
||||
Every weekday question must carry exactly one documented weekday tag.
|
||||
|
||||
Daily options must be:
|
||||
Every wildcard must carry `mode_wildcard` and `daily_wildcard`, and must not carry a weekday tag.
|
||||
|
||||
* complete answers to the prompt
|
||||
* short and natural
|
||||
* similar in weight
|
||||
* fun, sweet, flirty, silly, or date-like when possible
|
||||
* easy to pick in under 10 seconds
|
||||
A patch manifest containing only changed IDs is not the Daily production pack.
|
||||
|
||||
Daily options must not be mostly chores, household admin, or therapy phrasing.
|
||||
## Work artifacts are not production packs
|
||||
|
||||
Reject options centered on clean counters, bills, laundry, dishes, appointment scheduling, bedtime planning, saved blankets, or oddly specific domestic logistics unless the question clearly frames the option as playful and worth choosing.
|
||||
Do not place any of these under a production filename:
|
||||
|
||||
Schema validation only proves the file can be parsed. It does not prove the content is good.
|
||||
```text
|
||||
partial batch JSON
|
||||
simple-pack batch JSON
|
||||
patch manifest
|
||||
marked-fix list
|
||||
validation report
|
||||
coverage map
|
||||
continuation note
|
||||
apply script
|
||||
review summary
|
||||
```
|
||||
|
||||
Daily packs must also pass the fun gate in `QUESTION_QUALITY_CHECKLIST.md` and the loop in `QUESTION_REWRITE_PLAN.md`.
|
||||
A patch must be clearly named as a patch, applied to the complete source, and followed by validation of the complete resulting pack.
|
||||
|
||||
## Weekday Tags
|
||||
## Per-file validation
|
||||
|
||||
Daily questions must include the correct weekday tag:
|
||||
Before shipping:
|
||||
|
||||
* daily_monday_mood_check
|
||||
* daily_tuesday_tiny_win
|
||||
* daily_wednesday_real_one
|
||||
* daily_thursday_laugh
|
||||
* daily_friday_flirty
|
||||
* daily_saturday_side_quest
|
||||
* daily_sunday_slow_burn
|
||||
- JSON parses
|
||||
- top-level `category` exists
|
||||
- top-level `questions` exists
|
||||
- all required category fields exist
|
||||
- every required question field exists
|
||||
- every `category_id` matches `category.id`
|
||||
- every `depth` is integer `1`, `2`, or `3`
|
||||
- all IDs are unique inside the file
|
||||
- all question text is unique inside the file
|
||||
- all access values are valid
|
||||
- every tags value is an array
|
||||
- choice option IDs are unique per question
|
||||
- mirrored option arrays match exactly
|
||||
- scale and written settings are in `answer_config`
|
||||
- every `this_or_that` has exactly two choices
|
||||
- metadata counts match actual counts
|
||||
- the file is complete, not a batch or patch
|
||||
|
||||
If app code still uses older mode tags, include compatibility tags as needed without removing the new weekday tag.
|
||||
## Catalog-wide validation
|
||||
|
||||
## Depth
|
||||
Before rebuilding `app.db` or shipping content, validate every production pack together.
|
||||
|
||||
Documented depth values:
|
||||
Reject the build for:
|
||||
|
||||
* light
|
||||
* medium
|
||||
* deep
|
||||
- duplicate question IDs across files
|
||||
- duplicate exact question text across files
|
||||
- duplicate normalized question text after case and whitespace normalization
|
||||
- blocked near-duplicates across categories
|
||||
- any category resolving to `unknown`
|
||||
- any production filename containing a work artifact
|
||||
- any schema mismatch that can coerce a value or silently discard configuration
|
||||
|
||||
Do not use numeric depth values unless the active app import code still requires numeric depth for compatibility.
|
||||
A duplicate in two otherwise valid files can abort the entire import.
|
||||
|
||||
If compatibility requires numeric depth, document the exception in the pack README and fix the import code later.
|
||||
|
||||
## Access
|
||||
|
||||
Valid access values:
|
||||
|
||||
* free
|
||||
* premium
|
||||
|
||||
## Validation Requirements
|
||||
|
||||
Before shipping any pack:
|
||||
|
||||
* JSON must parse
|
||||
* all IDs must be unique
|
||||
* all question text must be unique
|
||||
* every type must be valid
|
||||
* every required field must exist
|
||||
* option IDs must be unique within a question
|
||||
* options must directly answer the prompt
|
||||
* access values must be valid
|
||||
* depth values must match current app compatibility rules
|
||||
* normal category packs must contain no more than 150 questions
|
||||
* normal category packs should contain no more than 5 written questions unless an exception is documented
|
||||
* special packs must match their documented counts and exception rules
|
||||
|
||||
## Content Validation Reminder
|
||||
|
||||
Do not confuse schema validity with quality.
|
||||
|
||||
A file can be valid JSON and still be bad content.
|
||||
|
||||
Daily packs require schema validation plus human content review.
|
||||
Per-file success does not override catalog failure.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
12052
seed/questions/fun.json
12052
seed/questions/fun.json
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue