phase: 138_markdown_blockquotes
Build and Push Containers / build-and-push-app (push) Successful in 2m36s
Build and Push Containers / build-and-push-db (push) Successful in 11s

**Phase 138 final verification pass — all green, no defects found.**

- Verified: blockquote pass in `frontend/assets/markdown.js` (step 2b, between escape+inline and paragraphs), guard extended to `|blockquote`, docstring updated; `.md-quote` CSS (left border + padding + `--ink-soft`, no fill, palette vars only, contrast comments, user-bubble override); unit pins + node behavior; dedicated E2E (chat / viewer / restore); mock `QUOTE_TRIGGER`/`QUOTE_ANSWER`; fixture `tests/fixtures/quote_docs/`. Thinking block confirmed on the same shared renderer (app.js L2025/L3095).
- `uv run pytest tests/e2e/test_markdown_blockquotes.py -v --no-cov` → 3 passed (isolation)
- `uv run pytest --cov=app --cov-report=term-missing` → 3268 passed, 9 skipped; TOTAL **99%** (>90%)
- `uv run ruff check . && uv run pyright` → All checks passed; 0 errors
- Criteria: quotes render in chat/viewer/restore ✅ (E2E) + thinking ✅ (shared renderer + unscoped AA CSS); `>>` collapses, fence/table `>` untouched ✅ (unit+e2e); non-quote byte-identical ✅ (exact fixture; phase-44/renderer suites pass **unedited**, confirmed by empty `git diff --stat`)
- No deviations; commit + phase-dir move left to the harness per pipeline rules (working tree clean of stray changes — only phase-138 diffs present)
- Next pending phase: **139_read_range_tool_lines**
This commit is contained in:
2026-09-30 10:49:40 -04:00
parent 0db6ab5df0
commit 1a657ce4ca
22 changed files with 1230 additions and 1 deletions
+60
View File
@@ -382,6 +382,19 @@ Implements just enough of the aipi surface:
``SUMMARY_MODE``), so a marker question always gets the table
answer; the E2E asks it against an on-topic fixture (HIGH gate) and
asserts non-deflection.
- user message containing ``show me the blockquote`` (phase 138,
markdown blockquotes, TODO.md L3) -> the fixed quote answer
(``QUOTE_ANSWER``): a single-line quote, a 3-line quote group, a
nested ``>>`` collapse line, an ``<script>`` XSS probe quote, a
``>`` inside a code fence, a ``>`` in a table cell, and a plain
closing line — byte-stable, so the story E2E can assert the four
rendered ``<blockquote class="md-quote">`` shapes, the inert XSS
line, the untouched fence and table-cell ``>``, and the restore
path's byte-identical re-render. Checked BEFORE the
``DEFLECT_MODE`` branch (a deflection prompt never carries the
marker, same reasoning as ``SUMMARY_MODE``), so a marker question
always gets the quote answer; the E2E asks it against an on-topic
fixture (HIGH gate) and asserts non-deflection.
- user message containing ``echo my history``
(``HISTORY_TRIGGER``, phase 74, TODO L4 — chat history with prior
thinking reaches the LLM) -> the deterministic HISTORY ECHO, derived
@@ -955,6 +968,43 @@ TABLE_ANSWER = (
"| value-one | value-two | value-three | value-four | value-five |"
)
#: Phase 138 (markdown blockquotes, TODO.md L3): a user message
#: containing this substring (case-insensitive) gets the fixed quote
#: answer (``QUOTE_ANSWER`` below) — the renderer's whole blockquote
#: contract in one byte-stable answer (see the module docstring).
#: Existing E2E questions do not contain the phrase, so every other
#: suite is unaffected.
QUOTE_TRIGGER = "show me the blockquote"
#: The fixed quote answer (phase 138) — byte-stable on purpose: the
#: story E2E asserts the four rendered <blockquote class="md-quote">
#: shapes (single line, 3-line group, nested collapse, XSS probe),
#: the untouched fence line and table cell, and the plain closing
#: line (the stream-completion marker — it lands last).
QUOTE_ANSWER = (
"Here's the shape, in quotes:\n"
"\n"
"> a short single-line quote\n"
"\n"
"> the first line\n"
"> the second line\n"
"> the third line\n"
"\n"
">> nested quotes collapse to one level\n"
"\n"
"> <script>alert(1)</script>\n"
"\n"
"```\n"
"> inside a fence stays fence content\n"
"```\n"
"\n"
"| Cell |\n"
"|---|\n"
"| > x stays a cell |\n"
"\n"
"And the quote ends here — a plain closing line."
)
# ---------------------------------------------------------------------------
# Phase 67 (LLM retry, TODO.md L3): deterministic failure injection
# ---------------------------------------------------------------------------
@@ -2720,6 +2770,16 @@ def compose_answer(body: dict[str, Any]) -> str:
# against an on-topic fixture, where the gate is HIGH, and
# asserts non-deflection as part of the table test.
answer = TABLE_ANSWER
elif QUOTE_TRIGGER in user.lower():
# Markdown blockquotes (phase 138, TODO.md L3): the story E2E's
# deterministic quote answer — the renderer's blockquote
# contract in one byte-stable stream (single-line quote,
# 3-line group, nested collapse, the <script> XSS probe, the
# fence-protected >, the table-cell >, the plain close).
# Checked BEFORE the DEFLECT_MODE branch, like TABLE_TRIGGER:
# the marker lives in the user message, so a marker question
# always gets the quote answer whatever the gate says.
answer = QUOTE_ANSWER
elif HISTORY_TRIGGER in user.lower():
# Phase 74 (TODO L4, chat history): the deterministic history
# echo — proves on the wire that the client's prior turns (and