mirror of
https://github.com/razzant/ouroboros.git
synced 2026-10-19 10:04:16 +00:00
Decision 3A. commit_reviewed(preflight_reviewer=...) names one enabled catalog
row, a review-pool member or not; its early look at the worktree is
review_change(subject=worktree, surface=preflight, reviewers=[row]) and only
informs. An unknown or disabled row, or a row named beside skip_advisory_review
or an author continuation, is a TOOL_ARG_ERROR before anything is staged. The
commit record states the fact: preflight performed (with the look's record id),
not_performed, or skipped. The deterministic release and syntax checks of
preflight_review_run.py move into commit_gate as they ran at commit time (the
staged-index lane) and, with the tests preflight, run ahead of every panel.
The advisory pipelines are deleted (claude_advisory_review.py,
preflight_review_run.py, preflight_review_prompt.py): advisory freshness, the
automatic advisory run, the audited bypass and advisory obligations no longer
gate a commit. preflight_review is a thin wrapper over the same review_change
call, registered once with advisory_review as its alias; deterministic_only
keeps the free release diagnostics. The skill review no longer feeds advisory
evidence to its reviewer, which moves the skill-review prompt contract
fingerprint once.
review_change gains the preflight and system surfaces. The surface is part of
every reuse and retry key, so a preflight record of the same tree never
answers the commit panel. compose_panel seats review-pool rows (PoolSeat,
seat_id = subagent_id, coupling_only as parts ("coupling",)) against package
A's pool contract, which the tests stand in for until it lands.
/review is review_change(subject=system, surface=system) with one seat: the
row the caller names, else Main. Every entry goes through
queue_deep_self_review_task(..., reviewer): the Web dialog offers the enabled
rows, a bare /review (Telegram) runs on Main, and the ack, the result and the
record link go to the asking chat.
The operator lane of scripts/run_external_review.py runs --preflight-reviewer
through review_change while keeping the hermetic tests, the isolated checkout,
the drift check and the helper's full answer (preflight.json, preflight.txt).
Domains, inventories and the size manifest are regenerated; git.py leaves the
giant list (1640 -> 1561 lines).
474 lines
23 KiB
Python
474 lines
23 KiB
Python
#!/usr/bin/env python3
|
|
"""Measure the commit triad packet for one staged change — offline, $0.
|
|
|
|
Reports, for the checkout at ``--repo`` (its INDEX is the reviewed change; every
|
|
governance corpus it selects — BIBLE.md, the checklist section + archive, and
|
|
whatever ``tools/governance_context.py`` inlines or navigates for these touched
|
|
paths — is read from that one checkout):
|
|
|
|
* the triad touched-file pack BEFORE and AFTER the disclosed pack exclusions
|
|
(``review_file_pack.triad_pack_exclusions``: span-only release carriers on a
|
|
VERSION-staged commit, governance docs byte-identical to the inlined prefix);
|
|
* the governance context the triad packet carries, tier by tier: the byte-stable
|
|
prefix (checklist section + archive + tier-1 inline rules), the change-class
|
|
selection and the navigation maps that open the dynamic tail, and the
|
|
constitutional head (preamble + BIBLE.md) each api row receives per round;
|
|
* the ZERO-DIFF message — everything an api row receives before the first pack
|
|
or diff byte, serialized as ``_multi_model_review_async`` sends it: the
|
|
constitutional head + the stable prefix + the governance tail and dynamic
|
|
scaffolding rendered with an empty pack and diff + the fixed user turn — the
|
|
quorum input limit of the rows that RECEIVE the packet (resolved by
|
|
``row_plan_retrieves``, exactly as ``review`` filters them before
|
|
``fit_triad_prompt``; native and session rows retrieve with their own tools
|
|
and never constrain the ladder), and the
|
|
headroom that limit leaves for the pack + diff. A panel whose every row
|
|
retrieves gets the explicit "no API pack is assembled for this panel" instead
|
|
of a number.
|
|
|
|
Units: chars, the host's own ``utils.estimate_tokens`` (chars/4 — the unit
|
|
``review_admission.fit_triad_prompt`` compares against the quorum limit, so every
|
|
limit/headroom figure is in it) and tiktoken ``o200k_base`` (not Anthropic's
|
|
tokenizer) are printed side by side and never conflated.
|
|
|
|
One change, checked: every arm reads the checkout's INDEX as the reviewed change
|
|
while the packs read working-tree text. Those coincide only when the index IS
|
|
the working tree, so a checkout with an unstaged edit or an untracked file is
|
|
refused with the typed :class:`MeasuredCheckoutDirty` (exit 2) instead of
|
|
measured across two changes.
|
|
|
|
Offline by construction: reviewer windows are read from the Capability Evidence
|
|
CACHE only (``capability_evidence.probe(allow_fetch=False)`` under
|
|
``$OUROBOROS_DATA_DIR``) — an unknown route is disclosed and sized at the fit
|
|
ladder's own full-window default, never probed or persisted; the o200k BPE is
|
|
served from the local tiktoken cache only (``TIKTOKEN_CACHE_DIR``) and a missing
|
|
BPE is a typed, disclosed miss, never a download. Nothing is dispatched.
|
|
|
|
Usage::
|
|
|
|
python devtools/measure_review_pack.py --repo /path/to/checkout [--json]
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import json
|
|
import pathlib
|
|
import subprocess
|
|
import sys
|
|
|
|
REPO_ROOT = pathlib.Path(__file__).resolve().parents[1]
|
|
sys.path.insert(0, str(REPO_ROOT))
|
|
|
|
# The fixed user turn `review._dispatch_unified_review` sends under the system
|
|
# packet (its `content=` argument): reviewer input the fit ladder never sees.
|
|
TRIAD_USER_TURN = "Review the staged diff and context provided in the instructions above."
|
|
FIT_UNITS = ("chars/4 (utils.estimate_tokens) — the unit review_admission.fit_triad_prompt "
|
|
"compares against the quorum limit")
|
|
NO_API_PACK_NOTE = (
|
|
"no API pack is assembled for this panel: every configured row retrieves the subject with its "
|
|
"own tools (session and native API rows), so there is no quorum input "
|
|
"limit and no headroom to report")
|
|
|
|
|
|
class TokenizerUnavailable(RuntimeError):
|
|
"""The o200k BPE is not in the local tiktoken cache; this measurer never downloads it."""
|
|
|
|
|
|
class MeasuredCheckoutDirty(RuntimeError):
|
|
"""The checkout's index is not its working tree: its arms would measure two different changes."""
|
|
|
|
|
|
def _o200k():
|
|
"""tiktoken ``o200k_base`` from the LOCAL BPE cache only.
|
|
|
|
``tiktoken.load.read_file_cached`` serves the cache and otherwise calls
|
|
``read_file`` (an HTTP GET) through its module namespace at call time, so
|
|
binding that name to a typed refusal for the duration of the load turns the
|
|
download path into :class:`TokenizerUnavailable` instead of a network call."""
|
|
import tiktoken
|
|
from tiktoken import load as tiktoken_load
|
|
|
|
def _refuse(blobpath: str) -> bytes:
|
|
raise TokenizerUnavailable(
|
|
f"o200k_base BPE is not cached locally (would fetch {blobpath}); "
|
|
"point TIKTOKEN_CACHE_DIR at a warmed cache — this measurer never downloads")
|
|
|
|
fetch = tiktoken_load.read_file
|
|
tiktoken_load.read_file = _refuse
|
|
try:
|
|
return tiktoken.get_encoding("o200k_base")
|
|
finally:
|
|
tiktoken_load.read_file = fetch
|
|
|
|
|
|
def _measure(text: str, enc) -> dict:
|
|
from ouroboros.utils import estimate_tokens
|
|
|
|
return {
|
|
"chars": len(text),
|
|
"chars_div_4": int(estimate_tokens(text)),
|
|
"o200k": len(enc.encode(text, disallowed_special=())) if enc is not None else None,
|
|
}
|
|
|
|
|
|
def _staged_entries(repo: pathlib.Path) -> list[tuple[str, str, str]]:
|
|
"""``(status, current_path, source_path)`` per staged entry, through the host's
|
|
own ``parse_git_name_status`` — so a rename names both ends and a staged
|
|
deletion stays a ``D`` entry instead of a current path that resolves to
|
|
nothing."""
|
|
from ouroboros.tools.review_file_pack import parse_git_name_status
|
|
|
|
out = subprocess.run(
|
|
["git", "diff", "--cached", "--name-status"], cwd=str(repo),
|
|
check=True, capture_output=True, text=True,
|
|
).stdout
|
|
return parse_git_name_status(out)
|
|
|
|
|
|
def _porcelain(repo: pathlib.Path) -> str:
|
|
"""``git status --porcelain`` — the worktree state the one-change invariant reads."""
|
|
return subprocess.run(
|
|
["git", "status", "--porcelain"], cwd=str(repo), check=True, capture_output=True, text=True).stdout
|
|
|
|
|
|
def _require_index_is_worktree(porcelain: str) -> None:
|
|
"""The one-change invariant every arm rests on (module docstring): an entry
|
|
with a worktree-column status — an unstaged edit (``XM``), an untracked file
|
|
(``??``) — means the packs would read text the index never staged. Typed
|
|
refusal, never a silently cross-arm number."""
|
|
dirty = [line for line in porcelain.splitlines() if len(line) > 1 and line[1] != " "]
|
|
if dirty:
|
|
raise MeasuredCheckoutDirty(
|
|
"the checkout's index is not its working tree — stage or stash these before measuring: "
|
|
+ "; ".join(dirty))
|
|
|
|
|
|
def _panel_rows(plan: dict) -> list[dict]:
|
|
"""Use commit_triad_delivery's resolved delivery, including direct native
|
|
API rows. Only packet recipients constrain the fit ladder and get a pack."""
|
|
from ouroboros.reviewer_slot_config import row_plan_retrieves
|
|
|
|
actors = list(plan.get("subagent_ids") or [])
|
|
rows = []
|
|
for i, (model, route) in enumerate(zip(plan["models"], plan["routes"])):
|
|
actor = str(actors[i] if i < len(actors) else "")
|
|
rows.append({"model": model, "route": str(getattr(route, "value", route)), "subagent_id": actor,
|
|
"receives_pack": not row_plan_retrieves(plan, i)})
|
|
return rows
|
|
|
|
|
|
def _checklist_section(repo: pathlib.Path) -> str:
|
|
"""``review._load_checklist_section()`` (body layer) read from the TARGET checkout.
|
|
|
|
The runtime reads the checklist from its own REPO_ROOT (a frozen contract);
|
|
this measurer measures one checkout, so the same layered sections + archive
|
|
come from ``repo`` (``review_helpers.load_checklist_layers`` pointed at it)."""
|
|
from ouroboros.tools.review_helpers import load_checklist_layers
|
|
|
|
section = load_checklist_layers("body", repo / "docs" / "CHECKLISTS.md")
|
|
archive = (repo / "docs" / "CHECKLISTS_ARCHIVE.md").read_text(encoding="utf-8").strip()
|
|
return f"{section}\n\n{archive}" if archive else section
|
|
|
|
|
|
def _governance_usable_window(models: list[str]) -> int:
|
|
"""The usable input window the packet's governance share is taken against,
|
|
by ``review._triad_governance_usable_window``'s own arithmetic on cache-only
|
|
windows (the runtime keys it by slot id; this measurer has models only)."""
|
|
from ouroboros.tools import review as _rv
|
|
|
|
usable: dict[str, int] = {}
|
|
for model in models:
|
|
window, _evidence = _cached_window(model)
|
|
output_reserve, tokenizer_margin = _rv.window_scaled_reserves(
|
|
window, output_reserve=_rv._review_output_budget(), tokenizer_margin=50_000)
|
|
usable[model] = max(0, int(window) - int(output_reserve) - int(tokenizer_margin))
|
|
return int(_rv._quorum_input_token_limit(list(usable), usable)) if usable else 0
|
|
|
|
|
|
def _governance_prefix(
|
|
repo: pathlib.Path, touched_paths: list[str], api_models: list[str],
|
|
) -> dict[str, str]:
|
|
"""The governance regions exactly as `_prepare_unified_review` assembles them.
|
|
|
|
``governance_context`` is the ONE decision about which rules a packet
|
|
carries: tier 1 rides the cache-marked stable prefix, the change-class
|
|
selection and the navigation maps open the dynamic tail. A panel with no api
|
|
row assembles no packet, so it asks for no governance — the same branch the
|
|
runtime takes. The principal checklist and repository documents are read
|
|
from ``repo``; the shared ownership section follows the runtime loader and
|
|
comes from the executing checkout. Cross-checkout measurements therefore
|
|
retain that disclosed mixed-source boundary."""
|
|
from ouroboros.tools import review
|
|
from ouroboros.tools.governance_context import GovernanceContext, governance_context
|
|
|
|
checklist = _checklist_section(repo)
|
|
governance = governance_context(
|
|
repo,
|
|
surface="triad",
|
|
touched_paths=touched_paths,
|
|
usable_window_tokens=_governance_usable_window(api_models),
|
|
delivery="packet",
|
|
checklist_section_text=checklist,
|
|
already_inline=("BIBLE.md", "docs/CHECKLISTS_ARCHIVE.md"),
|
|
) if api_models else GovernanceContext()
|
|
stable = review._REVIEW_PROMPT_TEMPLATE_STABLE.format(
|
|
preamble=review.review_preamble("body"),
|
|
critical_calibration=review.CRITICAL_FINDING_CALIBRATION,
|
|
json_contract=review.REVIEW_JSON_ARRAY_CONTRACT,
|
|
anti_pattern_lock_guard=review.anti_pattern_lock_guard("body"),
|
|
checklist_section=checklist,
|
|
) + (f"\n{governance.stable_inline}\n" if governance.stable_inline.strip() else "")
|
|
tail = "\n\n".join(
|
|
part for part in (governance.selected_inline, governance.navigation) if part.strip())
|
|
return {
|
|
"stable_prefix": stable,
|
|
"checklist_section": checklist,
|
|
"tier_1_inline_rules": governance.stable_inline,
|
|
"change_class_selection": governance.selected_inline,
|
|
"navigation_maps": governance.navigation,
|
|
"governance_tail": tail,
|
|
"manifest": governance.manifest,
|
|
"inline_whole_documents": governance.inline_whole_documents,
|
|
}
|
|
|
|
|
|
def _constitutional_head(repo: pathlib.Path) -> str:
|
|
"""`_multi_model_review_async`'s stable head: the preamble + BIBLE.md from ``repo``."""
|
|
from ouroboros.tools import review_multi_model as mm
|
|
from ouroboros.tools.review_helpers import load_governance_doc
|
|
|
|
bible = load_governance_doc(repo, "BIBLE.md", on_missing="explicit")
|
|
return mm._CONSTITUTIONAL_PREAMBLE + "### BIBLE.md (Full Text)\n\n" + bible + "\n\n---\n\n## REVIEW INSTRUCTIONS\n\n"
|
|
|
|
|
|
def _zero_diff_message(repo: pathlib.Path, prefix: dict[str, str], paths: list[str]) -> dict[str, str]:
|
|
"""Every byte an api row receives BEFORE the pack and the diff, part by part in
|
|
wire order: the constitutional head (prepended to the system content), the
|
|
stable prefix, the governance tail plus the dynamic scaffolding
|
|
(`_REVIEW_PROMPT_TEMPLATE_DYNAMIC` with an empty pack and diff: the goal
|
|
section of an empty commit message, no scope, no rebuttal, no history, the
|
|
changed-files list) and the fixed user turn."""
|
|
from ouroboros.tools import review
|
|
from ouroboros.tools.review_helpers import build_goal_section, build_scope_section
|
|
|
|
tail = prefix["governance_tail"]
|
|
dynamic = (f"{tail}\n\n" if tail else "") + review._REVIEW_PROMPT_TEMPLATE_DYNAMIC.format(
|
|
goal_section=build_goal_section("", "", ""),
|
|
scope_section=build_scope_section(""),
|
|
current_files_section="",
|
|
rebuttal_section="",
|
|
review_history_section="",
|
|
diff_text="",
|
|
changed_files="\n".join(paths),
|
|
task_evidence_section="", # No task trace is part of this zero-diff baseline.
|
|
)
|
|
return {
|
|
"constitutional_head_preamble_plus_BIBLE": _constitutional_head(repo),
|
|
"stable_prefix": prefix["stable_prefix"],
|
|
# `_assemble_prompt` joins stable + "\n" + dynamic; the separator rides
|
|
# with the tail, and the governance tail opens that dynamic half.
|
|
"dynamic_scaffolding_empty_pack_and_diff": "\n" + dynamic,
|
|
"user_turn": TRIAD_USER_TURN,
|
|
}
|
|
|
|
|
|
def _cached_window(model: str) -> tuple[int, str]:
|
|
"""``(sizing window, evidence)`` for one api row from the Capability Evidence CACHE.
|
|
|
|
The same route derivation as ``reviewer_window.resolve_reviewer_window``
|
|
(``reviewer_route`` + ``review_model_uses_local``), read through
|
|
``capability_evidence.probe(allow_fetch=False)`` — the hot-path reader that
|
|
returns a fresh record as-is, an expired one marked stale and ``unprobeable``
|
|
for an absent one, and never fetches provider metadata or writes the store.
|
|
An unknown window is disclosed and sized exactly as the fit ladder sizes it
|
|
(``reviewer_context_window``'s full-window default), so the limit derived here
|
|
is the limit the ladder would compute on the same cache."""
|
|
from ouroboros.capability_evidence import probe
|
|
from ouroboros.config import DATA_DIR
|
|
from ouroboros.provider_models import review_model_uses_local
|
|
from ouroboros.reviewer_window import REVIEWER_FULL_WINDOW, ReviewerWindow, reviewer_route
|
|
|
|
use_local = review_model_uses_local(model)
|
|
provider, base_url = reviewer_route(model)
|
|
ev = probe(DATA_DIR, provider="local" if use_local else provider, model=model,
|
|
base_url=base_url, use_local=use_local, allow_fetch=False)
|
|
window = ReviewerWindow(
|
|
window_tokens=int(getattr(ev, "window_tokens", 0) or 0),
|
|
status=str(getattr(ev, "status", "") or ""),
|
|
stale=bool(getattr(ev, "stale", False)),
|
|
observed_at=str(getattr(ev, "ts", "") or ""),
|
|
model=model,
|
|
)
|
|
if window.window_tokens > 0:
|
|
return window.sizing_window(), f"{window.status}{' stale' if window.stale else ''} (cache-only)"
|
|
return window.sizing_window(), (
|
|
f"window unknown (cache-only); sized at the ladder's {REVIEWER_FULL_WINDOW:,} default")
|
|
|
|
|
|
def _quorum_limit(models: list[str]) -> tuple[int, dict[str, dict]]:
|
|
"""The panel's quorum input limit exactly as ``fit_triad_prompt`` derives it, on
|
|
cache-only windows; the per-slot rows disclose window, evidence and limit."""
|
|
from ouroboros.tools import review as _rv
|
|
|
|
slots: dict[str, dict] = {}
|
|
for model in models:
|
|
window, evidence = _cached_window(model)
|
|
output_reserve, margin = _rv.window_scaled_reserves(
|
|
window, output_reserve=_rv._review_output_budget(), tokenizer_margin=50_000)
|
|
limit = max(0, _rv.calibrated_input_token_limit(
|
|
model, context_window=window, output_reserve=output_reserve,
|
|
tokenizer_margin=margin, budget_cap=_rv.REVIEW_PROMPT_TOKEN_BUDGET))
|
|
slots[model] = {"window": window, "evidence": evidence, "input_limit_chars_div_4": limit}
|
|
return int(_rv._quorum_input_token_limit(
|
|
models, {m: s["input_limit_chars_div_4"] for m, s in slots.items()})), slots
|
|
|
|
|
|
def measure(repo: pathlib.Path) -> dict:
|
|
from ouroboros.reviewer_slot_config import commit_triad_delivery
|
|
from ouroboros.tools.review_file_pack import build_touched_file_pack, triad_pack_exclusions
|
|
|
|
try:
|
|
enc, tokenizer = _o200k(), "o200k_base from the local tiktoken cache"
|
|
except (ImportError, TokenizerUnavailable) as exc:
|
|
enc, tokenizer = None, f"o200k unavailable (cache-only): {exc}"
|
|
entries = _staged_entries(repo)
|
|
paths = [ep[1] for ep in entries] # the --name-only list the triad packs
|
|
porcelain = _porcelain(repo)
|
|
_require_index_is_worktree(porcelain)
|
|
# The governance selection is change-relative and packet-only, so the panel
|
|
# is resolved first: an all-retrieving panel assembles neither.
|
|
panel_rows = _panel_rows(commit_triad_delivery())
|
|
api_models = [row["model"] for row in panel_rows if row["receives_pack"]]
|
|
prefix = _governance_prefix(repo, paths, api_models)
|
|
|
|
def _pack(exclude: set[str], note: str) -> str:
|
|
section, omitted = build_touched_file_pack(repo, paths, exclude_paths=exclude)
|
|
if omitted:
|
|
section += (f"\n\n⚠️ OMISSION NOTE: {len(omitted)} file(s) omitted from direct context: "
|
|
f"{', '.join(omitted)}")
|
|
if note:
|
|
section += f"\n\n{note}"
|
|
return section
|
|
|
|
excluded, note = triad_pack_exclusions(
|
|
repo, paths, prefix_texts=dict(prefix["inline_whole_documents"]))
|
|
before, after = _pack(set(), ""), _pack(excluded, note)
|
|
per_file = {}
|
|
for rel in paths:
|
|
one, _ = build_touched_file_pack(repo, [rel])
|
|
per_file[rel] = {**_measure(one, enc), "excluded": rel in excluded}
|
|
zero_parts = _zero_diff_message(repo, prefix, paths)
|
|
zero_tokens = _measure("".join(zero_parts.values()), enc)["chars_div_4"]
|
|
fit: dict = {
|
|
"units": FIT_UNITS,
|
|
"panel_models": [row["model"] for row in panel_rows],
|
|
"panel_rows": panel_rows,
|
|
"api_pack_models": api_models,
|
|
"zero_diff_message_chars_div_4": zero_tokens,
|
|
}
|
|
if api_models:
|
|
limit, slots = _quorum_limit(api_models)
|
|
headroom = limit - zero_tokens
|
|
fit.update({
|
|
"slots": slots,
|
|
"quorum_input_limit_chars_div_4": limit,
|
|
"headroom_after_zero_diff_message": headroom,
|
|
"headroom_for_diff_before": headroom - _measure(before, enc)["chars_div_4"],
|
|
"headroom_for_diff_after": headroom - _measure(after, enc)["chars_div_4"],
|
|
# fit_triad_prompt sizes stable prefix + dynamic tail only: the head
|
|
# and the user turn are billed input it never counts.
|
|
"uncounted_by_fit_triad_prompt_chars_div_4": _measure(
|
|
zero_parts["constitutional_head_preamble_plus_BIBLE"] + zero_parts["user_turn"], enc)["chars_div_4"],
|
|
})
|
|
else:
|
|
fit["no_api_pack"] = NO_API_PACK_NOTE
|
|
return {
|
|
"repo": str(repo),
|
|
"staged_paths": paths,
|
|
"tokenizer": tokenizer,
|
|
"touched_pack": {
|
|
"before": _measure(before, enc),
|
|
"after": _measure(after, enc),
|
|
"excluded_paths": sorted(excluded),
|
|
"exclusion_note": note,
|
|
"per_file": per_file,
|
|
},
|
|
"governance_context": {
|
|
"stable_prefix_total": _measure(prefix["stable_prefix"], enc),
|
|
"governance_tail_total": _measure(prefix["governance_tail"], enc),
|
|
"manifest": prefix["manifest"],
|
|
"parts": {
|
|
"checklist_section_plus_archive": _measure(prefix["checklist_section"], enc),
|
|
"tier_1_inline_rules": _measure(prefix["tier_1_inline_rules"], enc),
|
|
"change_class_selection": _measure(prefix["change_class_selection"], enc),
|
|
"navigation_maps": _measure(prefix["navigation_maps"], enc),
|
|
"constitutional_head_preamble_plus_BIBLE": _measure(
|
|
zero_parts["constitutional_head_preamble_plus_BIBLE"], enc),
|
|
},
|
|
},
|
|
"zero_diff_message": {
|
|
"components": list(zero_parts),
|
|
"total": _measure("".join(zero_parts.values()), enc),
|
|
"parts": {name: _measure(text, enc) for name, text in zero_parts.items()},
|
|
},
|
|
"fit": fit,
|
|
}
|
|
|
|
|
|
def main(argv: list[str] | None = None) -> int:
|
|
parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
|
parser.add_argument("--repo", required=True, help="checkout whose staged change is measured")
|
|
parser.add_argument("--json", action="store_true", help="print the full JSON report only")
|
|
args = parser.parse_args(argv)
|
|
try:
|
|
report = measure(pathlib.Path(args.repo).resolve())
|
|
except MeasuredCheckoutDirty as exc:
|
|
print(f"refused: {exc}", file=sys.stderr)
|
|
return 2
|
|
if args.json:
|
|
print(json.dumps(report, indent=2, ensure_ascii=False))
|
|
return 0
|
|
pack, fit = report["touched_pack"], report["fit"]
|
|
print(f"staged paths: {len(report['staged_paths'])}; excluded: {pack['excluded_paths']}")
|
|
for arm in ("before", "after"):
|
|
m = pack[arm]
|
|
print(f"touched pack {arm:6}: {m['chars']:>10,} chars {m['chars_div_4']:>9,} chars/4 {m['o200k']!s:>9} o200k")
|
|
for rel, m in sorted(pack["per_file"].items(), key=lambda kv: -(kv[1]["o200k"] or kv[1]["chars"])):
|
|
flag = "CUT " if m["excluded"] else "keep"
|
|
print(f" {flag} {rel:40} {m['chars']:>10,} chars {m['o200k']!s:>9} o200k")
|
|
print(f"governance context (one checkout: {report['repo']}), per api row, per round:")
|
|
for name, m in report["governance_context"]["parts"].items():
|
|
print(f" {name:42} {m['chars']:>10,} chars {m['o200k']!s:>9} o200k")
|
|
total = report["governance_context"]["stable_prefix_total"]
|
|
print(f" stable prefix total (without BIBLE head) {total['chars']:>10,} chars {total['o200k']!s:>9} o200k")
|
|
tail = report["governance_context"]["governance_tail_total"]
|
|
print(f" change-relative governance tail {tail['chars']:>10,} chars {tail['o200k']!s:>9} o200k")
|
|
for row in report["governance_context"]["manifest"]:
|
|
print(f" {str(row.get('path') or '?'):42} tier {row.get('tier')} {row.get('disposition')} "
|
|
f"{int(row.get('chars') or 0):>9,} chars — {row.get('reason')}")
|
|
zero = report["zero_diff_message"]
|
|
print(f"zero-diff message ({' + '.join(zero['components'])}): "
|
|
f"{zero['total']['chars']:,} chars {zero['total']['chars_div_4']:,} chars/4 {zero['total']['o200k']!s} o200k")
|
|
print("panel rows (delivery class as review.py decides it before fit_triad_prompt):")
|
|
for row in fit["panel_rows"]:
|
|
actor = f" via configured subagent {row['subagent_id']}" if row["subagent_id"] else ""
|
|
print(f" {row['model']:36} {row['route']}{actor}: "
|
|
f"{'receives the api pack' if row['receives_pack'] else 'retrieves — no pack, outside the fit ladder'}")
|
|
if "no_api_pack" in fit:
|
|
print(fit["no_api_pack"])
|
|
else:
|
|
print(f"api pack rows {fit['api_pack_models']}: quorum input limit "
|
|
f"{fit['quorum_input_limit_chars_div_4']:,} [{fit['units']}]")
|
|
for model, slot in fit["slots"].items():
|
|
print(f" {model:36} window {slot['window']:>9,} — {slot['evidence']}; slot limit {slot['input_limit_chars_div_4']:,}")
|
|
print(f"headroom after the zero-diff message: {fit['headroom_after_zero_diff_message']:,}; "
|
|
f"for the pack + diff: before {fit['headroom_for_diff_before']:,} -> after {fit['headroom_for_diff_after']:,}")
|
|
print(f" (fit_triad_prompt sizes stable prefix + dynamic tail only; it does not count the "
|
|
f"{fit['uncounted_by_fit_triad_prompt_chars_div_4']:,} chars/4 of constitutional head + user turn)")
|
|
print(f"tokenizer: {report['tokenizer']}")
|
|
print(pack["exclusion_note"] or "(no exclusion note)")
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
raise SystemExit(main())
|