Files
ducoterra 7fce6572d0
Build and Push Containers / build-and-push-app (push) Successful in 1m45s
Build and Push Containers / build-and-push-db (push) Successful in 13s
feat: phases 77–80 — navbar view refresh, static background, API tokens, history suggestion chips
Single consolidated commit for four completed, validated phases (77, 78,
79, 80). The pipeline run left all work uncommitted because the harness
commits only with PHASE_COMMIT=1 while child executors are forbidden from
committing; the phases themselves all passed validation and moved to
.agents/phases/complete/.

Phase 77 — navbar view refresh
- router.js dispatches bor:view-refresh on re-show / active re-click /
  popstate (gated on wasMounted; first show and boot exempt)
- History / RAG / Sources / Tuning re-fetch on refresh (admin branch);
  Chat deliberately excluded (stream survival)
- History "Refresh" button (admin-only, in-flight disable + status line)
- New story suite tests/e2e/test_navbar_refresh.py (7 tests)

Phase 78 — static background
- Removed the animated glow layers; static 44px grid over the flat --bg
  canvas; default and reduced-motion renders byte-identical
- Updated background/theme E2E suites; removed bg-glow test pins

Phase 79 — API tokens
- api_tokens model + migration 0012; hash-only token service
- Admin tokens API + Tokens admin view; POST /api/token-auth;
  live-revoking require_user on chat / suggestions / document content
- Frontend token gate with localStorage cache; anonymous E2E suites
  migrated to token login
- New story suite tests/e2e/test_api_tokens.py (9 tests)

Phase 80 — history suggestion chips
- last_questions() endpoint with SEED fallback; startNewChat() refetch
- Seed-semantics docs (config.py, .env.example, README)
- Integration state matrix + E2E suite rewritten to the 4 chip states

Also included: phase-76 report artifacts and the repo restore-test-db
skill (previously untracked), scripts/* ruff fixes from phase 77.

Final gate state (phase 80 final pass, covers everything above):
- uv run pytest --cov=app → 1637 passed, 0 failed, app/ coverage 99%
- uv run ruff check . && uv run pyright → clean, 0 errors
- Per-phase story E2E suites green in isolation
2026-09-07 12:39:01 -04:00

5.8 KiB

name, description
name description
restore-test-db Restores the large 1,000-document test knowledge base (the live-KB replica snapshot at data/bor_live_kb_replica.dump.sql) into the app's Postgres database in one atomic transaction — wipes the database's current contents, no re-embedding, no app code changes. Use when the user asks to restore the test database, the 1,000-document KB, or the big/live replica KB (e.g. "restore the test database", "put the 1000-doc KB back"). Do not confuse with the 8-document tool-calling fixture KB, which scripts/restore_test_kb.py restores.

Restore the 1,000-Document Test KB

Load data/bor_live_kb_replica.dump.sql (a full 99 MB pg_dump — schema and data — of the live KB replica) into the app database (brain_of_reese, per BOR_DATABASE_URL in .env). The embeddings come with the dump, so there is no re-embedding, no git clone, no app code change — the whole known state (documents, chunks + embeddings, source registry, KB overview, alembic version) lands in a few seconds.

The dump is a local artifact: data/ is gitignored and the dump is never committed. It is not in git — if it is missing on a fresh clone, stop and ask the user for it (it can only be made by pg_dumping the live/bor_eval KB).

Rules (non-negotiable)

  • The restore is destructive — it wipes the current contents of the app database. Always take the disposable backup in step 2 first, even when the database looks empty.
  • Do not edit app code (app/, tests/, alembic/). This skill swaps database contents, nothing else.
  • Run psql inside the db container (brain-of-reese_db_1), not with a host psql — the container's psql is 17.x, matching the dump (newer dumps use the \restrict metacommand that old host psqls choke on).
  • This KB and the 8-document tool-calling fixture KB (scripts/restore_test_kb.py) share the same database and overwrite each other. Say which one is loaded in the summary.

Procedure

All commands run from the repo root.

1. Preconditions (fast fail)

podman compose up -d db
ls -lh data/bor_live_kb_replica.dump.sql    # must exist, ~99 MB

Check the dump's schema version against the current head:

grep -A1 '^COPY public.alembic_version' data/bor_live_kb_replica.dump.sql | tail -1
uv run alembic heads

The two must match (the 2026-09-05 snapshot carries 0011). If the dump is older than head, proceed and run uv run alembic upgrade head after step 3. If the dump is newer than head, the local code is behind — stop and flag it to the user.

2. Disposable safety backup

Seconds, in /tmp (never committed):

podman exec brain-of-reese_db_1 pg_dump -U reese brain_of_reese -Fc \
  > /tmp/pre_restore_$(date +%Y%m%d_%H%M%S).dump

3. Reset the schema, load the dump (one transaction)

podman exec brain-of-reese_db_1 psql -U reese -d brain_of_reese -c \
  "DROP SCHEMA public CASCADE; CREATE SCHEMA public; GRANT ALL ON SCHEMA public TO PUBLIC;"

podman exec -i brain-of-reese_db_1 psql -U reese -d brain_of_reese \
  -v ON_ERROR_STOP=1 --single-transaction -q -f /dev/stdin \
  < data/bor_live_kb_replica.dump.sql

Why the schema reset: the dump contains plain CREATE TABLE (no IF NOT EXISTS, no --clean), so it can only load into an empty public schema. Why --single-transaction + ON_ERROR_STOP: a failed load rolls back instead of leaving a half-loaded KB. Known failure mode: the DROP SCHEMA is its own committed statement, so a failed load leaves an empty schema — that is fine, fix the cause and re-run step 3 (the reset is idempotent).

4. Verify the fingerprint

podman exec brain-of-reese_db_1 psql -U reese -d brain_of_reese -tAc \
  "select 'docs='||count(*) from documents
   union all select 'chunks='||count(*) from chunks
   union all select 'sources='||string_agg(distinct source, ',' order by source) from documents
   union all select 'alembic='||(select version_num from alembic_version);"

Expected for the 2026-09-05 snapshot: docs=1000, chunks=8866, sources=deploy,homelab,homepage,ServMon,skills,vibe-bot, alembic=0011. If any number differs, the dump file is a different snapshot — report the actual numbers to the user; do not guess.

5. Dev-server check (only if it is running)

pgrep -af "uvicorn app.main" && curl -s http://localhost:8000/api/health

Expect "db":"up" — the SQLAlchemy pool reconnects fine across the schema reset, so no restart is normally needed. If the server misbehaves after the restore, restart it (uv run uvicorn app.main:app --reload).

6. Summary

One line per verified number (docs / chunks / sources / alembic), the wall time, and a reminder of which KB is loaded: this is the 1,000-document live replica; the 8-document tool-calling fixture is brought back separately with uv run python -m scripts.restore_test_kb.

Troubleshooting

  • database unreachable / connection refused — the db container is down; podman compose up -d db, then re-run from step 1.
  • relation "public.<table>" already exists — the schema was not reset (or a previous load failed after the reset rolled back); re-run the DROP SCHEMA command from step 3.
  • psql: /dev/stdin:N: unknown command '\restrict' — you ran a host psql; use the container's, per the rules.
  • load succeeds but counts are wrong — compare the dump's alembic_version (step 1) and documents row count (sed -n '/^COPY public.documents/,/^\\./p' data/bor_live_kb_replica.dump.sql | wc -l, minus 2) against the loaded numbers; a mismatch means the dump was truncated in transfer — ask the user for the original file.
  • schema not applied-style errors from the app afterwards — run uv run alembic current; if the dump was older than head, step 1's alembic upgrade head was the missing piece.