phase: 191_source_name_labels_tags
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:
@@ -840,6 +840,124 @@
|
||||
pruned on that run).
|
||||
</p>
|
||||
|
||||
<!-- Phase 191 (task 03): the per-source NAME / LABEL / TAGS
|
||||
editor — "Sources need a name, a label, tag(s) so you can
|
||||
focus the model when prompting if you have a project name
|
||||
or a standard name you reference things by."
|
||||
|
||||
The ROW LIST is the phase-191 catalogue
|
||||
GET /api/source-labels (task 02) — the union of every
|
||||
registry row's DERIVED source name and every indexed
|
||||
document source — which is why this is its own table card
|
||||
and not a column of #git-sources-table above: a source can
|
||||
carry documents with no git_sources row of its own, and the
|
||||
one GET is the single source of truth for the row list.
|
||||
|
||||
THE ALIAS CONTRACT (D1/A1, owner-confirmed): the mono cell
|
||||
is the row's IDENTITY — the derived name, rendered with
|
||||
textContent and never an input. Editing a name here
|
||||
renames NOTHING: documents, citations, the KB tree, saved
|
||||
chats, shared pages and every source/path locator keep the
|
||||
derived name. A name and its tags only change what the
|
||||
model is TOLD (tasks 04/05).
|
||||
|
||||
ONE shared <template> (source-label-row-template) is cloned
|
||||
per row by git-sources.js: the three fields (Name ≤120,
|
||||
Label ≤200, the Tags token input — Enter or comma adds a
|
||||
chip, Backspace drops the last, ≤10 chips of ≤32 chars: the
|
||||
caps of the API's normalize_tags, mirrored so a long
|
||||
submission never round-trips as a 422) + Save / Reset + the
|
||||
§7.4 never-stale save-state line (role=status: "Saving…" →
|
||||
"saved" / the one-line error note, scoped PER ROW). The
|
||||
section lives INSIDE the admin-gated #git-sources-content,
|
||||
so an anonymous visitor never fetches it — the page's own
|
||||
gate is this list's gate too. -->
|
||||
<section class="source-labels" id="source-labels-section" aria-labelledby="source-labels-title">
|
||||
<h2 class="source-labels-title" id="source-labels-title">Names, labels and tags</h2>
|
||||
<p class="source-labels-sub">
|
||||
A name is what <em>you</em> call a source (“the git server”)
|
||||
and a tag is a focus token — the model is told both, so a
|
||||
question that uses the name finds that source’s documents.
|
||||
Nothing here renames a source: the mono name below stays the
|
||||
identity of every document, citation and link.
|
||||
</p>
|
||||
<!-- The catalogue's own load failure (role=alert). The table and
|
||||
the empty state are hidden while it is up, so the error is
|
||||
the only claim about the list (the #git-sources-load-error
|
||||
discipline, applied to this page's second list). -->
|
||||
<p class="source-labels-error" id="source-labels-error" role="alert" hidden></p>
|
||||
<div class="table-wrap source-labels-wrap" id="source-labels-wrap" role="region"
|
||||
aria-label="Source names, labels and tags" tabindex="0">
|
||||
<table class="source-labels-table" id="source-labels-table">
|
||||
<caption class="visually-hidden">Every source the knowledge base knows — git repositories, local directories and uploaded archives — each with the optional name, label and tags the model reads. The mono name in the first column is the source’s identity and never changes.</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Source</th>
|
||||
<th scope="col">Name, label and tags</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="source-labels-tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- Empty state — the catalogue answered with nothing at all
|
||||
(no registry row and no indexed document). -->
|
||||
<p class="source-labels-empty" id="source-labels-empty" hidden>No sources to name yet.</p>
|
||||
|
||||
<!-- ONE shared template, cloned per row by makeLabelRow.
|
||||
The ids written here are the FIRST row’s: the clone
|
||||
loop rewrites each input’s id, its <label for> and the
|
||||
editor’s id (the toggle’s aria-controls target) —
|
||||
a literal clone N times over would duplicate ids and make
|
||||
every label ambiguous (WCAG 1.3.1; the fields keep real
|
||||
<label for> pairs, never aria-label-only). Static markup
|
||||
only: every value lands through textContent or .value in
|
||||
git-sources.js (the XSS contract), and the editor ships
|
||||
hidden — the row’s toggle reveals it. -->
|
||||
<template id="source-label-row-template">
|
||||
<tr class="source-label-row">
|
||||
<td class="source-label-id-cell">
|
||||
<code class="source-label-source"></code>
|
||||
<!-- The stored-label state tag: TEXT + a background, never
|
||||
color alone (WCAG 1.4.1 — the
|
||||
.git-source-ignore-count idiom, phase 89/105/135). -->
|
||||
<span class="source-label-state" hidden>named</span>
|
||||
</td>
|
||||
<td class="source-label-edit-cell">
|
||||
<p class="source-label-summary" hidden></p>
|
||||
<button type="button" class="source-label-toggle" aria-expanded="false">Name this source</button>
|
||||
<div class="source-label-editor" id="source-label-editor" hidden>
|
||||
<div class="source-label-fields">
|
||||
<div class="source-label-field">
|
||||
<label class="source-label-field-label" for="source-label-name">Name <span class="field-hint">optional — what you call it</span></label>
|
||||
<input class="source-label-input source-label-name-input" id="source-label-name"
|
||||
type="text" maxlength="120" autocomplete="off"
|
||||
placeholder="the git server">
|
||||
</div>
|
||||
<div class="source-label-field">
|
||||
<label class="source-label-field-label" for="source-label-label">Label <span class="field-hint">optional — one line about it</span></label>
|
||||
<input class="source-label-input source-label-label-input" id="source-label-label"
|
||||
type="text" maxlength="200" autocomplete="off"
|
||||
placeholder="self-hosted forge + Gitea">
|
||||
</div>
|
||||
<div class="source-label-field">
|
||||
<label class="source-label-field-label" for="source-label-tags">Tags <span class="field-hint">optional — Enter or comma adds one</span></label>
|
||||
<ul class="source-label-tags" role="list"></ul>
|
||||
<input class="source-label-input source-label-tags-input" id="source-label-tags"
|
||||
type="text" maxlength="32" autocomplete="off"
|
||||
placeholder="gitea">
|
||||
</div>
|
||||
</div>
|
||||
<div class="source-label-actions">
|
||||
<button type="button" class="source-label-save" disabled>Save</button>
|
||||
<button type="button" class="source-label-reset" hidden>Reset</button>
|
||||
<span class="source-label-status" role="status" aria-live="polite"></span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
</section>
|
||||
|
||||
<!-- Phase 69 (owner request 2026-09-02): the remove
|
||||
confirmation — a real in-app alertdialog (the native
|
||||
confirm() retired): a row's Remove button opens it
|
||||
|
||||
Reference in New Issue
Block a user