Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions bin/fm-crosscheck-pi-reviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
from typing import Any


VERDICT_REPAIR_EFFORT = "low"


class ReviewError(RuntimeError):
"""A fail-closed Pi launch or verdict-protocol failure."""

Expand Down Expand Up @@ -331,7 +334,7 @@ def run(argv: list[str]) -> int:
"--model",
model,
"--thinking",
effort if attempt == 0 else "minimal",
effort if attempt == 0 else VERDICT_REPAIR_EFFORT,
"--tools",
"submit_crosscheck_verdict",
"--extension",
Expand Down Expand Up @@ -370,7 +373,7 @@ def run(argv: list[str]) -> int:
repair_prompt.write_text(
"VERDICT PROTOCOL REPAIR (trusted controller instruction):\n"
"Perform the exact independent review packet below in this fresh "
"minimal-reasoning attempt. Do not end with prose and do not call "
f"{VERDICT_REPAIR_EFFORT}-reasoning attempt. Do not end with prose and do not call "
"the tool more than once. Submit the complete schema-valid verdict "
"through submit_crosscheck_verdict exactly once.\n\n"
+ prompt.read_text(encoding="utf-8"),
Expand Down
2 changes: 1 addition & 1 deletion docs/crosscheck.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ The regular-lane reviewer record binds `review_depth_passes: "2"`, `review_depth
Successful current-contract `clear` and `blocking` records, including reusable records, fail validation when any of those fields is missing or contradictory.
Failed `tool-failure`, `unreviewed`, and `cannot-certify` attempts may omit terminal and depth evidence they never earned, so their ledgers remain reloadable for a later retry; they are never reusable.
Crosscheck accepts exactly one verdict tool call from each successful pass and preserves usage across Pi auto-retries.
If an attempt reaches the model but ends without exactly one well-formed verdict call, including an output-limit or provider terminal error, one fresh ephemeral minimal-reasoning attempt receives a fixed repair instruction plus the identical exact-head review packet.
If an attempt reaches the model but ends without exactly one well-formed verdict call, including an output-limit or provider terminal error, one fresh ephemeral low-reasoning attempt receives a fixed repair instruction plus the identical exact-head review packet.
The repair is attempted once per pass, its usage is included in the run economics, and a second protocol miss fails closed instead of selecting a convenient call or rotating to another reviewer.
Provider terminal-error diagnostics have credential-shaped values redacted, are whitespace-normalized and stripped of non-printable characters, and are limited to 512 characters before they reach operator-visible failure output.
The model decides the provider slot through an explicit mapping derived from the lane registry that maps each registered model to its own slot, maps `gpt-5.6-sol` to `openai-codex`, and refuses an unmapped model rather than guessing.
Expand Down
18 changes: 16 additions & 2 deletions tests/fm-crosscheck-azure.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2939,6 +2939,12 @@ scenario = os.environ["SCENARIO"]
effective = scenario
if scenario.endswith("-then-valid"):
effective = scenario.removesuffix("-then-valid") if len(captures) == 1 else "valid"
reasoning_effort = sys.argv[sys.argv.index("--thinking") + 1]
provider_reasoning_efforts = {
"low", "medium", "high", "xhigh", "max", "none", "adaptive",
}
if reasoning_effort not in provider_reasoning_efforts:
effective = "invalid-reasoning-effort"
if effective == "nonzero":
print("bounded fake provider failure", file=sys.stderr)
raise SystemExit(17)
Expand Down Expand Up @@ -3006,6 +3012,13 @@ elif effective == "terminal-error-secrets":
"secret='secret-visible'; access_token=access-visible; opaque="
+ "Z" * 48
)
elif effective == "invalid-reasoning-effort":
message["content"] = []
message["stopReason"] = "error"
message["errorMessage"] = (
"reasoning_effort must be low, medium, high, xhigh, max, none, or adaptive; "
f"received {reasoning_effort!r}"
)
print(json.dumps({"type": "turn_end", "message": message}))
print(json.dumps({"type": "agent_end"}))
if effective == "internal-retry":
Expand Down Expand Up @@ -3066,21 +3079,22 @@ def assert_launch(captured, account, prompt, schema, attempts=1):
active_prompt = prompt if index == 0 else prompt.parent / "repair-prompt.txt"
assert launch["argv"] == [
"--mode", "json", "--offline", "--provider", provider,
"--model", model, "--thinking", "xhigh" if index == 0 else "minimal",
"--model", model, "--thinking", "xhigh" if index == 0 else "low",
"--tools", "submit_crosscheck_verdict",
"--extension", str(extension),
"--system-prompt", system_prompt,
"--no-session", "--no-extensions",
"--no-skills", "--no-prompt-templates", "--no-themes",
"--no-context-files", "--no-approve", f"@{active_prompt}",
], launch["argv"]
assert "minimal" not in launch["argv"], launch["argv"]
assert launch["account"] == str(account), launch
assert launch["schema"] == str(schema), launch
if attempts == 2:
repair = captured[1]["prompt_text"]
assert repair.startswith("VERDICT PROTOCOL REPAIR"), repair
assert repair.endswith("PROMPT BY FILE"), repair
assert "fresh minimal-reasoning attempt" in repair, repair
assert "fresh low-reasoning attempt" in repair, repair
assert "submit_crosscheck_verdict exactly once" in repair, repair


Expand Down
Loading