phase: 138_markdown_blockquotes
**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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user