diff --git a/README.md b/README.md index 3e22549..b342d5c 100644 --- a/README.md +++ b/README.md @@ -28,9 +28,13 @@ and the stores must follow the same rule. **Tone.** Never judging, dramatic, overenthusiastic, or preaching. Always calm, curious, clear, respectful, structured, thoughtful. -**Design.** Scandinavian, clinical, quiet, precise, minimal. Off-white -background, near-black text, one dark blue-green accent. Generous white -space. No animations, no gradients. +**Design.** Clinical, Scandinavian, quiet, intelligent, premium, minimal. +Off-white background, near-black text, one dark blue-green accent. Generous +white space. No animations, no gradients. The rule: if something can be +removed without reducing user value, remove it. + +**Product principle.** One user. One conversation. One analysis. One +recommendation. That is the whole product. ## System overview @@ -57,8 +61,11 @@ it is not built. users use `POST /chat` with a **Cognito** JWT (Apple / Google / email via the hosted UI). 3. The Lambda calls the **OpenAI Responses API** with the Markdown knowledge - base (`services/api/knowledge/`) as system instructions. The model - returns structured output: a reply plus an `analysis_ready` flag. + base (`services/api/knowledge/`) as system instructions: neurosemantic + models, communication models, the conversation guide, reflection + exercises, and a question library. V1 invests in prompt design quality, + not infrastructure complexity. The model returns structured output: a + reply plus an `analysis_ready` flag. 4. Sign-in happens at the paywall, since a purchase must attach to an account. Usage counters live in **PostgreSQL** (Aurora Serverless v2). @@ -86,7 +93,7 @@ abuse backstop for the free tier — it is not the paywall. Conversations are never stored server-side; the client holds them in memory and sends the running transcript with each request. The database stores the -absolute minimum: `users` (id, email, provider, subscription_status, +absolute minimum: `users` (id, email, auth_provider, subscription, created_at) and `usage` (messages_used, last_reset). No profiling, no training on user data. Error logging is anonymized (no message content). @@ -193,12 +200,33 @@ API Gateway, Lambda, Cognito, S3, Secrets Manager and CloudWatch — and V1 does not even need S3. No Redis, no Kubernetes, no Kafka, no Elasticsearch, no queues, no microservices. One backend. Maximal simplicity. +## Definition of Done + +Version 1 is done when a user can: + +1. Open the app. +2. Start a conversation immediately. +3. Feel seen and understood. +4. Receive a number of well-considered follow-up questions. +5. Reach a natural premium boundary. +6. Buy Premium. +7. Continue the conversation. + +If a feature does not help the user reflect better, it is not built. +Version 1 must be small, fast, stable, and easy to maintain. + ## Roadmap -Build a strong core product first; only then build a network around it. +Build a strong core product first; only then build around it. - **Version 1 (this repo)** — Person ↔ Semantika. Nothing else. -- **Version 2** — Person ↔ Semantika ↔ small reflection groups. -- **Version 3** — certified coaches, live sessions, study circles, courses. +- **Version 2 (not now)** — journal, saved insights, community, certified + coaches, courses, voice conversations. None of these are built in V1. -Community features are deliberately absent from V1. +**Next step: a closed beta.** Put V1 in the hands of 20–50 test users +before adding anything. The minimal data model already answers several of +the key questions — how many come back (`usage.last_reset` vs activity), +how deep dialogues go (`messages_used`), and when users upgrade +(`subscription` transitions). Which starter questions create the most value +requires asking testers directly, since conversations are never stored. +Anything beyond that must justify itself against the privacy rule. diff --git a/db/migrations/001_init.sql b/db/migrations/001_init.sql index e5ebdf7..3ef20d9 100644 --- a/db/migrations/001_init.sql +++ b/db/migrations/001_init.sql @@ -2,10 +2,10 @@ -- no conversations, no profiling data. create table if not exists users ( - id text primary key, -- Cognito sub + id text primary key, -- Cognito sub, or guest: email text not null, - provider text not null, -- 'apple' | 'google' | 'email' - subscription_status text not null default 'free', -- 'free' | 'active' + auth_provider text not null, -- 'apple' | 'google' | 'email' | 'guest' + subscription text not null default 'free', -- 'free' | 'active' created_at timestamptz not null default now() ); diff --git a/services/api/knowledge/03-conversation-guide.md b/services/api/knowledge/03-conversation-guide.md index 42176fc..c92a5de 100644 --- a/services/api/knowledge/03-conversation-guide.md +++ b/services/api/knowledge/03-conversation-guide.md @@ -22,13 +22,14 @@ Every conversation moves through four steps. interrogation, and nothing should be analyzed to pieces — explore together. 3. **Lift.** Point to the user's resources. Name strengths, show progress, - build confidence. Praise must be concrete and credible — "You seem to - have thought this through carefully", "It takes courage to bring this - up" — never generic or exaggerated compliments. + build confidence. Appreciation must be genuine, concrete, and anchored + in what the user has actually expressed — "You seem to have thought + this through carefully", "It takes courage to bring this up" — never + generic or exaggerated compliments. 4. **Challenge.** Offer one new thought, one new question, one new model, or one new direction (a reframe, a higher frame, a well-formed outcome). - Every conversation should leave the user with at least one new - perspective. + Never preach — always inspire reflection. Every conversation should + leave the user with at least one new perspective. ## Educational philosophy diff --git a/services/api/knowledge/04-communication-models.md b/services/api/knowledge/04-communication-models.md new file mode 100644 index 0000000..3eeac3a --- /dev/null +++ b/services/api/knowledge/04-communication-models.md @@ -0,0 +1,56 @@ +# Communication models + +Practical models for helping users prepare for and reflect on +conversations. As with everything in this knowledge base, present these as +useful lenses — not scientifically proven techniques. + +## Perceptual positions + +Any situation can be viewed from three positions: + +1. **First position** — my own eyes: what I see, feel, and want. +2. **Second position** — the other person's perspective: what might they + see, feel, and want? +3. **Third position** — a neutral observer: what would someone watching + this interaction notice about the pattern between us? + +Walking a user through all three positions before a difficult conversation +often dissolves rigidity and reveals options. + +## Communication is the response you get + +A working assumption from NLP: the meaning of a message is the response it +elicits, not the intention behind it. When communication fails, the +question shifts from "Why don't they understand?" to "How else could I +say this so it lands?" + +## Observation vs. interpretation + +Much conflict lives in the gap between what happened (observable) and what +it was made to mean (interpretation). A clean structure for difficult +conversations: + +1. What I observed (specific, filmable). +2. What I made it mean / how I felt. +3. What matters to me here (the need or value). +4. What I want to ask for (a concrete, doable request). + +## Chunking up and down + +- **Chunk up** to find agreement: "What do we both ultimately want here?" +- **Chunk down** to find clarity: "What specifically would that look like?" + +Stuck conversations are often stuck at the wrong level of abstraction. + +## Backtracking meaning + +Before responding in a charged conversation, reflect back the _meaning_ you +heard — not the words — and check it: "So what matters most to you here is +being consulted before decisions — did I get that right?" Being understood +usually lowers the temperature more than being agreed with. + +## Boundary statements + +A calm boundary has three parts: what I am available for, what I am not +available for, and what I will do if the line is crossed — stated without +threat or apology. diff --git a/services/api/knowledge/05-reflection-exercises.md b/services/api/knowledge/05-reflection-exercises.md new file mode 100644 index 0000000..c2d7651 --- /dev/null +++ b/services/api/knowledge/05-reflection-exercises.md @@ -0,0 +1,49 @@ +# Reflection exercises + +Short, concrete exercises to offer in the Challenge step or as part of a +premium action plan. Offer one at a time, matched to the user's situation. +Frame them as experiments in reflection, never as treatments. + +## The meaning audit + +1. Name the triggering situation in one neutral sentence. +2. Ask: "What am I making this mean?" Write every meaning that comes up. +3. For each meaning, ask: "Is this the only possible meaning? What else + could this mean?" +4. Choose the meaning that is both honest and most useful to act from. + +## The meta-question + +When a feeling is sticky, go one level up: +"How do I feel about feeling this?" Then: "How would I like to relate to +this feeling instead — with acceptance, curiosity, patience?" Practice +bringing that chosen higher state to the original feeling. + +## Five frames + +Take one stuck interpretation and generate five alternative frames for the +same facts — including at least one from the other person's perspective and +one from five years in the future. The goal is flexibility, not finding +"the right one". + +## The observer replay + +Replay a difficult moment as if watching two strangers on film. Describe +only what a camera would record. Then ask: "What pattern do these two +people keep repeating? What would I suggest to the person playing me?" + +## Well-formed outcome walk-through + +For decisions and goals: + +1. What do I want, stated positively and in my own control? +2. How will I know, concretely, that I have it? +3. In which contexts do I want it — and where not? +4. What does it cost me, and what does it cost me to stay where I am? +5. What is the first small step, and when will I take it? + +## The evening question + +One question, answered briefly at the end of the day: +"Where today did I choose my response instead of reacting automatically — +and what made that possible?" diff --git a/services/api/knowledge/06-question-library.md b/services/api/knowledge/06-question-library.md new file mode 100644 index 0000000..5c61acf --- /dev/null +++ b/services/api/knowledge/06-question-library.md @@ -0,0 +1,54 @@ +# Question library + +Curated questions, organized by purpose. Use one at a time. Choose the +question that fits where the user is in the four-step arc — these mostly +serve Explore and Challenge. + +## Clarifying (recover the specifics) + +- What, specifically, happened — as a camera would have seen it? +- When you say "always" — has there been an exception? +- Afraid of what, exactly? +- What would happen if you did? +- How do you know that they think that? + +## Pattern (surface the structure) + +- Where else in your life does this same pattern show up? +- What usually happens right before you react this way? +- What does this situation remind you of? +- If this keeps repeating, what is the step you keep taking in the dance? + +## Meaning (examine the interpretation) + +- What are you making this mean? +- What else could it mean? +- Whose voice does that interpretation sound like? +- If your closest friend described this exact situation, what would you + say to them? + +## Perspective (shift the viewpoint) + +- How might this look from the other person's side? +- What would a calm observer notice about the two of you? +- How will you see this five years from now? +- What would this look like if it were not a problem but information? + +## Resource (locate strengths) + +- When have you handled something like this well? What did you do? +- What do you already know about yourself that helps here? +- Who do you become at your best — and what brings that out? + +## Decision (move toward choice) + +- What do you want — stated in what you can control? +- What does each option cost you, honestly? +- What would you choose if you trusted yourself a little more? +- What is the smallest step that would give you real information? + +## Closing (anchor the reflection) + +- What is the one thing you are taking with you from this conversation? +- What will you try before we speak again? +- How will you notice that something has shifted? diff --git a/services/api/src/chat.ts b/services/api/src/chat.ts index 0371ed8..48ffaf3 100644 --- a/services/api/src/chat.ts +++ b/services/api/src/chat.ts @@ -38,7 +38,9 @@ seen, respected, understood, capable, and hopeful. questions — so that over time they need the tool less. Positioning: -- Semantika is not therapy, not self-help, and not a course. +- Semantika is not therapy, not self-help, not a course, and not a chatbot + for general questions. It is an intelligent conversation partner with one + clear purpose. - Rather than giving quick advice, start by helping the user explore how they interpret their situation. Support reflection and perspective-taking through structured conversation, rather than delivering finished answers. @@ -49,15 +51,24 @@ Intellectual honesty: scientifically proven effects; many of these models lack broad scientific consensus, and it is enough to be clear about the inspiration. -Personality — sound like: a very experienced coach, a calm mentor, a -skilled teacher, a wise conversation partner. -Never sound like: a therapist, a salesperson, a preacher, or a guru. -Warmth, curiosity and structure — a thoughtful mentor, not a stage -performance. +Personality — Semantika is: calm, warm, intelligent, curious, respectful, +pedagogical, structured, thoughtful. +Semantika is never: judging, manipulative, dramatic, overly positive, +overconfident, preaching. +Sound like: a very experienced coach, a calm mentor, a skilled teacher, a +wise conversation partner. Never sound like: a therapist, a salesperson, a +preacher, or a guru. Warmth, curiosity and structure — a thoughtful mentor, +not a stage performance. -Tone — Semantika is never: judging, dramatic, overenthusiastic, preaching. -Tone — Semantika is always: calm, curious, clear, respectful, structured, -thoughtful. +Every reply should: +- feel personal — grounded in what this user has actually said, +- be calm and instill a sense of safety, +- give hope without promising results, +- help the user think more deeply and encourage self-reflection, +- contain at least one genuinely new thought or question. + +Every conversation should leave the user with greater clarity, greater +calm, a new perspective, and increased trust in their own ability. Principles: - Follow the four-step conversation arc in the knowledge base: @@ -88,11 +99,10 @@ Set "analysis_ready" to true ONLY when all of the following are genuinely met: When analysis_ready is true, the reply must be a calm, natural transition — not an interruption mid-answer. Summarize at a high level what you have -understood and that a concrete strategy is ready. Example of tone: -"I think I'm starting to understand what lies behind this situation, and I -can see some clear communication patterns. I also have a concrete strategy -I would recommend for your specific situation. Continue with Premium to see -the analysis and the recommended steps." +understood and that a concrete analysis is ready. Example of tone: +"I'm starting to see some recurring patterns in what you describe. I have +a concrete analysis and several recommendations that build on what we have +explored. Unlock Premium to continue." Never: - manufacture urgency or emotional pressure to drive a purchase, diff --git a/services/api/src/db.ts b/services/api/src/db.ts index b01973e..2388be0 100644 --- a/services/api/src/db.ts +++ b/services/api/src/db.ts @@ -24,8 +24,8 @@ export async function getPool(): Promise { export interface UserRow { id: string; email: string; - provider: string; - subscription_status: 'free' | 'active'; + auth_provider: string; + subscription: 'free' | 'active'; created_at: Date; } @@ -39,15 +39,15 @@ export interface UsageRow { export async function getOrCreateUser( id: string, email: string, - provider: string, + authProvider: string, ): Promise { const db = await getPool(); const result = await db.query( - `insert into users (id, email, provider) + `insert into users (id, email, auth_provider) values ($1, $2, $3) on conflict (id) do update set email = excluded.email returning *`, - [id, email, provider], + [id, email, authProvider], ); const user = result.rows[0]; if (!user) throw new Error('User upsert returned no row'); @@ -80,5 +80,5 @@ export async function setSubscriptionStatus( status: 'free' | 'active', ): Promise { const db = await getPool(); - await db.query(`update users set subscription_status = $2 where id = $1`, [userId, status]); + await db.query(`update users set subscription = $2 where id = $1`, [userId, status]); } diff --git a/services/api/src/handler.ts b/services/api/src/handler.ts index b6fa1bf..934e5ce 100644 --- a/services/api/src/handler.ts +++ b/services/api/src/handler.ts @@ -55,7 +55,7 @@ async function currentUsage(user: UserRow) { async function handleMe(user: UserRow): Promise { const usage = await currentUsage(user); return json(200, { - subscriptionStatus: user.subscription_status, + subscriptionStatus: user.subscription, messagesUsed: usage.messages_used, messageCap: config.messageCap, }); @@ -73,7 +73,7 @@ async function handleChat( user: UserRow, body: string | undefined, ): Promise { - const premium = user.subscription_status === 'active'; + const premium = user.subscription === 'active'; const parsed = body ? (JSON.parse(body) as { messages?: ChatMessage[] }) : {}; const messages = parsed.messages ?? []; const last = messages[messages.length - 1]; @@ -87,7 +87,7 @@ async function handleChat( } const usage = await currentUsage(user); - if (!canSendMessage(usage.messages_used, user.subscription_status, config.messageCap)) { + if (!canSendMessage(usage.messages_used, user.subscription, config.messageCap)) { return json(402, { error: 'message_cap_reached' }); } diff --git a/services/api/suggestions.json b/services/api/suggestions.json index f3ff6ca..31ede44 100644 --- a/services/api/suggestions.json +++ b/services/api/suggestions.json @@ -1,14 +1,12 @@ { "suggestions": [ "I need support before a difficult conversation.", + "I keep getting stuck in the same conflicts.", + "Help me make a difficult decision.", + "How do I become more secure in relationships?", "Help me prepare for a salary negotiation.", - "I'm going on a first date.", - "Why do I get stuck in the same conflicts?", - "Help me understand my reactions.", - "How do I set clearer boundaries?", - "I want to become a better communicator.", - "Help me prepare for a job interview.", + "I want to understand why I react so strongly.", "How do I build better self-confidence?", - "Help me make a difficult decision." + "I want to communicate more clearly." ] }