test(dryrun): make the router-stage unknown-handler pins able to fail, and populate meta_ops (BACKLOG #1688, #1692) - #1228
Conversation
… (BACKLOG #1688) Two of the three M-7 pins stayed green with route_only's fail-closed deleted, because a downstream path reaches the same ERROR. The dry-run pin matched only the ghost name. With the guard gone, transform_one's registry.handlers[hname] raises KeyError a stage later and dry_run renders that as "router/handler error: 'ghost'" -- still ERROR, still carrying the name. It now asserts the router-stage wording instead. The runner pin asserted ERROR and no delivered file, and nothing about the stage. With the guard gone a routed row IS committed for the ghost handler and the transform worker's own missing-handler branch dead-letters it -- same disposition, same absent file. It now reads the queue and pins the whole footprint: one dead ingress row, no routed row, and a stored error naming the guard. Negative control: with the raise replaced by pass, all three pins fail.
…HI gate (BACKLOG #1692) Neither dry_run path passed meta_ops=outcome.meta_ops to its DryRunResult, so the field was empty whatever a Handler declared, and the CLI output dict had no meta_ops key at all. A Handler's SetMeta was therefore invisible to the dryrun CLI and the Test Bench, while the SetState beside it came through. Both construction sites now pass it, and the CLI emits meta_ops next to state_ops. A SetMeta key and value are both message-derived in the general case, so both are redacted unless --show-phi is passed, matching a state write exactly. MetaOpPreview's docstring claimed that gate throughout the window in which nothing reached it; it now says what is gated and records that. Tests pin both construction sites (the HL7 path and the raw non-HL7 path fail independently) and both arms of the CLI gate.
LANDER INSPECTION -- labelled self-review, not a peer reviewPosted under the korus The vacuous-pin claim is correct, and verifiable from the source without running anything
The runner half reproduces the same way through a different route: without the guard, routing commits The replacements are shape-exact, and that is the difference
The third pin calls The production half is a separate, real bug
That is this session's defect class in a third register: not a test that cannot fail, but a control The VerdictVerdict: merge. |
Two rows that both land in
tests/test_dryrun.py, in separate regions (BACKLOG #1688, #1692).BACKLOG #1688: which pins could not fail, and which can now
Three pins guard the router-stage fail-closed in
route_only-- theraise ValueErrorthat refuses a Router naming a handler the registry does not hold. Only one of them could fail if that raise went away.test_dryrun.py::test_route_only_unknown_handler_raisestest_dryrun.py::test_router_to_unknown_handler_is_errortest_wiring_engine.py::test_inbound_unknown_handler_dead_letters_at_ingressThe dry-run pin asserted
ERRORand"ghost" in result.error. Neither is unique to the router stage. Delete the raise and the message reachestransform_one, whoseregistry.handlers[hname]raisesKeyError('ghost');dry_run's catch-all renders that asrouter/handler error: 'ghost'-- stillERROR, still carryingghost. It now assertsreturned unknown handler 'ghost', which only the router stage emits.The runner pin asserted
ERRORand that no file was delivered, and nothing about the stage. Delete the raise and a routed row IS committed for the ghost handler; the transform worker's own missing-handler branch (pinned separately intest_adr0071_dispatch_wiring.py) dead-letters it -- the same terminal disposition and the same absent file. It now reads the queue and pins the whole footprint: exactly one deadingressrow, noroutedrow, and a stored error naming the guard. The whole footprint rather than a filtered probe, because a bare "no routed row" query cannot tell a fail-closed apart from a message that never got past the listener.Negative control, run locally
route_only'sraise ValueError(...)replaced bypass, then the three pins run:The runner pin's failure is the predicted shape, not a generic red:
The guard was then restored and the three pins re-run green.
#1688 is tests-only. When the raise fires, its text already flows verbatim into
result.error, so no engine change was needed. The workbook forecast listingpipeline/dryrun.pyfor this row was wrong.BACKLOG #1692: meta_ops
Neither
dry_runpath passedmeta_ops=outcome.meta_opsto itsDryRunResult, so the field was empty whatever a Handler declared, and the CLI output dict carried nometa_opskey. A Handler'sSetMetawas invisible to the dryrun CLI and the Test Bench while theSetStatebeside it came through.Both construction sites now pass it, and the CLI emits
meta_opsnext tostate_ops.PHI gating. A
SetMetakey and value are both message-derived in the general case -- a Handler is free to build either from a field -- so both are redacted unless--show-phiis passed, matchingstate_opsmember for member.MetaOpPreview's docstring asserted that gate throughout the window in which nothing reached it; it now states what is gated and records that window.Tests pin both construction sites separately (the HL7 path and the raw non-HL7
_dry_run_rawpath, so fixing one does not cover the other) and both arms of the CLI gate. The redaction test asserts the fixture actually declared aSetMetabefore asserting the absences, so it cannot pass vacuously.#1690's recorded block on this row is discharged --"declined"shipped in PR 1169 -- so no serialization work was needed.Checks
ruff format --check .ruff check .mypy messagefoundrypytest, 6 filesTest files run:
test_dryrun.py,test_cli.py,test_wiring_engine.py,test_adr0071_dispatch_wiring.py,test_ingress_guard_parity.py,test_checks.py. Enumerated as the two files this branch edits, the two the changed code is consumed by, plus the two the dispatch brief named as neighbouring pins on the same fail-closed.The full local suite was not run -- it does not finish in one turn on this box.
mypy: 22 errors, all pre-existing in this worktree's venv and none in a file this branch touches. They are
import-not-foundand downstreamno-any-returninauth/webauthn.py,transports/dicom.py,parsing/dicom/_deps.pyandparsing/fhir/_deps.py, caused by the optional extras (fhir,dicom,x12,xml,webauthn,vault) being absent from the local interpreter. CI installs them.pipeline/dryrun.pyand__main__.pyare clean.Hosted legs a reviewer must read
Every leg, since nothing here ran against the optional extras locally. Specifically:
pytestmatrix with[dev,harness,fhir,dicom,x12,xml,webauthn,vault]installed -- every extras-gated module removed itself at collection time locally, so those counts are absent from the numbers above;mypystrict on a runner that has the extras, which is the only place the 22 errors above can be confirmed as environmental;windows-service-smoke, which never runs locally.Open question
scripts/hooks/context-budget.ps1was already modified in this worktree at creation, from a cleanorigin/maincheckout -- rewritten to a 20-line disabled stub citing an owner decision dated today. Nothing in this branch touches it and it is deliberately not staged, so it is absent from both commits. Flagging it because something is rewriting that file into fresh worktrees, and a session that rangit add -Awould have carried it into an unrelated PR.