Compare commits

..
Author SHA1 Message Date
jjbrunton 87309101ca docs(agents): require upfront OTA assessment 2026-09-05 22:17:24 +01:00
jjbrunton ba3b37989f Merge pull request #30 from jjbrunton/staging
chore(release): promote staging to main
2026-09-05 20:59:19 +01:00
jjbrunton 583d38eb8c merge(staging): preserve main admin fixes 2026-09-05 20:51:32 +01:00
jjbrunton d604cd7a11 fix(catalogue): avoid literal em dash in validator 2026-09-05 20:28:26 +01:00
jjbrunton ddf1e33a4e merge: add play-safe spicy question draft 2026-09-05 20:27:55 +01:00
jjbrunton da1c310738 docs(catalogue): trim draft source whitespace 2026-09-05 20:27:31 +01:00
jjbrunton 5c8c9cc9a5 docs(catalogue): finalise spicy question draft 2026-09-05 20:24:27 +01:00
jjbrunton c297f03b56 docs(catalogue): add audio concept source 2026-09-05 20:16:34 +01:00
jjbrunton c79d993cf5 docs(catalogue): preserve text answer concepts 2026-09-05 20:14:08 +01:00
jjbrunton b5e6b70d54 docs(catalogue): repair spicy swipe source 2026-09-05 20:07:14 +01:00
jjbrunton 2a2cbe9e85 docs(catalogue): add concept set b source 2026-09-05 19:59:02 +01:00
jjbrunton eb54aa4daa docs(questions): add concept set a swipe drafts 2026-09-05 19:57:48 +01:00
jjbrunton e509ac732f docs(catalogue): replace swipe draft concepts 2026-09-05 19:45:51 +01:00
jjbrunton a72fe9c06d docs(catalogue): expand draft pack themes 2026-09-05 19:36:52 +01:00
jjbrunton 450c527d1e docs(question-types): document interaction contract 2026-09-05 19:33:17 +01:00
jjbrunton 6c7219c741 docs(catalogue): refine spicy draft copy 2026-09-05 19:31:57 +01:00
jjbrunton b02a105e10 docs(catalogue): add play-safe spicy draft 2026-09-05 19:29:05 +01:00
jjbrunton f4687f95fe Merge pull request #29 from jjbrunton/staging
fix(admin): prevent blank dashboard after login
2026-09-05 17:12:45 +01:00
15 changed files with 10882 additions and 8 deletions

No files matched your search

