phase: 145_git_source_links
Build and Push Containers / build-and-push-app (push) Successful in 2m23s
Build and Push Containers / build-and-push-db (push) Successful in 13s

**Phase 145 final verification pass — all green, no code changes needed**

- **Verified (no defects found):** resolver `origin_for_source` + `_url_basename` (scp/`.git`/trailing-slash, sanitize, ambiguity WARNING); `origin:`/`branch:` lines in all four read shapes via shared `_origin_lines`; HIGH `<tools>` origin rule (verbatim + only-byte-change pins); `DocContent.origin_url/origin_branch` (absent-never-null serializer); `renderDocument` "Original" anchor (copyable verbatim URL, `_blank`/`noopener`, branch in title); mock scripted origin-link flow (disjoint trigger — grep-verified vs all other E2E files)
- **Tests:** `uv run pytest --cov=app --cov-report=term-missing` → 3402 passed, `app/` coverage **99%** (>90% gate); `uv run pytest tests/e2e/test_git_source_links.py -v --no-cov` → **4 passed** in isolation; `uv run ruff check .` → clean; `uv run pyright` → 0 errors, 0 warnings
- **Completion criteria:** (1) git read results carry origin/branch, local byte-identical — ✅ (unit pins + E2E control); (2) HIGH `<tools>` rule pinned, LOW byte-identical — ✅; (3) modal AND /document.html show Original link, branch in label when pinned, nothing for local — ✅; (4) full suite green + coverage + E2E + lint/types — ✅; (5) no completed-phase behavior change (read header line 1, mock prefix, done frame/SourceRef untouched, LOW sha) — ✅
- **Extra regression check:** re-ran shared-mock E2E suites `test_read_line_ranges.py` (2 passed) and `test_document_viewer.py` (8 passed) — no cross-suite regressions from the `mock_llm.py` addition
- **Documentation:** README.md reviewed in full (env table ⊇ `.env.example`, pages/routes, config defaults, scripts, 17-color palette, 300 s guard, 2 s sync poll, phase-145 provenance section matches implementation) and AGENTS.md (72rem container, no-CDN test, smoke test, gitignore, gates) — **verified consistent, no changes needed**
- **Notable:** phase-116 screenshot PNG was regenerated during this phase's E2E runs (byte re-encode only, visually identical, correct no-anchor local-source meta row) — left in tree for the harness commit
- **Next pending phase:** `146_search_tool_scope` (then 147, 148, 149)
This commit is contained in:
2026-10-01 11:13:43 -04:00
parent 486ceb4019
commit 32a3ec4f5e
37 changed files with 2856 additions and 22 deletions
+44 -3
View File
@@ -280,7 +280,7 @@ from sqlalchemy.orm import Session
from app.config import Settings
from app.models import Document, FolderSummary
from app.rag.folder_summaries import folder_of
from app.rag.git_sources import effective_sources
from app.rag.git_sources import effective_sources, origin_for_source
from app.rag.llm import (
LLMClient,
LLMError,
@@ -362,8 +362,11 @@ AGENT_TOOLS: list[dict[str, Any]] = [
"first line) and `limit` (number of lines) to read "
"exactly that part. Do not re-read a document you have "
"already read in full — its full text is already in "
"your prompt. Call one tool at a time — wait for this "
"result before your next call."
"your prompt. For a document from a git source, the "
"result also carries an `origin` line (and `branch` "
"when pinned) — include that original source in your "
"answer (see your tools instructions). Call one tool at "
"a time — wait for this result before your next call."
),
"parameters": {
"type": "object",
@@ -1272,6 +1275,32 @@ def _resolve_path(db: Session, combined: str) -> tuple[Document | None, str, str
return find_document(db, source, path), source, path
def _origin_lines(origin: tuple[str, str | None] | None) -> str:
"""Phase 145 (task 01) — the read result's origin lines, shared by
the four result builders (the lines are identical in every shape).
``None`` (the document's source has no unambiguous ``kind='git'``
row — a local source, zero matches, or an ambiguous basename) →
``""``: the result stays byte-identical to pre-phase. Otherwise
one ``origin: {url}`` line (the sanitized repository URL — the
phase-121 mask, so a legacy embedded-credential row's secret never
reaches the prompt) and, when a branch is pinned (non-NULL), one
``branch: {branch}`` line. The builders insert the block
IMMEDIATELY AFTER the ``date:`` line and BEFORE the image marker
(the marker keeps its phase-122 position relative to the content,
the origin lines ride above it); the FIRST line
(``Document {source}/{path}:``) is untouched — the E2E mock's
``_READ_RESULT_PREFIX`` header contract.
"""
if origin is None:
return ""
url, branch = origin
lines = f"origin: {url}\n"
if branch is not None:
lines += f"branch: {branch}\n"
return lines
def all_documents(db: Session) -> list[Document]:
"""Every indexed document (full rows), ordered by ``(source, path)``
— catalog order.
@@ -1728,6 +1757,14 @@ def _execute_tool(
if (doc.source, doc.path) not in known:
holder.read_docs.append(doc)
holder.tool_calls += 1
# Phase 145 (task 01): the git-sourced document's repository
# origin — computed ONCE per successful read, BEFORE the
# ranged/truncated branching (the lines are identical in all
# four result shapes, which each render the shared block via
# :func:`_origin_lines`). ``None`` (local source, no git row,
# ambiguous basename) → no lines: the result stays
# byte-identical to pre-phase.
origin = origin_for_source(db, doc.source)
# Phase 122 (task 05): an image document's content IS the vision
# description — the marker line (a third line between the
# byte-identical header/date lines and the text) tells the model
@@ -1775,6 +1812,7 @@ def _execute_tool(
return (
f"Document {doc.source}/{doc.path}:\n"
f"date: {doc.created_at:%Y-%m-%d}\n"
f"{_origin_lines(origin)}"
f"{marker}"
f"{doc.content[:cap]}\n"
f"{TRUNCATION_MARKER}\n"
@@ -1792,6 +1830,7 @@ def _execute_tool(
return (
f"Document {doc.source}/{doc.path}:\n"
f"date: {doc.created_at:%Y-%m-%d}\n"
f"{_origin_lines(origin)}"
f"{marker}"
f"{doc.content}"
)
@@ -1829,6 +1868,7 @@ def _execute_tool(
return (
f"Document {doc.source}/{doc.path}:\n"
f"date: {doc.created_at:%Y-%m-%d}\n"
f"{_origin_lines(origin)}"
f"lines: {first}-{end} of {total_lines}\n"
f"{marker}{slice_text[:cap]}\n"
f"{TRUNCATION_MARKER}\n"
@@ -1842,6 +1882,7 @@ def _execute_tool(
return (
f"Document {doc.source}/{doc.path}:\n"
f"date: {doc.created_at:%Y-%m-%d}\n"
f"{_origin_lines(origin)}"
f"lines: {first}-{end} of {total_lines}\n"
f"{marker}{slice_text}"
)