fix(hitl): planen et menneske signerer er TEKST, aldri en objekt-repr (BLOCKER-1, ORDRE 20260902T151931Z)
Fire steder gjorde `str(review.plan)` paa en MAF `Message` som ikke har noen `__str__` (maalt: `type(Message).__str__ is object.__str__`), saa BEGGE HITL-doerene viste og lagret `<agent_framework._types.Message object at 0x…>`: terminalen F4 spoer ved (`:1395`), de to opptakene i `plan_reviews` (`:1404` approve, `:1418` revise) og den PARKERTE spoersmaalsfila U12 (`:1478`) som er det ENESTE som krysser prosessgrensen. En ekspert som svarte `approve` signerte blindt. Fiksen er den eksisterende `_plan_text` (`:966`) paa alle fire; ingen ny hjelper. Fire asserts var gronne mot defekten fordi de var TRUTHINESS eller selv-sammenligning: `reviews[1]["plan"] != ""`, `waiting[0].plan`, og `reviews[0]["plan"][:40] in out` — den siste sammenlignet den samme repr-en med seg selv, saa de to flatene var enige mens begge var uleselige. Diskriminatoren er innhold som KUN kan komme av `Message.text`: MAF komponerer plan-meldingen rundt managerens svar ordrett (maalt), saa en sentinel i det skriptede svaret er til stede naar teksten ble tatt og fravaerende naar repr-en ble det. Sentinelen bor i et `reason`-felt fordi ingenting leser dem — aa nokle den til en ANSWER ville endret kjoringen den maaler. I tillegg en NEGATIV assert (` object at 0x` finnes ingen steder i stdout, artefaktet eller spoersmaalsfila), som fanger hele defektklassen og ikke bare denne ene. MAALT: fire mutasjoner, EN PER LINJE (ordrens ene samlede revert underteste — `:1418` er revise-grenens egen kopi og hadde ellers ikke noe roedt vitne): :1395 -> 1 roed (T2 stdout) · :1404 -> 2 roede · :1418 -> 1 roed (T1 alene) · :1478 -> 1 roed (async T9). Suite 2 failed (F15-diffen, KJENT) / 1078 passed / 5 skipped — uendret fra baseline. Golden `demo-transcript.stdout` BYTE-UENDRET (ea8c534773acdbe41ae68f2c55724d69aaf8be4f). Aerlighetsgrense, uttalt: `str(review.current_progress)` (`:1396`/`:1479`) er IKKE roert. Den er en `MagenticProgressLedger | None`, ikke en `Message`, og maalt renderer pydantic den lesbart — men i disse kjoringene er den `None`, saa terminalen skriver «progress so far: None». Det er en annen defekt og en annen ordre. Co-Authored-By: Claude <claude-opus-5>
This commit is contained in:
parent
e2d26c50ed
commit
4075b46a00
3 changed files with 84 additions and 11 deletions
|
|
@ -43,6 +43,18 @@ _BUNDLE_DIR = _REPO / "shared" / "examples" / "bygg-energi-mikro"
|
|||
_PID = "BYGG-KONTOR-NORD"
|
||||
_RUN_ID = "async-review"
|
||||
|
||||
#: See ``tests/test_plan_review_cli_door_loadbearing.py`` for the measurement behind this: MAF
|
||||
#: composes the task-ledger plan ``Message`` around the manager's answer verbatim, and ``Message``
|
||||
#: has no ``__str__``, so ``str(review.plan)`` renders ``<…Message object at 0x…>``. The sentinel
|
||||
#: can therefore appear only where the plan's TEXT was taken. Restated here rather than imported,
|
||||
#: because this file already carries its own copy of the whole scripted-reply block: the two
|
||||
#: scenarios park and resume differently and are meant to be readable apart.
|
||||
_PLAN_SENTINEL = "PLAN-TEXT-SENTINEL-4b1e"
|
||||
|
||||
#: The defect asserted NEGATIVELY — any default object repr on a surface an expert reads, not only
|
||||
#: this one.
|
||||
_REPR_LEAK = " object at 0x"
|
||||
|
||||
_PROPOSER_REPLY = json.dumps(
|
||||
{
|
||||
"measure": "LED-retrofit",
|
||||
|
|
@ -52,7 +64,7 @@ _PROPOSER_REPLY = json.dumps(
|
|||
)
|
||||
_MANAGER_REPLY = json.dumps(
|
||||
{
|
||||
"is_request_satisfied": {"reason": "r", "answer": True},
|
||||
"is_request_satisfied": {"reason": _PLAN_SENTINEL, "answer": True},
|
||||
"is_in_loop": {"reason": "r", "answer": False},
|
||||
"is_progress_being_made": {"reason": "r", "answer": True},
|
||||
"next_speaker": {"reason": "r", "answer": "hypothesiser"},
|
||||
|
|
@ -405,7 +417,23 @@ def test_a_parked_review_is_pending_until_its_own_answer_lands(tmp_path, capsys)
|
|||
|
||||
waiting = hitl.pending_plan_reviews(outbox, inbox)
|
||||
assert [p.run_id for p in waiting] == [_RUN_ID]
|
||||
assert waiting[0].plan, "an expert cannot answer a review that does not show them the plan"
|
||||
|
||||
# BLOCKER-1, on the ASYNCHRONOUS door. ``assert waiting[0].plan`` was truthiness, and a repr is
|
||||
# truthy: the register said an expert could answer while what it showed them was
|
||||
# ``<…Message object at 0x…>``. Both surfaces the expert actually reads are asserted — the
|
||||
# question file ``explore.py:1478`` writes, and the register that joins it to the inbox.
|
||||
question = _question(tmp_path)
|
||||
assert _PLAN_SENTINEL in question["plan"], (
|
||||
"the parked question is the ONLY thing crossing the process boundary; an expert answering "
|
||||
"days later can read nothing else, so it must carry the plan as text"
|
||||
)
|
||||
assert _REPR_LEAK not in question["plan"], (
|
||||
f"no default object repr may stand in for the plan: {question['plan'][:80]!r}"
|
||||
)
|
||||
assert _PLAN_SENTINEL in waiting[0].plan, (
|
||||
"an expert cannot answer a review that does not show them the plan — and a repr shows "
|
||||
"them nothing they can judge"
|
||||
)
|
||||
|
||||
_answer(tmp_path, decision="approve")
|
||||
inbox_file = tmp_path / "review-inbox" / f"{_RUN_ID}-plan-review-answer.json"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue