feat(major2): --proposal-review answers the review at the terminal; four refusals by name, EOF stops the run [skip-docs]
Ordre 20260904T173146Z-8102814273-from-portfolio-optimiser, steg 6 av 10.
(a) argparse --proposal-review. (b) rader i BEGGE partisjonene (report_forbidden og
single_only) - report-modus og portefoelje returnerer over dispatchen, saa en utelatelse
er et STILLE DROPP, ikke en nekt (F4-gapet). (c) TRE navngitte nekter i EN topp-nivaa-blokk
if args.proposal_review: - plasseringen er MAALT, ikke plassert paa oeyemaal: naboen
--scripted-replies/--live-dry-run er nostet under if args.scripted_replies, og
--explore/--live-dry-run under if args.explore, saa under noen av dem ville et bart
--live-dry-run --proposal-review falt rett gjennom til dry-run-dispatchen og droppet flagget.
(d) reviewer bygget paa KALLSTEDET + except ProposalReviewInputError -> "run stopped:" rc 1,
en DISTINKT kanal fra "run refused:". (e) proposal_review_notice printes fra kjoeringens EGEN
post. (f) _load_scripted_replies' aerlighetsgrense navngir review-stien.
--resume KOMPONERER (A3 verifisert av en arm, ikke utsatt): resume-blokka gir mandatet og
faller gjennom til SAMME full-run-dispatch.
RODT foer impl: 9 armer. T13 og T16 kjoerer i et BARN (P4). Nekt-armene kjoerer in-process
med _default_factory som REISER - ved exit-koden ser en nekt etter forbruket identisk ut med
en foer (oekt 57).
TO ARMER BLE FALSIFISERT AV MAALINGEN FOER de kunne gate noe:
(1) T18s rc-0-kontroll avslorte at F4-testens ledger-fixtur ({"entries": []}) faar rc 1 av
SavingsLedger.load ("must be a JSON array"), ikke av partisjonsraden - armen ville vaert
groenn mot en fjernet rad. Fixturen er naa en JSON-array, og kontrollen beviser at argv-en
ellers ville blitt AKSEPTERT.
(2) notice-null-armen ga BudgetExceeded i stedet for en avvist kjoering: et to-stegs
proposer-manus mot max_attempts=3 faller til default-svaret, som aldri parser, og rundeboka
fyrer - noeyaktig aerlighetsgrensen _load_scripted_replies uttaler, reprodusert ved uhell.
Manuset har naa like mange steg som forsoek.
Planens T19 er foldet inn i T13 og uttalt: "et bart, uskriptet flaggparse" ville kalt en
levende modell, saa argparse-vitnet er barnets egen unrecognized-arguments-assert.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
c391fb5d67
commit
fedf9897c4
2 changed files with 437 additions and 3 deletions
|
|
@ -104,7 +104,10 @@ from portfolio_optimiser.mcp_tools import (
|
|||
from portfolio_optimiser.proposal_review import (
|
||||
ProposalReview,
|
||||
ProposalReviewer,
|
||||
ProposalReviewInputError,
|
||||
proposal_review_notice,
|
||||
proposal_reviews_payload,
|
||||
terminal_proposal_reviewer,
|
||||
)
|
||||
from portfolio_optimiser.provenance import ProvenanceStamp
|
||||
from portfolio_optimiser.reference_domain import Project, load_reference_projects
|
||||
|
|
@ -2099,7 +2102,12 @@ def _load_scripted_replies(
|
|||
start, by the fresh client ``generate_via_llm`` builds. A script whose first step is a tool
|
||||
call therefore answers the generation call with a ``function_call`` too, which will not parse.
|
||||
That is the operator's to write correctly: guessing which steps were "meant for" which call
|
||||
site would be repair, and this loader validates."""
|
||||
site would be repair, and this loader validates.
|
||||
|
||||
The same limit binds ``--proposal-review`` (MAJOR-2), where it is easiest to trip: each
|
||||
``revise`` the piped answers request buys ONE more generation attempt, so a proposer script
|
||||
one entry short of them degrades to the selector's default reply, which never parses — and the
|
||||
round ledger fires on the parse-retry. Write the script long enough."""
|
||||
try:
|
||||
raw = json.loads(Path(path).read_text(encoding="utf-8"))
|
||||
except FileNotFoundError as exc:
|
||||
|
|
@ -2190,6 +2198,20 @@ def main(argv: list[str] | None = None) -> int:
|
|||
"round trip is recorded in {run_id}-exploration.json, feedback verbatim. Input that ends "
|
||||
"without an answer is an error, NEVER a sign-off",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--proposal-review",
|
||||
action="store_true",
|
||||
help="MAJOR-2 synchronous HITL door: after the deterministic validator ACCEPTS a "
|
||||
'candidate, show it AT THIS TERMINAL and read "approve" or "revise <what to change>". A '
|
||||
"revision buys ONE more attempt under the existing attempt/round budget — no new loop and "
|
||||
"no new cap — and the words go into the next hypothesis prompt verbatim. Every answer is "
|
||||
"recorded in {run_id}-proposal-reviews.json. Input that ends without an answer STOPS the "
|
||||
"run, NEVER a sign-off. An approve is not an expert verdict (that still arrives via "
|
||||
"--decision/--rationale or the verdict inbox). Refused with --portfolio, --report, "
|
||||
"--live-dry-run, --proposals-from-mandate and --checkpoint-dir (pass it at --resume "
|
||||
"instead); it composes with --explore --plan-review, which reads two doors from one stdin "
|
||||
"in sequence",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--checkpoint-dir",
|
||||
default=None,
|
||||
|
|
@ -2429,6 +2451,9 @@ def main(argv: list[str] | None = None) -> int:
|
|||
# this list is silently dropped rather than refused — which is the whole reason the
|
||||
# list enumerates every distinguishable flag instead of the ones that would misbehave.
|
||||
"--plan-review": args.plan_review,
|
||||
# MAJOR-2, and for the identical reason: report mode returns above the run dispatch, so
|
||||
# an omission here is a SILENT DROP — the door would be accepted and never asked.
|
||||
"--proposal-review": args.proposal_review,
|
||||
# The three U12 flags, listed for exactly that reason: report mode returns before the
|
||||
# resume dispatch, so an omission here is a silent drop, not a refusal.
|
||||
"--checkpoint-dir": args.checkpoint_dir is not None,
|
||||
|
|
@ -2507,6 +2532,11 @@ def main(argv: list[str] | None = None) -> int:
|
|||
# --explore" would tell an operator who wrote --portfolio --plan-review to add the one
|
||||
# flag this mode also refuses.
|
||||
"--plan-review": args.plan_review,
|
||||
# MAJOR-2: one terminal, and a portfolio pass interleaves its waves as coroutines —
|
||||
# several projects' candidates would arrive at the same prompt with nothing to tell
|
||||
# them apart. BY NAME, like its neighbours: falling through to a --bundle-dir
|
||||
# requirement would tell the operator to add a flag this mode also refuses.
|
||||
"--proposal-review": args.proposal_review,
|
||||
# And the asynchronous half of the same door, on the same side of the partition and by
|
||||
# NAME for the same reason.
|
||||
"--checkpoint-dir": args.checkpoint_dir,
|
||||
|
|
@ -2674,6 +2704,38 @@ def main(argv: list[str] | None = None) -> int:
|
|||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
# --- MAJOR-2. THREE modes return ABOVE generation, so each would accept the door and then
|
||||
# never ask it — the silent-drop class ``report_forbidden`` exists for. The block is at
|
||||
# FUNCTION scope on purpose, measured rather than placed by eye: the neighbouring
|
||||
# ``--scripted-replies``/``--live-dry-run`` refusal is nested under ``if args.scripted_replies
|
||||
# is not None`` and the ``--explore``/``--live-dry-run`` one under ``if args.explore is not
|
||||
# None``, so under either guard a bare ``--live-dry-run --proposal-review`` would fall straight
|
||||
# through to the dry-run dispatch and drop the flag. Every message carries prose the others do
|
||||
# not share, and each fires before the first model call.
|
||||
if args.proposal_review:
|
||||
if args.live_dry_run:
|
||||
print(
|
||||
"run refused: --proposal-review and --live-dry-run contradict (the dry run stops "
|
||||
"before the first model call, so no candidate ever reaches a reviewer) — drop one",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
if args.proposals_from_mandate:
|
||||
print(
|
||||
"run refused: --proposal-review has nothing to answer under "
|
||||
"--proposals-from-mandate (that mode settles the commission deterministically and "
|
||||
"never generates a candidate) — drop one",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
if args.checkpoint_dir is not None:
|
||||
print(
|
||||
"run refused: --proposal-review and --checkpoint-dir: a parked exploration "
|
||||
"returns before any candidate exists, so the review would never be asked — pass "
|
||||
"--proposal-review at --resume instead",
|
||||
file=sys.stderr,
|
||||
)
|
||||
return 1
|
||||
if args.resume is not None:
|
||||
if args.explore is not None:
|
||||
print(
|
||||
|
|
@ -3323,9 +3385,23 @@ def main(argv: list[str] | None = None) -> int:
|
|||
client_factory=scripted_client_factory,
|
||||
mandate=mandate,
|
||||
mcp_servers=mcp_servers,
|
||||
# Built at the CALL SITE, never inside the library (the ``--plan-review``
|
||||
# precedent): the terminal is the operator's, and ``run_project`` must stay
|
||||
# answerable by a persona, a test double or nobody at all.
|
||||
proposal_reviewer=(
|
||||
terminal_proposal_reviewer() if args.proposal_review else None
|
||||
),
|
||||
)
|
||||
),
|
||||
)
|
||||
except ProposalReviewInputError as exc:
|
||||
# A DISTINCT channel from ``run refused:`` below, and the class is what routes it here.
|
||||
# The argv was fine and the run had already spent tokens, so "refused" would mislabel it;
|
||||
# and a ``ValueError``-shaped error would sit one frame from ``_fetch_parsed``'s
|
||||
# ``except (ValidationError, ValueError, TypeError)`` and be captured as a parse failure
|
||||
# instead. ``run_project``'s ``finally`` has already written the review record.
|
||||
print(f"run stopped: {exc}", file=sys.stderr)
|
||||
return 1
|
||||
except (ValueError, FileNotFoundError, ValidationError, BudgetExceeded) as exc:
|
||||
# Structured refusal (rc 1, no traceback) for the full-run path: run_project's fail-fast
|
||||
# loaders (contracts, load_dimension, outbox run_id guard) surface here as one clean line.
|
||||
|
|
@ -3361,6 +3437,12 @@ def main(argv: list[str] | None = None) -> int:
|
|||
fold_notice = unkeyed_verdicts_notice(result.unkeyed_verdicts)
|
||||
if fold_notice is not None:
|
||||
print(fold_notice)
|
||||
# MAJOR-2: what the human answered, read off the run's OWN record rather than off argv — so
|
||||
# stdout and ``{run_id}-proposal-reviews.json`` cannot disagree. ``offered`` IS argv, because
|
||||
# "a reviewer was offered and never consulted" is a fact only the flag can supply.
|
||||
review_notice = proposal_review_notice(result.expert_revisions, offered=args.proposal_review)
|
||||
if review_notice is not None:
|
||||
print(review_notice)
|
||||
# The settlement against the commission (Trekk A4). Empty without a mandate, so an
|
||||
# un-commissioned run prints exactly what it printed before.
|
||||
settlement = settle(result.coverage)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue