fix(explore): a plan review never shows an expert the word None and calls it progress
[skip-docs]
PM addendum 4 to order 20260902T151931Z-250257273.
PlanReviewRequest.current_progress and ParkedExploration.current_progress were
both built with str(review.current_progress). The value is a
MagenticProgressLedger | None and is None in every run measured so far, so
str() produced the four characters None, the renderer's truthiness guard found
them non-empty, and the terminal printed a "progress so far" section whose only
content was None. The same four characters went into
{run_id}-plan-review.json -- the one thing that crosses the process boundary in
the asynchronous door, where nobody can ask what it meant.
_progress_text is the ONE conversion, beside _plan_text: absent becomes the
empty string, never "None". The data layer states absence by being absent, as
Bundle.skipped's empty tuple does. The TERMINAL states it in words -- this is
the one surface where omission is wrong, because silence at a gate somebody
signs is exactly what PlanReviewInputError already refuses for EOF.
Measured, and it changed the test: reverting both sites left ONLY a
source-inspection arm red, which is a lint and not a gate, because the first
arms construct a PlanReviewRequest themselves and never enter either site. Each
site now has a behavioural witness that drives the real door -- the terminal for
the synchronous one, the parked question FILE for the asynchronous one.
Load-bearing measured, five mutations, all red against the WHOLE suite, each
with its own signature: both sites reverted (3 red) - synchronous site alone
(2) - parked site alone (2) - the terminal goes silent again (2) - the
placeholder always fires, swallowing a real ledger (1, the control alone).
Green control 1202 passed / 5 skipped; golden demo-transcript.stdout unchanged
(ea8c534773acdbe41ae68f2c55724d69aaf8be4f).
The recorder wiring in resume_exploration is untouched, per the order.
This commit is contained in:
parent
b799cc527f
commit
7552c0ef73
2 changed files with 290 additions and 5 deletions
|
|
@ -514,9 +514,15 @@ def terminal_plan_reviewer(
|
|||
print(f"\nPLAN REVIEW #{request.index + 1}{stalled}", file=sink)
|
||||
print("--- the plan the exploration would run ---", file=sink)
|
||||
print(request.plan, file=sink)
|
||||
if request.current_progress.strip():
|
||||
print("--- progress so far ---", file=sink)
|
||||
print(request.current_progress, file=sink)
|
||||
print("--- progress so far ---", file=sink)
|
||||
# Stated, never omitted, and never a repr of absence. An expert about to sign has to be
|
||||
# able to tell "the loop has not reported progress yet" from "this section was dropped".
|
||||
print(
|
||||
request.current_progress
|
||||
if request.current_progress.strip()
|
||||
else "(no progress ledger yet)",
|
||||
file=sink,
|
||||
)
|
||||
while True:
|
||||
print(
|
||||
f'Answer "{_APPROVE_ANSWER}" to sign it off, '
|
||||
|
|
@ -1087,6 +1093,19 @@ def _plan_text(content: Any) -> str:
|
|||
return str(getattr(content, "text", "") or "")
|
||||
|
||||
|
||||
def _progress_text(content: Any) -> str:
|
||||
"""The progress ledger as text — and ABSENCE as the empty string, never ``"None"``.
|
||||
|
||||
``MagenticProgressLedger | None`` went through a bare ``str()`` at both construction sites, so
|
||||
in every run measured so far (the value is ``None`` until the manager has emitted a ledger) the
|
||||
expert was shown, and the parked question file stored, the four characters ``None``. That is
|
||||
worse than saying nothing: it looks like content. The data layer states absence by being
|
||||
absent — ``Bundle.skipped``'s empty-tuple rule — and the TERMINAL is where it is put in words,
|
||||
because silence at a gate somebody signs is what ``PlanReviewInputError`` already refuses.
|
||||
"""
|
||||
return "" if content is None else str(content)
|
||||
|
||||
|
||||
def _absorb(
|
||||
result: Any,
|
||||
*,
|
||||
|
|
@ -1512,7 +1531,7 @@ async def _drive(
|
|||
PlanReviewRequest(
|
||||
index=len(plan_reviews),
|
||||
plan=_plan_text(review.plan),
|
||||
current_progress=str(review.current_progress),
|
||||
current_progress=_progress_text(review.current_progress),
|
||||
is_stalled=_truthy(review.is_stalled),
|
||||
)
|
||||
)
|
||||
|
|
@ -1604,7 +1623,7 @@ async def _park(
|
|||
checkpoint_id=str(latest.checkpoint_id),
|
||||
index=len(trace.plan_reviews),
|
||||
plan=_plan_text(review.plan),
|
||||
current_progress=str(review.current_progress),
|
||||
current_progress=_progress_text(review.current_progress),
|
||||
is_stalled=_truthy(review.is_stalled),
|
||||
bundle_dirs=tuple(bundle_dirs),
|
||||
contract=contract,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue