Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions KNOWN-ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ What happens and how to reproduce it. What's known about the cause, with guesses
In one run during slice 11, and not on the run before or after: `tests/e2e/prd.spec.ts`, "reads Drafting while it works, and comes back with a line beside it when the answer fails" (since slice 13, "says Drafting beside the next section while it works, …"), waited past its 5 seconds with the fake model's answer stuck after the saved section. It passed five times in a row on its own and in the full suites afterwards. A guess: it waits on the fake model's timing, which six workers on a busy machine stretch. Fixing it means finding what it waits on and waiting for that condition instead; if it fails again, note the run here. (The unit test that failed with it was a test reading storage too early, fixed with slice 12; `tests/AGENTS.md`, *Writing tests*.)
Again during slice 14, in two full runs of five: 2 and then 3 tests (`prd.spec.ts`'s pop-over tests, "Start over asks once…", "follows the newest message…", `quota.spec.ts`'s "the chat and the PRD so far stay readable…"), each in `draftFirstSection` or its like, waiting past 5 seconds for the first draft's answer to end while the fake model was still streaming its reply. The same tests passed twice each on their own, and the three full runs after them passed, one of them without slice 14's change to the chat. So the wait is the fake model's answer under six workers, not what the page does.
Again during slice 15 (2026-10-20), in full runs back to back on the finished code: one failed 2 tests (not recorded), one passed all 59, one failed 4: `layout.spec.ts`'s three Jump to latest tests and `prd.spec.ts`'s "follows the newest message…", each waiting past 5 seconds for a first draft's answer while the fake model was still at a search or the check. One of them, "offers Jump to latest once opened searches push…", now drafts after three searches (`FAKE_SEARCH_AGAIN`), a longer answer, so it has less room in that wait.
Again during slice 20 (2026-10-21), in the first full run on the finished code: 6 tests (`prd.spec.ts`'s Drafting, light-up, See it and copy tests), each waiting past 5 seconds for the first draft's answer while it showed "Checking the quotes in Introduction / Overview…". All of `prd.spec.ts` passed on its own right after, and the next full run passed all 67.

## KI-8 · `next dev` adds its own block to `AGENTS.md`
**Found:** 2026-10-20 · **Severity:** low
Expand Down
20 changes: 6 additions & 14 deletions docs/plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,11 @@

### 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 19 is done on `feat/markdown-tables` (`.claude/worktrees/slice-19-tables`), from `main` at 32315ed (slice 18's merge), waiting for the owner's push and merge. Local checks on 2026-10-21: `verify` (468 unit tests; the card tests in `tests/unit/markdown.test.ts` and `tests/unit/rich-text.test.tsx` gave way to table tests) and `test:e2e` (66, 2 new in `tests/e2e/chat.spec.ts`) passed in two full runs; a first full run failed 10, the 2 new among them, all passing again with two workers, and the 2 new passed 12 times of 12 in parallel (KI-7). The page checked on a dev server from the worktree with the fakes, at 1440px and 375px: the header in medium weight over the stronger line, 12px padding, top-aligned; a six-column table scrolling inside the message, a stop for Shift+Tab with the white focus ring, and a two-column one fitting without a stop; the page never scrolls sideways. Unlike opencode's, each cell is at least 144px wide, since the six columns squeezed into the chat showed a word per line (`docs/decisions.md`, Design, 2026-10-20). It changes `src/lib/markdown.ts`, which only the browser uses, and nothing in `src/server`, so the measurement lane needn't wait for it.
- **Doing:** slice 20 is done on `feat/thinking-line` (`.claude/worktrees/slice-20-thinking`), from `main` at b446e43 (slice 19's merge), waiting for the owner's push and merge. Local checks on 2026-10-21: `verify` (488 unit tests, 2 files new or changed for the thinking lines) passed, its build on a second try after a network error reaching Google Fonts; `test:e2e` (67, 1 new in `tests/e2e/chat.spec.ts`; slice 11's and slice 15's tests now read "Thinking" and count the thought lines) passed in the second full run, the first failing 6 waiting on the fake model's answer (KI-7). The page checked on a dev server from the worktree with the fakes, at 1440px and 375px: "Thinking" at 13px, medium, on a 16px line with the moving band; "Thought 2s" with the word in white and the time in grey; inside "Used 3 searches" the thoughts and searches alternate, rows 28px high and 36px apart; the times kept through a reload; no sideways scroll on a phone. It changes only `src/features` and its tests, nothing in `src/server` or `src/lib`; the times go back to the server inside the answers, and a unit test shows the model's input is the same with and without them, so the measurement lane needn't wait for it.
- **Next:**
1. Slice 20 on its own branch from `main` once slice 19 is merged, in a fresh session; its form is decided (`docs/decisions.md`, Design, 2026-10-21). It changes only the browser's code and its tests, nothing in `src/server`.
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). On staging, a real reply's table. 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 `feat/markdown-tables`, its pull request and the merge; whether `next dev` may keep adding its block to `AGENTS.md` (KI-8); the GitHub description and topics whenever they like.
1. Slice 8, the rate limit, once 9d is done and the owner has decided its legal basis and size (*Deferred*). No other piece is planned for this lane.
- **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 `feat/thinking-line`, its pull request and the merge; 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.
Expand Down Expand Up @@ -113,13 +112,6 @@

### 18 · A designed typeface · risk: normal · status: done (32315ed)

### 19 · Tables like opencode's · risk: normal · status: done (on `feat/markdown-tables`; its id on `main` goes here after the merge)
### 19 · Tables like opencode's · risk: normal · status: done (71e6e93)

### 20 · Show when the model is thinking · risk: normal · status: todo
- **Goal:** the visitor sees when the model is thinking and for how long, never what it thinks.
- **Acceptance:**
- Its form is opencode's (the owner, 2026-10-21; `docs/decisions.md`, Design): "Thinking" while the model thinks, then "Thought" with the time in grey, as "Thought 8s", a line that doesn't open.
- The chat model gives whole replies (`src/server/ai/cloudflare.ts`; `src/server/AGENTS.md`, *Whole replies for chat*), so the server learns of the model's reasoning only with the reply, together with its text or its step, and can't say live when the reasoning starts or how long it took on its own. What the browser can see is each wait for a reply, and gpt-oss reasons before it replies (Harmony's analysis channel), so that wait is the sign: whenever an answer is on its way and no step is in progress and no text is arriving, slice 11's moving line reads as opencode's Thinking row instead of "Working…", and slice 11's tests change with it. The first wait starts with the request; a later one starts when a step finishes.
- Once the reply arrives, the line "Thought 8s" stays in the line's place in the answer: the wait, measured in the browser and kept with the conversation as slice 15 keeps the answer's time, so it outlasts a reload; an answer kept from before this piece shows none. With slice 15, these lines fold with the steps. For the answer's last reply the wait also covers the model writing its text; the owner chose opencode's word all the same.
- The reasoning's words never reach the browser, as today (`sendReasoning: false` in `src/server/chat.ts`; `src/server/AGENTS.md`, *Reasoning*): gpt-oss's chain of thought isn't held to the safety standards of its final answers, and OpenAI recommends hiding it from end users (https://github.com/openai/harmony/blob/main/docs/format.md, *Reasoning*, read 2026-10-20 through Context7).
- Nothing in `src/server` changes: the browser measures and keeps the waits itself, on the assistant's messages (a visitor's message may hold only text: `readChatRequest`). The model receives exactly what it receives today: a unit test runs the chat with the fake model on a conversation with and without the kept waits and shows the model's input is the same (the AI SDK's `convertToModelMessages` leaves out `data-` parts and a message's metadata, and `readChatRequest` accepts both; ai 7.0.143, read 2026-10-20). If that can't hold, it isn't built, and the owner hears why. `STORED_FORMAT` is raised if the kept form changes.
### 20 · Show when the model is thinking · risk: normal · status: done (on `feat/thinking-line`; its id on `main` goes here after the merge)
Loading
Loading