phase: 125_image_anchor_context
All completion criteria verified. The repository is left functional with all changes in the working tree (no commits made, per the harness protocol). ## Final report — phase 125 `image_anchor_context` (verification pass) All 6 tasks were already complete; this pass verified every criterion and ran the full gates: - **Migration 0023** `documents.image_context` (TEXT nullable, reversible) — applied (`alembic current` = 0023 head); round-trip test (4 passed) exercises real upgrade → downgrade 0022 → upgrade. - **Criterion 1** (referenced image enriched + context line in retrieval): integration `test_markdown_referenced_image_gets_context_and_enriched_chunks` + E2E `test_sync_enriches_the_image_doc_with_markdown_context`, `test_chat_answer_quotes_the_context_line` (wire prompt pinned: `<document>` block leads with the one-line `Referenced in …: "alt"` line) — verified present and passing. - **Criterion 2** (standalone image byte-identical): E2E `test_standalone_*` (NULL `image_context`, block body exactly the bare description, no `Referenced in` anywhere in the prompt) + unit `test_describe_image_without_context_is_byte_identical` — passing. - **Criterion 3** (markdown-first: record stored on markdown doc, image picks up context later): integration `test_markdown_indexed_before_image_stores_record_and_image_picks_it_up` + post-index enrichment pass test (markdown file gone from disk) — passing. - **Gates:** `uv run pytest` → 2938 passed, exit 0 · `uv run pytest --cov=app --cov-report=term-missing` → exit 0, TOTAL **99%** (>90%) · `uv run ruff check .` → All checks passed · `uv run pyright` → 0 errors, 0 warnings. - **Phase E2E in isolation:** `uv run pytest tests/e2e/test_image_anchor_context.py -v --no-cov` → 4 passed. - **Mock-LLM change regression check:** neighboring image E2E suites green in isolation — `test_smoke` (3), `test_image_documents` (16), `test_chat_image_questions` (17), `test_answer_images` (17). - No defects found; no code changes needed. No live-infrastructure changes (local dev DB only). No commit made (harness commits). - Next pending phase: check `.agents/phases/todo/` after the harness moves this phase.
This commit is contained in:
@@ -517,6 +517,28 @@ Implements just enough of the aipi surface:
|
||||
marker question always gets the echo whatever the honesty gate
|
||||
says. Verified 2026-09-26: no existing E2E question or fixture
|
||||
file contains the phrase, so every other suite is unaffected.
|
||||
- user message containing ``quote the image reference``
|
||||
(``CONTEXT_LINE_TRIGGER``, phase 125 task 06 — the image-anchor
|
||||
context suite ``tests/e2e/test_image_anchor_context.py``) -> the
|
||||
deterministic ZERO-READ answer that QUOTES THE CONTEXT LINE of the
|
||||
seeded image doc's ``<document>`` block (the same image-doc pick
|
||||
as ``DIAGRAM_TRIGGER``): an ENRICHED image doc's block (phase 125
|
||||
task 05, LOCKED A4) leads with the one-line
|
||||
``Referenced in <source>/<path>: "<alt>"`` context line BEFORE
|
||||
the description, so the mock's quote — the block's exact first
|
||||
line — proves the line WAS in the prompt (and that its text is
|
||||
the task-05 one-line rendering, not the stored multi-line block).
|
||||
A non-enriched (standalone) image doc's block is the bare
|
||||
description (byte-identical pre-phase): its first line never
|
||||
starts with ``Referenced in `` (the mock's fixed description
|
||||
cannot begin that way), so the mock answers that the image
|
||||
carries no reference line — the byte-identity criterion's
|
||||
deterministic witness. Checked BEFORE the ``DEFLECT_MODE``
|
||||
branch (the marker lives in the user message — the
|
||||
``TABLE_TRIGGER``/``HISTORY_TRIGGER`` convention), so a marker
|
||||
question always gets the echo whatever the honesty gate says.
|
||||
Verified 2026-09-27: no existing E2E question or fixture file
|
||||
contains the phrase, so every other suite is unaffected.
|
||||
|
||||
Failure injection (phase 67, LLM retry, TODO.md L3) — deterministic
|
||||
dead-endpoint behavior for the retry E2E suite (``tests/e2e/
|
||||
@@ -1843,6 +1865,26 @@ SHOW_IMAGE_TRIGGER = "show me the picture of"
|
||||
#: says.
|
||||
DIAGRAM_TRIGGER = "which diagram is in your notes"
|
||||
|
||||
#: The phase-125 (task 06, image-anchor context, LOCKED A4) trigger
|
||||
#: (case-insensitive): the mock QUOTES THE CONTEXT LINE of the seeded
|
||||
#: image doc's ``<document>`` block (the same image-doc pick as
|
||||
#: :data:`DIAGRAM_TRIGGER`). For an ENRICHED image doc the block
|
||||
#: leads with the one-line ``Referenced in <source>/<path>: "<alt>"``
|
||||
#: line (the task-05 rendering of the stored ``image_context``,
|
||||
#: BEFORE the description) — quoting the block's first line proves
|
||||
#: the line reached the prompt; for a non-enriched (standalone) doc
|
||||
#: the block is the bare description (byte-identical pre-phase) and
|
||||
#: its first line never starts with ``Referenced in `` (the mock's
|
||||
#: fixed description cannot begin that way) — the mock then answers
|
||||
#: that the image carries no reference line. Checked BEFORE the
|
||||
#: ``DEFLECT_MODE`` branch in :func:`compose_answer` (the marker
|
||||
#: lives in the user message — the TABLE/HISTORY convention), so a
|
||||
#: marker question always gets the echo whatever the honesty gate
|
||||
#: says. Disjoint from every other trigger phrase; verified
|
||||
#: 2026-09-27: no existing E2E question or fixture file contains the
|
||||
#: phrase, so every other suite is unaffected.
|
||||
CONTEXT_LINE_TRIGGER = "quote the image reference"
|
||||
|
||||
#: The agent's ``show_image`` SUCCESS result's confirming first line
|
||||
#: (``app.rag.agent``'s ``show_image`` branch — the line ``read``'s
|
||||
#: result never carries): the SHOW_IMAGE flow's follow-up request
|
||||
@@ -1878,6 +1920,26 @@ def _seeded_image_doc(system: str) -> tuple[str, str, str] | None:
|
||||
return None
|
||||
|
||||
|
||||
def _seed_context_line(block_content: str) -> str | None:
|
||||
"""The phase-125 context line of a seeded image doc's block
|
||||
(task 06, LOCKED A4) — or ``None``.
|
||||
|
||||
An ENRICHED image doc's ``<document>`` block leads with the
|
||||
one-line ``Referenced in …`` rendering (the task-05
|
||||
:func:`app.rag.prompts._extract_context_line` output, BEFORE the
|
||||
description): its first line, verbatim, is the line the mock
|
||||
quotes (the prompt-injection echo convention). A non-enriched
|
||||
block is the bare description — its first line never starts with
|
||||
``Referenced in `` (the mock's fixed
|
||||
:data:`IMAGE_DESCRIPTION_ANSWER` cannot begin that way), so
|
||||
``None`` pins the byte-identical pre-phase shape.
|
||||
"""
|
||||
first = block_content.split("\n", 1)[0].strip()
|
||||
if first.startswith("Referenced in "):
|
||||
return first
|
||||
return None
|
||||
|
||||
|
||||
def _show_image_flow(body: dict[str, Any]) -> tuple[str, ...] | None:
|
||||
"""Classify a phase-124 SHOW_IMAGE request (see the module
|
||||
docstring). Stateless over the messages, like the other marker
|
||||
@@ -2467,6 +2529,32 @@ def compose_answer(body: dict[str, Any]) -> str:
|
||||
else "Your notes don't carry that diagram — there is nothing to show. "
|
||||
"(Deterministic mock answer for E2E.)"
|
||||
)
|
||||
elif CONTEXT_LINE_TRIGGER in user.lower():
|
||||
# Phase 125 (task 06, image-anchor context, LOCKED A4): the
|
||||
# ZERO-READ context-line echo — the mock QUOTES THE CONTEXT
|
||||
# LINE of the seeded image doc's <document> block (the block's
|
||||
# FIRST line — the task-05 one-line rendering for an enriched
|
||||
# doc; a standalone doc's block is the bare description, whose
|
||||
# first line never matches — the "no reference line" answer
|
||||
# is the byte-identity witness). Checked BEFORE the
|
||||
# DEFLECT_MODE branch (the marker lives in the user message —
|
||||
# the TABLE/HISTORY convention), so a marker question always
|
||||
# gets the echo whatever the honesty gate says.
|
||||
image_doc = _seeded_image_doc(system)
|
||||
if image_doc is None:
|
||||
answer = (
|
||||
"Your notes don't carry that image — there is nothing to reference. "
|
||||
"(Deterministic mock answer for E2E.)"
|
||||
)
|
||||
else:
|
||||
context_line = _seed_context_line(image_doc[2])
|
||||
answer = (
|
||||
f"The notes reference the image thus: {context_line} "
|
||||
"(Deterministic mock answer for E2E.)"
|
||||
if context_line is not None
|
||||
else "The image stands alone — the notes carry no reference line for it. "
|
||||
"(Deterministic mock answer for E2E.)"
|
||||
)
|
||||
elif "DEFLECT_MODE" in system:
|
||||
answer = (
|
||||
"Ah — I haven't done anything like that, so I don't want to make stuff up! "
|
||||
|
||||
Reference in New Issue
Block a user