Repository navigation
fix(OMN-19056): derive the futility verdict instead of putting it on a wire the release refuses - #2758
Conversation
…a wire the release refuses OMN-19016 (#2755, squash 1d95761, dev 0.4.175) added no_rung_can_satisfy as a pydantic FIELD on ModelQualityGateResult. That model declares extra="forbid", the last released omnimarket is v0.4.166 and its copy of the model has no such field, so a deployed consumer at the release refuses the payload at its decode boundary: "no_rung_can_satisfy: Extra inputs are not permitted". The OMN-18868 wire-compatibility gate caught it on its first day. This is OMN-18852 one field later, and worse in one respect. published_at was None by default and could be excluded while unset, which bought a quiet window. This field defaults to False, False serialises, so it went on the wire on every dump from the moment it merged. There was no quiet window at all. WHY THE OMN-18852 MECHANISM IS NOT THE MECHANISM HERE. exclude_if (model_delegate_skill_request.py:261) does not answer this, and the gate says so in its own words: it grades the MAXIMAL shape a producer can emit, so a new optional field is a finding even when the producer excludes it while unset, and it has no waiver list. It reads model_fields, not a dump, so an exclude_if predicate is invisible to it by construction. It is right on both counts: an excluded-while-unset field is refused the moment anything sets it, and setting this one is the entire reason it exists. WHAT LANDS INSTEAD, AND WHY NOTHING IS LOST. The value was never independent information. The producer computed it as a predicate over failure_reasons -- is every reason refusing this response a SHAPE_REFUSED one -- and failure_reasons is a plain tuple[str, ...] that v0.4.166 already declares, already accepts, and carries the prefix through untouched. So the field is withdrawn and the verdict is derived by a property on the model. Both consumer read sites keep their attribute access unchanged (port_local_delegation_dispatch.py:1385 and :1511, handler_delegation_workflow.py:2841), and a consumer too old to know either keeps the pre-OMN-19016 climb-always behaviour, exactly as the withdrawn field's own description promised. A property, deliberately not a computed_field. A computed_field would put the key back on the wire AND be invisible to the gate, which reads model_fields and never model_computed_fields. A re-break the gate cannot see is worse than the original defect, so the shape is pinned by a test rather than by a comment. The model also TOLERATES the withdrawn key on input and drops it, which is the half that unblocks step 2. A producer sitting on dev 0.4.175 exactly keeps working, and the next release becomes the consumer that accepts the key -- so when step 2 re-adds it as a real emitted field, the released consumer's own model_validate no longer raises and the gate passes. Withdrawing the field without this validator would leave step 2 blocked by the same gate forever. OMN-19016 IS NOT REVERTED. The all-not-any derivation invariant, the zeroed score, the SHAPE_REFUSED verdict, the TERMINATE decision and both read sites are unchanged; only the serialisation moved. Its two behavioural modules stay green beside the new one. EVIDENCE, MEASURED. The gate's own whole-tree control, run off CI against this worktree before the fix, over all ten wire modules: WIRE_COMPAT_REFUSED with exactly one finding, "the released consumer ModelQualityGateResult at v0.4.166 REFUSES the key 'no_rung_can_satisfy': Extra inputs are not permitted". After the fix the same command returns WIRE_COMPAT_PASS. The new module reproduces that refusal byte for byte against a replica of the released decode boundary, pinned from git show v0.4.166 rather than derived from the model under test, with both controls a replica needs: it refuses the key, and it accepts the same payload without it. Its constants are spelled as literals so the module still COLLECTS against the parent commit -- the red is behavioural, not an ImportError proving only that a symbol is missing. Blast radius is latent, not realised. The lab dev lane on .201 read 0.4.172 across four containers and was force-recreated to 0.4.175 at 18:47:53Z, so both sides there now carry the field and neither refuses it. A scan of 900 messages across five onex.dlq.omnibase-infra topics found zero occurrences of the key and zero "Extra inputs are not permitted". The break bites a consumer pinned at 0.4.166 or older, which is every consumer the next release reaches. 1647 passed, 6 skipped across tests/unit/delegation and the delegate-skill, call-effect, routing-compute and delegation-projection node suites. mypy --strict clean over 3326 source files. Step 2, re-emitting the key once a tolerant release is deployed, is a child ticket of OMN-19056 with the floor version named. It is deliberately not built here.
|
Pre-PR proof: surface=105 receipt=$OMNI_HOME/.onex_state/prepr-proof/omn19056-105-20260921T193025Z/receipt.txt sha256=2c08cabcfe122effcd3f60c78d12b2db3e9daa4927ac9c475363bca573a0dae5 result=PASS 22 assertions, 0 failures, 1 negative control. Lease The surface is better than a replica for this change. The dogfood runtime runs omnimarket The branch's module was then copied over site-packages in both runtime containers and both were restarted, so every assertion below describes a process that actually loaded it. Identical results in
Both containers were restored from in-container backups, re-verified by sha256 against the original Not proven, recorded rather than omitted. No in-container gate evaluation was run and it would have been wrong to claim one: the 0.4.155 tree carries a Receipt path deviation: written to durable local state rather than a |
dev advanced to 0.4.178 with #2744 while this branch was in review, so the only conflict is the version line in pyproject.toml and uv.lock. Resolved to 0.4.182, re-enumerated live across every open head at the moment of the resolution rather than reused from the first push: dev holds 0.4.178, #2753 holds 0.4.179, #2757 holds 0.4.180 and #2756 holds 0.4.181, so the 0.4.180 this branch pushed with had been taken by a peer in the interim.
✅ Architectural Review — PASSEDErrors: 0 What this checks
No architectural violations found. Static architectural lint — no model inference (OMN-14176). |
…nimarket#2758 The declared checks were executed for real in the OmniNode-ai/omnimarket checkout at the PR head by the product-repo OCC receipt runner, and the results written append-only into this companion. Producer-minted PENDING receipts are superseded by a net-new record; no existing receipt was edited. Ticket: OMN-16859 (AC3b) Onex-Workflow: OCC Receipt Runner Onex-Run: https://github.com/OmniNode-ai/omnimarket/actions/runs/35646184269
✅ Hostile Reviewer — PASSEDBlocking findings (critical/error): 0 Gate semantics
Powered by omniintelligence.review_pairing.cli_review — node-based adversarial review via HandlerLlmCliSubprocess (OMN-8524) |
dev advanced to 0.4.181 with #2756. The version line in pyproject.toml and uv.lock is again the only conflict. Held at 0.4.182, re-enumerated live: dev 0.4.181, #2757 0.4.180, #2753 0.4.179, and every remaining open head below dev, so 0.4.182 is still the first free slot and needed no move this time.
…2758 OCC companion by node_pr_lifecycle_fix_effect (OMN-13317 F1 / OMN-13990 / OMN-14285). Product PR head 3d700b30bb8275d1fab6f70489408418b42c197b.
…nimarket#2758 The declared checks were executed for real in the OmniNode-ai/omnimarket checkout at the PR head by the product-repo OCC receipt runner, and the results written append-only into this companion. Producer-minted PENDING receipts are superseded by a net-new record; no existing receipt was edited. Ticket: OMN-16859 (AC3b) Onex-Workflow: OCC Receipt Runner Onex-Run: https://github.com/OmniNode-ai/omnimarket/actions/runs/35647387831
…imarket#2758 (#10724) * evidence(OMN-19056): author OCC companion for OmniNode-ai/omnimarket#2758 OCC companion by node_pr_lifecycle_fix_effect (OMN-13317 F1 / OMN-13990 / OMN-14285). Product PR head 3d700b30bb8275d1fab6f70489408418b42c197b. * evidence(OMN-19056): self-bind OCC#10724 + rebind contract_sha256 * evidence(OMN-16859): executed test_passes receipts for OmniNode-ai/omnimarket#2758 The declared checks were executed for real in the OmniNode-ai/omnimarket checkout at the PR head by the product-repo OCC receipt runner, and the results written append-only into this companion. Producer-minted PENDING receipts are superseded by a net-new record; no existing receipt was edited. Ticket: OMN-16859 (AC3b) Onex-Workflow: OCC Receipt Runner Onex-Run: https://github.com/OmniNode-ai/omnimarket/actions/runs/35647387831 --------- Co-authored-by: omnimarket-bot <bot@omninode.ai> Co-authored-by: onexbot-occ-writer[bot] <307849072+onexbot-occ-writer[bot]@users.noreply.github.com>
|
Pre-PR proof: surface=mac-scratch:omn19056 receipt=$OMNI_HOME/.onex_state/prepr-proof/OMN-19056-mac-scratch-omn19056-20260921T203844Z/receipt.txt sha256=d102ca9b1d4843a35b4a1e12a395636ed10cabb75d45f1ef3b512ff09237b30d result=PASS Standing, stated first. This arm ran after this PR merged. It is not the proof that gated the merge; that was the The gap it closes. Every released-consumer check so far, here and in the OMN-18868 gate itself, resolved v0.4.166 from the git tag by materialising that tree with It had not diverged, and that is the finding: Method. Three processes, because two versions of one package cannot share an interpreter.
Same script, same maximal shape, same published wheel. Only the producer commit differs, which is what makes the pass mean something. End to end through the real gate. The lab dev lane runs 0.4.175, so a delegation there would exercise the pre-fix code and prove nothing about this change. Recorded as a limitation rather than run and presented as evidence; it becomes a real test once the lab carries 0.4.182 or later. The gate evaluation was driven in the merged tree instead: the shape rule returns its Incidental finding, unrelated to this change and not fixed here. A full dependency resolution of the dev producer against the published index fails today: published Scratch environments and both detached worktrees are lane-named and removed. No container, cluster, database, image or deployment was touched. |
|
Pre-PR proof countersign: receipt sha256=2c08cabcfe122effcd3f60c78d12b2db3e9daa4927ac9c475363bca573a0dae5 actor=prepr-countersign verdict=COUNTERSIGNED-WITH-LIMITS I recomputed the receipt digest and it matches the value quoted above and in the ledger RELEASE row. The receipt names a commit that is not the one that merged: two merges of dev landed after the proof and the merged head is a later commit. I did not take the author's word that this is harmless. I read the proven module at both commits and it is byte-identical, content digest I also reproduced the central claim myself, on a different released version than the receipt used, in a scratch virtual environment holding the wheel the index serves today. That released model is declared to forbid extra keys, carries no such field, dumps exactly twenty keys, and refuses the gated key with an extra-forbidden error while the identical payload without the key decodes cleanly. Same variable, opposite outcomes, and it agrees with the receipt's own count. One thing to fix rather than repeat: the receipt prints the patched digest truncated to sixteen characters. A truncated digest cannot be checked, and it only checked out here because I could recover the full value from the repository. |
|
Pre-PR proof countersign: receipt sha256=d102ca9b1d4843a35b4a1e12a395636ed10cabb75d45f1ef3b512ff09237b30d actor=prepr-countersign verdict=COUNTERSIGNED I recomputed the receipt digest and it matches the value quoted above and the ledger RELEASE row. This arm closes a gap I had independently flagged when countersigning the earlier one, and it closes it the right way: every released-consumer check in the earlier arm and in the compatibility gate resolves the released version from the git tag, and none of them read the artifact the index actually serves. Showing that the published wheel and the tag are byte-identical is a negative finding, and it is worth as much as a positive one, because every other gate in the fleet assumes that equivalence without measuring it. The negative control is correctly isolated. The same script, the same maximal emitted shape and the same published wheel are held fixed while only the producer commit changes, and the pre-fix commit is refused with an extra-forbidden error naming the field while the merged commit is accepted at exactly twenty keys. I reproduced the consumer side myself, before reading this receipt, in a separate scratch environment holding the same wheel from the index. I measured the same three properties independently: extra keys forbidden, no such field declared, and exactly twenty fields. My reading agrees with this receipt on all three. The standing paragraph is also accurate, since this arm ran after the merge and says so first rather than presenting itself as the gate. |
A field that no released consumer can decode
OMN-19016 (
#2755, squash1d957613d, dev0.4.175) addedno_rung_can_satisfyas a pydantic field onModelQualityGateResult. That model declaresextra="forbid". The last released omnimarket is v0.4.166 and its copy of the model has no such field, so a deployed consumer at the release refuses the payload outright at its decode boundary:The OMN-18868 wire-compatibility gate caught it on its first day, on
#2751, in its own words:This is OMN-18852 one field later, and worse in one respect.
published_atwasNoneby default and could be excluded while unset, which bought a quiet window. This field defaults toFalse,Falseserialises, so it went on the wire on every dump from the moment it merged. There was no quiet window at all.Blast radius, measured rather than asserted
The break is latent, not realised, and saying so precisely is the point of measuring it.
0.4.1720.4.175Both sides of the lab now carry the field, so neither refuses it there, and a peer lane separately established that the 18:50Z delegation outage was a container health race rather than this break. What the field breaks is a consumer pinned at
0.4.166or older, which is every consumer the next release reaches. So the urgency comes from what a release does, not from what is dead-lettering now.One correction to the reported picture, since it changes where the gate is red: the gate script exists only on
#2751, so the Wire Compatibility Gate check-run appears on that PR alone and on no other open head. It becomes fleet-wide the moment#2751merges, which is an argument for landing this first.Why the OMN-18852 mechanism is not the mechanism here
The OMN-18852 remedy is a serialisation predicate that omits a field while it is unset (
model_delegate_skill_request.py:261). It does not transfer, and the gate says so in its own docstring: it grades the maximal shape a producer can emit, so a new optional field is a finding even when the producer excludes it while unset, and there is no waiver list. It reads the model's declared fields rather than a dump, so such a predicate is invisible to it by construction.The gate is right on both counts. An excluded-while-unset field is refused the moment anything sets it, and setting this one is the entire reason it exists. Forcing the older pattern here would have produced a change that reads like a fix and leaves the break in place.
What lands instead
The value was never independent information. The producer computed it as a predicate over
failure_reasons— is every reason refusing this response aSHAPE_REFUSEDone — andfailure_reasonsis a plaintuple[str, ...]that v0.4.166 already declares, already accepts, and carries the prefix through untouched.So the field is withdrawn and the verdict is derived by a property on the model (
model_quality_gate.py). Both consumer read sites keep their attribute access unchanged —port_local_delegation_dispatch.py:1385and:1511,handler_delegation_workflow.py:2841— and a consumer too old to know either keeps the pre-OMN-19016 climb-always behaviour, exactly as the withdrawn field's own description promised.A
property, deliberately not acomputed_field. A computed field would put the key back on the wire and be invisible to the gate, which reads declared fields and never computed ones. A re-break the gate cannot see is worse than the original defect, so the shape is pinned by a test rather than by a comment.The model also tolerates the withdrawn key on input and drops it. That half is what unblocks step 2: a producer sitting on dev
0.4.175exactly keeps working, and the next release becomes the consumer that accepts the key, so when step 2 re-adds it as a real emitted field the released consumer's own validator no longer raises and the gate passes. Withdrawing the field without this validator would leave step 2 blocked by the same gate forever.OMN-19016 is not reverted
The
all-not-anyderivation invariant, the zeroed score, theSHAPE_REFUSEDverdict, theTERMINATEdecision and both read sites are unchanged. Only the serialisation moved. Its two behavioural modules stay green beside the new one, and this was checked rather than assumed.A plain revert was considered and rejected: it would have thrown away a correct behavioural fix to solve a carriage problem, and the carriage problem has a remedy that costs nothing.
The gate's own whole-tree control, run off CI against this worktree over all ten wire modules, returns
WIRE_COMPAT_REFUSEDwith the single finding quoted above before the change andWIRE_COMPAT_PASSafter it.The new module reproduces that refusal byte for byte against a replica of the released decode boundary. The replica's accepted key set is pinned from the released source rather than derived from the model under test, because a set computed from the model agrees with any change that model makes, including the one that caused the break. It carries both controls a replica needs: it refuses the key, and it accepts the same payload without it, so a replica that accepted everything could not read as a pass.
Its two constants are spelled as literals rather than imported, so the module still collects against the parent commit. The red is behavioural — the released consumer refuses a real emitted payload — not an import error proving only that a symbol is missing.
tests/unit/delegationplus the delegate-skill, call-effect, routing-projection and delegation-projection node suites: 1647 passed, 6 skipped.mypy --strictclean over 3326 source files.Delegation through the deployed dev lane, for the record: run
78fb8a90-7a93-4d1e-9b9b-cfb63ccf82e1, correlation74e4eae8-3a55-4bae-b25b-ffdc4e817cdd, status completed, one attempt,quality_score1.000,escalation_count0. Summarization subtask run76c35447-88ab-4c75-a94f-e0bd2b5233ee, correlation2eba5d94-ac21-4d31-8b94-5dea1458d5ca, status completed.Not in this PR
Re-emitting the key as a real field once a tolerant release is deployed is step 2, filed as a child of OMN-19056 with the floor version named. It is deliberately not built here, because building it now would reintroduce exactly the defect this PR removes.
A note for the adjacent work, recorded because the gate does not cover it: this gate grades two validation error types only, extra-key and missing-key, and replays each field with one placeholder string. A new enum member on a wire-reachable field produces neither of those error types, so it passes the gate green and still breaks a released consumer at the same boundary. OMN-19004 is in that class.
Version
0.4.182, re-enumerated live across every open head at the moment of the resolution rather than reused from the first push.The first push went out at
0.4.180.#2744then merged, taking dev to0.4.178, which conflicted this branch on the version line alone and is why no workflow ran on the first head — GitHub does not run pull-request checks on a branch it cannot merge. While that was being resolved a peer took0.4.180on#2757and another took0.4.181on#2756, so the resolution moved to0.4.182.State at the resolution: dev
0.4.178,#27530.4.179,#27570.4.180,#27560.4.181, and every remaining open head below dev. The merge commit resolvespyproject.tomlanduv.lockand touches nothing else; the delegation suites and the gate control were both re-run against the new base,1647 passed, 6 skippedandWIRE_COMPAT_PASS.Ticket: OMN-19056. Parent: OMN-19016. Caught by: OMN-18868.
Evidence-Ticket: OMN-19056
Evidence-Source: OCC#10724