Standardize on the .agents/ directory (shared with project skills): phases/, user_stories/, reports/, screenshots/, validate.sh, and phase-sessions/ + pipeline.log all move to .agents/ (git mv preserves history; runtime artifacts move alongside). Updates every reference in AGENTS.md, README.md, .gitignore, app docstrings, and test story headers. Historical KB content in data/ and the runtime pipeline.log transcript are left untouched.
3.5 KiB
3.5 KiB
Phase 44 — Markdown tables (chat, viewer, thinking)
Source: TODO.md L6 — "Certain markdown formatting isn't working - tables for example don't get rendered as tables in the chat response."
Story: .agents/user_stories/markdown-tables.md
Context: frontend/assets/markdown.js is the shared escape-first renderer (no libs, A11): fence protection → escape → inline transforms (code, **bold**, *em*, h1–h3, lists) → paragraph pass → fence restore. It has no table support — GFM pipe tables render as one raw |-littered paragraph. The renderer serves the chat answer, the document viewer/modal, and the thinking block, so one change covers all three.
Objective
GFM pipe tables render as semantic, styled, XSS-safe <table> elements everywhere the shared renderer runs, with a horizontal-overflow guard for wide tables.
Dependencies
43_thinking_scroll_back(todo) — sequential only (the thinking block also renders markdown; no shared-file conflict beyond the renderer itself).08_story_dark_tech_theme(complete) — the palette tokens.md-tablemust use.26_document_modal_viewer/10_story_document_viewer(complete) — the second renderer consumer (viewer/modal).
Tasks
01_table_renderer_and_styles.md— table pass inmarkdown.js+.md-tableCSS.02_mock_table_trigger.md— deterministic table answer (incl. a wide table) inmock_llm.py.03_tables_e2e_and_commit.md— unit pins + story E2E suite + regressions + commit.
Testing & Quality
- Unit: new
tests/unit/test_markdown_tables.py— source pins in the house style (regex overmarkdown.js/styles.css): the table-protection pass exists and runs after the fence pass and before the escape pass; cells are escaped + inline-transformed; output carriesclass="md-table",<thead>,th scope="col", and the.md-table-wrapwrapper;styles.csshas the wrapper overflow rule + table borders + reduced-motion-relevant rules. (Behavior is browser-proven by the E2E; unit pins catch silent regressions without a browser — the established frontend pattern.) - Coverage: frontend-only —
app/TOTAL unchanged, >90%. - E2E (mandatory, A16):
tests/e2e/test_markdown_tables.py, run in isolation.
Completion Criteria
- A pipe table in a chat answer renders
<div class="md-table-wrap"><table class="md-table">with<thead>/<tbody>,<th scope="col">headers, correct cell texts; no raw|---|in the bubble. - A wide table scrolls inside its wrapper; the 46rem column does not overflow the page.
- XSS-safe (escaped cells), fences win over tables, lone pipes stay text.
- The document viewer/modal renders the same table for a fixture document containing one.
uv run pytestgreen; coverage TOTAL unchanged.uv run pytest tests/e2e/test_markdown_tables.py -v --no-covgreen in isolation (DB up).- Regression E2E suites green in isolation:
test_chat_rag.py,test_document_viewer.py,test_document_summaries.py,test_smoke.py. uv run ruff check . && uv run pyrightclean.- One
--no-gpg-signcommit; phase dir moved to.agents/phases/complete/.
Locked decisions
- A11 untouched — still the local ~90-line renderer, no library, no CDN.
- Owner-locked (2026-08-27, roadmap A3): scope = GFM pipe tables (header + separator + body); links/blockquotes/hr out of scope; alignment colons parsed but rendered left; wide tables get the
overflow-x: autowrapper. - A16/A17 honoured — one story E2E suite, one atomic commit.