mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-03 04:07:04 +00:00
Connect shared book sources to deep and skill reviews
NOT_REVIEWED private integration checkpoint. Use physical chapter composition and exact native coverage while preserving legacy evidence labels. Keep authored shared knowledge orientation resident with complete source-addressed navigation. Focused book, deep, skill and docs tests pass. Final required-manifest wiring, chapter migration and authoritative review remain pending.
This commit is contained in:
parent
45a816d5a4
commit
43dacbebe8
9 changed files with 382 additions and 49 deletions
|
|
@ -610,21 +610,25 @@ def build_knowledge_sections(
|
|||
# One mind keeps its authored common orientation across rooms. The generated
|
||||
# inventory is navigation, not a substitute for that understanding; a
|
||||
# project's shelf adds focus without hiding the common corpus.
|
||||
from ouroboros.knowledge import INDEX_FILE, OVERVIEW_TOPIC, read_knowledge_note, resolve_knowledge_address
|
||||
from ouroboros.knowledge import (INDEX_FILE, OVERVIEW_TOPIC, inventory_knowledge,
|
||||
read_knowledge_note, render_knowledge_index, resolve_knowledge_address)
|
||||
|
||||
pid = str(project_id or "").strip()
|
||||
global_address = resolve_knowledge_address(env.drive_root, OVERVIEW_TOPIC, "global")
|
||||
global_address = resolve_knowledge_address(env.drive_path("memory").parent, OVERVIEW_TOPIC, "global")
|
||||
authored_overview = False
|
||||
try:
|
||||
overview = read_knowledge_note(global_address)
|
||||
overview_text = overview.source.text_at(overview.source.body_span) if overview.source else overview.text
|
||||
if overview_text.strip():
|
||||
sections.append("## Shared understanding\n\n" + overview_text)
|
||||
authored_overview = overview.source is not None
|
||||
sections.append(f"## Shared understanding\n\nSource: knowledge_read(topic='{OVERVIEW_TOPIC}', scope='global').\n\n" + overview_text)
|
||||
except FileNotFoundError:
|
||||
pass # The generated index retains prior orientation until one is authored.
|
||||
except (OSError, UnicodeDecodeError) as exc:
|
||||
sections.append(f"Shared understanding source unavailable: knowledge_read(topic='{OVERVIEW_TOPIC}', scope='global'). {type(exc).__name__}.")
|
||||
knowledge_indexes = [(global_address.shelf / INDEX_FILE,
|
||||
"## Knowledge base", "knowledge index")]
|
||||
"## Knowledge base\n\nGlobal navigation: knowledge_list(scope='global'); read linked topics with knowledge_read(topic=..., scope='global').",
|
||||
"knowledge index")]
|
||||
if pid:
|
||||
from ouroboros.project_facts import project_knowledge_dir
|
||||
|
||||
|
|
@ -633,7 +637,8 @@ def build_knowledge_sections(
|
|||
if include_pattern_body:
|
||||
knowledge_indexes.append((env.drive_path("memory/knowledge/patterns.md"), pattern_header, "patterns register"))
|
||||
for path, header, label in knowledge_indexes:
|
||||
text = safe_read(path)
|
||||
text = (render_knowledge_index(inventory_knowledge(global_address), include_summaries=False)
|
||||
if authored_overview and path == global_address.shelf / INDEX_FILE else safe_read(path))
|
||||
if not text.strip():
|
||||
continue
|
||||
if warn_large and len(text) > _LARGE_CONTEXT_SECTION_CHARS:
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ receipts, coverage and completeness so consecutive reports stay comparable.
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import json
|
||||
import pathlib
|
||||
import posixpath
|
||||
import time
|
||||
|
|
@ -53,6 +54,7 @@ from ouroboros.utils import atomic_write_json, estimate_tokens, utc_now_iso # n
|
|||
from ouroboros.config import get_context_mode # noqa: E402
|
||||
from ouroboros.provider_models import provider_for_model, provider_has_credentials # noqa: E402
|
||||
from ouroboros.context_layout import generate_doc_nav_map # noqa: E402
|
||||
from ouroboros.reference_books import BOOK_ENTRYPOINTS, compose_book, load_reference_book, overview_book # noqa: E402
|
||||
from ouroboros.reviewer_slot_config import ( # noqa: E402
|
||||
ROUTE_KIND_API,
|
||||
ROUTE_KIND_SESSION,
|
||||
|
|
@ -138,8 +140,10 @@ How to work: you are reading the repository yourself with read-only tools. Read
|
|||
`BIBLE.md` IN FULL first (about {bible_chars:,} chars — in bounded chunks): every
|
||||
finding is checked against it, and a report that did not read it is not a deep
|
||||
self-review. The memory files below are inlined byte-exact; `docs/ARCHITECTURE.md`,
|
||||
`docs/DEVELOPMENT.md` and `docs/CHECKLISTS.md` are given as navigation maps — read the
|
||||
sections you need on demand. Then inspect the code (search_code, query_code,
|
||||
`docs/DEVELOPMENT.md` are book entrypoints; their overviews identify the actual
|
||||
physical chapter sources. `docs/CHECKLISTS.md` has its own navigation map. Read
|
||||
the needed sources on demand, using lines local to the physical file you open,
|
||||
never treating composed-book line numbers as an entrypoint address. Then inspect the code (search_code, query_code,
|
||||
read_file), cross-reference interactions between modules and follow call chains out
|
||||
of the files you open. Prioritize: CRITICAL > IMPORTANT > ADVISORY.
|
||||
|
||||
|
|
@ -335,27 +339,37 @@ def build_review_pack(
|
|||
memory = _append_memory_whitelist(memory_parts, skipped, drive_root=drive_root)
|
||||
memory_text = "\n".join(memory_parts)
|
||||
|
||||
# Low context mode: render ARCHITECTURE.md as a navigation map (full sections
|
||||
# read on demand) and exclude it from the atlas full-file selection instead of
|
||||
# inlining ~32K tokens. Reuses the atlas ``already_included`` mechanism so the
|
||||
# shared commit-gate atlas (scope / plan review) is unaffected.
|
||||
# The atlas still owns repository selection. Composed books have one
|
||||
# physical closure, included once here and excluded from duplicate atlas
|
||||
# content. Legacy Max monoliths retain their original atlas delivery.
|
||||
nav_parts: list[str] = []
|
||||
already_included: frozenset[str] = frozenset()
|
||||
if get_context_mode() == "low":
|
||||
book_parts: list[str] = []
|
||||
book_views: list[dict] = []
|
||||
already_included: set[str] = set()
|
||||
low = get_context_mode() == "low"
|
||||
for book_id, entrypoint in BOOK_ENTRYPOINTS.items():
|
||||
if entrypoint not in tracked:
|
||||
continue
|
||||
try:
|
||||
arch_text = (repo_dir / "docs" / "ARCHITECTURE.md").read_text(encoding="utf-8")
|
||||
except Exception:
|
||||
arch_text = ""
|
||||
if arch_text.strip():
|
||||
nav_parts.append(
|
||||
generate_doc_nav_map(
|
||||
arch_text, title="ARCHITECTURE.md", rel_path="docs/ARCHITECTURE.md"
|
||||
)
|
||||
book = load_reference_book(repo_dir, book_id)
|
||||
except (OSError, ValueError) as exc:
|
||||
return "", {"file_count": 0, "total_chars": 0, "skipped": [f"FATAL: {entrypoint} book unavailable: {exc}"]}
|
||||
if book.legacy and not (low and book_id == "architecture"):
|
||||
continue
|
||||
sources = (book.entrypoint, *book.chapters)
|
||||
partial = low and book_id == "architecture"
|
||||
already_included.update(s.source_path for s in sources)
|
||||
book_views.append({"book_id": book_id, "delivery": "overview" if partial else "full",
|
||||
"sources": [{"path": s.source_path, "sha256": s.sha256, "size": len(s.raw)} for s in sources]})
|
||||
if partial:
|
||||
navigation = (generate_doc_nav_map(book.entrypoint.text, title="ARCHITECTURE.md", rel_path=entrypoint)
|
||||
if book.legacy else overview_book(book).text)
|
||||
nav_parts.append(navigation
|
||||
+ "\n\nNote for this deep self-review call: this surface has no tool loop, "
|
||||
"so the navigation map is an index of omitted sections, not an actionable "
|
||||
"read_file instruction. Flag any needed full ARCHITECTURE.md section explicitly."
|
||||
)
|
||||
already_included = frozenset({"docs/ARCHITECTURE.md"})
|
||||
"read_file instruction. Flag any needed full Architecture chapter explicitly.")
|
||||
else:
|
||||
book_parts.append(f"## Reference book: {entrypoint}\n\n" + compose_book(book))
|
||||
|
||||
# Reserve the (bounded) omission section inside the atlas's fixed budget —
|
||||
# it is appended to the pack after the atlas fills, so an unreserved section
|
||||
|
|
@ -363,6 +377,7 @@ def build_review_pack(
|
|||
atlas_fixed_tokens = (
|
||||
int(fixed_prompt_tokens)
|
||||
+ estimate_tokens(memory_text)
|
||||
+ estimate_tokens("\n".join(book_parts))
|
||||
+ estimate_tokens("\n".join(nav_parts))
|
||||
+ _OMISSION_SECTION_RESERVE_TOKENS
|
||||
)
|
||||
|
|
@ -375,7 +390,7 @@ def build_review_pack(
|
|||
ReviewContextAtlasRequest(
|
||||
repo_dir=repo_dir,
|
||||
tracked_paths=tuple(tracked),
|
||||
already_included=already_included,
|
||||
already_included=frozenset(already_included),
|
||||
fixed_prompt_tokens=atlas_fixed_tokens,
|
||||
target_total_tokens=min(850_000, hard_budget),
|
||||
hard_total_tokens=hard_budget,
|
||||
|
|
@ -408,10 +423,17 @@ def build_review_pack(
|
|||
for record in atlas.omitted
|
||||
if record.disposition not in {"already_included", "manifest_only"}
|
||||
)
|
||||
parts = [atlas.text]
|
||||
if book_views:
|
||||
atlas.manifest["reference_book_views"] = book_views
|
||||
for row in atlas.manifest.get("coverage", []):
|
||||
view = next((v for v in book_views if any(s["path"] == row.get("path") for s in v["sources"])), None)
|
||||
if view:
|
||||
row["reason"] = ("physical book source included in full composition" if view["delivery"] == "full"
|
||||
else "book overview only; full chapter text omitted from this one-packet delivery")
|
||||
parts = [*book_parts, atlas.text]
|
||||
parts.extend(nav_parts)
|
||||
parts.extend(memory_parts)
|
||||
file_count = len(atlas.selected) + memory["inlined"]
|
||||
file_count = len(atlas.selected) + memory["inlined"] + sum(len(v["sources"]) for v in book_views if v["delivery"] == "full")
|
||||
_append_omission_section(parts, skipped)
|
||||
|
||||
pack_text = "\n".join(parts)
|
||||
|
|
@ -635,7 +657,10 @@ def _repo_relative(path: Any, repo_dir: pathlib.Path) -> str:
|
|||
|
||||
|
||||
def _native_read_coverage(usage: Dict[str, Any], repo_dir: pathlib.Path) -> Dict[str, Dict[str, Any]]:
|
||||
"""R8: how much of each mandatory read the host OBSERVED, from the episode's
|
||||
"""Prefer the native operation's exact manifest-bound source coverage.
|
||||
|
||||
Historical receipts retain only their explicitly labelled line evidence.
|
||||
R8: how much of each mandatory read the host OBSERVED, from the episode's
|
||||
receipts — the merged line intervals of every executed repository-root
|
||||
``read_file`` receipt for the path (a single result is capped, so a full
|
||||
read of BIBLE.md is multi-chunk by construction).
|
||||
|
|
@ -656,6 +681,27 @@ def _native_read_coverage(usage: Dict[str, Any], repo_dir: pathlib.Path) -> Dict
|
|||
traversal shapes before dispatch — see ``_repo_relative``). Disclosure,
|
||||
never a refusal: the report is delivered with the flag in its header.
|
||||
"""
|
||||
exact = usage.get("native_read_coverage")
|
||||
sources = exact.get("sources") if isinstance(exact, dict) else None
|
||||
if isinstance(sources, list) and sources:
|
||||
out = {}
|
||||
for index, source in enumerate(sources):
|
||||
source = source if isinstance(source, dict) else {}
|
||||
root, path = str(source.get("root") or ""), str(source.get("path") or f"source[{index}]")
|
||||
address = path if root in _REPO_ROOTS else f"{root}:{path}"
|
||||
if address in out:
|
||||
address = f"{root}:{path}@{source.get('source_revision', index)}"
|
||||
total, covered = source.get("complete_chars"), source.get("covered_chars")
|
||||
known = (type(total) is int and type(covered) is int and 0 <= covered <= total
|
||||
and isinstance(source.get("source_revision"), str) and len(source["source_revision"]) == 64)
|
||||
state = ("read" if source.get("status") == "complete" and known else
|
||||
"partial" if source.get("status") == "incomplete" and known and covered else
|
||||
"missing" if source.get("status") == "incomplete" and known else "unobserved")
|
||||
out[address] = {**source, "state": state, "covered_chars": covered if known else 0,
|
||||
"complete_chars": total if known else 0,
|
||||
"fraction": round(covered / total, 3) if known and total else 1.0 if state == "read" else 0.0,
|
||||
"evidence_basis": "source_ranges"}
|
||||
return out
|
||||
receipts = [r for r in (usage.get("native_tool_receipts") or []) if isinstance(r, dict)]
|
||||
capped = int(usage.get("native_tool_calls") or 0) > len(receipts)
|
||||
out: Dict[str, Dict[str, Any]] = {}
|
||||
|
|
@ -666,6 +712,7 @@ def _native_read_coverage(usage: Dict[str, Any], repo_dir: pathlib.Path) -> Dict
|
|||
named = r.get("opened_path") if isinstance(r.get("opened_path"), str) and r.get("opened_path") else r.get("path")
|
||||
root = r.get("opened_root") if isinstance(r.get("opened_root"), str) and r.get("opened_root") else str(r.get("root") or "")
|
||||
if (r.get("tool") != "read_file" or r.get("outcome") != "executed"
|
||||
or r.get("delivered") is False
|
||||
or root not in _REPO_ROOTS or _repo_relative(named, repo_dir) != rel):
|
||||
continue
|
||||
if not all(isinstance(r.get(k), int) for k in ("start_line", "end_line", "total_lines")):
|
||||
|
|
@ -691,7 +738,8 @@ def _native_read_coverage(usage: Dict[str, Any], repo_dir: pathlib.Path) -> Dict
|
|||
else:
|
||||
state = "partial" if covered else "missing"
|
||||
out[rel] = {"state": state, "covered_lines": covered, "total_lines": total,
|
||||
"fraction": round(covered / total, 3) if total else 0.0}
|
||||
"fraction": round(covered / total, 3) if total else 0.0,
|
||||
"evidence_basis": "legacy_lines"}
|
||||
return out
|
||||
|
||||
|
||||
|
|
@ -723,7 +771,11 @@ def _delivery_incomplete(delivery: str, usage: Dict[str, Any], message: Optional
|
|||
sets no usage finish reason at all) — meaning the report hit the output
|
||||
reserve; a session's completeness is not host-observable."""
|
||||
if delivery == "native_tool_rounds":
|
||||
return str(usage.get("native_incomplete") or "") or "none"
|
||||
reported = str(usage.get("native_incomplete") or "")
|
||||
coverage = usage.get("native_read_coverage") or {}
|
||||
if not reported and coverage.get("sources") and coverage.get("status") != "complete":
|
||||
reported = "required_source_coverage_incomplete"
|
||||
return reported or "none"
|
||||
if delivery == "api_packet":
|
||||
msg = message if isinstance(message, dict) else {}
|
||||
cut = (str(usage.get("response_finish_reason") or "") == "length"
|
||||
|
|
@ -761,6 +813,7 @@ def _provenance_header(delivery: str, model: str, usage: Dict[str, Any], memory:
|
|||
"end_reason": usage.get("native_end_reason", ""),
|
||||
"transcript": f"{usage.get('native_transcript_chars', 0)}/{usage.get('native_transcript_bound', 0)}",
|
||||
"landing": f"{usage.get('native_landing_notified', False)}/{usage.get('native_landing_sent', False)}",
|
||||
"coverage_basis": usage.get("deep_review_coverage_basis", "legacy_lines"),
|
||||
})
|
||||
elif delivery == "api_packet":
|
||||
facts["attestation"] = "packed"
|
||||
|
|
@ -798,7 +851,8 @@ def _failed(text: str, *, reason_code: str, usage: Optional[Dict[str, Any]] = No
|
|||
return text, out
|
||||
|
||||
|
||||
def _retrieving_task(repo_dir: pathlib.Path, drive_root: pathlib.Path) -> Tuple[str, Dict[str, Any]]:
|
||||
def _retrieving_task(repo_dir: pathlib.Path, drive_root: pathlib.Path, *,
|
||||
required_sources_ref: Optional[dict] = None) -> Tuple[str, Dict[str, Any]]:
|
||||
"""The route-owned task text for a retrieving row: role + method, the
|
||||
memory whitelist inline (byte-exact, as the packed pack carries it), and
|
||||
the governance navigation maps. BIBLE.md is a mandatory READ, never
|
||||
|
|
@ -811,8 +865,18 @@ def _retrieving_task(repo_dir: pathlib.Path, drive_root: pathlib.Path) -> Tuple[
|
|||
memory_parts: list[str] = []
|
||||
skipped: list[str] = []
|
||||
memory = _append_memory_whitelist(memory_parts, skipped, drive_root=drive_root)
|
||||
navigation = []
|
||||
for book_id, entrypoint in BOOK_ENTRYPOINTS.items():
|
||||
if not (repo_dir / entrypoint).is_file():
|
||||
navigation.append(f"[Missing reference book: {entrypoint}]")
|
||||
continue
|
||||
book = load_reference_book(repo_dir, book_id)
|
||||
navigation.append(generate_doc_nav_map(book.entrypoint.text, title=entrypoint, rel_path=entrypoint)
|
||||
if book.legacy else overview_book(book).text)
|
||||
navigation.append(governance_nav_maps(repo_dir, tuple(p for p in _NAV_MAP_DOCS if p not in BOOK_ENTRYPOINTS.values())))
|
||||
parts = [
|
||||
_ROLE_PROMPT + _RETRIEVING_METHOD.format(bible_chars=len(bible)),
|
||||
*navigation,
|
||||
"## Memory (runtime data root, inlined byte-exact)",
|
||||
*memory_parts,
|
||||
# EVERY whitelisted entry gets its disposition here — the omission
|
||||
|
|
@ -821,7 +885,9 @@ def _retrieving_task(repo_dir: pathlib.Path, drive_root: pathlib.Path) -> Tuple[
|
|||
f"Memory dispositions ({memory['total']} whitelisted): "
|
||||
+ "; ".join(f"{rel} {d}" for rel, d in memory["dispositions"].items()),
|
||||
]
|
||||
parts.append(governance_nav_maps(repo_dir, _NAV_MAP_DOCS))
|
||||
if required_sources_ref:
|
||||
parts.append("Read the complete required source manifest through its exact source handle and cover every listed source across your working views: "
|
||||
+ json.dumps(required_sources_ref, ensure_ascii=False))
|
||||
return "\n\n".join(parts), {"memory": memory, "bible_chars": len(bible)}
|
||||
|
||||
|
||||
|
|
@ -834,6 +900,8 @@ def _run_retrieving_review(
|
|||
*,
|
||||
task_id: str,
|
||||
deadline_at: str,
|
||||
required_sources: Optional[list] = None,
|
||||
required_sources_ref: Optional[dict] = None,
|
||||
) -> Tuple[str, Dict[str, Any]]:
|
||||
"""A retrieving row (native episode or delegated session) through the
|
||||
shared executor seam, exactly like the advisory: hand-built request, slot
|
||||
|
|
@ -847,7 +915,10 @@ def _run_retrieving_review(
|
|||
from ouroboros.review_substrate import ReviewRequest
|
||||
from ouroboros.usage_accounting import UsageScope, current_usage_scope, usage_scope
|
||||
|
||||
task_text, task_facts = _retrieving_task(repo_dir, drive_root)
|
||||
task_text, task_facts = _retrieving_task(repo_dir, drive_root, required_sources_ref=required_sources_ref)
|
||||
policy = {"output_contract": _REPORT_CONTRACT, "native_data_root": str(drive_root)}
|
||||
if required_sources is not None:
|
||||
policy.update(native_required_sources=required_sources, native_required_sources_ref=required_sources_ref or {})
|
||||
request = ReviewRequest(
|
||||
surface="deep_self_review",
|
||||
goal="Deep self-review of the whole Ouroboros system against BIBLE.md.",
|
||||
|
|
@ -859,7 +930,7 @@ def _run_retrieving_review(
|
|||
# REAL runtime root (R5), readable by the reviewer's own tools; memory
|
||||
# coverage itself is the inline whitelist in the task (byte-exact,
|
||||
# disposition-disclosed) — never receipts.
|
||||
policy={"output_contract": _REPORT_CONTRACT, "native_data_root": str(drive_root)},
|
||||
policy=policy,
|
||||
deadline_at=deadline_at,
|
||||
)
|
||||
# The logical window: the task's absolute ceiling narrowed by the owner
|
||||
|
|
@ -930,17 +1001,21 @@ def _run_retrieving_review(
|
|||
reason_code="deep_self_review_error", usage=usage)
|
||||
if delivery == "native_tool_rounds":
|
||||
detail = _native_read_coverage(usage, repo_dir)
|
||||
usage["deep_review_coverage_basis"] = "source_ranges" if any(c["evidence_basis"] == "source_ranges" for c in detail.values()) else "legacy_lines"
|
||||
coverage = {rel: (f"partial({c['fraction']:.2f})" if c["state"] == "partial" else c["state"])
|
||||
for rel, c in detail.items()}
|
||||
for rel, c in detail.items():
|
||||
if c["state"] != "read":
|
||||
covered, total, unit = (c["covered_chars"], c["complete_chars"], "characters") if c["evidence_basis"] == "source_ranges" else (c["covered_lines"], c["total_lines"], "lines")
|
||||
missing = f"no delivered range matching the required source revision of {rel}" if c["evidence_basis"] == "source_ranges" else f"no executed repository-root read_file receipt for {rel}"
|
||||
unobserved = f"the exact required source extent of {rel} is unobserved" if c["evidence_basis"] == "source_ranges" else f"the {rel} read extent is unobserved (receipts capped or extent not recorded)"
|
||||
usage["capability_delta"].append({
|
||||
"kind": "capability_delta",
|
||||
"requested": f"mandatory full read of {rel}",
|
||||
"effective": {
|
||||
"partial": f"{c['covered_lines']} of {c['total_lines']} lines of {rel} delivered (merged receipts)",
|
||||
"missing": f"no executed repository-root read_file receipt for {rel}",
|
||||
}.get(c["state"], f"the {rel} read extent is unobserved (receipts capped or extent not recorded)"),
|
||||
"partial": f"{covered} of {total} {unit} of {rel} delivered (merged receipts)",
|
||||
"missing": missing,
|
||||
}.get(c["state"], unobserved),
|
||||
"reason": f"deep_review_mandatory_read_{c['state']}",
|
||||
})
|
||||
else:
|
||||
|
|
@ -969,10 +1044,12 @@ def _run_retrieving_review(
|
|||
completeness = "complete" if incomplete == "none" else (
|
||||
"completeness not host-observed" if incomplete == "unobserved" else f"INCOMPLETE ({shown_reason})")
|
||||
if delivery == "native_tool_rounds":
|
||||
reads = "; ".join(
|
||||
f"{rel} " + (f"{c['fraction']:.0%} read ({c['covered_lines']}/{c['total_lines']} lines)" if c["state"] == "partial"
|
||||
else {"read": "read in full", "missing": "NOT read"}.get(c["state"], "read extent unobserved"))
|
||||
for rel, c in detail.items())
|
||||
reads = []
|
||||
for rel, c in detail.items():
|
||||
covered, total, unit = (c["covered_chars"], c["complete_chars"], "characters") if c["evidence_basis"] == "source_ranges" else (c["covered_lines"], c["total_lines"], "lines")
|
||||
reads.append(f"{rel} " + (f"{c['fraction']:.0%} read ({covered}/{total} {unit})" if c["state"] == "partial"
|
||||
else {"read": "read in full", "missing": "NOT read"}.get(c["state"], "read extent unobserved")))
|
||||
reads = "; ".join(reads)
|
||||
human = (
|
||||
f"Deep self-review: native inspection episode on {shown_model} — {int(usage.get('native_rounds') or 0)} rounds, "
|
||||
f"{int(usage.get('native_tool_calls') or 0)} tool calls ({len(usage.get('native_tool_receipts') or [])} host-observed receipts); "
|
||||
|
|
@ -1283,6 +1360,8 @@ def run_deep_self_review(
|
|||
task_id: str = "",
|
||||
deadline_at: str = "",
|
||||
slot: Optional[ConfiguredReviewerSlot] = None,
|
||||
required_sources: Optional[list] = None,
|
||||
required_sources_ref: Optional[dict] = None,
|
||||
) -> Tuple[str, Dict[str, Any]]:
|
||||
"""Execute the deep self-review on the configured row.
|
||||
|
||||
|
|
@ -1297,6 +1376,9 @@ def run_deep_self_review(
|
|||
refusal is budget vocabulary for the agent's budget-pause rail, not a
|
||||
review error.
|
||||
``slot`` overrides the configured row (tests, callers that already resolved it).
|
||||
``required_sources`` and its exact source handle come from the caller's
|
||||
existing immutable review assembler; this consumer never infers the
|
||||
complete required surface from a working-tree scan.
|
||||
"""
|
||||
try:
|
||||
try:
|
||||
|
|
@ -1313,6 +1395,7 @@ def run_deep_self_review(
|
|||
if row.retrieves:
|
||||
return _run_retrieving_review(
|
||||
repo_dir, drive_root, llm, emit_progress, row, task_id=task_id, deadline_at=deadline_at,
|
||||
required_sources=required_sources, required_sources_ref=required_sources_ref,
|
||||
)
|
||||
return _run_packed_review(repo_dir, drive_root, llm, emit_progress, row, str(model or ""))
|
||||
except BudgetExceeded:
|
||||
|
|
|
|||
|
|
@ -180,14 +180,15 @@ def _label(value: str) -> str:
|
|||
return value.replace("\\", "\\\\").replace("[", "\\[").replace("]", "\\]").replace("*", "\\*")
|
||||
|
||||
|
||||
def render_knowledge_index(rows: tuple[dict[str, Any], ...], legacy_context: str = "") -> str:
|
||||
def render_knowledge_index(rows: tuple[dict[str, Any], ...], legacy_context: str = "",
|
||||
*, include_summaries: bool = True) -> str:
|
||||
"""Generated navigation is separate from authored understanding."""
|
||||
entries = []
|
||||
for row in rows:
|
||||
topic, title = str(row["topic"]), str(row.get("title") or row["topic"])
|
||||
line = f"- **{_label(topic)}**: [{_label(title)}](<{quote(topic + '.md', safe='/')}>)"
|
||||
summary = row.get("summary")
|
||||
if isinstance(summary, str) and summary:
|
||||
if include_summaries and isinstance(summary, str) and summary:
|
||||
line += "\n" + "\n".join(" " + part for part in summary.splitlines())
|
||||
if row.get("read_error") or row.get("parse_error"):
|
||||
line += "\n (source metadata unavailable; read the original note)"
|
||||
|
|
|
|||
|
|
@ -101,7 +101,13 @@ _SINGLE_CONTENT = (
|
|||
|
||||
_SESSION_RETRIEVAL = (
|
||||
"Use native read/search tools inside the source-repository session root. Read "
|
||||
"`BIBLE.md`, `docs/ARCHITECTURE.md`, and `docs/DEVELOPMENT.md` in full; read "
|
||||
"`BIBLE.md` in full. Read the `docs/ARCHITECTURE.md` and `docs/DEVELOPMENT.md` "
|
||||
"book entrypoints in full, then every physical chapter in each entrypoint's "
|
||||
"`Chapters` membership list in full, in the order you choose. Resolve each chapter link "
|
||||
"relative to its entrypoint; a legacy monolith without `Chapters` is already "
|
||||
"the complete book. Cite physical file paths and file-local lines, never "
|
||||
"line numbers in a composed book. Reading an entrypoint alone does not "
|
||||
"cover its chapters; report any missing or unread chapter explicitly. Read "
|
||||
"the `Skill Review Checklist` section of `docs/CHECKLISTS.md`; then read "
|
||||
"`docs/CREATING_SKILLS.md`, `ouroboros/contracts/plugin_api.py`, and "
|
||||
"`ouroboros/extension_ui_validation.py` in full. Treat those source reads as "
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import logging
|
|||
import pathlib
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from ouroboros.reference_books import BOOK_ENTRYPOINTS, compose_book, load_reference_book
|
||||
from ouroboros.skill_review_history import count_attempts as _count_attempts_for_content
|
||||
from ouroboros.skill_review_status import CRITICAL_ITEMS
|
||||
from ouroboros.tools.review_helpers import (
|
||||
|
|
@ -67,6 +68,12 @@ def _load_governance_artifact(
|
|||
"""Load governance context with an explicit omission marker on failure."""
|
||||
from ouroboros.tools.review_helpers import load_governance_doc
|
||||
|
||||
for book_id, entrypoint in BOOK_ENTRYPOINTS.items():
|
||||
if relpath == entrypoint:
|
||||
try:
|
||||
return compose_book(load_reference_book(repo_root, book_id))
|
||||
except (OSError, ValueError) as exc:
|
||||
return f"[⚠️ OMISSION: {relpath} book could not be loaded: {exc}]"
|
||||
return load_governance_doc(repo_root, relpath, on_missing="explicit")
|
||||
|
||||
|
||||
|
|
@ -126,11 +133,11 @@ review enforcement mode.
|
|||
|
||||
## Governance context — docs/ARCHITECTURE.md
|
||||
|
||||
Use Section 10 (Key Invariants), Section 12 (Host Service / Companion /
|
||||
Chat IDs), and Section 13 (External Skills Layer)
|
||||
as the binding description of what the skill is allowed to touch. In
|
||||
particular invariant 11 is the authoritative rule: skills must not write
|
||||
to the self-modifying repo, and reviewed execution is the primary gate.
|
||||
Use the named sections "Key Invariants", "Host Service, Companion Processes,
|
||||
and Chat IDs", and "External Skills Layer" as the binding description of what
|
||||
the skill is allowed to touch. The "Skill gates do not collapse" criterion
|
||||
keeps executable review, owner grants, dependencies, enablement, and execution
|
||||
distinct; apply the Skill Review Checklist's `no_repo_mutation` item.
|
||||
|
||||
{architecture_text}
|
||||
|
||||
|
|
|
|||
|
|
@ -106,3 +106,18 @@ def test_large_nano_task_retains_exact_owner_source_without_changing_max(tmp_pat
|
|||
assert plan.messages_for("max")[-1]["content"] == task_text
|
||||
assert "FINAL OWNER CRITERION" in json.loads(core.user_content_json)
|
||||
assert plan.nano_projection.estimated_tokens < 81920
|
||||
|
||||
|
||||
def test_authored_common_understanding_is_resident_without_every_note_summary(tmp_path):
|
||||
from ouroboros.knowledge import resolve_knowledge_address, write_knowledge_note
|
||||
|
||||
env, _core, task = _capture(tmp_path)
|
||||
write_knowledge_note(resolve_knowledge_address(env.drive_root, "overview"),
|
||||
"Our shared understanding includes context-sensitive preferences.")
|
||||
write_knowledge_note(resolve_knowledge_address(env.drive_root, "people/alex"),
|
||||
"---\ntype: relationship\ntitle: Alex\nsummary: Details belong to their source.\n---\nDetailed source.")
|
||||
sections = "\n".join(context.build_knowledge_sections(env))
|
||||
assert "context-sensitive preferences" in sections
|
||||
assert "people/alex" in sections and "Alex" in sections
|
||||
assert "Details belong to their source" not in sections
|
||||
assert "scope='global'" in sections
|
||||
|
|
|
|||
142
tests/test_deep_review_book_sources.py
Normal file
142
tests/test_deep_review_book_sources.py
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
"""Deep review composes complete books and consumes revision-bound coverage."""
|
||||
|
||||
import hashlib
|
||||
import json
|
||||
|
||||
import pytest
|
||||
|
||||
from ouroboros import deep_self_review as deep
|
||||
from ouroboros.artifacts import read_actor_source_bytes
|
||||
from ouroboros.reference_books import compose_book, load_reference_book
|
||||
from tests.test_deep_review_slot import _native_row, _ScriptedLLM, _tool_call
|
||||
|
||||
|
||||
def _corpus(root):
|
||||
files = {
|
||||
"BIBLE.md": "# Constitution\n\nThe constitutional source.\n",
|
||||
"docs/CHECKLISTS.md": "# Checklists\n\n## Review\n\nCheck the actual contract.\n",
|
||||
"worker.py": "def work(): return True\n",
|
||||
}
|
||||
for book_id, entry in (("architecture", "docs/ARCHITECTURE.md"), ("development", "docs/DEVELOPMENT.md")):
|
||||
files[entry] = f"# {book_id.title()}\n\nPurpose of the {book_id} book.\n\n## Chapters\n\n- [Flow]({book_id}/flow.md)\n- [State]({book_id}/state.md)\n"
|
||||
for name in ("flow", "state"):
|
||||
files[f"docs/{book_id}/{name}.md"] = (
|
||||
f"# {name.title()}\n\nIntroduction to {book_id} {name}.\n\n## Contract\n\nExact {book_id} {name} contract body.\n")
|
||||
for rel, text in files.items():
|
||||
target = root / rel
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
target.write_text(text)
|
||||
return files
|
||||
|
||||
|
||||
def _required(root, rel):
|
||||
raw = (root / rel).read_bytes()
|
||||
normalized = raw.decode().replace("\r\n", "\n").replace("\r", "\n")
|
||||
return {"root": "system_repo", "path": rel, "source_revision": hashlib.sha256(raw).hexdigest(),
|
||||
"complete_sha256": hashlib.sha256(normalized.encode()).hexdigest(), "complete_chars": len(normalized),
|
||||
"range_basis": "unicode_text_universal_newlines"}
|
||||
|
||||
|
||||
def test_packed_chaptered_books_are_complete_once_and_stable_before_atlas(tmp_path, monkeypatch):
|
||||
monkeypatch.setattr(deep, "get_context_mode", lambda: "max")
|
||||
monkeypatch.setattr(deep, "_compute_graph_centrality", lambda *a: {})
|
||||
prefixes = []
|
||||
for name in ("first", "second"):
|
||||
repo, data = tmp_path / name, tmp_path / f"{name}-data"
|
||||
files = _corpus(repo)
|
||||
monkeypatch.setattr(deep, "_dulwich_tracked_paths", lambda *a: (list(files), []))
|
||||
monkeypatch.chdir(tmp_path)
|
||||
pack, stats = deep.build_review_pack(repo, data)
|
||||
for rel, text in files.items():
|
||||
if rel.startswith(("docs/architecture/", "docs/development/")):
|
||||
assert pack.count(text) == 1
|
||||
prefix = "\n".join(f"## Reference book: docs/{book_id.upper()}.md\n\n" + compose_book(load_reference_book(repo, book_id))
|
||||
for book_id in ("architecture", "development"))
|
||||
assert pack.startswith(prefix)
|
||||
assert str(repo) not in prefix
|
||||
assert all(view["delivery"] == "full" for view in stats["context_manifest"]["reference_book_views"])
|
||||
prefixes.append(prefix)
|
||||
assert prefixes[0] == prefixes[1]
|
||||
|
||||
|
||||
def test_low_packed_architecture_overview_does_not_reinline_omitted_chapters(tmp_path, monkeypatch):
|
||||
repo, data = tmp_path / "repo", tmp_path / "data"
|
||||
files = _corpus(repo)
|
||||
monkeypatch.setattr(deep, "get_context_mode", lambda: "low")
|
||||
monkeypatch.setattr(deep, "_compute_graph_centrality", lambda *a: {})
|
||||
monkeypatch.setattr(deep, "_dulwich_tracked_paths", lambda *a: (list(files), []))
|
||||
pack, stats = deep.build_review_pack(repo, data)
|
||||
assert "Introduction to architecture flow." in pack
|
||||
assert "Exact architecture flow contract body." not in pack
|
||||
assert "Exact development flow contract body." in pack
|
||||
assert "docs/architecture/flow.md" in pack
|
||||
row = next(r for r in stats["context_manifest"]["coverage"] if r["path"] == "docs/architecture/flow.md")
|
||||
assert "overview only" in row["reason"]
|
||||
|
||||
|
||||
def test_missing_declared_chapter_never_becomes_a_successful_partial_packed_book(tmp_path, monkeypatch):
|
||||
repo = tmp_path / "repo"
|
||||
files = _corpus(repo)
|
||||
(repo / "docs/architecture/state.md").unlink()
|
||||
monkeypatch.setattr(deep, "_dulwich_tracked_paths", lambda *a: (list(files), []))
|
||||
pack, stats = deep.build_review_pack(repo, tmp_path / "data")
|
||||
assert pack == "" and "book unavailable" in stats["skipped"][0]
|
||||
assert "state.md" in stats["skipped"][0]
|
||||
|
||||
|
||||
def test_retrieving_book_navigation_uses_only_physical_chapter_addresses(tmp_path, monkeypatch):
|
||||
repo, data = tmp_path / "repo", tmp_path / "data"
|
||||
_corpus(repo)
|
||||
# This legacy mapper must not be fed a composed book with invented entrypoint lines.
|
||||
monkeypatch.setattr(deep, "generate_doc_nav_map", lambda *a, **k: pytest.fail("chaptered book mapped as monolith"))
|
||||
task, _facts = deep._retrieving_task(repo, data)
|
||||
assert "Source: `docs/architecture/flow.md`" in task
|
||||
assert "Source: `docs/development/state.md`" in task
|
||||
assert "Introduction to architecture state." in task
|
||||
assert "Exact architecture state contract body." not in task
|
||||
assert "file you open" in task
|
||||
assert task.index("Introduction to architecture flow.") < task.index("## Memory")
|
||||
|
||||
|
||||
def test_exact_coverage_overrides_complete_legacy_lines_without_rebinding_current_file(tmp_path):
|
||||
files = _corpus(tmp_path)
|
||||
row = {**_required(tmp_path, "BIBLE.md"), "status": "incomplete", "covered_chars": 7,
|
||||
"missing_ranges": [[7, len(files["BIBLE.md"])]]}
|
||||
usage = {"native_read_coverage": {"status": "incomplete", "sources": [row]},
|
||||
"native_tool_receipts": [{"tool": "read_file", "outcome": "executed", "path": "BIBLE.md",
|
||||
"root": "system_repo", "start_line": 1, "end_line": 99, "total_lines": 99}]}
|
||||
(tmp_path / "BIBLE.md").write_text("Changed after the observed review source.\n")
|
||||
detail = deep._native_read_coverage(usage, tmp_path)["BIBLE.md"]
|
||||
assert detail["state"] == "partial" and detail["covered_chars"] == 7
|
||||
assert detail["source_revision"] == row["source_revision"]
|
||||
assert detail["evidence_basis"] == "source_ranges" and "covered_lines" not in detail
|
||||
assert deep._delivery_incomplete("native_tool_rounds", usage) == "required_source_coverage_incomplete"
|
||||
|
||||
|
||||
def test_legacy_line_evidence_stays_explicit_and_unsent_reads_never_complete_it(tmp_path):
|
||||
receipt = {"tool": "read_file", "outcome": "executed", "path": "BIBLE.md", "root": "system_repo",
|
||||
"start_line": 1, "end_line": 5, "total_lines": 5}
|
||||
usage = {"native_tool_receipts": [receipt]}
|
||||
detail = deep._native_read_coverage(usage, tmp_path)["BIBLE.md"]
|
||||
assert detail["state"] == "read" and detail["evidence_basis"] == "legacy_lines"
|
||||
assert "source_revision" not in detail
|
||||
receipt["delivered"] = False
|
||||
assert deep._native_read_coverage(usage, tmp_path)["BIBLE.md"]["state"] == "missing"
|
||||
|
||||
|
||||
def test_native_deep_review_reports_exact_chapter_gap_without_changing_the_finding(tmp_path, monkeypatch):
|
||||
repo, data = tmp_path / "repo", tmp_path / "data"
|
||||
_corpus(repo)
|
||||
monkeypatch.setenv("OPENROUTER_API_KEY", "fixture-only")
|
||||
report = "Read: Constitution.\n\nCRITICAL: a real consumer violates the contract."
|
||||
llm = _ScriptedLLM([
|
||||
{"tool_calls": [_tool_call("read_file", {"path": "BIBLE.md"})]}, {"content": report}])
|
||||
required = [_required(repo, "BIBLE.md"), _required(repo, "docs/architecture/flow.md")]
|
||||
text, usage = deep.run_deep_self_review(repo, data, llm, lambda m: None,
|
||||
slot=_native_row(), task_id="book-review", required_sources=required)
|
||||
assert text.endswith(report)
|
||||
assert usage["deep_review_coverage_basis"] == "source_ranges"
|
||||
assert usage["native_incomplete"] == "required_source_coverage_incomplete"
|
||||
assert "docs/architecture/flow.md NOT read" in text
|
||||
history = json.loads(read_actor_source_bytes(data, "book-review", usage["native_history_source"]))
|
||||
assert history["required_sources"] == required
|
||||
|
|
@ -558,3 +558,73 @@ def test_review_skill_persist_false_does_not_write(tmp_path, monkeypatch):
|
|||
# Default state: nothing written.
|
||||
assert persisted.status == "pending"
|
||||
assert persisted.content_hash == ""
|
||||
|
||||
|
||||
@pytest.mark.parametrize("chaptered", [False, True], ids=["legacy", "chapters"])
|
||||
def test_skill_governance_is_complete_and_stable_across_checkouts(tmp_path, monkeypatch, chaptered):
|
||||
import ouroboros.skill_review_prompt as prompt_owner
|
||||
|
||||
corpus = {"BIBLE.md": "# Constitution\n\nConstitutional body.\n"}
|
||||
for book_id, entrypoint in (
|
||||
("architecture", "docs/ARCHITECTURE.md"),
|
||||
("development", "docs/DEVELOPMENT.md"),
|
||||
):
|
||||
corpus[entrypoint] = f"# {book_id.title()}\n\nThe {book_id} introduction.\n"
|
||||
if chaptered:
|
||||
corpus[entrypoint] += (
|
||||
f"\n## Chapters\n\n- [First]({book_id}/first.md)\n"
|
||||
f"- [Second][second]\n\n[second]: {book_id}/second.md\n"
|
||||
)
|
||||
for name in ("first", "second"):
|
||||
corpus[f"docs/{book_id}/{name}.md"] = (
|
||||
f"# {name.title()}\n\nThe {book_id} {name} introduction.\n"
|
||||
f"\n## Details\n\nThe complete {book_id} {name} body.\n"
|
||||
)
|
||||
else:
|
||||
corpus[entrypoint] += f"\n## Details\n\nThe complete {book_id} monolith.\n"
|
||||
|
||||
monkeypatch.setattr(prompt_owner, "load_checklist_section", lambda _: "Checklist body.")
|
||||
monkeypatch.setattr(prompt_owner, "build_skill_host_context", lambda _: "Host contract body.")
|
||||
monkeypatch.chdir(tmp_path)
|
||||
stable_prefixes = []
|
||||
for name in ("checkout-one", "checkout-two"):
|
||||
root = tmp_path / name
|
||||
for path, text in corpus.items():
|
||||
target = root / path
|
||||
target.parent.mkdir(parents=True, exist_ok=True)
|
||||
target.write_text(text, encoding="utf-8")
|
||||
monkeypatch.setattr(prompt_owner, "_REPO_ROOT", root)
|
||||
prompt, boundary = prompt_owner._build_review_prompt(
|
||||
name, root / "external-skill", "{}", name, f"PAYLOAD-{name}",
|
||||
)
|
||||
stable = prompt[:boundary]
|
||||
stable_prefixes.append(stable)
|
||||
for text in corpus.values():
|
||||
assert stable.count(text) == 1
|
||||
assert str(root) not in stable
|
||||
assert "PAYLOAD-" not in stable
|
||||
assert "Skill gates do not collapse" in stable
|
||||
assert "invariant 11" not in stable
|
||||
assert prompt[boundary:].startswith("## Skill identity")
|
||||
assert f"PAYLOAD-{name}" in prompt[boundary:]
|
||||
assert stable_prefixes[0] == stable_prefixes[1]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("chapter_body", [None, "# Chapter\n\n## Missing introduction\n\nBody.\n"])
|
||||
def test_skill_governance_discloses_unavailable_book_without_partial_body(tmp_path, chapter_body):
|
||||
from ouroboros.skill_review_prompt import _load_governance_artifact
|
||||
|
||||
docs = tmp_path / "docs"
|
||||
docs.mkdir()
|
||||
(docs / "ARCHITECTURE.md").write_text(
|
||||
"# Architecture\n\nEntrypoint body must not stand in for the whole book.\n"
|
||||
"\n## Chapters\n\n- [Runtime](architecture/runtime.md)\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
if chapter_body is not None:
|
||||
(docs / "architecture").mkdir()
|
||||
(docs / "architecture" / "runtime.md").write_text(chapter_body, encoding="utf-8")
|
||||
text = _load_governance_artifact(tmp_path, "docs/ARCHITECTURE.md")
|
||||
assert "OMISSION" in text and "docs/ARCHITECTURE.md" in text
|
||||
assert "runtime.md" in text
|
||||
assert "Entrypoint body must not stand in" not in text
|
||||
|
|
|
|||
|
|
@ -121,6 +121,10 @@ def test_chunked_passes_keep_the_full_row_plan_and_exact_session_evidence():
|
|||
assert "STABLE::" not in call["session_task"]
|
||||
assert all(path in call["session_task"] for path in (
|
||||
"BIBLE.md", "docs/CHECKLISTS.md", "ouroboros/contracts/plugin_api.py"))
|
||||
assert "every physical chapter" in call["session_task"]
|
||||
assert "`Chapters` membership list in full, in the order you choose" in call["session_task"]
|
||||
assert "legacy monolith without `Chapters`" in call["session_task"]
|
||||
assert "physical file paths and file-local lines" in call["session_task"]
|
||||
assert f"PART {idx} of 2" in call["session_task"]
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue