storytellurx/README.md
2026-10-06 11:43:23 +02:00

88 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# StoryTellurX V2
One TanStack Start application serves the marketing landing and the story product, backed by the landing’s existing Autokit database.
- `src/apps/landing/`: marketing content, components, fonts and styles.
- `src/apps/product/`: story UI, onboarding, personas, AI and device-local history.
- `src/components/ui/`: shared UI primitives; `src/lib/`: shared pure helpers.
- `src/routes/`: the single router; `src/autokit/`: shared schema, runtime, admin and seeds.
## Run locally
```sh
pnpm install
cp .env.example .env
# Configure the existing landing APP_KEY and S3 credentials; add the AI provider keys.
pnpm dev
```
`/` renders the marketing landing. `/start` enters the story app; `/chat`, `/history` and `/about` are its internal routes. The desktop hero frames the native app outlet as a phone; mobile and tablet hide that preview. A pathless `src/routes/_public.tsx` layout keeps the header, animated background, locale, sound and disclosure state shared. TanStack view transitions move the app from the hero to the centered screen on app navigation. `/admin` and `/api/rpc/*` use the shared Autokit runtime. Saving stories remains device-local and opt-in.
## Build revision
The product menu shows the first seven characters of the commit used to build the app, with the full hash in its tooltip. The build reads a valid full SHA from `APP_REVISION`, or falls back to `git rev-parse HEAD` in the source checkout. The same value is embedded in the server and browser bundles, independently of the hosting provider.
When Git or revision metadata is unavailable, invalid, or times out, the menu shows `untracked` and the build continues. Container builds that exclude `.git` can supply `APP_REVISION` as a build environment variable. Restart the development server or rebuild to pick up a new commit; uncommitted edits are not represented by the hash.
## Shared database
The runtime uses Autokit 27 / Migration 19 with the landing’s S3 database configuration. Keep the existing `APP_KEY` stable so encrypted provider settings remain readable. Local uses `autokit--local.sqlite`; production uses `autokit.sqlite` in the configured bucket. No separate local S3 server or V2 database path is used.
The database CLI runs `src/autokit/server.ts` through Deno:
```sh
pnpm db plan --timeline local
pnpm db apply --timeline local
pnpm db plan --timeline production
```
Review the plan and preserve a backup before any destructive migration. Deployment runs the compiled `db apply` command as the predeploy step in the deployment timeline. Environment files and local backups are ignored by Git; `.env.example` documents names without credentials.
## Bunny Magic Containers
`.github/workflows/release-on-bunny.yml` pins a reviewed commit of [bureaudouble/deno-bunny-deploy](https://github.com/bureaudouble/deno-bunny-deploy) that supplies `APP_REVISION` during the build. The shared action contains the Dockerfile, ignore rules, and Deno launcher. It reads the existing install, build, predeploy, and runtime settings from `deno.json`; this repository needs no local Dockerfile or container launcher. Images and build caches are stored in private GHCR packages.
GitHub variables `BUNNY_APP_ID` and `BUNNY_APP_URL` select the deployment. Secrets `GH_PACKAGES_TOKEN` and `BUNNYNET_API_KEY` authenticate private package installation and deployment. Build credentials use secret mounts. The workflow runs the existing predeploy command with the selected Bunny container's environment before updating its image.
The container keeps the existing Cloudflare R2 bucket and database. Set `APP_DATABASE=production`, preserve `APP_KEY`, and keep the existing `S3_*` and AI provider credentials in Bunny. `APP_URL` is the public origin used by the shared Deno launcher. `/healthz` reports the running commit; CI verifies that revision and the home page before purging the `autokit` page tag. No database or object-storage transfer is part of this hosting setup.
To enable Bunny HTML caching, set runtime variables `BUNNY_PULL_ZONE_ID` and `BUNNY_CDN_API_KEY`, then run `node scripts/bunny/configure-cache.ts` with those variables to configure the pull zone. The script installs credential-header cache bypass before enabling HTML caching and keeps query strings and hostnames separate. Set the GitHub variable `BUNNY_PULL_ZONE_ID` too so CI purges old HTML after deployments.
The shared `@bureaudouble/autokit-operator/page-cache` Bunny adapter caches eligible anonymous HTML on `/`, `/about`, `/start`, and `/history` for at most 60 seconds. Bunny separates cached HTML by the `age_legal` and `locale` cookies; Deno varies by `Cookie`, `Authorization`, and `X-Publishable-Api-Key`. Consent and language cookies pass through unchanged. The legal modal reads consent during server rendering, so `age_legal=true` omits it even before hydration; absent or false consent still requires the existing two-step confirmation. Requests with login cookies, authorization, API keys, or query strings remain uncached. Run `scripts/bunny/configure-cache.ts` during provisioning to install the selected-cookie cache key and credential bypass before deploying cacheable cookie variants. Autokit change notifications purge the shared `autokit` page tag; hashed assets retain their immutable cache. Each host registers its native adapter plus a targeted `remote` adapter for the other host. Writes therefore invalidate both CDNs; target selection prevents forwarding loops. `BUNNY_CACHE_ORIGIN` and `DENO_CACHE_ORIGIN` override the pilot and existing Deno origins. Both hosts authenticate purges with the unchanged `APP_KEY`. The short TTL bounds stale pages if the other host is unavailable or still running an older deployment.
## Package ownership
- TanStack AI owns messages, parts, runs, transport, cancellation, generation and transcript persistence. Native structured parts stream text and state through `partial`; shared middleware validates the final Zod result before committing it. No parallel transcript or custom streaming protocol exists.
- Native `createChatUI` dispatches parts through typed persona components. Shadcn's headless MessageScroller owns scrolling and visible-message tracking; Base UI owns dialogs; Motion and Matter own animation and picker physics.
- StyleX owns persona composition. The story product uses StyleX; the imported landing retains its scoped Tailwind build, with new shared and preview UI styled in StyleX. Autokit's precompiled admin stylesheet is isolated from the public application.
- `@bureaudouble/typographe` formats French and English presentation through a native TanStack Markdown extension. Code, URLs, drafts, saved transcripts and inspector data remain exact.
Adding a persona starts with its validated descriptor, UI registry, styles, theme and server-only instructions. The core transport and storage do not change. The complete file rail and extension constraints are documented in [ARCHITECTURE.md](ARCHITECTURE.md).
## Verify
`patches/@tanstack__ai-gemini@0.29.1.patch` fixes the native adapter’s JSON Schema mapping in both structured-output paths. It uses Gemini’s `responseJsonSchema` field, preserving Zod literals and validation; the older `responseSchema` field rejects `const`. It also preserves HTTP error codes through TanStack’s native error utility so quota and availability failures are distinguishable. Wire-level regressions use the real SDK with intercepted HTTP requests. Keep the pnpm patch until an upstream release passes that regression.
```sh
pnpm check
pnpm test
pnpm build
```
Public Playwright tests use neutral native provider fixtures and a local preview on port 3000. Default screenshot assertions require separately captured V1 baselines. Explicit `STORYTELLURX_CAPTURE_CURRENT=1` runs the same behavioral assertions and attaches current images without comparing or updating baselines; these captures do not establish visual parity.
No push or deployment is part of this refactor. Full parity is claimed only after the reference comparison and behavioral matrix are complete.
## Offline streaming review
[Streaming layout review and code-first plan](reports/streaming-layout-review.md) links the recorded neutral Flash-Lite response, per-chunk DOM measurements and proposed fixes. Test fetch is denied by default; provider tests must replay fixtures. The separate one-time recording step is never run by tests or CI. The opt-in layout audit blocks unexpected API/external requests and exercises local native TanStack streams. Its current measurements diagnose jumps; they are not approved stability baselines.
Turn details use a closed-by-default receipt adapted from REUI `ai-chat-12`, composed with the installed Base UI primitives and StyleX persona tokens. Expanding reveals natural-height state and public explanation content, without inner scroll panels. `public-rationale-receipt.spec.ts` verifies streamed geometry and keyboard disclosure offline. See `reports/streaming-layout-review.md` for the corrected UX and historical measurements.
Chat layout follows the shadcn default `MessageScroller.Provider` + user `scrollAnchor` composition, with a static header and flow composer. Suggestions use headless Base UI ScrollArea. See `reports/chat-composition.md` for the source comparison and native behavior; `@shadcn/react` is unpatched.
`structured-completion-order.test.ts` checks that native combined structured output completes before its successful `RUN_FINISHED`, plus validation and server-cancellation failures. `@tanstack/ai` 0.61 orders these events upstream, so the former pnpm patch is gone.
App-owned images and fonts live in `src/apps/landing/assets`. Import images from TypeScript or reference fonts with relative CSS URLs so Vite emits content-hashed `/assets/` URLs. These built files receive `Cache-Control: public, max-age=31536000, immutable`. Keep fixed-name manifest resources in `public/`; they do not receive this immutable policy. The page imports its own favicon, while the fixed public icon remains for `public/product/manifest.json`.
Do not put private or generated user documents in `public/`, the client build output, or imported app assets. Serve them from an authenticated server route. Generated responses retain the default `Cache-Control: private, no-store`; keep them outside the explicit anonymous HTML cache allowlist. A file extension such as `.pdf` or `.jpg` does not opt a server response into shared caching.