phase: 89_source_ignore_paths
Build and Push Containers / build-and-push-app (push) Successful in 1m44s
Build and Push Containers / build-and-push-db (push) Successful in 13s

All verification complete — TODO.md was already cleared in the roadmap commit; the two extra unit-test diffs are necessary fake-signature adaptations for the new keywords. Everything is green, no fixes were needed.

## Phase 89 — final verification pass: ALL GREEN

**Verified (all 6 task files present in `complete/`):**
- `git_sources.ignore_paths` JSONB column + migration 0013; `alembic downgrade -1 && upgrade head` round-trips (head `0013`)
- Importer: `normalize_ignore_path`/`is_ignored`/`_ignore_for_root`, `ignore` in walk + progress pre-walk, `ignore_by_root` in `import_sources`
- API: GET/POST carry list; admin-only `PATCH` (replace, 404/422 fixed details, anonymous 403)
- Pipelines wired: `_run_sync`, `_run_upload` re-upload, `scripts/import_docs.py`
- Sources-page box: dialog, §7.4 save lifecycle, `N ignored` tag, a11y; env rows get no box

**Test/lint results:**
- `uv run pytest` → 1808 passed
- `uv run pytest --cov=app --cov-report=term-missing` → TOTAL **99%** (>90%)
- `uv run pytest tests/e2e/test_source_ignore_paths.py -v --no-cov` → 6 passed (isolated, DB up)
- Regressions in isolation: `test_git_sources_admin` 6, `test_archive_upload_sources` 5, `test_sync_button` 3, `test_smoke` 3 — all passed
- `uv run ruff check . && uv run pyright` → clean (0 errors)

**Completion criteria:** box→PATCH 200→count+GET round-trip ✅ · sync excludes `ignore/` (no docs/chunks/embeddings/summaries) + prunes newly-ignored (pruned==2) ✅ · no-mid-path rule E2E ✅ · PATCH 404/422/replace/clear/403 ✅ · full gate green ✅ · commit + phase move left to harness per rules.

**Deviations:** none blocking — E2E pins `files == 4` (overview's "5" was an off-by-one vs its own 6-file tree, documented in-test); `tests/unit/test_importer.py` + `test_sync_button.py` test-double fakes extended for the new keywords (needed for the suite to stay green).

**Next pending phase:** none — `todo/` holds only this phase.
This commit is contained in:
2026-09-09 01:45:42 -04:00
parent 0495e4e7e4
commit 8c706259e9
49 changed files with 3717 additions and 63 deletions
+13 -1
View File
@@ -16,7 +16,9 @@ Data model — see ``.agents/PLAN.md`` §Data Model:
* ``git_sources`` — admin-managed source registry (git URLs + local
directories) the Sync button and import_docs
import (phase 35; ``kind`` discriminator added in
phase 38).
phase 38; ``ignore_paths`` (phase 89 — JSONB list
of normalized path prefixes, server default
``'[]'``)).
* ``saved_chats`` — owner-saved chat conversations: one row per
explicitly Saved conversation (auto-``title`` +
the ``bor.chat.v1`` message list as JSONB, phase
@@ -72,6 +74,7 @@ from sqlalchemy import (
Text,
UniqueConstraint,
func,
text,
)
from sqlalchemy.dialects.postgresql import JSONB, UUID
from sqlalchemy.orm import Mapped, mapped_column, relationship
@@ -226,6 +229,15 @@ class GitSource(Base):
#: Absolute directory of a ``local`` source; NULL for git rows.
#: Unique — Postgres treats NULLs as distinct under a unique index.
path: Mapped[str | None] = mapped_column(Text, unique=True)
#: Per-source ignore paths (phase 89, A1/A4): the normalized,
#: non-empty source-relative path prefixes the owner types into the
#: box on the Sources page. A file is ignored when its
#: source-relative POSIX path starts with any entry (raw prefix —
#: no mid-path matching, no globs). Server default '[]' — every
#: pre-phase-89 row imports exactly as before.
ignore_paths: Mapped[list] = mapped_column(
JSONB, default=list, server_default=text("'[]'"), nullable=False
)
added_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())