phase: 145_git_source_links
**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:
+44
-3
@@ -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}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user