You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 2eac8ed
Browse filesBrowse the repository at this point in the historyBrowse files
Spec 013, Story 2. The search page's AI answer can now be continued in a
chat tab, the way analysis summaries can since #294.
**Each answer gets its own ID, emitted in `done`.** It cannot be keyed by
question: two readers of one search get different answers (~0.33
similarity run to run), and a question-keyed cache would let one continue
the other's. `/api/answer` keeps each answered stream under a fresh
`answer_id`, present in `done` only when `state` is `answered`, for an hour.
What is kept is exactly what the page was sent -- the text after anchor and
sources stripping, and the citations -- not the raw model output.
`POST /api/handoff` now takes a discriminated request:
`{"kind": "analysis", "token", "disclosure"}` or
`{"kind": "search", "answer_id"}`. A search request carrying analysis fields
is rejected rather than read as one.
**No human-presence claim for a search handoff.** `/api/answer`, which
produced the answer, deliberately does not require it -- public pathway
text -- so the search page may have none to send. The analysis handoff
still requires it, because it releases a reader's own analysis. Pinned in
both directions: requiring presence for search fails one test, dropping it
for analysis fails two.
The handoff record is two types, `AnalysisHandoff` and `SearchHandoff`,
rather than one with optional fields, so a search handoff cannot carry a
disclosure tier that means nothing for it. mypy then found every place that
had assumed a handoff was an analysis.
The chat opens on the reader's own question as the human turn and the
answer, with its cited sources, as the model's.
Verified end to end through the real Turnstile gate as a first-time
visitor, over HTTPS: a real answer from the real endpoint carried an
answer_id; a handoff was minted with no human claim; the tab opened on the
question and the same answer; "which protein kinase were we just
discussing?" was answered "CDK5". The control -- the same question with no
handoff -- could not say.
Sabotage: storing anything other than the streamed text fails
`test_what_is_kept_is_exactly_what_the_page_was_sent`. Two of the presence
sabotages first came back void -- ruff had reformatted the conditional, so
the anchor matched nothing -- and one printed "applied" over an unchanged
file; both were redone with the change asserted.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: specs/013-continue-chat-hand/spec.md
+20-1Lines changed: 20 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,7 +4,7 @@
4
4
5
5
**Created**: 2026-09-25
6
6
7
-
**Status**: Story 1 built and verified end to end; Stories 2 and 3 not started
7
+
**Status**: Stories 1 and 2 built and verified end to end; Story 3 needs a shared store
8
8
9
9
**Input**: Adam, 2026-09-25: "with the chat on both the search page and the analysis results we want there to be a button to go to the [chat] interface … two options … either the logged in version or the guest version of the chat app and have the context already be set with either the search results or the analysis summary with the summary data." And: "the chat should open in a new tab."
10
10
@@ -165,3 +165,22 @@ on "couldn't load the summary". Two ways to do Story 3, to decide later:
165
165
- the website calls the logged-in deployment's own summary and handoff
166
166
endpoints -- simpler, but the summary would be generated a second time in
167
167
that process, and would not be the one the reader saw (FR-002).
168
+
169
+
## Story 2, 2026-09-25
170
+
171
+
**Search-page answers can be continued.**`/api/answer` now keeps each answered
172
+
stream under an `answer_id`, emitted in `done` only when `state` is
173
+
`answered`, and `POST /api/handoff` accepts `{"kind": "search", "answer_id"}`.
174
+
Keyed per answer, never per question: two readers of one search get different
175
+
answers, and neither may continue the other's. What is kept is what the page
176
+
was sent -- after anchor and sources stripping -- not the raw model output.
177
+
178
+
**No human-presence claim for a search handoff**, unlike an analysis one.
179
+
`/api/answer` does not require it either (public pathway text), so the search
180
+
page may have none to send. Pinned in both directions: requiring it for search
181
+
fails one test, dropping it for analysis fails two.
182
+
183
+
Verified end to end through the real Turnstile gate as a first-time visitor:
184
+
a real answer, a handoff minted with no human claim, the tab opens on the
185
+
question and the same answer, and "which protein kinase were we just
186
+
discussing?" is answered "CDK5". The control, with no handoff, cannot say.
0 commit comments