phase: 102_extensionless_filenames
Build and Push Containers / build-and-push-app (push) Successful in 1m38s
Build and Push Containers / build-and-push-db (push) Successful in 13s

All verification complete — every gate green, no defects found in previously completed work.

**Phase 102 final verification pass — report**

Verified (all three task files present in `complete/`; working-tree implementation matches D1–D5 design):
- `match_extension` choke point in `app/rag/importer.py` (walk + `formats` counter), `doc_format` name-token badge in `app/api/docs.py`, config/`.env.example` docs, fixture `tests/fixtures/extensionless_kb/`, integration + E2E suites — all present and correct
- Completion criteria: end-to-end sync (✓ integration + E2E), case matrix incl. `mydockerfile`/`Dockerfile.dev`/`.dockerfile` exclusions (✓ unit), `formats=dockerfile:1` not `unknown` (✓ log-line assertion), badge `dockerfile`/`containerfile` + `text` fallback + suffixed unchanged (✓ unit/integration/E2E), prune-on-token-removal (✓ `pruned==2`), suffixed-path rule byte-identical (✓ single-line swap, existing cases untouched)

Test / lint results (exact commands):
- `uv run pytest --cov=app --cov-report=term-missing` → 2084 passed, **99%** coverage (>90% gate)
- `uv run pytest tests/e2e/test_extensionless_import.py -v --no-cov` → 2 passed, isolated, DB up
- Regressions isolated: `test_import_documents` 3✓, `test_import_extensions_env` 2✓, `test_quadlet_jinja_import` 4✓, `test_document_viewer` 7✓, `test_kb_tree` 8✓
- `uv run ruff check .` → clean; `uv run pyright` → 0 errors, 0 warnings

Notable: commit intentionally not made (harness commits the phase); 102's task files already sit in `complete/`, overview stays in `todo/` for the harness.
Next pending phases: 98, 99, 103, 104, 105 (numeric next after 102: `103_suggestions_session_openers`).
This commit is contained in:
2026-09-12 15:56:43 -04:00
parent 4dbac1660a
commit 3b2dea5685
29 changed files with 1276 additions and 11 deletions
+20 -5
View File
@@ -35,11 +35,13 @@ from sqlalchemy import func, select
from sqlalchemy.orm import Session
from app.api.sync import _sanitize_error
from app.config import get_settings
from app.core.auth import require_admin, require_user
from app.db import get_db
from app.models import Chunk, Document, FolderSummary
from app.rag.agent import list_source_names
from app.rag.folder_summaries import folder_of
from app.rag.importer import match_extension
from app.rag.llm import EmbeddingError, LLMClient
from app.schemas import (
DocContent,
@@ -58,11 +60,24 @@ from app.schemas import (
router = APIRouter(tags=["kb"])
def doc_format(path: str) -> str:
def doc_format(path: str, extensions: frozenset[str] = frozenset()) -> str:
"""Lowercased path suffix without its dot (``kubernetes.md`` → ``md``,
``notes/deep.Markdown`` → ``markdown``); ``text`` when the path has no
suffix — the value shown in the viewer's format badge."""
return Path(path).suffix.lower().lstrip(".") or "text"
``notes/deep.Markdown`` → ``markdown``) — returned **unconditionally**
for a non-empty suffix (display never depends on the import list — an
out-of-scope ``readme.rst`` still badges ``rst``); ``text`` when the
path has no suffix **and its name is not a configured token** (phase
102: a suffix-less ``Dockerfile`` badges ``dockerfile`` when its
lowercased full filename is one of *extensions* — the importer's
:func:`app.rag.importer.match_extension` rule). The value shown in
the viewer's format badge; the default empty *extensions* keeps the
pre-phase-102 suffix-only result for every path (byte-identical).
"""
p = Path(path)
suffix = p.suffix.lower().lstrip(".")
if suffix:
return suffix
matched = match_extension(p, extensions)
return matched if matched is not None else "text"
@router.get("/docs", response_model=DocList)
@@ -140,7 +155,7 @@ def get_document_content(
source=doc.source,
path=doc.path,
title=doc.title,
format=doc_format(doc.path),
format=doc_format(doc.path, get_settings().import_extension_set),
summary=doc.summary,
content=doc.content,
indexed_at=doc.indexed_at.isoformat(),