Repository navigation
feat(OMN-19038): no contract in the corpus populates requirements[].acceptance[] — the DoD serializer's input surface is empty - #2766
Conversation
…s into the contract model The transcriber splits a ticket body into criterion units, hashes each one, and writes a binding naming the label and the digest. It holds the criterion sentence in unit.text at that moment and drops it, so binds_ac carries labels, ac_bindings carries digests, and the words survive nowhere. Measured on onex_change_control origin/dev at 605ebfd729: 9,300 contracts, 370 carrying a binds_ac entry, 1 carrying requirements. The OMN-18270 serializer renders requirements[].acceptance[] into the ticket body and treats the contract as the model, and had almost no input anywhere in the corpus. ModelTranscribedBinding gains a required statement taken from the same unit the digest is computed over, and both contract head templates gain a top-level requirements block rendered from the same record list that renders binds_ac. One filtered list, two renderings, so the declared set and the bound set are equal by construction rather than by agreement between two renderers that drift. Wired on both producers because OMN-18332 landed on one, missed the other, and seven of the next nine companions minted unbound. An empty record list renders no key at all, so a ticket that declared no falsifiers mints the contract it always minted. RED measured against the unmodified branch point: 10 failed, 2 passed, the two being the byte-identity guards that must pass on both sides. Onex-Lane: omn19038-requirements-producer Onex-Session: c8ec7a9acf01451eb44da687c35b27ba
✅ Architectural Review — PASSEDErrors: 0 What this checks
No architectural violations found. Static architectural lint — no model inference (OMN-14176). |
✅ Hostile Reviewer — PASSEDBlocking findings (critical/error): 0 Gate semantics
Powered by omniintelligence.review_pairing.cli_review — node-based adversarial review via HandlerLlmCliSubprocess (OMN-8524) |
|
Pre-PR proof: surface=mac-scratch:omn19038-requirements-producer receipt=/Users/jonah/Code/omni_home/.onex_state/prepr-proof/OMN-19038-mac-scratch-20260921T223344Z/receipt.txt sha256=84de0c94bda165f51ea82b9ec4fcff76e5f8506cf435e3f52d189268d3beb8e8 result=PASS Three isolated environments, no shared site-packages, every read a separate process: the branch build at The same body and the same emit script under both builds mint the identical bindings,
The control does not merely do less; it refuses by name for the reason the ticket was filed. Two things the receipt records that this summary would otherwise flatter. Against a realistic body — one that already carries a hand-written criteria section — both sides still refuse, and only the rule discriminates: the branch has moved on from "the model is missing" to Separately measured: PR stays DRAFT and unarmed. |
…nimarket#2766 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/35663713047
…imarket#2766 (#10754) * evidence(OMN-19038): author OCC companion for OmniNode-ai/omnimarket#2766 OCC companion by node_pr_lifecycle_fix_effect (OMN-13317 F1 / OMN-13990 / OMN-14285). Product PR head 4bec87098d2187d1ded1f73e874bf45084e9843c. * evidence(OMN-19038): self-bind OCC#10754 + rebind contract_sha256 * evidence(OMN-16859): executed test_passes receipts for OmniNode-ai/omnimarket#2766 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/35663713047 --------- Co-authored-by: omnimarket-bot <bot@omninode.ai> Co-authored-by: onexbot-occ-writer[bot] <307849072+onexbot-occ-writer[bot]@users.noreply.github.com>
# Conflicts: # pyproject.toml # uv.lock
Closes OMN-19038.
What is broken
The autobinder reads a ticket's body, splits it into acceptance-criterion units, hashes each one, and writes a binding naming the criterion's label and its digest. It holds the criterion's actual sentence in memory at that moment, in
unit.text, and drops it.Nothing downstream can recover it.
binds_acis labels only.ac_bindingspins acriterion_hash, which is a digest and not the words. So acceptance criteria live only in the tracker, which inverts the direction of authority the OMN-18270 serializer is built on: the contract is meant to be the model and the body a rendering of it.Measured on
onex_change_controlorigin/devat605ebfd729on 2026-09-21:contracts/binds_acrequirements370 live bindings reference criterion text that exists nowhere in the contract corpus, and
onex-serialize-ac-sectionhas essentially no input anywhere.The fix
ModelTranscribedBindinggains a requiredstatement, populated from the sameunitthe digest is taken over, and the two contract head templates gain a top-levelrequirements:block rendered from the same record list that rendersbinds_ac.The load-bearing property is not that a
requirementskey appears. A block built from a second filter would satisfy that and would be worse than the absence it replaced, because the serializer refuses a contract binding a label its model does not enumerate (ac_section_unenumerated_binding) and the OMN-18333 ruleac_binding_criterion_unboundrefuses the converse. A disagreement between the two would be invisible at mint time and permanent afterwards.So both keys are rendered from one filtered list — label present, falsifier declared, and the consumer's reader resolves the label — which makes the declared set and the bound set equal by construction rather than by agreement between two pieces of code that will drift.
TestDeclaredSetEqualsBoundSetasserts that as a set identity over the parsed contract, because two presence checks pass on any overlap at all.statementis required with no default. A default of empty would let a binding render a model that declares a criterion with no content, which reads to the serializer as an enumerated criterion and is a worse failure than the missing key.Wired on both producers, deliberately
OMN-18332 is the precedent and the reason: that transcription landed on the compute producer, was never wired into the born producer, and seven of the next nine companions minted with no bindings at all. A model emitted by one producer and not the other reproduces that exactly, one layer up, and a corpus census would show it only as "some contracts have requirements".
test_both_producers_emit_the_identical_blockcompares the two rendered blocks for equality, so a second differently-spelled renderer fails too.The repair-row renderer passes the empty string, by construction and not by omission: it keeps only the tail from the evidence marker onward, so anything in the head's preamble is discarded. Appending one evidence row to an existing contract does not re-author that contract's model.
RED, measured before the fix and not inherited
The new test file was run against the unmodified branch point
d75f913c6:The two that passed are the regression guards — a ticket with no falsifiers must mint a byte-identical contract — which correctly pass before and after. Every defect test failed.
GREEN after:
134 passedacrosstests/unit/occ, and584 passed, 5 skippedacross the emitter, golden-fixture and yamlfmt-stability suites.mypy --strictclean on both changed modules.ruff formatandruff checkclean.Negative controls
Five, because a renderer that bought the headline by enumerating everything would pass a presence check:
binds_acnorrequirements— this is the population the transcriber has always refused to bind, and the model must agree or it declares a criterion nothing claims;AC01case, read verbatim here and canonicalised toAC1there — appears in neither;requirementskey at all, on both producers, so the overwhelming majority of companions are byte-identical to what they minted before;criterion_unitsread independently from the same body, so writing the label, the falsifier or an empty string into it fails;Formatter safety
Criterion text is prose and routinely long. Each
statementrenders throughrender_check_value_field, which emits a literal block scalar for any value the formatter would refold — a plain scalar there gets the formatter's sentinel injected into the value and the OMN-15479 contamination ratchet then rejects the file, correctly.Proven against the real binary rather than argued: a contract rendered from a two-criterion body with realistic long prose, formatted with
yamlfmtv0.21.0 under the actualonex_change_controlconfig, is byte-identical before and after (sha256prefix7403ed9028d879c0both readings), the statements parse back intact, and no sentinel appears anywhere in the file.canonical_criterion_textcollapses whitespace runs, so a statement is always single-line, which is what the literal-block rendering assumes. That property is pinned by a test here rather than left to be discovered when a multi-line criterion breaks the block.Honest limits
This populates the model for contracts minted from now on. The 370 existing bound contracts are untouched and are separate work under OMN-19046.
It also does not let the serializer render the tickets that already exist.
_has_unmanaged_ac_headingrefuses any body carrying an acceptance-criteria heading outside its sentinels, and on a 14-ticket even spread across the bound corpus all 14 carry one and none carries the sentinel. That is filed as OMN-19116 and is not attempted here.Evidence-Ticket: OMN-19038
Evidence-Source: OCC#10754