diff --git a/README.md b/README.md index 54d4531..5dc77aa 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,11 @@ The local database dashboard (Supabase Studio) is at http://127.0.0.1:57323. Bef ### Replayed real answers -`npm run dev:replay` shows the interface with the real model's answers, for free and without a token. It replays answers the measurement recorded on gpt-oss-120b ([`replays.json`](src/server/ai/replays.json)), each reply whole, after the time the real model took. The intro's button, the grocery delivery suggestion and the other measured requests replay; any other message gets the scripted fake's answer, at the same pace. Searches and saves still run on your machine, so the search lines show local passages, while the words come from the recording. The header says "Replaying recorded answers". To refresh the copy after a new measurement, run `npm run replays:copy -- --from docs/records/measurements/` where the records are. +`npm run dev:replay` shows the interface with the real model's answers, for free and without a token. It replays answers the measurement recorded on gpt-oss-120b ([`replays.json`](src/server/ai/replays.json)), each reply whole, after the time the real model took. The intro's button, the grocery delivery suggestion and the other measured requests replay; any other message gets the scripted fake's answer, at the same pace. Searches and saves still run on your machine, so the search lines show local passages, while the words come from the recording. The header says "Replaying recorded answers". + +A **scenario tour** in the corner of the page walks through every state the chat and the PRD can show, which recorded answers alone never reach: rich text and tables, folded and refused searches, a section with every kind of statement, a long wait to stop, each kind of failure and, last, the used-up daily allowance. Each step says what to watch for; "Play next" sends it. The scripted answers run through the same chat code, so their searches, saves and quote checks are real. + +To refresh the copy after a new measurement, run `npm run replays:copy -- --from docs/records/measurements/` where the records are. ### The real chat model on your machine diff --git a/docs/plan.md b/docs/plan.md index 7bd6bc2..5696bdf 100644 --- a/docs/plan.md +++ b/docs/plan.md @@ -20,12 +20,12 @@ ### Build lane - **Where:** its own worktree per piece, on a branch from an up-to-date `main`. If the session isn't in a worktree yet (`git worktree list` shows its folder as the main checkout, not under `.claude/worktrees/`), it creates one before touching anything, and never checks out a branch in the main folder, where the measurement lane works. Claude Code's worktree option puts it under `.claude/worktrees/`; run `npm ci` there first. A worktree made with `git worktree add` gets no `.env.local`: run `npm run env:local` there, or the end-to-end tests find no interviews. Its browser check uses `app-build` in `.claude/launch.json` (port 3200), never `app` or `app-real`, which are the measurement lane's on port 3000. It never runs `measure`, `measure:fake` or `dev:real`, never edits the measurement lane's files above, and changes code in `src/server` only where its piece needs it, never what the model receives (listed above); its pull request says which `src/server` files it changed and why. -- **Doing:** slice 23, the README rewritten for reviewers, is done on `docs/readme-for-reviewers` (`.claude/worktrees/readme`), from `main` at 6e1dae5 (slice 22's merge), waiting for the owner's push and merge. It opens on the demo's story (the owner's wish, 2026-10-21: a story, not terms such as "citation check" before the reader knows what the app does), then what's worth a look, the status and how it works; running it, the commands and the database follow, and the deploy steps fold into two collapsed sections. The facts were checked against the code at 6e1dae5; "How it works" keeps its heading (`tests/unit/readme.test.ts`), and so does "The real chat model on your machine" (`src/lib/env-guard.ts`). Prettier passed on it; nothing else ran, since only documents changed. +- **Doing:** slice 24, the scenario tour, is done on `feat/scenario-tour` (`.claude/worktrees/slice-22-replay`), from `main` at e4bd98e (slice 23's merge), waiting for the owner's look, push and merge. The owner asked for it on 2026-10-21, after finding that the replay never shows a table, among others, and chose all four groups of states, played in one sequence from a menu in the replay. Local checks on 2026-10-21: `verify` (527 unit tests) and `test:e2e` (69, of which 1 new in `tests/e2e/replay.spec.ts`) passed in one full run each. All 17 steps were played from the panel with `npm run dev:replay` at `REPLAY_SPEED=4` from the worktree on port 3200, read through the page's text (the Browser pane was hidden, so no screenshots): the tables, the rich text, the folded and refused searches, the section of every kind (8 of 13 quotes confirmed, as scripted), the refused save, Stop during the long wait, each failure with "Try again", the busy message and the quota notice. "Breaking off partway" first lost its words, fixed before the commit. Not seen: Jump to latest during the long answer, and the wide table's sideways scroll, which a hidden pane doesn't measure. It adds `src/server/ai/scenarios.ts` and `src/lib/scenarios.ts` and changes `src/server/ai/replay-chat-model.ts`, all for `dev:replay` only; nothing the real model receives changes, so the measurement lane needn't wait for it, though its comparison will list those files as code. - **Next:** 1. A short GIF near the README's top, from `npm run dev:replay`, once the owner has looked at the replay themselves (asked 2026-10-21). The screen recording for launch stays (`docs/decisions.md`, 2026-10-17). 2. Slice 8, the rate limit, once 9d is done and the owner has decided its legal basis and size (*Deferred*). - **Unverified:** on staging, a reload keeping the PRD and the downloaded file (local Chromium only); a stop ending the answer on Cloudflare too, where the call in flight may still be finished and counted; the quota message against Cloudflare's real refusal (the tests replay its reply of 2026-10-20) and whether its per-minute 429 carries a retry header; a source name's pop-over, "Your decision, not from the interviews." under a claim of the visitor's, the table fix and the intro screen; Safari and Firefox (only Chromium ran; without anchor positioning the pop-over is a centred box, by design, and without `field-sizing`, before Safari 26.2 and Firefox 152, the message box keeps its three lines; the moving band, whose fallback is the still grey line; the PRD's highlight and rolling count; Jump to latest; the folded line's slide, which falls back to showing at once; the words' fade; the copy button, its label and the clipboard; Inter and its resized fallback, which needs Arial installed, so Linux and Android show the system's font until Inter arrives; a table's cells keeping their 144px and its box's sideways scroll; the Thinking line's band). On staging, a real reply's table, and the Thinking line through the real model's waits with the times it shows (the fake model's are about 0s). On staging, a reply that arrives whole fading in as one, and a section's copy over HTTPS pasted into another app. Expect the Security Advisor's lint 0028 on `search_passages` on staging; it's intended (`supabase/AGENTS.md`). Production waits for launch and its Cloudflare token. -- **Waiting on the owner:** pushing `docs/readme-for-reviewers`, its pull request and the merge; a look at `npm run dev:replay` before the GIF; whether `next dev` may keep adding its block to `AGENTS.md` (KI-8); the GitHub description and topics whenever they like. +- **Waiting on the owner:** a look at `npm run dev:replay` and its tour, then pushing `feat/scenario-tour`, its pull request and the merge; the GIF after that; whether `next dev` may keep adding its block to `AGENTS.md` (KI-8); the GitHub description and topics whenever they like. ### Shared rules - One branch per piece, from an up-to-date `main`; the owner pushes it, opens the pull request and merges it (`AGENTS.md`, Branches). After the other lane's merge, rebase an unpushed branch onto `main`, and merge `main` into a pushed one; never rewrite a pushed branch. @@ -121,4 +121,12 @@ ### 22 · Replayed real answers, to look at the interface here · risk: normal · status: done (6e1dae5) -### 23 · The README, for reviewers · risk: normal · status: done (on `docs/readme-for-reviewers`; its id on `main` goes here after the merge) +### 23 · The README, for reviewers · risk: normal · status: done (e4bd98e) + +### 24 · A tour of every state the chat and the PRD can show · risk: normal · status: done (on `feat/scenario-tour`; its id on `main` goes here after the merge) +- **Goal:** the owner and the build lane can see, step by step, every state the chat and the PRD can show, at the real model's pace, for free and without the token: the replay shows only what the model happened to do in its recorded runs, and never a table (the owner, 2026-10-21). +- **Acceptance:** + - With `npm run dev:replay`, a small "Scenario tour" panel lists the steps in order, with what to watch for in each, which were played, and "Play next"; any step can be played on its own. It never shows in a build for staging or production, nor with `npm run dev`. + - Each step sends its own words as the visitor's message, and its answer is scripted like a recording: each model call's parts, whole, after a set wait. The searches, the saves and the check run here for real, on the local database and the fake embedder, as with the replay. + - The steps, in order: two real recorded answers (the first section, the grocery delivery claim); rich text (bold, code, lists, and marks the chat strips); tables (wide, line breaks in cells, empty cells, a row longer than the header); searches folding into one line with text between runs and a long query cut short; a refused search and a step that doesn't exist; one section with every kind of statement (confirmed, many sources, an unverified quote, the interviewer's question quoted, the wrong passage cited, an unknown source, citing nothing, the visitor's decision); a save refused for its form, then saved; Open Questions; a long answer for Jump to latest; a long wait for Stop; failing before the first word and partway, each once so "Try again" works; an answer cut off at its output limit; an answer with no words; the busy message; and last, the used-up allocation, which lasts until a reload. + - Unit tests: the scripted quotes meant to pass match the interviews and those meant to fail don't, a failure fails once, and the panel's next step and what it sends; an end-to-end check that the tables step's words, sent to the replay's server, show a table (that server runs a build, which never carries the panel). Nothing the real model receives changes; the pull request names the `src/server` and `src/lib` files it adds. diff --git a/src/features/AGENTS.md b/src/features/AGENTS.md index 83a325a..bd7c9df 100644 --- a/src/features/AGENTS.md +++ b/src/features/AGENTS.md @@ -37,6 +37,10 @@ - **Its label** (`.copy-tip` in `src/app/globals.css`) is CSS, `aria-hidden`: shown on `.copy-button:hover` after 0.4s, closed by `data-pressed` from the press until the pointer leaves, held open by `data-open` to say "Couldn't copy". - **After a copy** the name is "Copied" or "Couldn't copy" for `COPY_OUTCOME_MS`, and an `aria-live="polite"` span says it once, since screen readers don't reliably announce a focused button's new name. Not `role="status"`: the chat's status line is the page's one. `navigator.clipboard.writeText` rejects when refused (MDN, read 2026-10-21 through Context7), and a page that isn't a secure context has no `navigator.clipboard`; both read "Couldn't copy". The desktop app's Browser pane refuses the clipboard while it's hidden, so a successful copy is seen only in the end-to-end tests there (found 2026-10-21). +## The scenario tour (`workspace/scenario-tour.tsx`, slice 24) +- **Only with `npm run dev:replay`:** `Workspace` renders it when `NEXT_PUBLIC_CHAT_REPLAY` was "1" at compile time, as the header's "Replaying recorded answers", so no build for staging or production has it, and neither does the end-to-end tests' replay server, which runs that build. It's a `fixed` `