This commit is contained in:
null 2026-07-12 16:02:05 -05:00
parent 157f080ee6
commit 75a644e1ba
15 changed files with 33335 additions and 63353 deletions

1
.gitignore vendored
View File

@ -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

View File

@ -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).

View File

@ -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,16 @@ 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.
## Light
Light questions should be easy, fun, and low-pressure.
@ -698,39 +729,160 @@ 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.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 +890,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 +908,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 +978,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 +1307,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 +1482,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 +1505,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.
```

View File

@ -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
```

View File

@ -1,4 +1,4 @@
# Closer Question Quality Checklist v8
# Closer Question Quality Checklist v9
**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,27 @@ 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.
## Normal Pack Hard Checks
For every normal category pack, confirm before content review:
@ -43,6 +64,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 +83,9 @@ 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
* every daily question uses integer depth and includes category_id, access, and tags
## Daily Fun Gate
@ -241,6 +266,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 +300,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

View File

@ -1,4 +1,4 @@
# QUESTION_REWRITE_PLAN.md
# Closer Question Rewrite Plan v9
## Purpose
@ -8,6 +8,27 @@ The goal is not just clean JSON.
The goal is questions real couples want to answer.
## 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:

View File

@ -1,153 +1,307 @@
# Closer Question Schema v7
# Closer Question Schema v8
**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)
## Purpose
This document defines the JSON schema, question types, validation rules, and required counts for Closer question packs.
This document defines the production JSON contract used by the active Closer importer and Room database.
For writing philosophy and tone, see `QUESTION_CONTENT_GUIDE.md`.
The active importer is authoritative. Planning labels and old authoring formats must not override the production contract.
For rewrite workflow and special pack exceptions, see `QUESTION_REWRITE_PLAN.md`.
## Production File Shape
Every production question pack must use this top-level shape:
```json
{
"category": {
"id": "quality_time",
"display_name": "Quality Time",
"description": "Questions about being present and enjoying meaningful time together.",
"access": "mixed",
"icon_name": "schedule",
"schema_version": "question_v2",
"metadata": {}
},
"questions": []
}
```
Required top-level keys:
```text
category
questions
```
Required `category` fields:
```text
id
display_name
description
access
icon_name
```
Optional category fields include:
```text
schema_version
metadata
```
Never rely on the filename to manufacture a missing category.
The old simple-pack form is not production-valid:
```json
{
"id": "rebuilding_trust",
"title": "Rebuilding Trust",
"count": 25,
"questions": []
}
```
## Required Question Fields
Every production question requires:
```text
id
category_id
type
text
depth
access
tags
```
Rules:
- `category_id` must exactly match the enclosing `category.id`
- `type` must be one of the supported type names
- `text` must be non-empty and catalog-unique
- `depth` must be the 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 exception exists
- `sex` is optional and nullable for ordinary packs
- require `sex` only when a documented feature, such as feature-specific targeting, actually reads it
## Depth
Production depth is numeric:
| Meaning | Value |
|---|---:|
| Light | `1` |
| Medium | `2` |
| Deep | `3` |
Do not ship:
```json
"depth": "light"
```
Ship:
```json
"depth": 1
```
The strings `light`, `medium`, and `deep` may appear in coverage maps or review notes only.
## Question Types
Use these type names exactly:
* written
* single_choice
* multi_choice
* scale
* this_or_that
```text
written
single_choice
multi_choice
scale
this_or_that
```
Do not rename them unless the app code is updated first.
Do not rename them unless the app code and importer are updated first.
## multi_choice
Select every option that applies.
Use when more than one answer can be true.
Use 4 to 6 options.
Example:
Use 4 to 6 options by default.
```json
{
"id": "quality_time_001",
"category_id": "quality_time",
"type": "multi_choice",
"text": "What helps you feel relaxed on a date with me?",
"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": "good_food", "text": "Good food" },
{ "id": "easy_conversation", "text": "Easy conversation" }
]
{ "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:
* 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.
- top-level `options` are required
- `answer_config.options` must mirror the top-level options exactly
- selection bounds, when present, must be valid for the number of options
- options must directly and grammatically answer the prompt
- options must not overlap excessively
## single_choice
Select one best answer.
Use when the user selects one best answer.
Use 4 to 6 options.
Example:
Use 4 to 6 options by default.
```json
{
"id": "quality_time_002",
"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" }
{ "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:
* 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.
- top-level `options` are required
- `answer_config.options` must mirror them exactly
- every option must be a plausible single best answer
- options should be similar in effort, emotional weight, and intimacy
## scale
Rate agreement, comfort, importance, confidence, or frequency.
Example:
Use for one measurable dimension such as comfort, closeness, energy, readiness, satisfaction, confidence, importance, or frequency.
```json
{
"id": "quality_time_003",
"category_id": "quality_time",
"type": "scale",
"text": "How much do you feel like we need a real date soon?",
"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 much",
"max_label": "Very much"
"min_label": "Not easy yet",
"max_label": "Very easy"
}
}
```
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.
- use `answer_config`, not a top-level `scale` object
- `min`, `max`, `min_label`, and `max_label` are required
- labels must be gentle and neutral
- measure one thing only
## this_or_that
Very fast playful questions.
Example:
Use for a fast two-option choice.
```json
{
"id": "quality_time_004",
"category_id": "quality_time",
"type": "this_or_that",
"text": "Planned date or spontaneous date?"
"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" }
]
}
}
```
Rules:
* Prompts should be quick.
* Avoid choices that imply one partner is wrong.
* Best used in fun, date, intimacy, home, and lifestyle packs.
- exactly two top-level options
- `answer_config.options` must mirror them exactly
- the choice must be quick and balanced
## written
Reserved for questions where a short written response adds meaningful value.
Example:
Use only when a short written response adds meaningful value.
```json
{
"id": "quality_time_005",
"category_id": "quality_time",
"type": "written",
"text": "What is one small thing I do that makes you feel cared for?",
"answer": {
"max_length": 500
"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": {
"min_length": 1,
"max_length": 500,
"placeholder": "Write your answer..."
}
}
```
Written questions require an `answer` configuration with `max_length`.
Rules:
Do not use written questions for basic preferences.
- use `answer_config`, not a top-level `answer` object
- `max_length` is required
- do not use written questions for basic preferences
- normal packs should contain 0 to 5 written questions
## Standard Category Packs
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 normal category pack may contain up to 150 questions.
For a full 150-question mixed pack, use this planning target:
A full 150-question mixed pack normally targets:
| Type | Count |
|---|---:|
@ -157,27 +311,16 @@ For a full 150-question mixed pack, use this planning target:
| this_or_that | 10 |
| written | 5 |
Free and premium split:
Normal full-pack access target:
* 45 free
* 105 premium
```text
45 free
105 premium
```
Normal-pack rules:
The count is a ceiling, not a quota. Smaller packs are valid when documented and stronger.
* 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
## Daily Single-Choice Weekday System
Pack id:
@ -191,161 +334,96 @@ Compatibility file name:
daily_fun_multiple_choice_v3.json
```
Target counts — weekday pack (frozen; do not change these 500):
Required production totals:
| Field | Count |
|---|---:|
| weekday total | 500 |
| weekday free | 75 |
| weekday premium | 425 |
| single_choice | 500 |
| weekday questions | 500 |
| wildcard questions | 11 |
| total questions | 511 |
| free | 86 |
| premium | 425 |
| single_choice | 511 |
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:
The compatibility filename does not change the schema. The file must still be a complete production pack with a required `category` object and `questions` array.
| Field | Count |
|---|---:|
| wildcard free | 11 |
Every daily question must include:
**New pack totals (weekday + wildcard):** 511 total · 86 free · 425 premium · 511 single_choice.
- `category_id`
- integer `depth`
- `access`
- `tags`
- top-level `options`
- mirrored `answer_config.options`
These counts do not apply to standard category packs.
Every weekday question must have exactly one weekday tag.
## Daily Pack Metadata Example
Every wildcard must have `mode_wildcard` and `daily_wildcard` and no weekday tag.
```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.",
"questions": []
}
A daily patch manifest is not the daily production pack.
## Work Artifacts Are Not Packs
The following must not be placed under a production filename or inside an importer-scanned production directory:
```text
partial batch JSON
simple-pack batch JSON
patch manifest
marked-fix list
validation report
coverage map
continuation note
apply script
review summary
```
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.
A patch should be clearly named, for example:
## 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
daily_fun_multiple_choice_v3.patch.json
```
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.
It must be applied to the complete production source, and the complete resulting pack must pass validation before shipping.
## Daily Question Object Example
## Per-File Validation Requirements
```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" }
]
}
```
Before shipping a pack:
## Daily Single Choice Content Rules
- JSON parses
- top-level `category` object exists
- top-level `questions` array exists
- required category fields exist
- every question has every required field
- 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 types are valid
- tags arrays exist
- access values are valid
- option IDs are unique within each question
- top-level options and `answer_config.options` match where required
- metadata counts match actual counts
- normal packs contain no more than 150 questions
- normal packs contain no more than 5 written questions unless an approved exception exists
- special packs match their exact documented contract
- the file is a complete pack, not a patch or partial batch
Daily options must be:
## Catalog-Wide Validation Requirements
* 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
Before rebuilding `app.db` or shipping question content, validate all production files together.
Daily options must not be mostly chores, household admin, or therapy phrasing.
The full catalog must have:
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.
- globally unique question IDs
- globally unique exact question text
- no duplicate text after case and whitespace normalization
- no blocked near-duplicates across categories
- no category resolving to `unknown`
- no production file containing a work artifact
- no field coercion caused by a schema mismatch
Schema validation only proves the file can be parsed. It does not prove the content is good.
A duplicate question in two different files is a hard failure and may abort the entire import.
Daily packs must also pass the fun gate in `QUESTION_QUALITY_CHECKLIST.md` and the loop in `QUESTION_REWRITE_PLAN.md`.
## Weekday Tags
Daily questions must include the correct weekday tag:
* 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
If app code still uses older mode tags, include compatibility tags as needed without removing the new weekday tag.
## Depth
Documented depth values:
* light
* medium
* deep
Do not use numeric depth values unless the active app import code still requires numeric depth for compatibility.
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 a catalog-wide 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

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