chore(agent): track .agent/ planning tree in git
Build and Push Containers / build-and-push-app (push) Successful in 12s
Build and Push Containers / build-and-push-db (push) Successful in 10s

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:
2026-09-01 10:18:22 -04:00
parent 5fa620fde5
commit 4971e2859d
818 changed files with 23964 additions and 4 deletions
@@ -0,0 +1,13 @@
Verification complete — all green. Final report:
**Phase 17 — final verification pass (all tasks already in `complete/`, commit `b16deb2` present)**
- **Verified, no code changes needed:** StreamPiece→SSE `thinking` plumbing (llm.py/chat.py/schemas/config), collapsible `.thinking` block (streaming open, auto-collapse, user-toggle, persisted collapsed, stream-drop guard), mock trigger + story suite, docs (user story, PLAN §2/§4/§7.4/§7.5/§9/§12 all carry the 2026-08-23 owner-permission notes, `.env.example` `BOR_STREAM_THINKING`)
- `uv run pytest --cov=app --cov-report=term-missing` → **288 passed, 99% coverage** (>90%)
- `uv run pytest tests/e2e/test_thinking_display.py -v --no-cov` → **5/5 passed** in isolation
- Regression suites in isolation: `test_chat_rag` 3 ✓, `test_loading_feedback` 5 ✓, `test_chat_persistence` 4 ✓, `test_honest_deflection` 3 ✓
- `uv run ruff check . && uv run pyright` → **clean** (0 errors)
- **Live check (real aipi, dev server):** thinking frames stream before all deltas, `done` shape unchanged, log line shows `thinking_chars=5317` before `total_ms`; with `BOR_STREAM_THINKING=0` → 0 thinking frames, deltas/`done` identical, chars still counted
- **UI Structure Check:** Phase-08 tokens, contrast 8.7:1/6.9:1, 44px summary target, focus-visible, chevron gated by `prefers-reduced-motion`, no CDN tags, 46rem column intact
- Only anomaly seen was transient — my own parallel pytest runs sharing one Postgres caused 1 error + 1 E2E failure; both green on sequential re-run (not a code defect)
- Next pending phase: **`18_follow_bottom_scroll`** (then `19_shared_header`)
@@ -0,0 +1,34 @@
_______________ coverage: platform linux, python 3.13.13-final-0 _______________
Name Stmts Miss Cover
--------------------------------------------
app/__init__.py 1 0 100%
app/api/__init__.py 0 0 100%
app/api/auth.py 22 0 100%
app/api/chat.py 95 0 100%
app/api/docs.py 23 0 100%
app/api/health.py 10 0 100%
app/api/steering.py 33 0 100%
app/api/suggestions.py 8 0 100%
app/config.py 56 0 100%
app/core/__init__.py 0 0 100%
app/core/auth.py 20 0 100%
app/core/debugging.py 29 2 93%
app/core/logging.py 13 0 100%
app/db.py 21 0 100%
app/main.py 36 0 100%
app/models.py 46 0 100%
app/rag/__init__.py 0 0 100%
app/rag/chunker.py 206 4 98%
app/rag/importer.py 150 5 97%
app/rag/llm.py 105 0 100%
app/rag/prompts.py 44 0 100%
app/rag/retriever.py 102 3 97%
app/rag/suggestions.py 27 0 100%
app/schemas.py 62 0 100%
--------------------------------------------
TOTAL 1109 14 99%
coverage gate: app/ 99% (>90%) OK
All checks passed!
0 errors, 0 warnings, 0 informations
validation OK
@@ -0,0 +1,19 @@
Task 01 complete — all gates green.
**Implemented**
- `app/rag/llm.py`: frozen `StreamPiece(kind, text)`; `chat_stream` now yields `AsyncIterator[StreamPiece]` — `delta.reasoning_content` (verified wire field) → thinking piece with `delta.reasoning` fallback, thinking before content per chunk; `LLMError` wrap + gen params unchanged; docstrings updated
- `app/schemas.py`: `ChatThinkingEvent` (`{type:"thinking", text}`)
- `app/config.py`: `stream_thinking: bool = True` (`BOR_STREAM_THINKING` kill-switch)
- `app/api/chat.py`: pieces → `thinking`/`delta` SSE events; `thinking_chars` counted always, emitted only when enabled; `thinking_chars=N` inserted before `total_ms=N` in the per-turn log line
- `.env.example`: `BOR_STREAM_THINKING=1`; tests extended (llm client ×4 new, SSE frame, config, integration ×2); adapted pre-existing string-yielding fakes (`test_chat_gate.py`)
**Results**
- `uv run pytest tests/unit/test_llm_client.py tests/unit/test_sse_events.py tests/unit/test_config.py tests/integration/test_chat_api.py -v --no-cov` → 55 passed
- `uv run pytest --cov=app --cov-report=term` → 279 passed, **99% coverage** (>90%)
- `uv run ruff check . && uv run pyright` → clean, 0 errors
- Live curl vs real aipi: 1360 `thinking` frames precede all 170 `delta` frames, `done` last, log line carries `thinking_chars=5612`; with `BOR_STREAM_THINKING=0` zero thinking frames, deltas intact, still counted (`thinking_chars=7187`)
- Frontend untouched; its `readSSE` branches only on `delta`/`done`/`error`, so unknown `thinking` frames are ignored (no regression)
**Deviations**: none. No commit made — the atomic commit belongs to task 04 per the phase plan.
**Next pending task**: `.agent/phases/todo/17_thinking_display/02_frontend_thinking_block.md`
@@ -0,0 +1,34 @@
_______________ coverage: platform linux, python 3.13.13-final-0 _______________
Name Stmts Miss Cover
--------------------------------------------
app/__init__.py 1 0 100%
app/api/__init__.py 0 0 100%
app/api/auth.py 22 0 100%
app/api/chat.py 95 0 100%
app/api/docs.py 23 0 100%
app/api/health.py 10 0 100%
app/api/steering.py 33 0 100%
app/api/suggestions.py 8 0 100%
app/config.py 56 0 100%
app/core/__init__.py 0 0 100%
app/core/auth.py 20 0 100%
app/core/debugging.py 29 2 93%
app/core/logging.py 13 0 100%
app/db.py 21 0 100%
app/main.py 36 0 100%
app/models.py 46 0 100%
app/rag/__init__.py 0 0 100%
app/rag/chunker.py 206 4 98%
app/rag/importer.py 150 5 97%
app/rag/llm.py 105 0 100%
app/rag/prompts.py 44 0 100%
app/rag/retriever.py 102 3 97%
app/rag/suggestions.py 27 0 100%
app/schemas.py 62 0 100%
--------------------------------------------
TOTAL 1109 14 99%
coverage gate: app/ 99% (>90%) OK
All checks passed!
0 errors, 0 warnings, 0 informations
validation OK
@@ -0,0 +1,9 @@
**Task 02 complete — frontend collapsible Thinking block.**
- `app.js`: `thinking` SSE branch (accumulate → clear 120s guard → create wrap without state change → remove typing dots → stream escape-first markdown into the block, bottom-pinned while open); `ensureThinkingBlock`/`closeThinkingBlock` helpers; delta branch now transitions `thinking → streaming` and auto-collapses (never reopens); `done` sets `sawDone`, closes the block, renders + persists the shared `EMPTY_ANSWER_FALLBACK` on thinking-without-answer, and persists `thinking: thinkingAcc || undefined` (key dropped when absent — no version bump); `sawDone` stream-drop guard after `readSSE`; restore renders a collapsed block; header docs extended.
- `styles.css`: `details.thinking`/`summary`/`.thinking-text` in Phase-08 tokens (44px target, 8.7:1 / 6.9:1, CSS chevron), chevron transition stills under `prefers-reduced-motion`.
- Unit pins added in `test_frontend_feedback.py` (+6) and `test_chat_persistence.py` (+3); one stale pin (`rememberBrainTurn(acc` → `rememberBrainTurn(finalText || acc`) updated per the task's mandated call-site change.
- `uv run pytest tests/unit/test_frontend_feedback.py tests/unit/test_chat_persistence.py -v --no-cov` → 23 passed; `uv run pytest` → 288 passed; `uv run pytest --cov=app` → 99% (>90%, unchanged from baseline); `uv run ruff check . && uv run pyright` → clean.
- Browser-verified with a throwaway 5-scenario Playwright run (live app + thinking-capable mock, deleted after): streams open → collapses on first answer token, toggle works, reload restores collapsed block with same text, no-trigger turn renders with no block and no `thinking` key stored, deflection coexists. E2E regressions green in isolation: `test_chat_rag` (3), `test_loading_feedback` (5), `test_chat_persistence` (4), `test_honest_deflection` (3).
- Note: the stream-drop guard is unit-pinned only — a clean early LLM-stream end is client-invisible (the app always emits `done` from its own generator); task 03's 5 scenarios don't E2E-cover it either.
- Next pending task: `.agent/phases/todo/17_thinking_display/03_e2e_mock_and_story_suite.md` (mock `think out loud` trigger + `test_thinking_display.py`).
@@ -0,0 +1,34 @@
_______________ coverage: platform linux, python 3.13.13-final-0 _______________
Name Stmts Miss Cover
--------------------------------------------
app/__init__.py 1 0 100%
app/api/__init__.py 0 0 100%
app/api/auth.py 22 0 100%
app/api/chat.py 95 0 100%
app/api/docs.py 23 0 100%
app/api/health.py 10 0 100%
app/api/steering.py 33 0 100%
app/api/suggestions.py 8 0 100%
app/config.py 56 0 100%
app/core/__init__.py 0 0 100%
app/core/auth.py 20 0 100%
app/core/debugging.py 29 2 93%
app/core/logging.py 13 0 100%
app/db.py 21 0 100%
app/main.py 36 0 100%
app/models.py 46 0 100%
app/rag/__init__.py 0 0 100%
app/rag/chunker.py 206 4 98%
app/rag/importer.py 150 5 97%
app/rag/llm.py 105 0 100%
app/rag/prompts.py 44 0 100%
app/rag/retriever.py 102 3 97%
app/rag/suggestions.py 27 0 100%
app/schemas.py 62 0 100%
--------------------------------------------
TOTAL 1109 14 99%
coverage gate: app/ 99% (>90%) OK
All checks passed!
0 errors, 0 warnings, 0 informations
validation OK
@@ -0,0 +1,17 @@
All gates green and the repository is left functional. Final report:
**Task 03 (E2E mock + story suite) — complete**
Implemented:
- `tests/e2e/mock_llm.py`: `THINKING_TRIGGER = "think out loud"`, `compose_thinking()` (809-char deterministic 4-step scratchpad quoting the question, includes `Step 2: Check my notes`), `_sse_stream(answer, delay, thinking="")` (12-char `reasoning_content` frames at same 0.02s cadence before content frames; byte-identical without trigger), non-streaming `reasoning_content` future-proofing, docstring trigger bullet
- `tests/e2e/test_thinking_display.py` (new, mock-only header note + determinism note): `seeded_kb` fixture (8-doc seed, truncate teardown, conftest `db_ready` skip) + `send_and_wait` helper + all 5 scenarios: stream open→auto-collapse, post-done toggle (keyboard-focusable summary), reload restore (closed block, matching text, raw-persisted check), no-block-without-trigger, thinking+deflection coexistence
Results:
- `uv run pytest tests/e2e/test_thinking_display.py -v --no-cov` → 5/5 passed (in isolation)
- Regression, each in isolation: `test_chat_rag.py` 3 passed, `test_loading_feedback.py` 5 passed, `test_chat_persistence.py` 4 passed, `test_honest_deflection.py` 3 passed
- `uv run pytest --cov=app --cov-report=term-missing` → 288 passed, app coverage 99% (>90%)
- `uv run ruff check . && uv run pyright` → clean (fixed a generator-fixture `Iterator[None]` annotation)
Decisions: trigger substring verified collision-free (only "cloud-init" contains "loud"); no app/frontend bugs found — one fix was to my own test's whitespace-normalization assertion (renderMarkdown `<br>` drops newlines in textContent).
Next pending task: `.agent/phases/todo/17_thinking_display/04_story_docs_plan_commit.md`
@@ -0,0 +1,34 @@
_______________ coverage: platform linux, python 3.13.13-final-0 _______________
Name Stmts Miss Cover
--------------------------------------------
app/__init__.py 1 0 100%
app/api/__init__.py 0 0 100%
app/api/auth.py 22 0 100%
app/api/chat.py 95 0 100%
app/api/docs.py 23 0 100%
app/api/health.py 10 0 100%
app/api/steering.py 33 0 100%
app/api/suggestions.py 8 0 100%
app/config.py 56 0 100%
app/core/__init__.py 0 0 100%
app/core/auth.py 20 0 100%
app/core/debugging.py 29 2 93%
app/core/logging.py 13 0 100%
app/db.py 21 0 100%
app/main.py 36 0 100%
app/models.py 46 0 100%
app/rag/__init__.py 0 0 100%
app/rag/chunker.py 206 4 98%
app/rag/importer.py 150 5 97%
app/rag/llm.py 105 0 100%
app/rag/prompts.py 44 0 100%
app/rag/retriever.py 102 3 97%
app/rag/suggestions.py 27 0 100%
app/schemas.py 62 0 100%
--------------------------------------------
TOTAL 1109 14 99%
coverage gate: app/ 99% (>90%) OK
All checks passed!
0 errors, 0 warnings, 0 informations
validation OK
@@ -0,0 +1,20 @@
Task 04 complete. Final report:
**Implemented**
- `.agent/user_stories/thinking-display.md` — all five sections (header, narrative, 7 acceptance criteria, UI visualization & structure, Playwright Mapping Rule with the 5 task-03 scenarios verbatim)
- README: new "Thinking" section after "Using the UI" (what it is, model's call, `BOR_STREAM_THINKING=0` kill-switch); `.env.example` line 19 verified present
- `.agent/PLAN.md` revisions, all noting owner permission 2026-08-23: header revisions line, §4 `thinking` SSE frames + client rules + revision note, §7.4 "Thinking (model reasoning)" row + note, §7.5 `.thinking`/`.thinking-text`, §9 `thinking_chars=` in the per-turn log line, §12 roadmap row 17; no anchor text altered, nothing renumbered
- One atomic commit: `b16deb2 feat(chat): stream model thinking over SSE and show it in a collapsible block` (18 files, `--no-gpg-sign`)
**Gates (all green)**
- `uv run pytest --cov=app --cov-report=term-missing` → 288 passed, 99% on `app/` (>90%)
- `uv run pytest tests/e2e/test_thinking_display.py -v --no-cov` → 5/5 in isolation
- Regressions in isolation: `test_chat_rag` 3/3, `test_loading_feedback` 5/5, `test_chat_persistence` 4/4, `test_honest_deflection` 3/3
- `uv run ruff check . && uv run pyright` → clean; no CDN tags (block is dynamic JS, index template untouched)
- Live aipi check: 1580 `thinking` frames before 267 `delta` frames, log line `thinking_chars=5581`; with `BOR_STREAM_THINKING=0` → 0 thinking frames, deltas unchanged, `thinking_chars=5011` still counted
**Decisions / deviations**
- Skipped the task's `mv …/17_thinking_display → complete/` — the harness protocol explicitly moves task files on success; left phase files in place
- Commit staged the tracked `.agent/PLAN.md` + code/docs; gitignored phase/story files stay untracked (matches repo convention since phase 11; the task's `git add -A .agent/` resolves to exactly that)
**Next pending task:** `18_follow_bottom_scroll` (after the harness moves task 04 to complete/).
@@ -0,0 +1,34 @@
_______________ coverage: platform linux, python 3.13.13-final-0 _______________
Name Stmts Miss Cover
--------------------------------------------
app/__init__.py 1 0 100%
app/api/__init__.py 0 0 100%
app/api/auth.py 22 0 100%
app/api/chat.py 95 0 100%
app/api/docs.py 23 0 100%
app/api/health.py 10 0 100%
app/api/steering.py 33 0 100%
app/api/suggestions.py 8 0 100%
app/config.py 56 0 100%
app/core/__init__.py 0 0 100%
app/core/auth.py 20 0 100%
app/core/debugging.py 29 2 93%
app/core/logging.py 13 0 100%
app/db.py 21 0 100%
app/main.py 36 0 100%
app/models.py 46 0 100%
app/rag/__init__.py 0 0 100%
app/rag/chunker.py 206 4 98%
app/rag/importer.py 150 5 97%
app/rag/llm.py 105 0 100%
app/rag/prompts.py 44 0 100%
app/rag/retriever.py 102 3 97%
app/rag/suggestions.py 27 0 100%
app/schemas.py 62 0 100%
--------------------------------------------
TOTAL 1109 14 99%
coverage gate: app/ 99% (>90%) OK
All checks passed!
0 errors, 0 warnings, 0 informations
validation OK