phase: 191_source_name_labels_tags
Build and Push Containers / build-and-push-app (push) Successful in 7m24s
Build and Push Containers / build-and-push-db (push) Successful in 36s

All gates re-verified against a stable tree. Report:

**Verified (all 6 tasks in `complete/`; this was the final pass)**
- `SourceLabel`+alembic `0030` (dev DB `alembic current` = `0030 (head)`, table present, 0 rows); admin-only `GET/PUT/DELETE /api/source-labels` (`require_admin` router-wide, `resolve_source_name`, partial-update caps/404s); Sources-page "Names, labels and tags" card (read-only derived-name `<code>`, per-row `saveInFlight`, 404 note that survives the settle, chips reuse `.git-source-hidden-count`); `question_name_hits` D2 rule; ONE `all_labels` read per turn in `chat.py` feeding both the `source_name_hit` flag and the prompt surfaces; `source_name_hit_bonus=0.005` in `_selection_order`; ` name=`/` label=` in `build_high_prompt` + `build_overview_prompt(names=…)`.
- **Completion criteria:** E2E `tests/e2e/test_source_name_labels.py` ✅ 7 passed (arms a–e: editor all kinds, control/named question, rename-never-breaks-identity, 404 note, reset) · full suite ✅ · coverage ✅ · README/AGENTS ✅ · phase dir in `complete/` ✅ · commit left to the harness.
- **Gates:** `uv run pytest --cov=app --cov-report=term-missing` → **exit 0, TOTAL 7160 stmts / 16 missed = 99%** (>90%) · `uv run pytest tests/e2e/test_source_name_labels.py -v --no-cov` → **7 passed, exit 0** · regression E2E isolated, exit 0: `test_document_viewer`, `test_admin_auth`, `test_api_tokens`, `test_git_sources_admin`, `test_kb_tree`, `test_local_directory_sources` · `uv run ruff check .` → All checks passed · `uv run pyright` → 0 errors.
- **Battery (task 04 criterion):** `uv run python -m scripts.eval_retrieval --from-file tests/fixtures/retrieval_battery.txt` at default vs `BOR_SOURCE_NAME_HIT_BONUS=0` → **`diff` empty (line-for-line identical)**, matching the recorded arms (dev KB is empty, so every question deflects; the observable boost is unit/E2E-pinned).
- **Documentation:** README — fixed the `/git-sources.html` rows (nav label is **Sources**) and cross-linked the phase-191 card from that section; full audit otherwise clean: all 86 `BOR_*` settings documented with defaults matching `app/config.py`, 0 broken file/dir references and 0 broken anchors in README+AGENTS, AGENTS rule 17 re-checked line-by-line against the code.
- **Notable / deviations:** (1) a **concurrent pipeline process** (`phased-execution/progress.mjs` for `191_…_00_phase`) was mutating this same working tree mid-pass — it rewrote `tests/e2e/test_source_name_labels.py` (11→7 tests), `frontend/assets/git-sources.js`, `tests/unit/test_source_labels_editor_ui.py` and `AGENTS.md` at 05:27; every gate above was (re-)run after those edits with the tree no source file newer than the green full run. (2) The task-06 report/phase text claims regression suites `test_chat_token_gate.py`, `test_git_sources_page.py`, `test_rag_browser.py` are green — **those files have never existed in git history** (those runs exit 4); I ran the real equivalents listed above instead (phase files are not mine to edit). (3) **No live changes**: the VM DB already has `source_labels` (0 rows) but the deployed app predates this uncommitted code, so deploying `/api/source-labels` to the live service is the owner's post-commit step, not part of this phase's criteria.
- **Next pending phase:** none — `191_source_name_labels_tags` is the only entry in `.agents/phases/todo/`.
This commit is contained in:
2026-10-19 06:13:32 -04:00
parent c6ffa050a6
commit 4f20c256d7
70 changed files with 7378 additions and 50 deletions
+209
View File
@@ -39,6 +39,12 @@ phase-124 ``show_image`` clause through one mechanism, and every build
the flags do not select stays byte-identical (the constant's own sha pin
lives in :mod:`tests.unit.test_prompt_lock`).
And the phase-191 source names (task 05, D4): a ``<document>`` block can
carry the owner's label row as the ADDITIVE ``name`` / ``label``
attributes (order ``source`` → ``name?`` → ``label?`` → ``path`` →
``title`` → ``date``, XML-escaped values), with no map / no entry for the
source byte-identical to pre-phase (the kill pin).
And the phase-119 cite discipline (task 04, LOCKED A5): the intro's
final sentence ("cite the document(s) you used, by path", phase 118)
is REPLACED — the HIGH prompt carries the discipline sentence exactly
@@ -65,6 +71,7 @@ from app.rag.prompts import (
SHOW_TEXT_CLAUSE,
SUGGEST_INTRO,
TOOLS_SECTION,
_attr,
_base,
_document_body,
_extract_context_line,
@@ -74,6 +81,7 @@ from app.rag.prompts import (
build_steering_section,
)
from app.rag.retriever import TRUNCATION_MARKER
from app.rag.source_labels import SourceLabelView
#: A small multi-line outline standing in for the lite-generated one.
OVERVIEW = "- Homelab\n - Kubernetes (k3s)\n- Deployments\n - Borg backups"
@@ -1944,3 +1952,204 @@ def test_high_prompt_pdf_image_doc_block_exact_shape() -> None:
assert prompt.index(line) < prompt.index("A diagram of the cluster.")
assert block_start < prompt.index(line) < prompt.index("</document>")
assert "The second page of the guide" not in prompt
# ---------------------------------------------------------------------------
# Phase 191 (D4, task 05): the ``<document>`` block learns the owner's name
# ---------------------------------------------------------------------------
#: The owner's display name for the fixture source (phase 191 D1 — an
#: ALIAS beside the derived name, never a replacement for it).
GIT_SERVER = "the git server"
#: The owner's one-line description of the same source.
FORGE_LABEL = "self-hosted forge + Gitea"
def _label_view(
source: str = "Homelab",
display_name: str | None = None,
label: str | None = None,
tags: tuple[str, ...] = (),
) -> SourceLabelView:
"""One label-row view (task 01's frozen dataclass) for the pins."""
return SourceLabelView(
source=source,
display_name=display_name,
label=label,
tags=tags,
)
def _block_head(prompt: str) -> str:
"""The opening tag of the FIRST ``<document>`` block (attribute
order/escaping pins read it whole, so nothing is asserted loosely)."""
start = prompt.index("<document ")
return prompt[start : prompt.index(">", start) + 1]
@pytest.mark.parametrize(
"names",
[
None,
{},
{"Other": _label_view("Other", display_name=GIT_SERVER, label=FORGE_LABEL)},
],
ids=["names=None", "names={}", "no-entry-for-the-source"],
)
def test_high_prompt_without_a_label_entry_is_byte_identical_to_pre_phase(
names: dict[str, SourceLabelView] | None,
) -> None:
"""THE kill pin (phase 191 D4): no map, an empty map and a map with no
entry for the document's source each build a prompt byte-identical to
the pre-phase build — no ``name``, no ``label``, no stray space."""
doc = _doc(
"kubernetes.md",
"Talos Linux on three nodes.",
"Kubernetes Homelab Cluster",
summary="A Talos Linux cluster on three nodes.",
)
pre = build_high_prompt([doc])
assert build_high_prompt([doc], names=names) == pre
assert ' name="' not in pre and ' label="' not in pre
def test_no_label_block_keeps_the_exact_pre_phase_head() -> None:
"""The block head for a label-less source, byte for byte (the shape the
phase-106/118/125 pins rely on: ``source`` → ``path`` → ``title`` →
``date``, single-spaced)."""
doc = _doc("kubernetes.md", "CONTENT", "Kubernetes Homelab Cluster", summary="S.")
assert _block_head(build_high_prompt([doc], names={})) == (
'<document source="Homelab" path="kubernetes.md" '
'title="Kubernetes Homelab Cluster" date="2024-06-15">'
)
def test_document_block_name_attribute_sits_immediately_after_source() -> None:
"""A ``display_name``-only row: exactly `` name="…"`` added, directly
after ``source="…"`` and before ``path`` (the attribute-order pin)."""
doc = _doc("kubernetes.md", "CONTENT", "Kubernetes", summary="S.")
prompt = build_high_prompt([doc], names={"Homelab": _label_view(display_name=GIT_SERVER)})
assert _block_head(prompt) == (
'<document source="Homelab" name="the git server" path="kubernetes.md" '
'title="Kubernetes" date="2024-06-15">'
)
assert ' label="' not in prompt
def test_document_block_label_only_view_adds_exactly_the_label_attribute() -> None:
"""A ``label``-only row gains ONE attribute — an unset field adds
NOTHING (never an empty ``name=""``)."""
doc = _doc("kubernetes.md", "CONTENT", "Kubernetes", summary="S.")
prompt = build_high_prompt([doc], names={"Homelab": _label_view(label=FORGE_LABEL)})
assert _block_head(prompt) == (
'<document source="Homelab" label="self-hosted forge + Gitea" '
'path="kubernetes.md" title="Kubernetes" date="2024-06-15">'
)
assert ' name="' not in prompt
def test_document_block_row_with_neither_field_is_byte_identical() -> None:
"""A row that exists but carries neither name nor label (the owner
saved only tags) leaves the block untouched — tags never render in the
block (they are a retrieval input, D2/D3)."""
doc = _doc("kubernetes.md", "CONTENT", "Kubernetes", summary="S.")
pre = build_high_prompt([doc])
names = {"Homelab": _label_view(tags=("gitea", "forge"))}
built = build_high_prompt([doc], names=names)
assert built == pre
assert "gitea" not in built
def test_document_block_carries_both_attributes_in_the_locked_order() -> None:
"""Both fields set → both attributes, in the fixed order
``source`` → ``name`` → ``label`` → ``path`` → ``title`` → ``date``."""
doc = _doc("kubernetes.md", "CONTENT", "Kubernetes", summary="S.")
names = {"Homelab": _label_view(display_name=GIT_SERVER, label=FORGE_LABEL)}
head = _block_head(build_high_prompt([doc], names=names))
order = ['source="', 'name="', 'label="', 'path="', 'title="', 'date="']
positions = [head.index(attr) for attr in order]
assert positions == sorted(positions)
assert head == (
'<document source="Homelab" name="the git server" '
'label="self-hosted forge + Gitea" path="kubernetes.md" '
'title="Kubernetes" date="2024-06-15">'
)
def test_document_block_name_and_label_are_xml_escaped() -> None:
"""The escaping pin (task 05 work item 1): a ``"`` in owner text renders
as ``&quot;`` so the attribute cannot break; ``&``/``<`` fold too."""
doc = _doc("kubernetes.md", "CONTENT", "Kubernetes", summary="S.")
names = {
"Homelab": _label_view(
display_name='Reese "forge" & <co>',
label='say "hi"',
)
}
prompt = build_high_prompt([doc], names=names)
assert ' name="Reese &quot;forge&quot; &amp; &lt;co&gt;"' in prompt
assert ' label="say &quot;hi&quot;"' in prompt
# The unescaped form never reaches the wire: the attribute stays closed.
assert ' name="Reese "forge"' not in prompt
head = _block_head(prompt)
assert head.count('"') % 2 == 0, "an unescaped quote would break the attribute"
def test_attribute_escaping_helper_covers_the_xml_five() -> None:
"""``_attr`` is the ONE escaper for the new attribute values: the XML
specials go, the single quote stays (the delimiter is the double
quote, and ``Reese's`` must stay readable to the model)."""
assert _attr('a & b < c > d " e') == "a &amp; b &lt; c &gt; d &quot; e"
assert _attr("Reese's forge") == "Reese's forge"
def test_names_apply_per_document_source_and_never_touch_the_body() -> None:
"""Only the blocks whose source has an entry gain the attribute; the
summary body (and the other source's block) are untouched."""
named = _doc("a.md", "CONTENT_A", "Title A", summary="SUMMARY A.")
other = _doc("b.md", "CONTENT_B", "Title B", summary="SUMMARY B.")
other.source = "Other"
prompt = build_high_prompt(
[named, other],
names={"Homelab": _label_view(display_name=GIT_SERVER)},
)
assert 'source="Homelab" name="the git server" path="a.md"' in prompt
assert 'source="Other" path="b.md"' in prompt
assert prompt.count(GIT_SERVER) == 1, "the name rides the attribute, nothing else"
assert "SUMMARY A." in prompt and "SUMMARY B." in prompt
def test_names_on_an_empty_documents_build_change_nothing() -> None:
"""No blocks → nothing to annotate: the honest "(no documents
matched…)" build is byte-identical with a label row in hand."""
names = {"Homelab": _label_view(display_name=GIT_SERVER, label=FORGE_LABEL)}
assert build_high_prompt([], names=names) == build_high_prompt([])
def test_names_compose_with_folder_lines_and_display_clauses() -> None:
"""The additive attributes ride alongside the phase-119 folder lines and
the phase-124/182 display clauses — the section order and the intro are
untouched (the folder lines stay plain, no new markup)."""
doc = _doc("kubernetes.md", "CONTENT", "Kubernetes", summary="S.")
names = {"Homelab": _label_view(display_name=GIT_SERVER)}
prompt = build_high_prompt(
[doc],
folder_lines=["docs/gitea.md — Gitea install"],
show_image=True,
show_text=True,
names=names,
)
assert 'source="Homelab" name="the git server" path="kubernetes.md"' in prompt
assert prompt.index(SUGGEST_INTRO) < prompt.index("docs/gitea.md — Gitea install")
assert prompt.index("docs/gitea.md — Gitea install") < prompt.index("<document ")
assert prompt.endswith(TOOLS_SECTION[: -len("</tools>")]
+ SHOW_IMAGE_CLAUSE + "\n\n" + SHOW_TEXT_CLAUSE + "\n</tools>")
def test_deflect_prompt_has_no_name_or_label_surface() -> None:
"""D4 touches the suggested-tier block only: the deflected prompt is
byte-identical (it carries no document blocks to annotate)."""
prompt = build_deflect_prompt(["Kubernetes Homelab Cluster"])
assert ' name="' not in prompt and ' label="' not in prompt
assert GIT_SERVER not in prompt