Add chat-first onboarding and intelligent context-based paywall

Replaces the hardcoded message limit with a model-driven paywall and
removes registration before the first question:

- Onboarding: the app opens directly into the chat. Dynamic conversation
  starters are served by GET /suggestions (services/api/suggestions.json),
  updatable with a deploy — no app release needed. Guests chat via
  POST /guest/chat, identified by an app-generated device id; sign-in
  moves to the paywall, where a purchase must attach to an account.
- Free experience: the model runs in discovery mode — follow-up
  questions, pattern identification, visible understanding — building an
  analysis without delivering the full solution.
- Intelligent paywall: the model returns structured output (reply +
  analysis_ready). Only when the problem is described, the information is
  sufficient and an action plan is ready does it write a calm transition
  and pause the conversation. Manufactured urgency, emotional pressure,
  fake readiness and mid-answer stops are explicitly forbidden.
- Premium: on unlock the app resends the transcript and the backend
  immediately delivers the full analysis, strategies and exercises, then
  the dialogue continues without restriction.
- The old FREE_MESSAGE_LIMIT becomes MESSAGE_CAP (default 200/30 days),
  kept purely as an abuse backstop — it is not the paywall.
- WelcomeScreen removed; the app is now two views (Chat, Paywall).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0118DaxZR36RpnY524vRqx3z
This commit is contained in:
Claude
2026-08-03 14:42:44 +00:00
parent 07fbeea09b
commit 6b7f0263e4
11 changed files with 498 additions and 221 deletions
+17 -2
View File
@@ -1,5 +1,5 @@
import { CfnOutput, Duration, RemovalPolicy, Stack, type StackProps } from 'aws-cdk-lib';
import { HttpApi, HttpMethod } from 'aws-cdk-lib/aws-apigatewayv2';
import { HttpApi, HttpMethod, HttpNoneAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2';
import { HttpJwtAuthorizer } from 'aws-cdk-lib/aws-apigatewayv2-authorizers';
import { HttpLambdaIntegration } from 'aws-cdk-lib/aws-apigatewayv2-integrations';
import * as cognito from 'aws-cdk-lib/aws-cognito';
@@ -141,7 +141,7 @@ export class NeuroSemanticsStack extends Stack {
DB_SECRET_ARN: db.secret?.secretArn ?? '',
DB_NAME,
KNOWLEDGE_DIR: 'knowledge',
FREE_MESSAGE_LIMIT: '50',
MESSAGE_CAP: '200',
USAGE_RESET_DAYS: '30',
},
});
@@ -161,6 +161,21 @@ export class NeuroSemanticsStack extends Stack {
api.addRoutes({ path: '/chat', methods: [HttpMethod.POST], integration });
api.addRoutes({ path: '/subscription/verify', methods: [HttpMethod.POST], integration });
// Public routes: the user meets the chat before any registration.
const publicAuthorizer = new HttpNoneAuthorizer();
api.addRoutes({
path: '/suggestions',
methods: [HttpMethod.GET],
integration,
authorizer: publicAuthorizer,
});
api.addRoutes({
path: '/guest/chat',
methods: [HttpMethod.POST],
integration,
authorizer: publicAuthorizer,
});
new CfnOutput(this, 'ApiUrl', { value: api.apiEndpoint });
new CfnOutput(this, 'UserPoolId', { value: userPool.userPoolId });
new CfnOutput(this, 'UserPoolClientId', { value: userPoolClient.userPoolClientId });