chore(agent): track .agent/ planning tree in git
Remove the blanket .agent/ gitignore so the phase roadmap, user stories, reports, and PLAN.md are versioned with the code. Only runtime artifacts (.agent/phase-sessions/, .agent/pipeline.log) remain ignored. Update AGENTS.md git protocol rule to match.
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
# Story: Sources Mid-Stream — an in-flight answer is not lost on navigation
|
||||
|
||||
**Phase:** `20_sources_midstream_bug` · **E2E:** `tests/e2e/test_sources_midstream_bug.py`
|
||||
|
||||
## Bug report (TODO.md L3, verbatim)
|
||||
|
||||
> *"Clicking "sources" while chat is generating clears chat and result
|
||||
> will never show up"*
|
||||
|
||||
## Narrative
|
||||
|
||||
As **a user with a question in flight**, I want to **click "Sources"
|
||||
while Brain is still generating** without losing the answer — when I
|
||||
come back to the chat, what had already streamed should be waiting for
|
||||
me.
|
||||
|
||||
- **Given** I asked a question and Brain's answer is still streaming
|
||||
- **When** I click the "Sources" nav link (or otherwise leave the chat
|
||||
page) mid-stream
|
||||
- **Then** the stream is aborted by the navigation (no server-side
|
||||
resume — A10 stays stateless), but returning to `/` shows my question
|
||||
**and** the partial answer that had already streamed, rendered exactly
|
||||
like any brain message (thinking block included, if any had streamed).
|
||||
|
||||
## Owner-confirmed (2026-08-24, roadmap A1)
|
||||
1. **A partial answer is persisted as a plain brain message** — no
|
||||
"(partial)" marker, no sources/suggestions (the turn is dead; the
|
||||
user can re-ask for the full answer).
|
||||
2. Navigation **before the first answer token** (pure thinking) persists
|
||||
nothing brain-side: the question is restored, no empty/partial
|
||||
bubble.
|
||||
3. The deliberate **New Chat** `clearChatStorage()` (sources/viewer
|
||||
pages) is untouched — that clear is by design (phase 14/19).
|
||||
4. No server-side resume (A10 stays stateless) and no "leave page?"
|
||||
confirmation dialog.
|
||||
|
||||
## Acceptance criteria
|
||||
1. `pagehide` (navigate-away / bfcache store) with an in-flight turn
|
||||
(`thinking` or `streaming`) and non-empty streamed text persists the
|
||||
partial raw answer via the existing `rememberBrainTurn` save-point
|
||||
helper (idempotent per turn — `persistedOnLeave`).
|
||||
2. Returning to `/` restores the question and the partial brain message
|
||||
through the existing restore path (escape-first markdown, optional
|
||||
collapsed thinking block) — no new UI surface, no error banner.
|
||||
3. Navigation during pure thinking (no answer tokens) persists nothing
|
||||
brain-side: exactly the user message is restored.
|
||||
4. A completed turn persists exactly as before (done metadata intact;
|
||||
no duplicate brain message).
|
||||
5. New Chat from the sources page still clears the conversation
|
||||
(`clearChatStorage`, phase 14/19 — untouched).
|
||||
6. Unit + integration green, `app/` coverage ≥ today's number (>90%
|
||||
gate), story E2E green in isolation, one `--no-gpg-sign` commit.
|
||||
|
||||
## Playwright Mapping Rule
|
||||
**Test Scenario → `tests/e2e/test_sources_midstream_bug.py`** (mock LLM,
|
||||
seeded KB):
|
||||
1. `test_partial_answer_survives_sources_nav_midstream` — admin (login,
|
||||
`next=/`), send the ~9s long-answer question, wait for the first
|
||||
streamed frames to render, click `#nav-sources` (lands on
|
||||
`/sources.html`), `page.goto("/")` → question + partial answer
|
||||
rendered, no `role="alert"`, `bor.chat.v1` holds the partial brain
|
||||
message (starts with the first streamed chunk, shorter than the full
|
||||
answer, no done metadata).
|
||||
2. `test_no_orphan_brain_message_when_navigated_before_first_token` —
|
||||
the mock streams the phase-17 thinking scratchpad, then a 4s
|
||||
pre-token pause; navigate during the pause, return to `/` → exactly
|
||||
one user message, zero brain messages, in the DOM and in storage.
|
||||
3. `test_completed_turn_unaffected` — a fully settled turn → trip to
|
||||
Sources and back → full answer, source chip, and a byte-identical
|
||||
`bor.chat.v1` payload (done metadata intact, no duplicate).
|
||||
4. `test_new_chat_still_clears_conversation` — completed turn →
|
||||
`/sources.html` → click the sources-page New Chat button → lands on
|
||||
`/` with the empty state and `bor.chat.v1` removed.
|
||||
Reference in New Issue
Block a user