All completion criteria verified. Final report:
**Phase 159 — Edit the last question and re-ask it: VERIFIED COMPLETE** (all 4 tasks already in `complete/`; this pass verified end-to-end)
**Verified (implementation landed + tests green):**
- `app.js`: `EDIT_ICON`, `lastUserWrap`, `appendEditButton`/`markLastEditable` (single-target discipline, in-flight guard), `openEditor` (labelled textarea, `maxlength=4000`, Escape/Ctrl+⌘+Enter, empty guard), save path: replace → splice tail → DOM removal → `lastBrainWrap=null` → `saveConversation`+`persistConversation` → `runTurn(text,{reask:true})` — matches README exactly
- `styles.css`: `.edit-btn`/`.edit-form`/`.edit-error`/`.is-editing` + mobile 44px; unit pins 40, E2E 7, integration `test_chats_api.py` extended
**Test/lint results (all green):**
- `uv run pytest` — 3,661 tests, exit 0
- `uv run pytest --cov=app --cov-report=term-missing` — TOTAL **99%** (>90%)
- `uv run ruff check . && uv run pyright` — all checks passed; 0 errors
- `uv run pytest tests/e2e/test_edit_and_resend.py -v --no-cov` (DB up) — **7/7 passed**
- Regressions in isolation: `test_retry_answer.py` 4/4, `test_failed_turn_retry.py` 4/4, `test_chat_persistence.py` 4/4
**Completion criteria:** all 7 ✓ — Edit on last bubble + prefill + Escape/Cancel restore (E2E #3); replace+truncate+re-ask with no duplicated question (E2E #1); refresh survival (E2E #4); failed/“question too long” turn editable + trim re-asks (E2E #5); earlier bubbles un-editable + byte-identical Retry/Stop/persistence (E2E #2 + regressions + unit pins); suite/coverage/lint ✓; E2E + regressions isolated ✓. Commit + phase-dir move: harness's job (no git ops by me, per protocol).
**Documentation:** README.md — full review; phase 159 section cross-checked against `app.js` source (cap, keys, truncate, image survival) — consistent, no changes needed; all 30 `.env.example` vars documented. AGENTS.md — full review; all rules/quick-ref commands valid — verified consistent, no changes needed.
**Notable:** none — no defects found in prior work; no live-infrastructure changes required.
**Next pending phase:** `160_tool_budget_telemetry`
70 lines
2.4 KiB
TOML
70 lines
2.4 KiB
TOML
[project]
|
|
name = "brain-of-reese"
|
|
version = "0.1.0"
|
|
description = "Brain of Reese — a chippy RAG chatbot that answers questions about Reese's Homelab and Deployments, powered by self-hosted models."
|
|
readme = "README.md"
|
|
requires-python = ">=3.12"
|
|
dependencies = [
|
|
# --- Web framework ---
|
|
"fastapi>=0.115,<1.0",
|
|
"uvicorn[standard]>=0.30,<1.0",
|
|
"pydantic>=2.7,<3.0",
|
|
"pydantic-settings>=2.3,<3.0",
|
|
"python-dotenv>=1.0,<2.0",
|
|
# --- Database (PostgreSQL 17 + pgvector) ---
|
|
"sqlalchemy>=2.0,<2.1",
|
|
"alembic>=1.13,<2.0",
|
|
"psycopg[binary]>=3.1,<4.0",
|
|
"pgvector>=0.3,<1.0",
|
|
# --- LLM client (OpenAI-compatible, self-hosted "aipi") ---
|
|
"httpx>=0.27,<1.0",
|
|
"openai>=1.40,<3.0",
|
|
# Phase 16: starlette's SessionMiddleware signs the session cookie with
|
|
# itsdangerous — an OPTIONAL starlette extra ("full") since starlette 1.x,
|
|
# so the app declares it directly (narrower than starlette[full]).
|
|
"itsdangerous>=2.2,<3.0",
|
|
# Phase 49: FastAPI's multipart parser for the archive upload form
|
|
# (POST /api/git-sources/upload) — an A2 FastAPI implementation detail,
|
|
# not a new architectural anchor (phase 49 locked decisions).
|
|
"python-multipart>=0.0.9,<0.1",
|
|
# Phase 126: PDF support (locked A1) — PyMuPDF (import name `fitz`) is the
|
|
# PDF parser: page-by-page text extraction AND embedded image extraction
|
|
# in a single API. Chosen over pdfplumber (text-only) per phase 126 design.
|
|
"pymupdf>=1.24,<2.0",
|
|
]
|
|
|
|
[dependency-groups]
|
|
dev = [
|
|
"debugpy>=1.8",
|
|
"ruff>=0.5",
|
|
"pyright>=1.1",
|
|
"pytest>=8.2",
|
|
"pytest-cov>=5.0",
|
|
"playwright>=1.45",
|
|
]
|
|
|
|
[tool.ruff]
|
|
line-length = 100
|
|
target-version = "py312"
|
|
|
|
[tool.ruff.lint]
|
|
select = ["E", "F", "W", "I", "UP", "B", "SIM"]
|
|
|
|
# The phase-159 task-01 test embeds the FULL byte-pin of app.js's
|
|
# ``runTurn`` (32KB, 584 lines — the house byte-pin convention for the
|
|
# phase's "NOT touched" contract). The pin must stay byte-identical to
|
|
# the JS source, so its long JS comment lines cannot be wrapped — E501
|
|
# is ignored for that file only (the test code itself is wrapped).
|
|
[tool.ruff.lint.per-file-ignores]
|
|
"tests/unit/test_frontend_edit_message.py" = ["E501"]
|
|
|
|
[tool.pyright]
|
|
pythonVersion = "3.12"
|
|
typeCheckingMode = "standard"
|
|
include = ["app", "scripts", "alembic", "tests"]
|
|
|
|
[tool.pytest.ini_options]
|
|
testpaths = ["tests/unit", "tests/integration"]
|
|
pythonpath = ["."]
|
|
addopts = "-q"
|