Files
brain-of-reese/AGENTS.md
T
ducoterra 814a400e6d
Build and Push Containers / build-and-push-app (push) Successful in 1m57s
Build and Push Containers / build-and-push-db (push) Successful in 15s
phase: 181_svg_document_preview
**Phase 181 — final verification pass (all tasks already complete; verified + docs pass)**

- Verified: `app/rag/svg_text.py` (100 % cov), vector branch in `_index_image_file`, `_row_kind_mismatch` text→image migration, `IMAGE_MIMES[".svg"]`, sandboxed CSP on `GET /api/documents/{id}/image`, `.doc-image-img--vector` in `document.js`/`styles.css`, figure/thumbnail deliberate non-changes.
- Tests/lint (exact commands): `uv run pytest --cov=app --cov-report=term-missing` → **4265 passed, TOTAL 99 %** (exit 0) · `uv run pytest tests/e2e/test_svg_document_preview.py -v --no-cov` → **5 passed in isolation** · `uv run ruff check .` → clean · `uv run pyright` → **0 errors**.
- Regression (each run in isolation, `--no-cov`, all rc=0): `test_image_documents`, `test_chat_image_questions`, `test_answer_images`, `test_chat_answer_clusters`, `test_image_anchor_context`, `test_image_upload_feedback`, `test_markdown_inline_links_images`, `test_prose_word_wrap`, `test_pdf_support`, `test_pictorial_pdf`, `test_garbage_pdf`, `test_pdf_image_extraction`, `test_pdf_image_context`, `test_pdf_pages_in_sources`, `test_pdf_strip_additive_selfheal`, `test_sync_image_progress`.
- Completion criteria: ①SVG→`is_image`+`image_path`, no vision call, own `<title>/<desc>/<text>` — **met**; ②`image/svg+xml` + `default-src 'none'; sandbox` + `private, max-age=3600`, raster headers byte-unchanged — **met**; ③chat figure → modal `<img>`, no XML — **met**; ④`/document.html` full-width vector in `.doc-image` + text below — **met**; ⑤`![alt](/api/documents/<id>/image)` paints — **met**; ⑥raster/PDF unchanged — **met**; ⑦all gates green — **met**; ⑧commit + phase-dir move — harness-owned.
- Documentation: found and fixed README inconsistencies — stale forward reference "left to phase 181 / is phase 181's job" → completed-tense sizing note; "Updating the documents" standalone-image list missing `svg`; new **Raster only** bullet for question uploads (`.svg` → 422) documenting the `chat_images` guard; missing `svg_indexed=N` / `import: svg indexed` log contract; env-table heading `122/123/124` → `122/123/124/181`; new troubleshooting entry for an `.svg` showing raw XML. Full audit: every `Settings` field and every `.env.example` var is documented; pages/commands/scripts all exist. AGENTS.md verified accurate (phase-181 §5 clause present) — no changes needed beyond what the phase wrote.
- Notable deviation (kept from task 02): `app/api/chat_images.py` subtracts `_VECTOR_IMAGE_EXTENSIONS` from the question-image accept set — a required consequence of LOCKED A7 (`svg` in the default image set) since `_CHAT_IMAGE_FILENAME_RE` refuses to *serve* `.svg`; the regex itself is untouched, as the phase specified.
- Note for the commit: changes include `README.md`, `AGENTS.md` and `.env.example` — the phase file's `git add app/ frontend/ tests/ .env.example .agents/phases/` line omits the two markdown files; stage them too.
- Next pending phase: `182_verbatim_show_text`.
2026-10-14 22:54:43 -04:00

108 lines
6.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AGENTS.md — Operating Rules for All Agents on This Repo
1. **Always read `.agents/PLAN.md` first.** It contains the architecture, the
LOCKED DECISIONS, the UI/UX standards, the data model, and the roadmap.
Do not design anything that contradicts it.
2. **Follow the phased execution protocol in `.agents/phases/`.** Work through
`.agents/phases/todo/` in numeric order. When a phase is complete, move its
directory to `.agents/phases/complete/`.
3. **Strictly adhere to the LOCKED DECISIONS** (PLAN §2). If you believe a
LOCKED decision is wrong, stop and flag it — do not silently deviate.
4. **"One Story, One Phase":** each user story in `.agents/user_stories/`
corresponds to a distinct execution phase, and **every** phase — story-driven
or owner-directed — includes its own dedicated Playwright E2E test suite
(one test file for the phase, run in isolation).
5. **"UI Structure Check":** before finalizing any UI component, verify that
it follows the layout principles in `.agents/PLAN.md` §7 (proper container
usage, the 72rem centered reading column — every page rides the 72rem
container at every viewport; phase 100 supersedes §7.1's 46rem chat
column, full-width tables on Sources — no skinny wasted-space lists;
the catalogue tables (RAG, Sources, History) are bounded scroll
panels — `max-height: min(70vh, 64rem)` — with a sticky header row;
a clipped action label shows an ellipsis hint (`.cell-clip`) with
the full text kept reachable via the control's aria-label
(phase 156); an in-flight turn never paints an empty answer
bubble — the box is created empty as the reasoning anchor and
`.msg .bubble:empty { display: none }` hides it until the first
answer token (phase 166); an answer's inline `[text](url)`,
`![alt](src)` and `####`+ heading render through the ONE shared
renderer into an allowlist-gated `a.md-link` (underlined, never a
`javascript:`/`data:` element), a container-capped
`img.md-img` (`max-width: 100%` — no horizontal overflow at 375px)
and an `h5`/`h6` sized into the existing type scale — never printed
reference text (phase 180); an SVG document paints as a
graphic — the viewer's image block adds
`.doc-image-img--vector` (`width: 100%`, `object-fit: contain`)
when `format` is `svg` so a `viewBox`-only drawing spans the
`.doc-image` card instead of the 300×150 default box, while the
chat figure and Sources thumbnail keep their fixed caps and the
bytes stay an `<img>` src (never inlined) (phase 181); the
document viewer's page strip is
CLASS-scoped —
the primary class shows the strip in place of the text, the
additive class never hides the text: Text/Pages toggle group
(`aria-pressed`, Text default) with the strip behind it
(phase 163); a lazily-drilled KB-tree level keeps the level the
user is ON rendered while its node fetch runs, and its ONE
in-flight row (`role="status"`) / alert + Retry row
(`role="alert"`, a real `<button>`) sits inside that same bounded
scroll panel — and the stat cards read the server's `totals`,
never a walk of a depth-limited tree (phase 167)) and
meets WCAG 2.1 AA basics (semantic
landmarks, labels, contrast ≥4.5:1, focus-visible, aria-live for the
stream).
6. **"No CDN Rule":** all CSS, JS, fonts, and images must be served statically
from within the FastAPI application. No `<script src="https://…">` or
`<link href="https://…">` tags in HTML templates unless bundled locally.
An integration test enforces this on the index page.
7. **"Debugpy Check":** any new entrypoint must keep the conditional import —
`debugpy` is imported **only** when `DEBUGPY=1` (see
`app/core/debugging.py`); default off, never imported otherwise.
8. **Git protocol:** Git is mandatory. One **atomic, professional commit per
completed phase** (Conventional Commits, e.g. `feat(rag): stream RAG
answers over SSE with source citations`). Always append `--no-gpg-sign`
(the repo also sets `commit.gpgsign=false`). `.agents/` is **tracked and
committed** with the phase — only runtime artifacts (`.agents/phase-sessions/`,
`.agents/pipeline.log`) are gitignored.
9. **Test gates are non-negotiable:** a phase is complete only when unit +
integration tests pass, `app/` coverage is **>90%**
(`uv run pytest --cov=app --cov-report=term-missing`), and the phase's
Playwright E2E file passes **in isolation**
(`uv run pytest tests/e2e/test_<story>.py -v --no-cov`).
10. **Amply log, visibly feed back:** server-side, log the required
per-turn line (PLAN §9) **and** the per-response byte-budget line
(`app/core/response_budget.py` — one
`api method=… path=… status=… bytes=… enc=… duration_ms=…` line per
`/api/*` response, INFO at/over `BOR_LOG_LARGE_RESPONSE_BYTES`, read it
with `grep ' :: api '`, audit it with
`uv run python -m scripts.api_payload_audit`, phase 167); UI-side,
honor the "never stale" feedback contract (PLAN §7.4).
11. **Never block the event loop:** anything blocking (a `subprocess`,
synchronous file/network I/O, a heavy walk) reached from an `async`
handler or a spawned background coroutine goes through
`asyncio.to_thread` (or `create_subprocess_exec`). The sync's git
calls do (phase 179: a long `git clone` used to park the single
uvicorn loop and freeze the whole HTTP surface, the 2 s
`GET /api/sync/status` poll included — pinned by
`tests/integration/test_sync_git_freeze.py` with a 3 s stub and by
`tests/e2e/test_sync_git_freeze.py` with a real ~9 s `file://`
clone). A cooperative cancel unblocks the *run* at the `to_thread`
await; the worker thread itself runs to completion (threads are not
interruptible) while the loop keeps serving — pinned by the same E2E
module's cancel-during-clone test.
## Quick reference
```bash
podman compose up -d db # start Postgres 17 + pgvector
cp .env.example .env # once; then edit
uv run alembic upgrade head # apply migrations
uv run uvicorn app.main:app --reload # dev server (add DEBUGPY=1 to debug)
uv run pytest # unit + integration
uv run pytest --cov=app --cov-report=term-missing
uv run playwright install chromium # once
uv run pytest tests/e2e/test_smoke.py -v --no-cov
uv run ruff check . && uv run pyright # lint + types
uv run python -m scripts.eval_retrieval --from-file tests/fixtures/retrieval_battery.txt # retrieval regression battery (phases 119/162)
uv run python -m scripts.api_payload_audit # endpoint byte audit vs the 256 KB budget (phase 167; app must be running)
```