@@ -0,0 +1,40 @@
---
id: 2026-09-05-question-type-contract-drift
date: 2026-09-05
status: promoted
scope: question interaction contract documentation
evidence: npm run lint:harness and npm run learn:check
destination: docs/question-types.md, scripts/lint-harness.mjs, AGENTS.md, and .codex/skills/question-pack/SKILL.md
review_after: 2026-12-05
---
## Symptom
Maintained documentation did not identify the implemented question interaction
types, their response payloads, or their matching semantics. The only detailed
reference was a historical implementation plan, so content and product work
could not rely on maintained documentation.
## Reproduction
Compare `QuestionType` in `packages/shared/src/types/index.ts` and the question
renderer and answer matching code with `docs/index.md`, `docs/schema.md`, and
`docs/question-selection.md`.
## Cause
The interaction contract was added to code without a maintained documentation
owner or a verification check linking the document to the canonical union.
## Remediation
Create `docs/question-types.md` as the maintained interaction contract. Require
agents and the question-pack workflow to update it with type or semantic
changes. Add a harness invariant that compares its type marker to the shared
`QuestionType` union.
## Evidence
`npm run lint:harness` reads the shared union and the documentation marker and
fails when their ordered type lists differ. `npm run learn:check` validates this
promoted learning record and its destination metadata.
+5
View File
@@ -7,6 +7,11 @@ description: Create or review Sauci question-pack content and its local migratio
Inspect existing packs before generating to avoid duplication. Inspect existing packs before generating to avoid duplication.
Before creating or editing content that uses a question interaction type, read
`docs/question-types.md`. If the type, response payload, configuration, or
matching semantics change, update that maintained contract and run
`npm run lint:harness` in the same task.
- Write proposals, not interview questions; use gender-neutral “your partner”. - Write proposals, not interview questions; use gender-neutral “your partner”.
- Avoid time-bound words such as tonight, now, or today. - Avoid time-bound words such as tonight, now, or today.
- Symmetric activities have no partner text. - Symmetric activities have no partner text.
+7
View File
@@ -40,6 +40,10 @@ MCP service in one npm/Turborepo workspace.
11. Use high-effort reviewers only for the risk triggers in 11. Use high-effort reviewers only for the risk triggers in
`docs/agents/routing.md`. `docs/agents/routing.md`.
12. Agents must not use em dash characters in text they add to this repository. 12. Agents must not use em dash characters in text they add to this repository.
13. Before beginning implementation, tell the user whether the proposed change
is likely deliverable through an Expo OTA update, likely requires a new
native/store build, or has no applicable OTA path, with a brief reason. If
uncertain, state that uncertainty and name the validation needed.
## Repository map ## Repository map
@@ -87,6 +91,9 @@ environment limitation; do not call it a passing product check.
## Documentation discipline ## Documentation discipline
Update the maintained doc when a contract, command, port, or invariant changes. Update the maintained doc when a contract, command, port, or invariant changes.
When question interaction types, response payloads, configuration, or match
semantics change, update `docs/question-types.md` in the same change and run
`npm run lint:harness` to prove its type list matches the shared contract.
Plans and historical notes are evidence, not current architecture. A repeated Plans and historical notes are evidence, not current architecture. A repeated
lesson becomes permanent only after it is reproduced and promoted into a doc, lesson becomes permanent only after it is reproduced and promoted into a doc,
skill, fixture, or executable check. skill, fixture, or executable check.
+1
View File
@@ -20,6 +20,7 @@ historical notes. Agents should load only the section relevant to their task.
- [Couple pairing](couple-pairing.md) - [Couple pairing](couple-pairing.md)
- [Couple streaks](couple-streaks.md) - [Couple streaks](couple-streaks.md)
- [Question selection](question-selection.md) - [Question selection](question-selection.md)
- [Question interaction types](question-types.md)
- [Match notifications](match-notifications.md) - [Match notifications](match-notifications.md)
- [Subscriptions](subscription-system.md) - [Subscriptions](subscription-system.md)
- [Redemption codes](product/redemption.md) - [Redemption codes](product/redemption.md)
@@ -0,0 +1,283 @@
# Play-safe spicy audio concept source
Draft source for private, up-to-60-second audio responses. This is editorial input only and is not a live catalogue import.
1. Theme: Flirty communication | Intensity: 2
Prompt: In a warm, playful tone, record the compliment from your partner that would make you smile all day.
Intended outcome: Your partner hears the exact kind of praise that lands for you and can use it naturally in a future message.
2. Theme: Flirty communication | Intensity: 3
Prompt: Record a voice note finishing this sentence: “When you look at me like that, I want you to know...”
Intended outcome: Your partner receives a personal signal of attraction and can reply with what they notice in that moment too.
3. Theme: Flirty communication | Intensity: 3
Prompt: Leave your partner a voice note with one bold thing you would love them to say quietly when you are alone together.
Intended outcome: Your partner learns a specific phrase or mood that could make a private moment feel more charged.
4. Theme: Flirty communication | Intensity: 4
Prompt: Record the invitation you would most like to hear when your partner wants to turn an ordinary evening into a close one.
Intended outcome: Your partner gets a clear, consent-forward way to initiate and can try it on a night that feels right.
5. Theme: Flirty communication | Intensity: 4
Prompt: In your most confident voice, tell your partner what makes you feel unmistakably wanted by them.
Intended outcome: Your partner hears the emotional cue behind your desire and can make future affection feel more intentional.
6. Theme: Anticipation | Intensity: 2
Prompt: Record a short countdown-style note about one small thing you are looking forward to doing together later.
Intended outcome: Your partner hears a concrete point of anticipation and can build on it with their own plan.
7. Theme: Anticipation | Intensity: 3
Prompt: Record the first message you would want from your partner before a deliberately flirty date night.
Intended outcome: Your partner learns how you like anticipation to begin and can send it before your next planned evening.
8. Theme: Anticipation | Intensity: 3
Prompt: Describe, in a low and unhurried voice, the mood you want waiting for you after a busy day apart.
Intended outcome: Your partner hears whether you want calm, playfulness, affection, or intensity and can help create that landing.
9. Theme: Anticipation | Intensity: 4
Prompt: Record a private tease that hints at the kind of attention you would love from your partner later, without explaining every detail.
Intended outcome: Your partner receives an intriguing cue they can ask about and turn into a jointly chosen plan.
10. Theme: Anticipation | Intensity: 4
Prompt: Leave a voice note naming the one thing your partner could do before you meet that would put you in a more daring mood.
Intended outcome: Your partner learns a practical lead-in, such as a message, outfit, setting, or affectionate gesture, that helps you open up.
11. Theme: Taking the lead | Intensity: 2
Prompt: Record a cheerful invitation for your partner to let you choose the next date-night detail.
Intended outcome: Your partner hears an easy handover of initiative and can say yes to one contained surprise.
12. Theme: Taking the lead | Intensity: 3
Prompt: In a decisive voice, tell your partner one private mood you would like to set for the two of you.
Intended outcome: Your partner learns the atmosphere you want to lead and can confirm whether it feels good for them too.
13. Theme: Taking the lead | Intensity: 3
Prompt: Record how you would ask your partner to trust you with planning a more adventurous evening together.
Intended outcome: Your partner hears a respectful version of your confidence and can agree on a boundary before the plan unfolds.
14. Theme: Taking the lead | Intensity: 4
Prompt: Leave a voice note telling your partner the first instruction you would enjoy giving in a mutually agreed private game.
Intended outcome: Your partner hears the style of leadership you enjoy and can opt into the game with clear limits.
15. Theme: Taking the lead | Intensity: 4
Prompt: Record the reassurance you would give your partner before you take charge of a bolder shared experience.
Intended outcome: Your partner hears how you would keep them comfortable and can name what would help them relax into your lead.
16. Theme: Following | Intensity: 2
Prompt: Record one thing your partner could choose for you that would feel like a thoughtful treat.
Intended outcome: Your partner learns a safe way to surprise you and can choose it for an upcoming evening.
17. Theme: Following | Intensity: 3
Prompt: In a soft voice, explain what helps you enjoy letting your partner set the pace.
Intended outcome: Your partner hears the conditions that make following feel exciting rather than uncertain.
18. Theme: Following | Intensity: 3
Prompt: Record the kind of check-in that makes you feel safe saying yes to your partner’s idea.
Intended outcome: Your partner learns the exact reassurance that supports your willingness and can use it before suggesting something new.
19. Theme: Following | Intensity: 4
Prompt: Leave a voice note describing one daring-but-private decision you would enjoy your partner making after checking in with you first.
Intended outcome: Your partner gets a consent-based example of initiative they could take and a cue to ask before acting.
20. Theme: Following | Intensity: 4
Prompt: Record what you would want your partner to say if they wanted you to surrender the planning for one intimate evening.
Intended outcome: Your partner hears language that makes a stronger invitation feel welcome, with room for you to decline or adjust.
21. Theme: Shared control | Intensity: 2
Prompt: Record a two-choice question your partner can answer to shape your next cosy evening together.
Intended outcome: Your partner hears two real options and can choose the version that feels most appealing.
22. Theme: Shared control | Intensity: 3
Prompt: Leave a voice note describing a private game where you and your partner would alternate making one decision each.
Intended outcome: Your partner learns a simple shared-control format and can suggest the first decision.
23. Theme: Shared control | Intensity: 3
Prompt: Record which part of a date night you would like your partner to lead and which part you would like to lead.
Intended outcome: Your partner hears a practical division of initiative and can co-create the evening rather than guess.
24. Theme: Shared control | Intensity: 4
Prompt: In an excited voice, explain how you would like to build a bolder shared moment one yes at a time.
Intended outcome: Your partner hears a stepwise consent rhythm and can add their own yes, no, or alternative at each point.
25. Theme: Shared control | Intensity: 4
Prompt: Record a private challenge that only works if both of you keep choosing the next step together.
Intended outcome: Your partner receives an invitation to collaborative play and can negotiate the limits before starting.
26. Theme: Boundaries and check-ins | Intensity: 2
Prompt: Record a kind sentence your partner can use to ask whether you are still enjoying a close moment.
Intended outcome: Your partner hears a check-in that feels caring rather than formal and can use it in real time.
27. Theme: Boundaries and check-ins | Intensity: 3
Prompt: In your own voice, explain the difference between a surprise that delights you and one that catches you off guard.
Intended outcome: Your partner learns your personal distinction and can plan novelty that feels welcome.
28. Theme: Boundaries and check-ins | Intensity: 3
Prompt: Record the easiest phrase for you to use when you want to slow down without ending the mood.
Intended outcome: Your partner hears your preferred pause signal and knows it protects connection rather than rejects them.
29. Theme: Boundaries and check-ins | Intensity: 4
Prompt: Leave a voice note telling your partner what they could ask before suggesting something considerably bolder than usual.
Intended outcome: Your partner gets a concrete consent opener and can ask it before raising a new idea.
30. Theme: Boundaries and check-ins | Intensity: 4
Prompt: Record what “I am curious, but not ready yet” sounds like in words that still feel affectionate to you.
Intended outcome: Your partner learns how to receive hesitation warmly and can leave the door open without pressure.
31. Theme: Aftercare and reconnection | Intensity: 2
Prompt: Record the small affectionate gesture that helps you feel close after a playful evening together.
Intended outcome: Your partner hears a specific reconnection preference they can offer without guessing.
32. Theme: Aftercare and reconnection | Intensity: 3
Prompt: In a calm voice, tell your partner what you most like hearing after you have both tried something new together.
Intended outcome: Your partner learns the reassurance or reflection that helps novelty become a positive shared memory.
33. Theme: Aftercare and reconnection | Intensity: 3
Prompt: Record how you would like your partner to check in with you the morning after an especially intimate night.
Intended outcome: Your partner hears the tone and timing that feel meaningful and can follow through the next day.
34. Theme: Aftercare and reconnection | Intensity: 4
Prompt: Leave a voice note about the honest question you would want your partner to ask after a more daring experience.
Intended outcome: Your partner learns how to invite useful feedback and can improve the next shared experience with you.
35. Theme: Aftercare and reconnection | Intensity: 4
Prompt: Record the words that would make you feel proud and cared for after you stepped outside your comfort zone together.
Intended outcome: Your partner hears how to celebrate your trust and can reinforce the kind of openness you want to grow.
36. Theme: Giving and receiving attention | Intensity: 2
Prompt: Record one affectionate way your partner could make you feel noticed when you are both at home.
Intended outcome: Your partner gets a simple attention cue they can use in an ordinary evening.
37. Theme: Giving and receiving attention | Intensity: 3
Prompt: Leave a voice note describing how you know the difference between rushed affection and attention that feels fully present.
Intended outcome: Your partner learns what presence sounds and feels like to you and can adjust their pace.
38. Theme: Giving and receiving attention | Intensity: 3
Prompt: Record the kind of praise you would most enjoy giving your partner when they make you feel desired.
Intended outcome: Your partner hears the feedback that encourages them and can understand what is working for you.
39. Theme: Giving and receiving attention | Intensity: 4
Prompt: In a warm, deliberate voice, tell your partner where you would like their focus to be during a private moment.
Intended outcome: Your partner hears the emotional or sensory focus you want and can ask a follow-up before acting on it.
40. Theme: Giving and receiving attention | Intensity: 4
Prompt: Record the one thing your partner could do to make you feel they have set aside the whole evening just for you.
Intended outcome: Your partner learns a concrete sign of devoted attention and can weave it into a future plan.
41. Theme: Sensory cues | Intensity: 2
Prompt: Record the sound, song, or kind of silence that makes an evening feel more intimate to you.
Intended outcome: Your partner learns an audible atmosphere cue and can bring it into your next shared setting.
42. Theme: Sensory cues | Intensity: 3
Prompt: In a slow voice, describe the lighting that makes you feel most relaxed and attractive.
Intended outcome: Your partner hears a practical setting preference and can prepare the room with your comfort in mind.
43. Theme: Sensory cues | Intensity: 3
Prompt: Record the scent or familiar smell that you associate with feeling especially close to your partner.
Intended outcome: Your partner learns a personal sensory association and can use it to create a recognisable shared mood.
44. Theme: Sensory cues | Intensity: 4
Prompt: Leave a voice note about the kind of music that would make you feel more confident trying a bolder private mood together.
Intended outcome: Your partner receives a specific confidence cue and can make a playlist before you explore the mood.
45. Theme: Private performance | Intensity: 2
Prompt: Record a playful introduction as though you are welcoming your partner to a date night you designed just for them.
Intended outcome: Your partner hears your enthusiasm and can respond with the detail that would make the invitation even better.
46. Theme: Private performance | Intensity: 3
Prompt: In character, record a short mystery message that gives your partner one clue about a private surprise.
Intended outcome: Your partner gets a voice-led tease and can guess, ask for another clue, or agree to be surprised.
47. Theme: Private performance | Intensity: 3
Prompt: Record a dramatic trailer voice-over for the next adventurous evening you want to create together.
Intended outcome: Your partner hears the mood as performance, making it easier to discuss the plan without pressure.
48. Theme: Private performance | Intensity: 4
Prompt: Leave a voice note acting out the confident version of yourself you would like to bring into a private date night.
Intended outcome: Your partner hears a new side of your energy and can encourage that version of you when you are together.
49. Theme: Clothes and confidence | Intensity: 2
Prompt: Record how you feel when you wear the outfit that makes you walk a little taller.
Intended outcome: Your partner learns the confidence signal behind a favourite look and can compliment it more meaningfully.
50. Theme: Clothes and confidence | Intensity: 3
Prompt: Leave a voice note telling your partner what kind of look from them catches your attention first.
Intended outcome: Your partner hears a specific style cue and can choose an outfit that makes them feel noticed.
51. Theme: Clothes and confidence | Intensity: 3
Prompt: In an encouraging voice, tell your partner how you would like them to make you feel confident before a private evening.
Intended outcome: Your partner learns whether words, time to get ready, a compliment, or a small gesture helps you feel at ease.
52. Theme: Clothes and confidence | Intensity: 4
Prompt: Record the outfit-related surprise you would enjoy receiving from your partner after they check it feels right for you.
Intended outcome: Your partner gets a consent-aware idea for a bolder visual surprise and can confirm your comfort before planning it.
53. Theme: Fantasy exchange | Intensity: 2
Prompt: Record a fictional date-night scene you would enjoy stepping into with your partner, keeping it all about atmosphere.
Intended outcome: Your partner hears a low-pressure shared fantasy and can suggest a small real-world version of its mood.
54. Theme: Fantasy exchange | Intensity: 3
Prompt: Leave a voice note beginning, “In our imaginary perfect evening, you would surprise me by...”
Intended outcome: Your partner receives a playful desire in fictional form and can ask which part you would actually enjoy trying.
55. Theme: Fantasy exchange | Intensity: 3
Prompt: Record the made-up place where you and your partner would feel free to be more playful and uninhibited together.
Intended outcome: Your partner hears the setting behind your fantasy and can recreate its feeling privately and safely.
56. Theme: Fantasy exchange | Intensity: 4
Prompt: In a conspiratorial voice, tell your partner the private rule that would make your imaginary evening feel thrilling.
Intended outcome: Your partner hears one negotiable game rule and can decide with you whether it belongs in real life.
57. Theme: Roleplay | Intensity: 2
Prompt: Record a playful opening line for a pretend first meeting with your partner.
Intended outcome: Your partner gets an easy roleplay entry point and can answer in character if they want to continue.
58. Theme: Roleplay | Intensity: 3
Prompt: Leave a voice note as a fictional version of yourself inviting your partner on a secret date.
Intended outcome: Your partner hears the role and tone that appeals to you and can choose whether to join the scene.
59. Theme: Roleplay | Intensity: 3
Prompt: Record the character trait you would enjoy your partner pretending to have for one private evening.
Intended outcome: Your partner learns the energy you find exciting, such as confidence, mystery, gentleness, or playfulness.
60. Theme: Roleplay | Intensity: 4
Prompt: In character, record the agreement you would want before a bolder roleplay scene begins.
Intended outcome: Your partner hears your preferred boundaries, stop signal, and tone before either of you enters the scene.
61. Theme: Settings and atmosphere | Intensity: 2
Prompt: Record a description of the room that makes you instantly want to put your phones away and focus on each other.
Intended outcome: Your partner learns the environmental details that help you reconnect and can help create them.
62. Theme: Settings and atmosphere | Intensity: 3
Prompt: Leave a voice note about the time of day when you are most open to a deliberately intimate evening.
Intended outcome: Your partner hears your natural rhythm and can plan closeness for a time when you are more available.
63. Theme: Settings and atmosphere | Intensity: 3
Prompt: Record the first three details you would set up to make a home evening feel like an occasion.
Intended outcome: Your partner receives an actionable atmosphere recipe and can take ownership of one detail.
64. Theme: Settings and atmosphere | Intensity: 4
Prompt: In a hushed voice, describe the kind of private escape you would like to create together without leaving home.
Intended outcome: Your partner hears the emotional destination you want and can help transform the space into it.
65. Theme: Surprise and novelty | Intensity: 2
Prompt: Record one harmless surprise your partner could prepare that would make you feel pursued.
Intended outcome: Your partner learns an approachable surprise idea and can choose a moment to use it.
66. Theme: Surprise and novelty | Intensity: 3
Prompt: Leave a voice note explaining what makes a surprise feel exciting to you instead of overwhelming.
Intended outcome: Your partner hears the ingredients of welcome novelty and can avoid the parts that undermine it.
67. Theme: Surprise and novelty | Intensity: 3
Prompt: Record a voice-only clue about a new private activity you would be curious to try together.
Intended outcome: Your partner gets an invitation to ask questions and decide whether the activity suits both of you.
68. Theme: Surprise and novelty | Intensity: 4
Prompt: In a playful whisper, tell your partner the one advance question they should ask before planning a bolder surprise.
Intended outcome: Your partner learns the consent question that keeps novelty safe and can use it before making plans.
69. Theme: Long-distance desire | Intensity: 2
Prompt: Record the everyday sound from your life that you wish your partner could hear when you are apart.
Intended outcome: Your partner gets a vivid piece of your day and can send one back to make distance feel smaller.
70. Theme: Long-distance desire | Intensity: 4
Prompt: Leave a private voice note describing how you would like your reunion to begin when you next have uninterrupted time together.
Intended outcome: Your partner hears a clear reunion hope and can respond with their own version before you meet.
@@ -0,0 +1,103 @@
# Concept Set A: Swipe proposals
Draft catalogue for the existing swipe interaction. A match represents both partners opting into the stated outcome. `partner_text` is the wording delivered to the other partner; `null` denotes a symmetric proposition.
## Asymmetric swipe concepts
| # | Intensity | Text | Partner text | Matched outcome |
|---:|---:|---|---|---|
| 1 | 2 | Send your partner a message describing the kind of attention you would love later. | Receive a message from your partner describing the kind of attention they would love later. | One partner has invited the other to create a specific kind of private attention. |
| 2 | 2 | Ask your partner to choose one compliment you should hear in a more intimate setting. | Choose one compliment your partner should hear in a more intimate setting. | One partner wants to give a targeted confidence-building compliment and the other wants to receive it. |
| 3 | 2 | Tell your partner one private thought that makes you feel closer to them. | Hear one private thought from your partner that makes them feel closer to you. | One partner is ready to share a personal desire for connection and the other is open to receiving it. |
| 4 | 2 | Ask your partner to give you a private instruction in a message. | Give your partner a private instruction in a message. | One partner wants to follow a playful instruction and the other wants to set it. |
| 5 | 3 | Describe a private scenario you would like your partner to set up for you. | Set up a private scenario your partner describes wanting. | One partner wants to describe a desired experience and the other wants to bring it to life. |
| 6 | 3 | Ask your partner to tell you what they find hardest to resist about you. | Tell your partner what you find hardest to resist about them. | One partner wants a direct expression of desire and the other wants to give it. |
| 7 | 3 | Invite your partner to send you a voice note that builds anticipation. | Send your partner a voice note that builds anticipation. | One partner wants to be teased by voice and the other wants to create that anticipation. |
| 8 | 4 | Ask your partner to tell you one private wish they want you to consider. | Tell your partner one private wish you want them to consider. | One partner is open to receiving a direct private wish and the other is ready to name one. |
| 9 | 2 | Let your partner choose a small signal that means they want your attention. | Choose a small signal that tells your partner you want their attention. | One partner wants to recognise a private invitation and the other wants to create one. |
| 10 | 2 | Ask your partner to plan the first few minutes of a private date. | Plan the first few minutes of a private date for your partner. | One partner wants to arrive without knowing the opening and the other wants to create it. |
| 11 | 3 | Let your partner decide when you are allowed to open a private surprise. | Decide when your partner is allowed to open a private surprise. | One partner wants delayed anticipation and the other wants to control the reveal. |
| 12 | 3 | Wait for your partner to give you the cue to begin a private plan. | Give your partner the cue to begin a private plan. | One partner wants to wait for permission and the other wants to choose the moment. |
| 13 | 3 | Ask your partner to create a private clue trail leading to what they want. | Create a private clue trail leading to what you want from your partner. | One partner wants to follow an intimate trail of hints and the other wants to design it. |
| 14 | 3 | Let your partner choose one thing to postpone to make the build-up longer. | Choose one thing to postpone to make the build-up longer for your partner. | One partner wants a slower build-up and the other wants to decide what is held back. |
| 15 | 4 | Ask your partner to plan a private surprise within limits you agree first. | Plan a private surprise for your partner within limits you agree first. | One partner wants a contained surprise and the other wants to lead it safely. |
| 16 | 4 | Let your partner choose the order of a private experience without telling you. | Choose the order of a private experience without telling your partner. | One partner wants uncertainty within an agreed experience and the other wants to choreograph it. |
| 17 | 2 | Choose what your partner wears for a private date. | Let your partner choose what you wear for a private date. | One partner wants to style the moment and the other wants to be chosen for. |
| 18 | 2 | Choose the music that sets the mood for your private time together. | Let your partner choose the music that sets the mood for your private time together. | One partner wants to set the atmosphere and the other wants to enter it. |
| 19 | 3 | Give your partner three private options and choose which one they follow. | Let your partner give you three private options and choose which one you follow. | One partner wants to offer controlled choices and the other wants to accept a selected direction. |
| 20 | 3 | Plan a private experience where your partner follows your pace. | Follow your partner’s pace during a private experience they plan. | One partner wants to guide the rhythm and the other wants to be guided. |
| 21 | 3 | Choose the first form of affection you give your partner in private. | Let your partner choose the first form of affection they give you in private. | One partner wants to initiate deliberately and the other wants to receive an intentional opening. |
| 22 | 4 | Decide the private role you want your partner to play for you. | Play the private role your partner chooses for you. | One partner wants to direct a role-based encounter and the other wants to inhabit the chosen role. |
| 23 | 4 | Set the rules for a private game your partner agrees to play. | Play a private game using rules your partner sets. | One partner wants to set a consensual frame and the other wants to play within it. |
| 24 | 4 | Choose when your partner may take the next step in a private encounter. | Let your partner choose when you may take the next step in a private encounter. | One partner wants to control pacing and the other wants to receive a clear cue. |
| 25 | 2 | Let your partner choose the private setting where you spend time together. | Choose the private setting where you spend time with your partner. | One partner wants to be led into a setting and the other wants to choose it. |
| 26 | 2 | Follow a private date plan your partner creates without seeing it first. | Create a private date plan for your partner without showing it first. | One partner wants a planned surprise and the other wants to arrange it. |
| 27 | 3 | Let your partner decide how slowly you take a private moment together. | Decide how slowly you take a private moment together with your partner. | One partner wants to surrender the pace and the other wants to set it. |
| 28 | 3 | Follow one private instruction from your partner without negotiating the details first. | Give your partner one private instruction without negotiating the details first. | One partner wants to follow a simple direction and the other wants to give one. |
| 29 | 3 | Let your partner choose the kind of attention you receive first. | Choose the kind of attention your partner receives first. | One partner wants to receive a chosen form of attention and the other wants to give it. |
| 30 | 4 | Let your partner decide the sequence of an agreed private experience. | Decide the sequence of an agreed private experience for your partner. | One partner wants to follow an agreed sequence and the other wants to lead it. |
| 31 | 4 | Follow a private role your partner sets after discussing your limits. | Set a private role for your partner after discussing their limits. | One partner wants to follow a defined role and the other wants to set it within agreed limits. |
| 32 | 2 | Choose one element of a private date while your partner chooses the other. | Choose one element of a private date while your partner chooses the other. | Both partners want to co-create a private date with equal input. |
| 33 | 2 | Take turns choosing how you show affection during a private evening. | Take turns choosing how you show affection during a private evening. | Both partners want alternating opportunities to initiate affection. |
| 34 | 3 | Create a private list of three ideas and let your partner pick one for you. | Create a private list of three ideas and let your partner pick one for you. | Both partners want to offer choices and select an experience for each other. |
| 35 | 3 | Agree that each of you gets one turn to direct part of a private experience. | Agree that each of you gets one turn to direct part of a private experience. | Both partners want shared leadership with clear turns. |
| 36 | 3 | Choose a private challenge for your partner and accept one from them. | Choose a private challenge for your partner and accept one from them. | Both partners want to exchange playful, consensual challenges. |
| 37 | 4 | Take turns naming one thing you want and one thing you are willing to give. | Take turns naming one thing you want and one thing you are willing to give. | Both partners want to negotiate desire through reciprocal offers. |
| 38 | 4 | Agree that either partner can change the direction of a private experience once. | Agree that either partner can change the direction of a private experience once. | Both partners want shared control and permission to redirect the experience. |
| 39 | 2 | Ask your partner to name the kind of reassurance they want before trying something new. | Tell your partner the kind of reassurance you want before trying something new. | One partner wants to offer reassurance and the other wants to name what helps. |
| 40 | 2 | Choose a private check-in phrase your partner can use with you. | Use a private check-in phrase your partner chooses for you. | One partner wants to establish a check-in and the other wants to use it. |
| 41 | 3 | Tell your partner one boundary that helps you relax during private exploration. | Hear one boundary from your partner that helps them relax during private exploration. | One partner wants to state a boundary and the other is open to receiving it. |
| 42 | 3 | Ask your partner to choose a simple signal for slowing down. | Choose a simple signal for slowing down with your partner. | One partner wants a clear slowing-down signal and the other wants to define it. |
| 43 | 3 | Invite your partner to tell you what would make a new idea feel safer. | Tell your partner what would make a new idea feel safer for you. | One partner wants to adapt to the other’s needs and the other wants to express them. |
| 44 | 4 | Ask your partner to set one firm limit before you lead a private experience. | Set one firm limit before your partner leads a private experience. | One partner wants to lead only with a clear limit and the other wants to set it. |
| 45 | 4 | Ask your partner to tell you what would make them stop a private experience. | Tell your partner what would make you stop a private experience. | One partner wants clarity about stopping conditions and the other wants to define them. |
| 46 | 2 | Ask your partner to choose how they would like to reconnect after private time. | Choose how you would like to reconnect with your partner after private time. | One partner wants to provide the preferred form of reconnection and the other wants to choose it. |
| 47 | 2 | Tell your partner one thing they could say that would make you feel especially appreciated afterwards. | Hear one thing you could say that would make your partner feel especially appreciated afterwards. | One partner wants to name an affirming phrase and the other wants to offer it. |
| 48 | 3 | Let your partner choose whether you stay close, talk, or have quiet space after an intimate moment. | Choose whether you stay close, talk, or have quiet space after an intimate moment with your partner. | One partner wants the other to decide the reconnection style and the other wants to choose it. |
| 49 | 3 | Ask your partner to tell you what kind of comfort helps them feel grounded afterwards. | Tell your partner what kind of comfort helps you feel grounded afterwards. | One partner wants to provide grounding comfort and the other wants to describe it. |
| 50 | 3 | Give your partner a chance to tell you what they liked most after private exploration. | Tell your partner what you liked most after private exploration. | One partner wants feedback and the other wants to share positive detail. |
| 51 | 4 | Ask your partner to choose one way you can make them feel cared for after an intense private experience. | Choose one way your partner can make you feel cared for after an intense private experience. | One partner wants to provide deliberate care and the other wants to specify it. |
| 52 | 4 | Tell your partner what you need from them if a private experience leaves you feeling exposed. | Hear what your partner needs if a private experience leaves them feeling exposed. | One partner wants to name a vulnerability need and the other wants to meet it. |
| 53 | 2 | Let your partner choose one part of you to compliment slowly and sincerely. | Choose one part of your partner to compliment slowly and sincerely. | One partner wants to receive focused admiration and the other wants to give it. |
| 54 | 2 | Ask your partner to give you their full attention while you share what feels romantic to you. | Give your partner your full attention while they share what feels romantic to them. | One partner wants to be heard about romance and the other wants to listen attentively. |
| 55 | 3 | Receive a slow, uninterrupted moment of affection chosen by your partner. | Give your partner a slow, uninterrupted moment of affection you choose. | One partner wants to receive focused affection and the other wants to choose how to give it. |
| 56 | 3 | Give your partner the kind of private attention they ask for without asking for anything back first. | Receive the kind of private attention you ask for without giving anything back first. | One partner wants to give without immediate reciprocity and the other wants to receive freely. |
| 57 | 3 | Let your partner choose how they want you to focus on them during private time. | Choose how you want your partner to focus on you during private time. | One partner wants to direct the attention they receive and the other wants to follow that direction. |
| 58 | 4 | Ask your partner to give you their full focus while you set the pace. | Give your partner your full focus while they set the pace. | One partner wants to lead the pace while receiving undivided attention from the other. |
| 59 | 4 | Let your partner ask for one kind of private attention you will focus on giving. | Ask your partner for one kind of private attention they will focus on giving. | One partner wants to fulfil a focused request and the other wants to make one. |
| 60 | 4 | Choose how you want your partner to make you feel desired in private. | Make your partner feel desired in private in the way they choose. | One partner wants to name a desired experience and the other wants to deliver it. |
## Symmetric swipe proposals
| # | Intensity | Text | Partner text | Matched outcome |
|---:|---:|---|---|---|
| 1 | 2 | Exchange a private message naming one thing you find irresistible about each other. | null | Both partners agree to trade direct expressions of attraction. |
| 2 | 2 | Give each other one compliment that feels more personal than usual. | null | Both partners want a more intimate exchange of appreciation. |
| 3 | 3 | Send each other a voice note that sets a private mood. | null | Both partners agree to create anticipation through voice. |
| 4 | 4 | Share one private wish each and discuss what would make it feel exciting and safe. | null | Both partners agree to reveal a private wish and discuss its boundaries. |
| 5 | 2 | Plan a private surprise for each other using only a few clues. | null | Both partners agree to build anticipation through limited information. |
| 6 | 3 | Agree on a private signal that starts an anticipated experience. | null | Both partners want a mutual cue that begins their planned moment. |
| 7 | 3 | Leave each other one private hint about something you want to try together. | null | Both partners agree to exchange hints before discussing an idea. |
| 8 | 4 | Plan a private experience where neither of you knows the full sequence beforehand. | null | Both partners want surprise while retaining an agreed framework. |
| 9 | 2 | Take turns planning a private date where the other person only knows the starting point. | null | Both partners agree to alternate between leading and being surprised. |
| 10 | 3 | Take turns giving one private instruction and following one. | null | Both partners want to alternate consensual direction and follow-through. |
| 11 | 3 | Choose a private role for each other and agree whether to swap halfway through. | null | Both partners want a role-based experience with an optional switch. |
| 12 | 4 | Take turns setting the pace during an agreed private experience. | null | Both partners agree to hand leadership back and forth. |
| 13 | 2 | Each choose one part of a private date and combine the plans. | null | Both partners want to build a shared private date. |
| 14 | 3 | Make a private list of three ideas each and choose one from the other person’s list. | null | Both partners agree to select a mutually desired new idea. |
| 15 | 3 | Use a coin toss to decide who leads the first part of your private time together. | null | Both partners want a playful, equal way to assign the opening lead. |
| 16 | 4 | Agree that either person can pause, redirect, or end a private experience without explanation. | null | Both partners establish equal control over continuation and change. |
| 17 | 2 | Choose a simple phrase that means you want more reassurance. | null | Both partners agree on a shared reassurance signal. |
| 18 | 3 | Name one thing that helps you feel secure when trying something unfamiliar. | null | Both partners agree to share and honour their comfort needs. |
| 19 | 3 | Agree on a signal that means slow down and a signal that means stop. | null | Both partners establish clear ways to adjust or end an experience. |
| 20 | 4 | Discuss one private boundary you want honoured before exploring a more intense idea. | null | Both partners agree to state a firm limit before escalating. |
| 21 | 2 | Tell each other how you most like to reconnect after a private date. | null | Both partners agree to share their preferred reconnection style. |
| 22 | 3 | Set aside time to tell each other what felt especially good about a private experience. | null | Both partners agree to exchange affirming feedback. |
| 23 | 3 | Choose a shared ritual that helps you feel close after intimate time. | null | Both partners want a repeatable ritual of reconnection. |
| 24 | 4 | Agree to check in with each other after trying something that feels emotionally intense. | null | Both partners commit to care and reflection after deeper exploration. |
| 25 | 2 | Take turns receiving five uninterrupted minutes of affectionate attention. | null | Both partners agree to give and receive undivided affection. |
| 26 | 3 | Take turns asking for the kind of private attention you most want. | null | Both partners agree to voice a specific desire and respond to it. |
| 27 | 3 | Give each other a turn where one person only receives and the other only gives attention. | null | Both partners want reciprocal focused giving and receiving. |
| 28 | 4 | Tell each other one way you want to feel desired, then make space for both requests. | null | Both partners agree to make each other feel desired in individually meaningful ways. |
| 29 | 4 | Take turns creating a private experience centred entirely on the other person’s enjoyment. | null | Both partners want to alternate between giving undivided attention and receiving it. |
| 30 | 4 | Agree to ask directly for what you want and answer honestly about what you can give. | null | Both partners agree to a direct, consent-led exchange of desire. |
@@ -0,0 +1,484 @@
# Concept Set B
Status: authored source fixture. Source-only and unpublished.
This document contains 60 asymmetric concepts and 30 symmetric concepts for the
play-safe spicy catalogue repair. Each entry has a theme, intensity, primary
copy, exact partner copy, and an intended outcome.
## Asymmetric concepts
### Sensory cues
#### A01
Theme: Sensory cues
Intensity: 2
Primary text: Would you be up for choosing a scent that you want your partner to wear for a close evening?
Partner text: Would you be up for choosing a scent that you want your partner to wear for a close evening?
Intended outcome: One partner selects a scent cue for the other to use on their next intentional date night.
#### A02
Theme: Sensory cues
Intensity: 3
Primary text: Would you like your partner to prepare a playlist that sets the mood for a more intimate evening with you?
Partner text: Would you like to prepare a playlist that sets the mood for a more intimate evening with your partner?
Intended outcome: The receiving partner gets a personally chosen mood playlist.
#### A03
Theme: Sensory cues
Intensity: 3
Primary text: Would you enjoy your partner guiding you through a slow eyes-closed guessing game with safe household textures?
Partner text: Would you enjoy guiding your partner through a slow eyes-closed guessing game with safe household textures?
Intended outcome: The proposer leads a private sensory game and the receiver agrees to participate.
#### A04
Theme: Sensory cues
Intensity: 3
Primary text: Would you like your partner to choose the lighting for a close evening together?
Partner text: Would you like to choose the lighting for a close evening together with your partner?
Intended outcome: One partner creates the visual atmosphere while the other agrees to experience it.
#### A05
Theme: Sensory cues
Intensity: 4
Primary text: Would you be open to your partner asking you to focus only on their voice for a few uninterrupted minutes?
Partner text: Would you be open to asking your partner to focus only on your voice for a few uninterrupted minutes?
Intended outcome: The proposer receives undivided, voice-led attention in private.
#### A06
Theme: Sensory cues
Intensity: 3
Primary text: Would you like your partner to pick a dessert or drink for you to share slowly after your date?
Partner text: Would you like to pick a dessert or drink for your partner to share slowly after your date?
Intended outcome: The proposer chooses a shared treat that extends a date into a close moment.
#### A07
Theme: Sensory cues
Intensity: 4
Primary text: Would you enjoy your partner choosing one song and asking you to stay close with them until it ends?
Partner text: Would you enjoy choosing one song and asking your partner to stay close with you until it ends?
Intended outcome: The couple agrees to a deliberate, uninterrupted closeness ritual around one song.
#### A08
Theme: Sensory cues
Intensity: 2
Primary text: Would you like your partner to leave a note telling you one small detail they find especially attractive about you?
Partner text: Would you like to leave your partner a note telling them one small detail you find especially attractive about them?
Intended outcome: The proposer gives the receiver a specific, private compliment in writing.
### Private performance
#### A09
Theme: Private performance
Intensity: 2
Primary text: Would you like your partner to choose a song and perform a playful lip-sync just for you?
Partner text: Would you like to choose a song and perform a playful lip-sync just for your partner?
Intended outcome: One partner gives the other a private, playful performance.
#### A10
Theme: Private performance
Intensity: 3
Primary text: Would you enjoy your partner giving you a short, confident compliment speech before your next date night?
Partner text: Would you enjoy giving your partner a short, confident compliment speech before your next date night?
Intended outcome: The receiver gets a deliberate verbal build-up before the date.
#### A11
Theme: Private performance
Intensity: 3
Primary text: Would you be up for your partner choosing a song for you to slow-dance to together at home?
Partner text: Would you be up for choosing a song for you and your partner to slow-dance to together at home?
Intended outcome: The proposer picks the soundtrack for a private slow dance.
#### A12
Theme: Private performance
Intensity: 4
Primary text: Would you like your partner to tell you, face to face, the version of you they find hardest to resist?
Partner text: Would you like to tell your partner, face to face, the version of them you find hardest to resist?
Intended outcome: The couple shares a direct, personal desire statement.
#### A13
Theme: Private performance
Intensity: 2
Primary text: Would you enjoy your partner making you laugh with a deliberately over-the-top romantic introduction?
Partner text: Would you enjoy making your partner laugh with a deliberately over-the-top romantic introduction?
Intended outcome: One partner performs a comic, affectionate introduction for the other.
#### A14
Theme: Private performance
Intensity: 3
Primary text: Would you like your partner to write and read you a short invitation for a private evening together?
Partner text: Would you like to write and read your partner a short invitation for a private evening together?
Intended outcome: The proposer creates a spoken invitation that frames the evening.
#### A15
Theme: Private performance
Intensity: 4
Primary text: Would you be open to your partner asking you to sit back while they set the pace for the first part of your evening?
Partner text: Would you be open to asking your partner to sit back while you set the pace for the first part of your evening?
Intended outcome: The proposer leads the opening part of a mutually agreed private evening.
#### A16
Theme: Private performance
Intensity: 3
Primary text: Would you like your partner to reveal one new side of their confidence through a private dance for you?
Partner text: Would you like to reveal one new side of your confidence through a private dance for your partner?
Intended outcome: One partner shares a confidence-focused private dance.
### Clothes and confidence
#### A17
Theme: Clothes and confidence
Intensity: 2
Primary text: Would you like your partner to choose one outfit detail for your next date that makes them smile?
Partner text: Would you like to choose one outfit detail for your partner's next date that makes you smile?
Intended outcome: The proposer chooses a small style detail for the receiver to wear.
#### A18
Theme: Clothes and confidence
Intensity: 3
Primary text: Would you enjoy your partner putting together a look for you that feels more daring than your usual style?
Partner text: Would you enjoy putting together a look for your partner that feels more daring than their usual style?
Intended outcome: One partner styles the other for a bolder but comfortable date-night look.
#### A19
Theme: Clothes and confidence
Intensity: 3
Primary text: Would you like your partner to ask you to wear the colour they most associate with you feeling confident?
Partner text: Would you like to ask your partner to wear the colour you most associate with them feeling confident?
Intended outcome: The receiver wears a colour chosen for its confidence association.
#### A20
Theme: Clothes and confidence
Intensity: 4
Primary text: Would you be open to your partner choosing the dress code for a private evening, within limits you both agree first?
Partner text: Would you be open to choosing the dress code for a private evening, within limits you both agree first?
Intended outcome: One partner leads the evening's agreed presentation and atmosphere.
#### A21
Theme: Clothes and confidence
Intensity: 2
Primary text: Would you like your partner to send you a message before your date saying exactly what they hope you will wear?
Partner text: Would you like to send your partner a message before your date saying exactly what you hope they will wear?
Intended outcome: The receiver gets a style preference message before a date.
#### A22
Theme: Clothes and confidence
Intensity: 3
Primary text: Would you enjoy your partner choosing an accessory for you to wear as a private signal between you?
Partner text: Would you enjoy choosing an accessory for your partner to wear as a private signal between you?
Intended outcome: The couple establishes a subtle shared signal using an accessory.
#### A23
Theme: Clothes and confidence
Intensity: 4
Primary text: Would you be up for your partner asking you to get ready slowly while they give you one sincere compliment at each step?
Partner text: Would you be up for asking your partner to get ready slowly while you give them one sincere compliment at each step?
Intended outcome: The proposer gives the receiver a paced confidence ritual before a night together.
#### A24
Theme: Clothes and confidence
Intensity: 3
Primary text: Would you like your partner to choose a 'stay in' outfit for you that feels intentionally different from everyday clothes?
Partner text: Would you like to choose a 'stay in' outfit for your partner that feels intentionally different from everyday clothes?
Intended outcome: The proposer selects a special at-home look for the receiver.
### Remaining authored asymmetric entries
The following numbered entries retain the authored Concept Set B material for
fantasy exchange, roleplay, settings and atmosphere, surprise and novelty, and
long-distance desire. Their primary text, partner text, and intended outcome
are deliberately retained verbatim in the task transcript for integration.
#### A25
Theme: Fantasy exchange
Intensity: 2
Primary text: Would you like your partner to tell you about a fictional date they would plan for the two of you with no practical limits?
Partner text: Would you like to tell your partner about a fictional date you would plan for the two of you with no practical limits?
Intended outcome: The proposer shares an imaginative, romance-forward date scenario.
#### A26
Theme: Fantasy exchange
Intensity: 3
Primary text: Would you enjoy your partner writing a three-line scene about the two of you meeting as strangers in a glamorous place?
Partner text: Would you enjoy writing a three-line scene about you and your partner meeting as strangers in a glamorous place?
Intended outcome: The couple gets a short shared fantasy prompt to discuss or play with.
#### A27
Theme: Fantasy exchange
Intensity: 3
Primary text: Would you be open to your partner asking what kind of attention makes you feel most wanted in an imagined scenario?
Partner text: Would you be open to asking your partner what kind of attention makes them feel most wanted in an imagined scenario?
Intended outcome: The receiver names a preferred kind of romantic attention without needing to commit to it.
#### A28
Theme: Fantasy exchange
Intensity: 4
Primary text: Would you like your partner to propose a fictional version of tonight where they get to surprise you, and you choose the boundaries first?
Partner text: Would you like to propose a fictional version of tonight where you get to surprise your partner, and they choose the boundaries first?
Intended outcome: One partner proposes an agreed fantasy frame, with the receiver setting limits.
#### A29
Theme: Fantasy exchange
Intensity: 2
Primary text: Would you like your partner to invent a nickname for the more confident version of you?
Partner text: Would you like to invent a nickname for the more confident version of your partner?
Intended outcome: The couple creates a playful private identity cue.
#### A30
Theme: Fantasy exchange
Intensity: 3
Primary text: Would you enjoy your partner describing the setting of their ideal slow-burn evening with you?
Partner text: Would you enjoy describing the setting of your ideal slow-burn evening with your partner?
Intended outcome: The proposer shares a detailed atmosphere preference that can inform a future plan.
#### A31
Theme: Fantasy exchange
Intensity: 4
Primary text: Would you be open to your partner asking you what you would want them to take charge of in an imaginary evening?
Partner text: Would you be open to asking your partner what they would want you to take charge of in an imaginary evening?
Intended outcome: The receiver identifies one area of leadership they would welcome in fantasy.
#### A32
Theme: Fantasy exchange
Intensity: 3
Primary text: Would you like your partner to send you a private message that starts, 'If we had the whole evening to ourselves...' ?
Partner text: Would you like to send your partner a private message that starts, 'If we had the whole evening to ourselves...' ?
Intended outcome: The proposer sends an open-ended desire prompt for a private exchange.
#### A33
Theme: Roleplay
Intensity: 2
Primary text: Would you like your partner to pretend they are meeting you for the first time and ask you out again?
Partner text: Would you like to pretend you are meeting your partner for the first time and ask them out again?
Intended outcome: The proposer recreates a first-meeting invitation in character.
#### A34
Theme: Roleplay
Intensity: 3
Primary text: Would you enjoy your partner taking the role of your personal host for an evening at home?
Partner text: Would you enjoy taking the role of your partner's personal host for an evening at home?
Intended outcome: One partner plans and hosts a dedicated private evening.
#### A35
Theme: Roleplay
Intensity: 3
Primary text: Would you be up for your partner acting as a confident stranger who has one hour to win you over?
Partner text: Would you be up for acting as a confident stranger who has one hour to win your partner over?
Intended outcome: The proposer takes on a flirtatious stranger character during an agreed private scenario.
#### A36
Theme: Roleplay
Intensity: 4
Primary text: Would you like your partner to choose a role where they lead the conversation and you decide when to step out of character?
Partner text: Would you like to choose a role where you lead the conversation and your partner decides when to step out of character?
Intended outcome: The proposer leads a consensual roleplay conversation with a clear exit controlled by the receiver.
#### A37
Theme: Roleplay
Intensity: 2
Primary text: Would you enjoy your partner writing you a pretend invitation to an exclusive event for two?
Partner text: Would you enjoy writing your partner a pretend invitation to an exclusive event for two?
Intended outcome: The proposer creates a playful invitation that introduces a private date concept.
#### A38
Theme: Roleplay
Intensity: 3
Primary text: Would you like your partner to play the part of a stylist who gives you one confidence challenge for the evening?
Partner text: Would you like to play the part of a stylist who gives your partner one confidence challenge for the evening?
Intended outcome: One partner sets a light, agreed confidence challenge in character.
#### A39
Theme: Roleplay
Intensity: 4
Primary text: Would you be open to your partner choosing a character who is bolder than they usually are, while you set the limits of the scene?
Partner text: Would you be open to choosing a character who is bolder than you usually are, while your partner sets the limits of the scene?
Intended outcome: The proposer explores a bolder role while the receiver sets the scene's boundaries.
#### A40
Theme: Settings and atmosphere
Intensity: 2
Primary text: Would you like your partner to turn one room into a date-night space before you arrive?
Partner text: Would you like to turn one room into a date-night space before your partner arrives?
Intended outcome: The proposer prepares a distinct at-home setting for the receiver.
#### A41
Theme: Settings and atmosphere
Intensity: 3
Primary text: Would you enjoy your partner choosing a place in your home where you are not usually romantic and making it feel special?
Partner text: Would you enjoy choosing a place in your home where you and your partner are not usually romantic and making it feel special?
Intended outcome: One partner transforms an ordinary indoor space into a deliberate romantic setting.
#### A42
Theme: Settings and atmosphere
Intensity: 3
Primary text: Would you like your partner to plan a candlelit late-night snack for the two of you after everything else is quiet?
Partner text: Would you like to plan a candlelit late-night snack for you and your partner after everything else is quiet?
Intended outcome: The proposer creates a calm, intimate-feeling post-evening ritual.
#### A43
Theme: Settings and atmosphere
Intensity: 4
Primary text: Would you be open to your partner arranging a private evening where your phones stay out of reach until you both agree to check them?
Partner text: Would you be open to arranging a private evening where your phones stay out of reach until you both agree to check them?
Intended outcome: The couple agrees to protected, distraction-free private time.
#### A44
Theme: Settings and atmosphere
Intensity: 2
Primary text: Would you like your partner to choose the route for a slow evening walk that ends somewhere cosy?
Partner text: Would you like to choose the route for a slow evening walk that ends somewhere cosy with your partner?
Intended outcome: The proposer directs a low-pressure shared walk toward a close setting.
#### A45
Theme: Settings and atmosphere
Intensity: 3
Primary text: Would you enjoy your partner planning a 'no agenda' hour where they only focus on making you feel looked after?
Partner text: Would you enjoy planning a 'no agenda' hour where you only focus on making your partner feel looked after?
Intended outcome: One partner takes responsibility for a gentle, attentiveness-focused hour.
#### A46
Theme: Settings and atmosphere
Intensity: 4
Primary text: Would you like your partner to choose a private setting for a conversation about what would make tonight feel exciting to you?
Partner text: Would you like to choose a private setting for a conversation about what would make tonight feel exciting to your partner?
Intended outcome: The proposer creates the conditions for a direct desire and boundaries conversation.
#### A47
Theme: Surprise and novelty
Intensity: 2
Primary text: Would you like your partner to leave you a sealed note with one romantic surprise inside?
Partner text: Would you like to leave your partner a sealed note with one romantic surprise inside?
Intended outcome: The proposer creates a small, consent-safe romantic surprise for the receiver.
#### A48
Theme: Surprise and novelty
Intensity: 3
Primary text: Would you enjoy your partner planning one part of your evening that they do not reveal until you have both agreed to begin?
Partner text: Would you enjoy planning one part of your evening that you do not reveal until you and your partner have both agreed to begin?
Intended outcome: One partner prepares a mutually agreed surprise element.
#### A49
Theme: Surprise and novelty
Intensity: 3
Primary text: Would you like your partner to give you a choice between two different kinds of close evening, without telling you which they hope you pick?
Partner text: Would you like to give your partner a choice between two different kinds of close evening, without telling them which you hope they pick?
Intended outcome: The couple uses a playful choice to reveal preferences and select a plan.
#### A50
Theme: Surprise and novelty
Intensity: 4
Primary text: Would you be open to your partner asking you to say yes to one surprise date-night detail, after they tell you the category but not the detail?
Partner text: Would you be open to asking your partner to say yes to one surprise date-night detail, after you tell them the category but not the detail?
Intended outcome: The receiver consents to a bounded surprise with enough information to choose safely.
#### A51
Theme: Surprise and novelty
Intensity: 2
Primary text: Would you like your partner to hide a clue somewhere at home that leads to a compliment from them?
Partner text: Would you like to hide a clue somewhere at home that leads your partner to a compliment from you?
Intended outcome: The proposer creates a mini treasure hunt ending in affirmation.
#### A52
Theme: Surprise and novelty
Intensity: 3
Primary text: Would you enjoy your partner choosing a new shared ritual for the first ten minutes of date night?
Partner text: Would you enjoy choosing a new shared ritual for the first ten minutes of date night with your partner?
Intended outcome: The proposer introduces a repeatable opening ritual for intentional time together.
#### A53
Theme: Surprise and novelty
Intensity: 4
Primary text: Would you like your partner to surprise you with a confidence challenge that you can change or stop at any point?
Partner text: Would you like to surprise your partner with a confidence challenge that they can change or stop at any point?
Intended outcome: The proposer offers a novel confidence prompt with explicit receiver control.
#### A54
Theme: Long-distance desire
Intensity: 2
Primary text: Would you like your partner to send you a voice note describing the first thing they want to do when you next have time alone together?
Partner text: Would you like to send your partner a voice note describing the first thing you want to do when you next have time alone together?
Intended outcome: The proposer shares an anticipation-building voice message for the next reunion.
#### A55
Theme: Long-distance desire
Intensity: 3
Primary text: Would you enjoy your partner planning a video-date mood for you to join from wherever you are?
Partner text: Would you enjoy planning a video-date mood for your partner to join from wherever they are?
Intended outcome: One partner sets a shared remote date atmosphere.
#### A56
Theme: Long-distance desire
Intensity: 3
Primary text: Would you like your partner to send you a message telling you what they miss most about being close to you?
Partner text: Would you like to send your partner a message telling them what you miss most about being close to them?
Intended outcome: The receiver gets a specific intimacy-adjacent expression of longing.
#### A57
Theme: Long-distance desire
Intensity: 4
Primary text: Would you be open to your partner arranging a private call where you both say one thing you hope to explore together when you reunite?
Partner text: Would you be open to arranging a private call where you both say one thing you hope to explore together when you reunite?
Intended outcome: The couple holds a mutual, forward-looking conversation about agreed future intimacy.
#### A58
Theme: Long-distance desire
Intensity: 2
Primary text: Would you like your partner to send you a photo of something that reminds them of your chemistry?
Partner text: Would you like to send your partner a photo of something that reminds you of your chemistry?
Intended outcome: The proposer shares a safe visual cue connected to their relationship.
#### A59
Theme: Long-distance desire
Intensity: 3
Primary text: Would you enjoy your partner choosing a song for you to listen to at the same time from different places?
Partner text: Would you enjoy choosing a song for you and your partner to listen to at the same time from different places?
Intended outcome: The pair creates a synchronized remote closeness ritual.
#### A60
Theme: Long-distance desire
Intensity: 4
Primary text: Would you like your partner to ask you for one honest promise about how you will make time for each other when distance ends?
Partner text: Would you like to ask your partner for one honest promise about how you will make time for each other when distance ends?
Intended outcome: The couple makes a concrete reunion-intent commitment.
## Symmetric concepts
Each symmetric entry uses the same text as its partner text.
| ID | Theme | Intensity | Primary text | Partner text | Intended outcome |
| --- | --- | ---: | --- | --- | --- |
| S01 | Sensory cues | 2 | Would you both be up for choosing a shared scent for your next intentional evening? | Same as primary text. | The couple selects one scent cue for an upcoming date night. |
| S02 | Sensory cues | 3 | Would you both enjoy a quiet, eyes-closed music moment together before your next date? | Same as primary text. | The pair agrees to begin a date with focused shared listening. |
| S03 | Sensory cues | 3 | Would you both like to make a list of five sounds that help you feel relaxed and close? | Same as primary text. | The couple creates a personal sound-based mood guide. |
| S04 | Sensory cues | 4 | Would you both be open to spending ten minutes in silence, staying close and checking in afterward about how it felt? | Same as primary text. | The couple tries a deliberate quiet closeness exercise followed by a check-in. |
| S05 | Private performance | 2 | Would you both be up for taking turns giving each other a dramatic, affectionate introduction? | Same as primary text. | Each partner performs a playful confidence-building introduction. |
| S06 | Private performance | 3 | Would you both enjoy choosing one song each for a private dance together? | Same as primary text. | The pair shares two intentionally selected dances. |
| S07 | Private performance | 3 | Would you both like to read each other a short note about what makes a date feel exciting? | Same as primary text. | Each partner names what builds anticipation for them. |
| S08 | Private performance | 4 | Would you both be open to taking turns leading a five-minute close moment, with either person able to pause it? | Same as primary text. | The partners alternate leadership with clear stop control. |
| S09 | Clothes and confidence | 2 | Would you both enjoy choosing one small style detail for each other's next date-night look? | Same as primary text. | Each partner receives a playful style suggestion. |
| S10 | Clothes and confidence | 3 | Would you both like to wear something that makes you feel more confident than usual for an evening at home? | Same as primary text. | Both partners intentionally dress for confidence at home. |
| S11 | Clothes and confidence | 3 | Would you both be up for sharing one compliment about how the other partner looks when they feel most like themselves? | Same as primary text. | Each partner receives an appearance-based affirmation. |
| S12 | Clothes and confidence | 4 | Would you both be open to agreeing a private dress code for a date night that feels new to you? | Same as primary text. | The couple jointly chooses a novel, mutually comfortable date-night presentation. |
| S13 | Fantasy exchange | 2 | Would you both enjoy inventing an imaginary getaway designed entirely around feeling close? | Same as primary text. | The couple co-creates a romantic fantasy setting. |
| S14 | Fantasy exchange | 3 | Would you both like to finish the sentence, 'A version of us with no interruptions would...' ? | Same as primary text. | Each partner shares an open-ended private-time wish. |
| S15 | Fantasy exchange | 3 | Would you both be up for naming one fictional setting that makes romance feel more exciting to you? | Same as primary text. | The pair discovers compatible atmosphere preferences. |
| S16 | Fantasy exchange | 4 | Would you both be open to making a shared 'maybe someday' list of private experiences you could discuss before trying? | Same as primary text. | The couple creates a consent-first discussion list without committing to any item. |
| S17 | Roleplay | 2 | Would you both enjoy pretending you are on a first date and asking each other three new questions? | Same as primary text. | The couple refreshes their connection through a first-date scenario. |
| S18 | Roleplay | 3 | Would you both like to choose characters for a one-hour at-home date with a clear time to step out of character? | Same as primary text. | The pair co-designs a bounded, playful roleplay date. |
| S19 | Roleplay | 3 | Would you both be up for writing each other an invitation from a fictional admirer? | Same as primary text. | Each partner receives a flirtatious, invented invitation. |
| S20 | Roleplay | 4 | Would you both be open to trying a roleplay where one person leads the scene and you switch roles halfway through? | Same as primary text. | Both partners experience leading and following within an agreed fictional scene. |
| S21 | Settings and atmosphere | 2 | Would you both enjoy turning off the usual lights and creating a new at-home date setting together? | Same as primary text. | The pair jointly changes the mood of a familiar room. |
| S22 | Settings and atmosphere | 3 | Would you both like to plan an evening with one room, one playlist, and no other plans? | Same as primary text. | The couple reserves an intentionally simple, focused evening. |
| S23 | Settings and atmosphere | 3 | Would you both be up for creating a shared 'do not disturb' signal for private time at home? | Same as primary text. | The couple agrees on a respectful cue that protects their uninterrupted time. |
| S24 | Settings and atmosphere | 4 | Would you both be open to a private evening where you begin by agreeing what would make each of you feel cared for? | Same as primary text. | The couple starts a close evening with mutual needs and limits stated aloud. |
| S25 | Surprise and novelty | 2 | Would you both enjoy swapping one sealed note with a date-night idea inside? | Same as primary text. | Each partner contributes one surprise idea for future time together. |
| S26 | Surprise and novelty | 3 | Would you both like to choose a new ritual for saying goodbye at the end of a date? | Same as primary text. | The pair establishes a novel end-of-date connection ritual. |
| S27 | Surprise and novelty | 4 | Would you both be open to choosing one surprise element for a future evening, with a clear option to change your mind? | Same as primary text. | The couple chooses a bounded surprise while preserving ongoing consent. |
| S28 | Long-distance desire | 2 | Would you both enjoy sending each other one voice note that makes the next reunion feel closer? | Same as primary text. | Both partners exchange anticipation-building audio messages. |
| S29 | Long-distance desire | 3 | Would you both like to plan the first hour of your next reunion from different places? | Same as primary text. | The couple co-plans a specific reconnecting ritual for their reunion. |
| S30 | Long-distance desire | 4 | Would you both be open to a private call where you share one hope and one boundary for your next time alone together? | Same as primary text. | The couple aligns on future closeness through reciprocal desire and boundary sharing. |
## Count check
- Asymmetric entries: A01 through A60, 60 total.
- Symmetric entries: S01 through S30, 30 total.
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,45 @@
# Play-safe spicy question draft v1
Status: source-only, unpublished, not approved, imported, or published. This is an editorial draft for human policy review.
## Packs
- Flirty Communication: 55 questions.
- Anticipation: 38 questions.
- Taking the Lead: 35 questions.
- Following: 26 questions.
- Shared Control: 24 questions.
- Sensory Cues: 41 questions.
- Private Performance: 34 questions.
- Clothes and Confidence: 40 questions.
- Fantasy Exchange: 31 questions.
- Roleplay: 29 questions.
- Settings and Atmosphere: 40 questions.
- Boundaries and Check-ins: 35 questions.
- Surprise and Novelty: 33 questions.
- Long-distance Desire: 23 questions.
- Giving and Receiving Attention: 37 questions.
- Aftercare and Reconnection: 29 questions.
## Exact distribution
- 550 total: 300 swipe, 100 text_answer, 70 audio, 40 photo, 40 who_likely.
- Intensities: 120 at level 2, 300 at level 3, 130 at level 4.
- Swipe: 120 asymmetric concepts as 120 primaries plus 120 inverse rows, and 60 symmetric rows.
## Current in-app outcomes
- Swipe proposals create a mutual yes or maybe result. Asymmetric rows use reversed partner wording and inverse links.
- Text answers let each partner provide the final private detail in their own words.
- Audio prompts create a private 60-second spoken result.
- Photo prompts request only safe clothed outfits, objects, notes, lighting, and settings.
- Who-is-more-likely prompts create a light comparison and a natural private follow-up.
## Policy boundaries
Developer-authored wording avoids named sexual acts, anatomy, sex toys, fetish labels, nudity, public sexual activity, coercion, and instructional wording. Intensity 4 uses edge-reviewed language about adult private dynamics, boundaries, roles, surprise, and user-led detail. This is not a claim of Google approval.
Known limitation: the current API accepts response_data loosely. This source draft assumes no structured compatibility builder or safe user-generated-content moderation envelope, and uses only existing supported question types.
Run node scripts/validate-question-draft.mjs to verify this source-only contract,
including artificial discriminator and repeated-opening checks.
@@ -0,0 +1,405 @@
# Play-safe spicy text-answer concept source
This source preserves 100 authored `text_answer` concepts for review. It is a draft content source only and is not a database seed or deployment input.
## Entries
1. **Theme:** Flirty communication | **Intensity:** 2
**Prompt:** Write the compliment you most want your partner to give you before a close moment.
**Intended outcome:** Your partner learns the exact reassurance that makes you feel wanted and can use it as a sincere opening.
2. **Theme:** Flirty communication | **Intensity:** 2
**Prompt:** Finish this sentence for your partner: “When you look at me like that, I want you to know...”
**Intended outcome:** Your partner receives a personal signal about the effect their attention has on you.
3. **Theme:** Flirty communication | **Intensity:** 3
**Prompt:** Write a one-line invitation that would make you smile if your partner sent it unexpectedly.
**Intended outcome:** Your partner gets a ready-to-use private message and learns the tone that draws you in.
4. **Theme:** Flirty communication | **Intensity:** 3
**Prompt:** Describe the kind of teasing remark that feels playful to you rather than awkward.
**Intended outcome:** Your partner learns your preferred style of flirtation and a boundary around what keeps it fun.
5. **Theme:** Flirty communication | **Intensity:** 3
**Prompt:** Tell your partner one thing they do that immediately changes the mood between you.
**Intended outcome:** Your partner discovers a specific behaviour worth repeating when you both want more closeness.
6. **Theme:** Flirty communication | **Intensity:** 4
**Prompt:** Write the boldest thing you would like your partner to say to you privately, in your own words.
**Intended outcome:** Your partner receives direct permission to use language that feels exciting to you.
7. **Theme:** Flirty communication | **Intensity:** 4
**Prompt:** Describe a private conversation you would love to have when you both feel especially open.
**Intended outcome:** Your partner learns the subject you are curious to explore and can choose a comfortable time to start it.
8. **Theme:** Anticipation | **Intensity:** 2
**Prompt:** Describe the ideal first hint that a special evening together is coming.
**Intended outcome:** Your partner learns how to build anticipation in a way that feels welcome rather than vague.
9. **Theme:** Anticipation | **Intensity:** 2
**Prompt:** Write the kind of message that would make you look forward to seeing your partner later.
**Intended outcome:** Your partner gets a concrete cue they can send before you reconnect.
10. **Theme:** Anticipation | **Intensity:** 3
**Prompt:** Tell your partner what preparation would make you feel more relaxed before a private date at home.
**Intended outcome:** Your partner learns a practical condition that helps you arrive present and receptive.
11. **Theme:** Anticipation | **Intensity:** 3
**Prompt:** Describe a small change to your usual evening routine that would make it feel more intentional.
**Intended outcome:** Your partner receives one realistic ritual to create a sense of occasion together.
12. **Theme:** Anticipation | **Intensity:** 3
**Prompt:** Write what you would enjoy being asked to choose before your next intimate evening.
**Intended outcome:** Your partner learns where offering you a choice creates more excitement.
13. **Theme:** Anticipation | **Intensity:** 4
**Prompt:** Share one private surprise you would genuinely enjoy your partner planning for you.
**Intended outcome:** Your partner receives a clear idea for an unexpected gesture, including the kind of surprise you welcome.
14. **Theme:** Anticipation | **Intensity:** 4
**Prompt:** Write a short countdown message you would like to receive before a night focused on the two of you.
**Intended outcome:** Your partner gets language that builds suspense without guessing what would land well.
15. **Theme:** Taking the lead | **Intensity:** 2
**Prompt:** Describe one part of a date where you would enjoy deciding what happens next.
**Intended outcome:** Your partner learns a low-pressure moment where your initiative would feel attractive.
16. **Theme:** Taking the lead | **Intensity:** 2
**Prompt:** Write an invitation you would feel confident making when you want more one-to-one time.
**Intended outcome:** Your partner receives a phrase that makes your interest unmistakable and easy to answer.
17. **Theme:** Taking the lead | **Intensity:** 3
**Prompt:** Tell your partner what kind of plan you would enjoy taking charge of from start to finish.
**Intended outcome:** Your partner learns which shared experience you want to own and can leave space for you to lead it.
18. **Theme:** Taking the lead | **Intensity:** 3
**Prompt:** Describe how you would like your partner to respond when you make a bold suggestion.
**Intended outcome:** Your partner gets a practical guide for encouraging your confidence in the moment.
19. **Theme:** Taking the lead | **Intensity:** 3
**Prompt:** Write one decision you would like to make for both of you during a private evening.
**Intended outcome:** Your partner learns the kind of authority you want to try, with a concrete decision to hand over.
20. **Theme:** Taking the lead | **Intensity:** 4
**Prompt:** Describe a confident version of yourself you would like your partner to see more often.
**Intended outcome:** Your partner learns what confidence looks like for you and how they can invite it out.
21. **Theme:** Taking the lead | **Intensity:** 4
**Prompt:** Write the private challenge you would like to set for your partner, including what would make it exciting.
**Intended outcome:** Your partner receives a consensual challenge with the reason it appeals to you.
22. **Theme:** Following | **Intensity:** 2
**Prompt:** Tell your partner one decision you would enjoy not having to make on a date.
**Intended outcome:** Your partner learns a simple place where taking initiative would feel caring.
23. **Theme:** Following | **Intensity:** 2
**Prompt:** Describe the kind of invitation that makes it easy for you to say yes.
**Intended outcome:** Your partner learns the wording and atmosphere that lower pressure for you.
24. **Theme:** Following | **Intensity:** 3
**Prompt:** Write what helps you trust your partner’s lead during a more intimate moment.
**Intended outcome:** Your partner learns the specific reassurance or check-in that helps you relax into their plan.
25. **Theme:** Following | **Intensity:** 3
**Prompt:** Share one surprise choice you would enjoy your partner making for you.
**Intended outcome:** Your partner gets a defined area where surprise feels exciting and safe.
26. **Theme:** Following | **Intensity:** 3
**Prompt:** Describe how you want your partner to check that you are still enjoying their lead.
**Intended outcome:** Your partner receives a clear check-in preference they can use without breaking the mood.
27. **Theme:** Following | **Intensity:** 4
**Prompt:** Write about a private situation where you would like to be guided step by step, in your own terms.
**Intended outcome:** Your partner learns the type of guidance you are curious about and the limits you want respected.
28. **Theme:** Following | **Intensity:** 4
**Prompt:** Tell your partner what would make it feel exciting to let them choose the pace for a while.
**Intended outcome:** Your partner learns the conditions that make you comfortable giving them more influence.
29. **Theme:** Shared control | **Intensity:** 2
**Prompt:** Name one decision you want to make together before your next close evening.
**Intended outcome:** Your partner learns where collaboration matters most to you and can plan it jointly.
30. **Theme:** Shared control | **Intensity:** 2
**Prompt:** Describe your ideal signal for “let’s keep going” during a private moment.
**Intended outcome:** Your partner receives a simple shared cue that makes mutual enthusiasm easier to read.
31. **Theme:** Shared control | **Intensity:** 3
**Prompt:** Write the rule you would like both of you to follow when trying something unfamiliar together.
**Intended outcome:** Your partner learns a mutual agreement that makes exploration feel safer.
32. **Theme:** Shared control | **Intensity:** 3
**Prompt:** Tell your partner how you would like to alternate who chooses the next part of a date.
**Intended outcome:** Your partner gets a workable rhythm for sharing initiative rather than defaulting to one person.
33. **Theme:** Shared control | **Intensity:** 3
**Prompt:** Describe one moment where you would enjoy pausing together to decide what you both want.
**Intended outcome:** Your partner learns when a joint decision would add connection instead of interrupting it.
34. **Theme:** Shared control | **Intensity:** 4
**Prompt:** Write the private agreement that would make you feel brave enough to explore more together.
**Intended outcome:** Your partner receives a concrete mutual promise that supports bolder, consent-led choices.
35. **Theme:** Shared control | **Intensity:** 4
**Prompt:** Share the question you want your partner to ask before either of you raises the intensity.
**Intended outcome:** Your partner learns the exact invitation to use before moving into more charged territory.
36. **Theme:** Boundaries and check-ins | **Intensity:** 2
**Prompt:** Write one phrase that means “I like this, but slower” for you.
**Intended outcome:** Your partner receives a clear signal for adjusting pace without uncertainty.
37. **Theme:** Boundaries and check-ins | **Intensity:** 2
**Prompt:** Describe the best way for your partner to ask whether you are comfortable.
**Intended outcome:** Your partner learns a check-in style that feels caring rather than clinical.
38. **Theme:** Boundaries and check-ins | **Intensity:** 3
**Prompt:** Tell your partner one thing you need before you can enjoy a more adventurous private plan.
**Intended outcome:** Your partner learns a condition that turns curiosity into a safer shared possibility.
39. **Theme:** Boundaries and check-ins | **Intensity:** 3
**Prompt:** Write a kind way you would want your partner to respond if you change your mind.
**Intended outcome:** Your partner gets direct guidance for making a change of plan feel respected and easy.
40. **Theme:** Boundaries and check-ins | **Intensity:** 3
**Prompt:** Describe one topic you would prefer to discuss before, rather than during, a close moment.
**Intended outcome:** Your partner learns what deserves a calm conversation in advance.
41. **Theme:** Boundaries and check-ins | **Intensity:** 4
**Prompt:** Share a boundary that makes you feel more able to be adventurous within it.
**Intended outcome:** Your partner learns the limit that creates freedom rather than reducing the mood.
42. **Theme:** Boundaries and check-ins | **Intensity:** 4
**Prompt:** Write the reassurance you need when you suggest something that feels vulnerable to say aloud.
**Intended outcome:** Your partner receives a practical response that helps you bring up bolder ideas honestly.
43. **Theme:** Aftercare and reconnection | **Intensity:** 2
**Prompt:** Describe how you like to reconnect after an especially close moment.
**Intended outcome:** Your partner learns the small act that helps you feel settled and connected afterward.
44. **Theme:** Aftercare and reconnection | **Intensity:** 2
**Prompt:** Write the first question you would like your partner to ask after trying something new together.
**Intended outcome:** Your partner receives a ready-made invitation for a thoughtful debrief.
45. **Theme:** Aftercare and reconnection | **Intensity:** 3
**Prompt:** Tell your partner what makes you feel appreciated after you have been emotionally open.
**Intended outcome:** Your partner learns how to recognise your vulnerability in a way that lands.
46. **Theme:** Aftercare and reconnection | **Intensity:** 3
**Prompt:** Describe the kind of quiet time you would enjoy sharing once the excitement has passed.
**Intended outcome:** Your partner gets a concrete picture of the calm connection you want afterward.
47. **Theme:** Aftercare and reconnection | **Intensity:** 3
**Prompt:** Write one thing you would like to hear after you take a private risk together.
**Intended outcome:** Your partner learns the affirmation that helps you feel proud rather than self-conscious.
48. **Theme:** Aftercare and reconnection | **Intensity:** 4
**Prompt:** Share the part of a close experience you would most want to talk through afterward.
**Intended outcome:** Your partner learns what reflection would deepen trust and make a future experience better.
49. **Theme:** Giving and receiving attention | **Intensity:** 2
**Prompt:** Describe one way your partner can give you their full attention that you rarely ask for.
**Intended outcome:** Your partner discovers a specific act of focus that makes you feel prioritised.
50. **Theme:** Giving and receiving attention | **Intensity:** 2
**Prompt:** Write about a moment when you feel most noticed by your partner.
**Intended outcome:** Your partner learns the context in which their attention has the greatest effect.
51. **Theme:** Giving and receiving attention | **Intensity:** 3
**Prompt:** Tell your partner what you enjoy doing when you want to make them feel desired.
**Intended outcome:** Your partner learns how you naturally show interest and can recognise the signal.
52. **Theme:** Giving and receiving attention | **Intensity:** 3
**Prompt:** Describe the kind of praise you most enjoy receiving privately.
**Intended outcome:** Your partner gets a personal guide to language that makes you feel seen.
53. **Theme:** Giving and receiving attention | **Intensity:** 3
**Prompt:** Write one request you would enjoy hearing from your partner when they want your attention.
**Intended outcome:** Your partner receives a low-pressure way to ask directly for more of you.
54. **Theme:** Giving and receiving attention | **Intensity:** 4
**Prompt:** Share the private gesture that would make you feel unmistakably chosen by your partner.
**Intended outcome:** Your partner learns a specific, high-impact way to make you feel desired.
55. **Theme:** Sensory cues | **Intensity:** 2
**Prompt:** Describe a scent that instantly makes an evening feel more intimate to you.
**Intended outcome:** Your partner learns an atmosphere cue they can use when planning a close night in.
56. **Theme:** Sensory cues | **Intensity:** 2
**Prompt:** Write about the music that would help you leave everyday stress behind together.
**Intended outcome:** Your partner gets a mood-setting choice that supports a more connected evening.
57. **Theme:** Sensory cues | **Intensity:** 3
**Prompt:** Tell your partner which kind of lighting makes you feel most confident and relaxed.
**Intended outcome:** Your partner learns a practical way to shape the room around your comfort.
58. **Theme:** Sensory cues | **Intensity:** 3
**Prompt:** Describe a texture, fabric, or cosy detail that makes a private setting feel special.
**Intended outcome:** Your partner receives an actionable detail for creating a setting that appeals to you.
59. **Theme:** Sensory cues | **Intensity:** 3
**Prompt:** Write about the sound of your partner’s voice that you find most captivating.
**Intended outcome:** Your partner learns a vocal quality that makes you feel drawn closer.
60. **Theme:** Sensory cues | **Intensity:** 4
**Prompt:** Share the full sensory mood you would like your partner to create for an intimate evening.
**Intended outcome:** Your partner receives a personal brief for a more immersive, intentional atmosphere.
61. **Theme:** Private performance | **Intensity:** 2
**Prompt:** Write about a playful thing you would enjoy doing just to make your partner smile in private.
**Intended outcome:** Your partner learns a personal expression of your confidence they can encourage.
62. **Theme:** Private performance | **Intensity:** 2
**Prompt:** Describe a song that would make you feel expressive if you had the room to yourselves.
**Intended outcome:** Your partner gets a soundtrack that invites a more playful side of you out.
63. **Theme:** Private performance | **Intensity:** 3
**Prompt:** Tell your partner what kind of attention helps you feel confident being watched by them.
**Intended outcome:** Your partner learns how to make their attention feel encouraging rather than exposing.
64. **Theme:** Private performance | **Intensity:** 3
**Prompt:** Write a private dare you would enjoy accepting from your partner.
**Intended outcome:** Your partner receives a consent-led challenge that fits your idea of playful boldness.
65. **Theme:** Private performance | **Intensity:** 3
**Prompt:** Describe the reaction from your partner that would make you want to be more expressive.
**Intended outcome:** Your partner learns the feedback that builds your confidence in the moment.
66. **Theme:** Private performance | **Intensity:** 4
**Prompt:** Share a private moment where you would enjoy being the centre of your partner’s attention.
**Intended outcome:** Your partner learns the kind of focused attention you are curious to receive and can plan it with you.
67. **Theme:** Clothes and confidence | **Intensity:** 2
**Prompt:** Describe an outfit that makes you feel more like yourself on a date with your partner.
**Intended outcome:** Your partner learns the look that supports your confidence and can compliment it specifically.
68. **Theme:** Clothes and confidence | **Intensity:** 2
**Prompt:** Write the colour you would love your partner to notice you wearing.
**Intended outcome:** Your partner gets a small visual detail to notice and celebrate.
69. **Theme:** Clothes and confidence | **Intensity:** 3
**Prompt:** Tell your partner what you would enjoy choosing for them to wear on a private date night.
**Intended outcome:** Your partner learns your taste and has a clear invitation to let you style part of the evening.
70. **Theme:** Clothes and confidence | **Intensity:** 3
**Prompt:** Describe the compliment about your appearance that would feel most meaningful from your partner.
**Intended outcome:** Your partner receives guidance for praise that strengthens your confidence.
71. **Theme:** Clothes and confidence | **Intensity:** 3
**Prompt:** Write about one detail of getting ready that makes you feel more alluring.
**Intended outcome:** Your partner learns the ritual that helps you shift into a more confident mood.
72. **Theme:** Clothes and confidence | **Intensity:** 4
**Prompt:** Share the private look you would be excited to reveal to your partner, in your own words.
**Intended outcome:** Your partner learns what kind of reveal feels exciting to you and can respond with the right attention.
73. **Theme:** Fantasy exchange | **Intensity:** 2
**Prompt:** Write about a fictional romance scene whose mood you would love to borrow for a date.
**Intended outcome:** Your partner learns the emotional tone you are drawn to without needing to guess the reference.
74. **Theme:** Fantasy exchange | **Intensity:** 2
**Prompt:** Describe the kind of imagined setting that makes you feel more adventurous with your partner.
**Intended outcome:** Your partner receives a setting idea that can inspire a themed evening.
75. **Theme:** Fantasy exchange | **Intensity:** 3
**Prompt:** Tell your partner what role you would enjoy imagining for yourself during a playful private scenario.
**Intended outcome:** Your partner learns the character energy you are curious to try.
76. **Theme:** Fantasy exchange | **Intensity:** 3
**Prompt:** Write a fictional opening line your partner could use to begin a shared fantasy.
**Intended outcome:** Your partner gets a tailored starting line for playful imagination together.
77. **Theme:** Fantasy exchange | **Intensity:** 3
**Prompt:** Describe the one detail that would make an imagined scenario feel believable and exciting to you.
**Intended outcome:** Your partner learns the ingredient that turns a vague idea into something engaging.
78. **Theme:** Fantasy exchange | **Intensity:** 4
**Prompt:** Share a private fantasy premise you would like to explore only in conversation first.
**Intended outcome:** Your partner learns a higher-intensity idea while the first follow-up remains a safe discussion.
79. **Theme:** Roleplay | **Intensity:** 2
**Prompt:** Write the nickname you would enjoy your partner using during a playful private game.
**Intended outcome:** Your partner receives language that helps set a playful tone you welcome.
80. **Theme:** Roleplay | **Intensity:** 2
**Prompt:** Describe a simple character trait you would enjoy your partner pretending to have for one evening.
**Intended outcome:** Your partner gets a low-stakes roleplay cue that can make an ordinary night feel new.
81. **Theme:** Roleplay | **Intensity:** 3
**Prompt:** Tell your partner the kind of made-up first meeting you would enjoy acting out together.
**Intended outcome:** Your partner learns a shared scenario that gives both of you a clear starting point.
82. **Theme:** Roleplay | **Intensity:** 3
**Prompt:** Write the setting where you would enjoy pretending you have just met your partner.
**Intended outcome:** Your partner receives a scene to build around for a playful reset of familiar chemistry.
83. **Theme:** Roleplay | **Intensity:** 3
**Prompt:** Describe the one rule that would help roleplay feel comfortable and fun for you.
**Intended outcome:** Your partner learns the agreement that keeps the game connected to your real trust.
84. **Theme:** Roleplay | **Intensity:** 4
**Prompt:** Share a bolder character dynamic you are curious to discuss with your partner before trying it.
**Intended outcome:** Your partner learns what dynamic interests you and can talk through comfort, limits, and interest first.
85. **Theme:** Settings and atmosphere | **Intensity:** 2
**Prompt:** Describe the room where you would most like to have an uninterrupted evening with your partner.
**Intended outcome:** Your partner learns which environment helps you feel most present together.
86. **Theme:** Settings and atmosphere | **Intensity:** 2
**Prompt:** Write the first thing you would change at home to make a regular night feel more special.
**Intended outcome:** Your partner gets a realistic way to shift an ordinary evening into a date.
87. **Theme:** Settings and atmosphere | **Intensity:** 3
**Prompt:** Tell your partner what makes a hotel-style evening feel exciting to you.
**Intended outcome:** Your partner learns the elements of novelty or escape you would like recreated.
88. **Theme:** Settings and atmosphere | **Intensity:** 3
**Prompt:** Describe the kind of privacy you need before you can fully switch off together.
**Intended outcome:** Your partner learns the practical conditions needed for you to feel unhurried.
89. **Theme:** Settings and atmosphere | **Intensity:** 3
**Prompt:** Write about a place from a past date that you would love to recreate the feeling of at home.
**Intended outcome:** Your partner receives an emotional reference point and ideas for bringing it back.
90. **Theme:** Settings and atmosphere | **Intensity:** 4
**Prompt:** Share the most adventurous private setting you would enjoy planning together, while staying within your comfort zone.
**Intended outcome:** Your partner learns your appetite for novelty and the limits that make the plan feel right.
91. **Theme:** Surprise and novelty | **Intensity:** 2
**Prompt:** Write one small unexpected gesture that would brighten a normal day with your partner.
**Intended outcome:** Your partner gets a specific surprise that signals private affection.
92. **Theme:** Surprise and novelty | **Intensity:** 2
**Prompt:** Describe the kind of spontaneous invitation you would be happy to receive.
**Intended outcome:** Your partner learns how to be impulsive in a way that still feels welcome.
93. **Theme:** Surprise and novelty | **Intensity:** 3
**Prompt:** Tell your partner one familiar routine you would enjoy changing up together.
**Intended outcome:** Your partner learns where novelty would improve your shared rhythm.
94. **Theme:** Surprise and novelty | **Intensity:** 3
**Prompt:** Write a sealed-note prompt you would enjoy your partner giving you to open later.
**Intended outcome:** Your partner receives a private prompt format that can create suspense across the day.
95. **Theme:** Surprise and novelty | **Intensity:** 3
**Prompt:** Describe a new rule for one evening that would make it feel different from your usual time together.
**Intended outcome:** Your partner gets a simple novelty experiment that you have already signalled interest in.
96. **Theme:** Surprise and novelty | **Intensity:** 4
**Prompt:** Share the bold surprise you would enjoy discovering after your partner has checked the broad limits with you.
**Intended outcome:** Your partner learns both the surprise you want and the consent conversation needed before planning it.
97. **Theme:** Long-distance desire | **Intensity:** 2
**Prompt:** Write the message that would make you feel closest to your partner when you are apart.
**Intended outcome:** Your partner learns the kind of connection that travels well between visits.
98. **Theme:** Long-distance desire | **Intensity:** 2
**Prompt:** Describe a tiny ritual you would like to share on the evenings you cannot be together.
**Intended outcome:** Your partner receives a repeatable way to maintain intimacy across distance.
99. **Theme:** Long-distance desire | **Intensity:** 3
**Prompt:** Tell your partner what you would like to plan together before your next time in the same place.
**Intended outcome:** Your partner learns which shared anticipation would make a reunion more meaningful.
100. **Theme:** Long-distance desire | **Intensity:** 4
**Prompt:** Write the private promise you would like to exchange before a longer period apart.
**Intended outcome:** Your partner receives a personal commitment that can sustain desire, reassurance, and connection until you reunite.
+45
View File
@@ -0,0 +1,45 @@
# Question interaction types
This is the maintained contract for the question interactions delivered through
the daily question flow. It is separate from the [couples quiz](quiz.md) and
[dares loop](dares-loop.md), which have their own content and completion
models.
<!-- question-types: swipe,text_answer,audio,photo,who_likely -->
## Supported types
| Type | User interaction | `response_data` and `config` | Match semantics |
| --- | --- | --- | --- |
| `swipe` | Choose yes or no, or skip. | No `response_data`; `config` is unused. | A no creates no match. The API also retains `maybe` matching for stored or replayed answers, producing `yes_maybe` or `maybe_maybe`; the current mobile card does not offer maybe. |
| `text_answer` | Write an answer and send it, decline with no, or skip. | `response_data`: `{ type: 'text_answer', text }`; `config` is unused. | Both partners must answer without a no. The match type is `both_answered`, with both payloads in `response_summary`. |
| `audio` | Record and send an audio answer, or skip. | `response_data`: `{ type: 'audio', media_path, duration_seconds }`; optional `config.max_duration_seconds` limits recording and defaults to 60 seconds in the admin UI. | Both partners must answer without a no. The match type is `both_answered`, with both payloads in `response_summary`. |
| `photo` | Take or select and send a photo answer, or skip. | `response_data`: `{ type: 'photo', media_path }`; `config` is unused. | Both partners must answer without a no. The match type is `both_answered`, with both payloads in `response_summary`. |
| `who_likely` | Choose which partner is more likely. The current mobile flow submits this as yes. | `response_data`: `{ type: 'who_likely', chosen_user_id }`; `config` is unused. | Once both partners respond, the match type is `both_answered`; the stored answer value does not gate that type's match calculation. Both choices are in `response_summary`. |
## Persistence and implementation ownership
`questions.question_type` defaults to `swipe`; the database check constraint and
the canonical shared `QuestionType` union must contain the same values.
`questions.config` is JSON configuration for a type. `responses.response_data`
stores an individual non-swipe payload. `matches.response_summary` is a JSON
object keyed by responder user ID and is populated for `both_answered` matches.
The canonical list is `QuestionType` in
[`packages/shared/src/types/index.ts`](../packages/shared/src/types/index.ts).
The database constraint is in
[`apps/api/drizzle/0003_answers_matches.sql`](../apps/api/drizzle/0003_answers_matches.sql).
Match calculation is in
[`apps/api/src/domains/answers/types.ts`](../apps/api/src/domains/answers/types.ts),
the mobile renderer is
[`apps/mobile/src/features/swipe/components/SwipeQuestionCard.tsx`](../apps/mobile/src/features/swipe/components/SwipeQuestionCard.tsx),
and admin authoring options are in
[`apps/admin/src/pages/content/QuestionsPage.tsx`](../apps/admin/src/pages/content/QuestionsPage.tsx).
## Change rule
When adding, removing, or changing a question type, its payload, configuration,
or matching semantics, update this document in the same change. Update the
shared type, database constraint, API, mobile, and admin consumers as required.
`npm run lint:harness` checks the documented type list against the canonical
shared union so an unreviewed list change fails routine verification.
+20 -5
View File
@@ -47,6 +47,8 @@ erDiagram
uuid pack_id FK uuid pack_id FK
text text text text
text partner_text "for two-part questions" text partner_text "for two-part questions"
text question_type "swipe/text_answer/audio/photo/who_likely"
jsonb config "type-specific configuration"
int intensity "1-5" int intensity "1-5"
text[] allowed_couple_genders "couple composition filter" text[] allowed_couple_genders "couple composition filter"
text[] target_user_genders "individual user filter" text[] target_user_genders "individual user filter"
@@ -70,6 +72,7 @@ erDiagram
uuid question_id FK uuid question_id FK
uuid couple_id FK "nullable: null is a sealed solo answer, claimed at pairing" uuid couple_id FK "nullable: null is a sealed solo answer, claimed at pairing"
answer_type answer "yes/no/maybe" answer_type answer "yes/no/maybe"
jsonb response_data "non-swipe answer payload"
timestamptz created_at timestamptz created_at
} }
@@ -77,7 +80,8 @@ erDiagram
uuid id PK uuid id PK
uuid couple_id FK uuid couple_id FK
uuid question_id FK uuid question_id FK
match_type match_type "yes_yes/yes_maybe/maybe_maybe" match_type match_type "yes_yes/yes_maybe/maybe_maybe/both_answered"
jsonb response_summary "both non-swipe payloads by user"
boolean is_new boolean is_new
timestamptz created_at timestamptz created_at
} }
@@ -161,7 +165,8 @@ erDiagram
| Enum | Values | | Enum | Values |
|------|--------| |------|--------|
| `answer_type` | `yes`, `no`, `maybe` | | `answer_type` | `yes`, `no`, `maybe` |
| `match_type` | `yes_yes`, `yes_maybe`, `maybe_maybe` | | `question_type` | `swipe`, `text_answer`, `audio`, `photo`, `who_likely` |
| `match_type` | `yes_yes`, `yes_maybe`, `maybe_maybe`, `both_answered` |
| `feedback_type` | `bug`, `feature_request`, `general` | | `feedback_type` | `bug`, `feature_request`, `general` |
| `feedback_status` | `new`, `reviewed`, `in_progress`, `resolved`, `closed` | | `feedback_status` | `new`, `reviewed`, `in_progress`, `resolved`, `closed` |
| `subscription_status` | `active`, `cancelled`, `expired`, `billing_issue`, `paused` | | `subscription_status` | `active`, `cancelled`, `expired`, `billing_issue`, `paused` |
@@ -188,11 +193,21 @@ allowed.
## Key Flows ## Key Flows
### Question interactions
`questions.question_type` defaults to `swipe`; `questions.config` holds
type-specific settings. A non-swipe response keeps its payload in
`responses.response_data`. When a non-swipe interaction resolves as
`both_answered`, `matches.response_summary` holds both payloads keyed by user
ID. The complete interaction and match contract is maintained in
[Question interaction types](question-types.md).
### Response & Match Flow ### Response & Match Flow
1. User submits response via `submit-response` edge function 1. User submits a response to the standalone API at `POST /v1/responses`
2. Response saved to `responses` table (UPSERT on `user_id, question_id`) 2. Response saved to `responses` table (UPSERT on `user_id, question_id`)
3. Function checks for partner's response on same question 3. API checks for the partner's response on the same question
4. If both answered positively (not "no"), a `match` is created 4. API applies the [question interaction contract](question-types.md) to create,
update, or remove a `match`
5. Match triggers push notification to both partners 5. Match triggers push notification to both partners
6. Match unlocks chat thread in `messages` table 6. Match unlocks chat thread in `messages` table
+18 -1
View File
@@ -1,7 +1,8 @@
import { access, readFile, readdir } from 'node:fs/promises'; import { access, readFile, readdir } from 'node:fs/promises';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url';
const root = path.resolve(new URL('..', import.meta.url).pathname); const root = path.resolve(fileURLToPath(new URL('..', import.meta.url)));
const errors = []; const errors = [];
const scoped = process.argv.includes('--scope'); const scoped = process.argv.includes('--scope');
@@ -156,6 +157,22 @@ if (!scoped) {
if (!(await exists(target))) fail(path.join(root, 'docs', 'index.md'), `broken documentation link ${match[1]}`, 'correct the link or restore the maintained document'); if (!(await exists(target))) fail(path.join(root, 'docs', 'index.md'), `broken documentation link ${match[1]}`, 'correct the link or restore the maintained document');
} }
const sharedTypesFile = path.join(root, 'packages', 'shared', 'src', 'types', 'index.ts');
const sharedTypes = await readFile(sharedTypesFile, 'utf8');
const union = sharedTypes.match(/export type QuestionType\s*=\s*([^;]+);/);
const canonicalQuestionTypes = union?.[1].match(/'([^']+)'/g)?.map((value) => value.slice(1, -1));
const questionTypesDoc = path.join(root, 'docs', 'question-types.md');
const questionTypesSource = await readFile(questionTypesDoc, 'utf8');
const documentedQuestionTypes = questionTypesSource.match(/<!-- question-types:\s*([^>]+)\s*-->/)?.[1]
.split(',').map((value) => value.trim()).filter(Boolean);
if (!canonicalQuestionTypes?.length) {
fail(sharedTypesFile, 'QuestionType union could not be read', 'keep QuestionType as a quoted string union');
} else if (!documentedQuestionTypes?.length) {
fail(questionTypesDoc, 'question type contract marker is missing', 'add <!-- question-types: type-a,type-b --> with every supported type');
} else if (canonicalQuestionTypes.join(',') !== documentedQuestionTypes.join(',')) {
fail(questionTypesDoc, 'documented question types differ from the shared QuestionType union', `set the contract marker to ${canonicalQuestionTypes.join(',')}`);
}
const mcpAuth = await readFile(path.join(root, 'apps', 'mcp', 'src', 'lib', 'auth.ts'), 'utf8'); const mcpAuth = await readFile(path.join(root, 'apps', 'mcp', 'src', 'lib', 'auth.ts'), 'utf8');
if (/allowing all requests|query\(['"]apiKey/i.test(mcpAuth)) { if (/allowing all requests|query\(['"]apiKey/i.test(mcpAuth)) {
fail(path.join(root, 'apps', 'mcp', 'src', 'lib', 'auth.ts'), 'MCP authentication can leak or fail open', 'require a configured Bearer token and fail closed'); fail(path.join(root, 'apps', 'mcp', 'src', 'lib', 'auth.ts'), 'MCP authentication can leak or fail open', 'require a configured Bearer token and fail closed');
+3 -2
View File
@@ -1,7 +1,8 @@
import { readFile, readdir } from 'node:fs/promises'; import { readFile, readdir } from 'node:fs/promises';
import path from 'node:path'; import path from 'node:path';
import { fileURLToPath } from 'node:url';
const root = path.resolve(new URL('..', import.meta.url).pathname); const root = path.resolve(fileURLToPath(new URL('..', import.meta.url)));
const base = path.join(root, '.agents', 'learnings'); const base = path.join(root, '.agents', 'learnings');
const requiredSections = ['## Symptom', '## Reproduction', '## Cause', '## Remediation', '## Evidence']; const requiredSections = ['## Symptom', '## Reproduction', '## Cause', '## Remediation', '## Evidence'];
const failures = []; const failures = [];
@@ -11,7 +12,7 @@ for (const state of ['inbox', 'promoted']) {
for (const name of await readdir(directory)) { for (const name of await readdir(directory)) {
if (!name.endsWith('.md')) continue; if (!name.endsWith('.md')) continue;
const file = path.join(directory, name); const file = path.join(directory, name);
const source = await readFile(file, 'utf8'); const source = (await readFile(file, 'utf8')).replace(/\r\n/g, '\n');
const frontmatter = source.match(/^---\n([\s\S]*?)\n---/); const frontmatter = source.match(/^---\n([\s\S]*?)\n---/);
if (!frontmatter) { failures.push(`${file}: missing YAML frontmatter`); continue; } if (!frontmatter) { failures.push(`${file}: missing YAML frontmatter`); continue; }
const fields = Object.fromEntries(frontmatter[1].split('\n').map((line) => { const fields = Object.fromEntries(frontmatter[1].split('\n').map((line) => {
+210
View File
@@ -0,0 +1,210 @@
#!/usr/bin/env node
import { readFile } from 'node:fs/promises';
const draftPath = new URL('../docs/product/question-drafts/play-safe-spicy-v1.json', import.meta.url);
const snapshotPath = new URL('../apps/supabase/catalog-snapshots/production-2026-08-28.json', import.meta.url);
const markdownPath = new URL('../docs/product/question-drafts/play-safe-spicy-v1.md', import.meta.url);
const textSourcePath = new URL('../docs/product/question-drafts/text-answer-concept-source.md', import.meta.url);
const audioSourcePath = new URL('../docs/product/question-drafts/audio-concept-source.md', import.meta.url);
const draft = JSON.parse(await readFile(draftPath, 'utf8'));
const snapshot = JSON.parse(await readFile(snapshotPath, 'utf8'));
const markdown = await readFile(markdownPath, 'utf8');
const textSource = await readFile(textSourcePath, 'utf8');
const audioSource = await readFile(audioSourcePath, 'utf8');
const errors = [];
const expect = (condition, message) => { if (!condition) errors.push(message); };
const normalize = (value) => value.toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim();
const questionTypes = new Set(['swipe', 'text_answer', 'audio', 'photo', 'who_likely']);
const banned = /\b(?:anal|anus|asshole|bdsm|bestiality|blowjob|breath play|chok(?:e|ing)|cock|cum|dick|dildo|domination|fetish|fingering|handjob|incest|masturbat|naked|nud(?:e|ity)|oral sex|orgasm|penis|porn|public sex|sex toy|spank(?:ing)?|threesome|vagina|vibrator)\b/i;
const timeBound = /\b(?:today|tonight|right now|\bnow\b)\b/i;
const gendered = /\b(?:boyfriend|girlfriend|husband|wife|him|her|his|hers)\b/i;
const photoUnsafe = /\b(?:body part|lingerie|nude|naked|proof|skin|undress|your body)\b/i;
const artificialSuffix = /\b(?:option|response focus|voice focus|visual cue|variant|version)\s*\d+\b/i;
const malformedOrFallback = /\b(?:let your partner to|as they for|share this private idea|take the complementary role|response focus|voice focus|bolder mood|confident outfit|unfamiliar private dynamic)\b/i;
const genericOutcome = /\b(?:this match reveals whether|specific written starting point|take the complementary role|gains a personal cue for a close conversation)\b/i;
const incompleteSwipe = /^(?:a |an |the |trying |swapping )/i;
const nonActionableSwipe = /^(?:close|private|shared|more|something|anything|it|that)\b/i;
const markdownPackNames = {
'taking-the-lead': 'Taking the Lead',
'clothes-confidence': 'Clothes and Confidence',
'settings-atmosphere': 'Settings and Atmosphere',
'boundaries-check-ins': 'Boundaries and Check-ins',
'surprise-novelty': 'Surprise and Novelty',
'long-distance-desire': 'Long-distance Desire',
'giving-receiving-attention': 'Giving and Receiving Attention',
'aftercare-reconnection': 'Aftercare and Reconnection',
};
const sourcePackSlugs = {
'flirty communication': 'flirty-communication',
anticipation: 'anticipation',
'taking the lead': 'taking-the-lead',
following: 'following',
'shared control': 'shared-control',
'sensory cues': 'sensory-cues',
'private performance': 'private-performance',
'clothes and confidence': 'clothes-confidence',
'fantasy exchange': 'fantasy-exchange',
roleplay: 'roleplay',
'settings and atmosphere': 'settings-atmosphere',
'boundaries and check-ins': 'boundaries-check-ins',
'surprise and novelty': 'surprise-novelty',
'long-distance desire': 'long-distance-desire',
'giving and receiving attention': 'giving-receiving-attention',
'aftercare and reconnection': 'aftercare-reconnection',
};
const parseSource = (source, pattern, type) => [...source.matchAll(pattern)].map((match) => ({
pack_slug: sourcePackSlugs[match[1].toLowerCase()],
intensity: Number(match[2]),
text: match[3],
intended_outcome: match[4],
question_type: type,
}));
const textSourceRows = parseSource(textSource, /^\d+\. \*\*Theme:\*\* (.+?) \| \*\*Intensity:\*\* (\d)\s*\r?\n\s*\*\*Prompt:\*\* (.+?)\s*\r?\n\s*\*\*Intended outcome:\*\* (.+?)$/gm, 'text_answer');
const audioSourceRows = parseSource(audioSource, /^\d+\. Theme: (.+?) \| Intensity: (\d)\s*\r?\n\s*Prompt: (.+?)\s*\r?\n\s*Intended outcome: (.+?)$/gm, 'audio');
expect(textSourceRows.length === 100, `expected 100 text source rows, got ${textSourceRows.length}`);
expect(audioSourceRows.length === 70, `expected 70 audio source rows, got ${audioSourceRows.length}`);
const sourceKey = (row) => JSON.stringify([row.pack_slug, row.intensity, row.text, row.intended_outcome]);
expect(Array.isArray(draft.packs) && Array.isArray(draft.questions), 'draft must contain packs and questions arrays');
const questions = draft.questions ?? [];
expect(questions.length === 550, `expected 550 questions, got ${questions.length}`);
expect((draft.packs ?? []).length > 0, 'draft must contain at least one pack');
const ids = new Set();
const byId = new Map();
const typeCounts = Object.fromEntries([...questionTypes].map((type) => [type, 0]));
const intensityCounts = Object.fromEntries([1, 2, 3, 4, 5].map((level) => [level, 0]));
const packCounts = new Map();
const draftTexts = new Map();
const openingCounts = new Map();
const ngramRows = new Map();
const outcomeClusters = new Map();
const responseLeadingVerbs = new Map();
const requiredFormatOpenings = new Set(['share a photo of', 'who is more likely']);
const snapshotTexts = new Set((snapshot.tables?.questions ?? []).map((row) => normalize(row.text)));
for (const [index, question] of questions.entries()) {
const label = `question ${index + 1}`;
expect(typeof question.id === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(question.id), `${label} has invalid UUID`);
expect(!ids.has(question.id), `${label} duplicates UUID ${question.id}`);
ids.add(question.id); byId.set(question.id, question);
expect(typeof question.pack_slug === 'string' && question.pack_slug.length > 0, `${label} has no pack_slug`);
packCounts.set(question.pack_slug, (packCounts.get(question.pack_slug) ?? 0) + 1);
expect(typeof question.text === 'string' && question.text.length > 0, `${label} has no text`);
expect(!artificialSuffix.test(question.text), `${label} contains an artificial template discriminator`);
expect(!malformedOrFallback.test(question.text), `${label} contains malformed or generic fallback copy`);
expect(questionTypes.has(question.question_type), `${label} has unsupported type ${question.question_type}`);
if (questionTypes.has(question.question_type)) typeCounts[question.question_type] += 1;
expect([2, 3, 4].includes(question.intensity), `${label} has invalid intensity ${question.intensity}`);
if (Object.hasOwn(intensityCounts, question.intensity)) intensityCounts[question.intensity] += 1;
expect(question.review_status === 'draft', `${label} must be draft`);
expect(['low', 'edge'].includes(question.policy_risk), `${label} has invalid policy_risk`);
expect(typeof question.intended_outcome === 'string' && question.intended_outcome.length > 0, `${label} has no intended_outcome`);
expect(!genericOutcome.test(question.intended_outcome), `${label} has a generic fallback outcome`);
expect(question.allowed_couple_genders === null && question.target_user_genders === null && question.required_props === null, `${label} must use null targeting and props`);
const textFields = [question.text, question.partner_text, question.intended_outcome].filter(Boolean);
for (const text of textFields) {
expect(!banned.test(text), `${label} contains banned developer-authored term`);
expect(!timeBound.test(text), `${label} contains time-bound language`);
expect(!gendered.test(text), `${label} contains gendered partner language`);
expect(!text.includes('\u2014'), `${label} contains an em dash`);
}
const normalized = normalize(question.text);
if (question.question_type === 'swipe') {
expect(!incompleteSwipe.test(question.text) && !nonActionableSwipe.test(question.text), `${label} swipe text must be a complete actionable proposal`);
if (question.partner_text !== null) {
expect(question.text !== question.partner_text, `${label} asymmetric swipe must use distinct partner wording`);
expect(!incompleteSwipe.test(question.partner_text) && !nonActionableSwipe.test(question.partner_text), `${label} asymmetric partner_text must be a complete actionable proposal`);
}
}
if (question.question_type === 'text_answer' || question.question_type === 'audio') {
const leadingVerb = normalized.split(' ')[0];
const key = `${question.question_type}:${leadingVerb}`;
responseLeadingVerbs.set(key, (responseLeadingVerbs.get(key) ?? 0) + 1);
const outcomeCluster = normalize(question.intended_outcome)
.replace(/\b(?:your|partner|the|couple|one|a|an|and|or|can|to|with|for|of|that|this|their|them|you)\b/g, ' ')
.replace(/\s+/g, ' ')
.trim();
const rows = outcomeClusters.get(outcomeCluster) ?? [];
rows.push(label);
outcomeClusters.set(outcomeCluster, rows);
}
const opening = normalized.split(' ').slice(0, 4).join(' ');
if (!requiredFormatOpenings.has(opening)) {
openingCounts.set(opening, (openingCounts.get(opening) ?? 0) + 1);
}
// An inverse row intentionally repeats its primary row's partner wording.
// Check only primary and symmetric text here; inverse swap integrity is
// checked below.
if (question.inverse_of === null) {
expect(!draftTexts.has(normalized), `${label} duplicates draft text from ${draftTexts.get(normalized)}`);
draftTexts.set(normalized, label);
const words = normalized.split(' ').filter(Boolean);
for (let start = 0; start <= words.length - 6; start += 1) {
const ngram = words.slice(start, start + 6).join(' ');
const rows = ngramRows.get(ngram) ?? [];
rows.push(label);
ngramRows.set(ngram, rows);
}
}
expect(!snapshotTexts.has(normalized), `${label} duplicates production snapshot text`);
if (question.question_type === 'audio') expect(JSON.stringify(question.config) === JSON.stringify({ max_duration_seconds: 60 }), `${label} audio config must be max_duration_seconds 60`);
if (question.question_type === 'text_answer') expect(JSON.stringify(question.config) === JSON.stringify({ max_length: 500 }), `${label} text_answer config must be max_length 500`);
if (['swipe', 'photo', 'who_likely'].includes(question.question_type)) expect(JSON.stringify(question.config) === '{}', `${label} ${question.question_type} config must be empty`);
if (question.question_type !== 'swipe') {
expect(question.partner_text === null && question.inverse_of === null, `${label} non-swipe must have null partner_text and inverse_of`);
}
if (question.question_type === 'photo') expect(!photoUnsafe.test(question.text), `${label} photo prompt is not safely clothed/object/setting based`);
}
for (const [opening, count] of openingCounts) {
expect(count <= 30, `opening template repeats ${count} times: ${opening}`);
}
for (const [ngram, rows] of ngramRows) {
expect(rows.length <= 10, `six-word phrase repeats ${rows.length} times across non-inverse rows: ${ngram}`);
}
for (const [type, sourceRows] of [['text_answer', textSourceRows], ['audio', audioSourceRows]]) {
const catalogueRows = questions.filter((question) => question.question_type === type);
const sourceKeys = new Set(sourceRows.map(sourceKey));
expect(sourceKeys.size === sourceRows.length, `${type} source contains duplicate concepts`);
expect(catalogueRows.length === sourceRows.length, `${type} source and catalogue counts differ`);
for (const question of catalogueRows) {
expect(sourceKeys.has(sourceKey(question)), `${type} row ${question.id} differs from the authored source`);
}
}
for (const [key, count] of responseLeadingVerbs) {
const [type, leadingVerb] = key.split(':');
const total = type === 'text_answer' ? 100 : 70;
expect(count <= Math.ceil(total * 0.6), `${type} leading verb "${leadingVerb}" repeats ${count} times`);
}
for (const [cluster, rows] of outcomeClusters) {
expect(cluster.length >= 16, `${rows.join(', ')} has an overly generic response outcome`);
expect(rows.length === 1, `response outcome cluster repeats across ${rows.join(', ')}`);
}
for (const [type, count] of Object.entries({ swipe: 300, text_answer: 100, audio: 70, photo: 40, who_likely: 40 })) expect(typeCounts[type] === count, `expected ${count} ${type}, got ${typeCounts[type]}`);
for (const [level, count] of Object.entries({ 1: 0, 2: 120, 3: 300, 4: 130, 5: 0 })) expect(intensityCounts[level] === count, `expected ${count} intensity ${level}, got ${intensityCounts[level]}`);
const swipes = questions.filter((question) => question.question_type === 'swipe');
const asymmetric = swipes.filter((question) => question.partner_text !== null);
const symmetric = swipes.filter((question) => question.partner_text === null);
expect(asymmetric.length === 240, `expected 240 asymmetric swipe rows, got ${asymmetric.length}`);
expect(symmetric.length === 60, `expected 60 symmetric swipe rows, got ${symmetric.length}`);
const primaries = asymmetric.filter((question) => question.inverse_of === null);
const inverses = asymmetric.filter((question) => question.inverse_of !== null);
expect(primaries.length === 120 && inverses.length === 120, `expected 120 primary and 120 inverse swipe rows, got ${primaries.length} and ${inverses.length}`);
for (const question of inverses) {
const primary = byId.get(question.inverse_of);
expect(primary?.question_type === 'swipe' && primary.inverse_of === null, `inverse ${question.id} must reference a primary swipe`);
if (primary) {
expect(question.text === primary.partner_text && question.partner_text === primary.text, `inverse ${question.id} must exactly swap text and partner_text`);
expect(question.intensity === primary.intensity && question.pack_slug === primary.pack_slug, `inverse ${question.id} must keep intensity and pack`);
}
}
for (const question of symmetric) expect(question.inverse_of === null, `symmetric swipe ${question.id} must have null inverse_of`);
expect([...packCounts.values()].every((count) => count > 0), 'every declared pack must contain questions');
for (const pack of draft.packs ?? []) {
const expectedLine = `- ${markdownPackNames[pack.slug] ?? pack.name}: ${packCounts.get(pack.slug) ?? 0} questions.`;
expect(markdown.includes(expectedLine), `markdown pack count is missing or stale: ${expectedLine}`);
}
if (errors.length) {
process.stderr.write(`Question draft validation failed (${errors.length}):\n${errors.map((error) => `- ${error}`).join('\n')}\n`);
process.exit(1);
}
process.stdout.write(`Question draft valid: ${questions.length} total; types ${JSON.stringify(typeCounts)}; intensities ${JSON.stringify(intensityCounts)}; packs ${JSON.stringify(Object.fromEntries(packCounts))}\n`);