Repository navigation
feat(OMN-18988): re-emit a lab-pass receipt for any sha the converged image contains whose only failures were binding-class - #3921
jonahgabriel wants to merge 1 commit into
Conversation
…e only failures were binding-class Part one (OMN-18976, #3914) stopped a queued merge being recorded as a terminal FAIL, which left the sha with no receipt at all: strictly better, still not an answer. A later run has to speak for it, and this is that run. THE WINDOW, and why it is not ancestry. A verify run reads the lane twice, at the revision it started on and at the one it converged to. Every merge strictly between those two was queued behind this one. That set, and only that set, is what a convergence may answer for -- emphatically not every ancestor of the converged revision, because the lane contains thousands of shas it never individually converged on. A queue is not a history. Every unresolved case yields an empty window, because a wrong PASS is the one outcome this must not introduce while removing a wrong FAIL. THE WIDENING. A receipt can also exist and say FAIL for a reason that is not about the software. deployed_revision and probe_generation_bound are binding-class: they answer whether the run could bind its own observation to the sha, not whether the lane was healthy. Such a receipt is now eligible, under three refusals, each with its own test: deployed_revision must be INDETERMINATE and never FAIL, because a FAIL is a measured mismatch and an INDETERMINATE an unreadable one; every failing check must be binding-class, so one failing health check refuses the whole receipt; and a passing receipt is left alone. THE BINDING IS RE-ESTABLISHED, never inherited. The converged run may itself have failed to bind -- that is the case this exists for -- so its answer is not evidence for anybody else. Each candidate is resolved fresh, in the verify job that already holds the lane, across the container revision label, the agent's /health loaded_code_sha and the lane's /ready version, in that order. First to establish containment wins. A surface readable but not containing the candidate does not veto a later one: that is one lane fact disagreeing, not a verdict. All three silent means the candidate is omitted and named in the log, and no receipt is written -- silence is not a pass. The winning surface is recorded as converged_via, so a reader gets which lane fact bound the sha. The evidence is re-keyed, not re-taken: the converged run's probes were read against the image that contains each of these shas, so they are the reads a lab pass for that sha would have made at the only moment it could. A failing convergence re-emits a failure. Rebuilt on dev after #3914 squashed; carries OMN-18990's queue-lag staleness arm untouched. Replaces #3915.
|
No OCC evidence companion was minted for this PR. this PR is already bound to OCC#10656; its evidence companion exists and nothing needs authoring To clear this: Nothing to do — the companion already exists. Reported by |
|
OCC autobind did not rebind this PR. This PR's evidence-source stamp line names To clear this: Reported by |
…ibase_infra#3921 (#10667) * evidence(OMN-18988): author OCC companion for OmniNode-ai/omnibase_infra#3921 OCC companion by node_pr_lifecycle_fix_effect (OMN-13317 F1 / OMN-13990 / OMN-14285). Product PR head ebad7030132f997658fc6293b43287374bc09481. * evidence(OMN-18988): self-bind OCC#10667 + rebind contract_sha256 --------- Co-authored-by: omnimarket-bot <bot@omninode.ai>
|
| Surface | Meaning | Blocks merge? |
|---|---|---|
| Review threads | Per-finding, posted by the reviewer | No (informational) |
Hostile Review Thread Gate |
Deterministic: unresolved hostile-reviewer threads exist | Fails until resolved (not yet a required context) |
degraded verdict |
Fewer than 2 models succeeded (infra) | No |
Powered by omniintelligence.review_pairing.cli_review — multi-model adversarial review: qwen3-review, qwen3-review-b, glm-review (OMN-8468/OMN-8524/OMN-17492)
|
Superseded by a replacement on the same branch. Not a code problem: this description was copied from #3915 and carried #3915's change-control evidence line, so when autobind minted the companion for #3921 the description held two. The receipt gate refuses two; the body-stamp guard refuses any edit that removes one, including the fenced form the receipt gate itself recommends, because it discounts a fenced line. No edit satisfies both. Filed as OMN-19015. The replacement carries a description that never held the stale line. Nothing is disabled and nothing is relaxed. |
Pull request was closed
Ticket: OMN-18988, a child of OMN-18976. Option (1) per the team lead's ruling, then widened by the team lead's scope extension (the section The widening below). Part one merged as
e5af728ee(#3914), so this branch is rebuilt on current dev and the diff is part two alone.What part one left
#3910stopped a queued merge being recorded as a terminal FAIL. That left the sha with no receipt at all, which is strictly better and still not an answer: the delivery gate keeps refusing it, and its own verify run is over. Only a later run can speak for it.The window, and why it is not ancestry
A verify run reads the lane twice: at the revision it started on, and at the one it converged to. Every merge that landed strictly between those two was queued behind this one — its own verify run found the lane still on the earlier revision and exited without a receipt. That set, and only that set, is what a convergence may answer for.
The bound is the whole point. It is emphatically not every ancestor of the converged revision: the lane has run for weeks and contains thousands of shas it never individually converged on, and emitting for those would manufacture receipts for merges nothing observed. A queue is not a history.
Every unresolved case yields an empty window — no previous revision, no converged revision, a lane that never moved, an unreadable comparison. Emitting nothing leaves the gate refusing exactly as it does today; a wrong PASS is the one outcome this must not introduce while removing a wrong FAIL.
The evidence is re-keyed, not re-taken
The converged run's probes were read against the image the lane came to run, and that image contains each of these shas — so they are precisely the reads a lab pass for that sha would have made, at the only moment it could. Re-probing here would read a newer lane state and attribute it to an older sha, which is the weaker claim and the easier one to get wrong.
A failing convergence re-emits a failure. Every sha in the window inherits what the run found. A re-emission that quietly upgraded to PASS would be the worst possible outcome of this ticket, and there is a test for exactly that direction.
Provenance, and two refusals
Each re-emitted receipt carries
converged_via, so a receipt written by a sha's own run and one written by a later convergence stay tellable apart. Both are honest; they are not the same evidence. The field is written only when non-empty, on the same terms asnode_inventory, soRECEIPT_VERSIONis unchanged and every receipt already in flight parses byte-identically — there is a test asserting an ordinary receipt's wire shape is unchanged.The re-key refuses two things outright: onto its own sha, which would race two artifacts of one name from one job; and from a receipt that is itself a re-emission, because provenance that chains is provenance nobody can read.
Shape
A matrix job, because the number of queued shas is discovered at run time and a
uses:step cannot be looped.fromJSONover the verify job's output, and an empty array skips the job — the common case, since the lane usually converges on the very next merge.Five gates refused the first draft, each fixed at cause
Worth listing, because every one of them was right:
if-no-files-found: erroruploader undeclared, and then its pre-upload assertion missingif: always()timeout-minutesubuntu-latestinstead of the fork-aware selectorsetup-uvinstead of the shared CI Python environmentThe matrix leg keys its receipt directory by sha as well as run and attempt, because two legs sharing one directory would upload each other's receipt.
Verification
tests/ci/andtests/unit/ci/are green whole -- 4453 passed, 7 skipped -- withmypy --strictandruffclean on both changed modules. The window and re-key have nineteen tests, all boundaries: two-sha queue in, converged sha excluded, outside-the-window excluded, empty window, both missing endpoints, unresolvable comparison, lane that never moved, dedup. The failing-convergence direction is among them: a FAIL convergence re-emits a FAIL. The widening adds twenty-five: the eligibility decision over every failure shape (binding-only, one health check among binding ones,deployed_revisionFAIL rather than INDETERMINATE, already-passing, unreadable), and surface resolution over each of the three surfaces winning, a readable non-containing surface not vetoing a later one, and all three silent yielding no candidate.The last two commits also re-point the two OMN-18144 tests the part-one rename moved, which splits 1 and 2 were correctly red on. Re-pointed, not relaxed: each now asserts the not-a-lane-failure property directly rather than via whichever enum member carries it.
Split 12 is red on the committed lane census aging past its 3-day threshold. Unrelated, pre-existing on dev, left alone.
The semantic, stated plainly
A re-emitted PASS for
Smeans: the converged image containingSpassed the lane'schecks at
X. It does not mean the lane was observed applyingSby itself, and itnever can —
Swas queued behind another deploy and no run watched it land alone. Thatis what a lab pass for a queued-behind merge can ever mean, and
converged_viais on thereceipt so a reader gets that sentence rather than reconstructing it from timestamps.
The widening
Part one and the first draft of this PR answered for one class only: a sha that was
queued behind another deploy and therefore emitted no receipt at all. The trigger is
now wider, and the widening is what makes this the path that unblocks a cut.
A receipt can also exist and say
FAILfor a reason that is not about the software. Thetwo checks
deployed_revisionandprobe_generation_boundare binding-class: theyanswer whether the run could bind its own observation to the sha under test, not whether
the lane was healthy. A run that could not read the lane's revision records a binding
failure and the sha is then refused delivery forever, on evidence that was never about
the code. That receipt is now eligible.
Three refusals bound it, and each is a separate test:
deployed_revisionmust beINDETERMINATE, neverFAIL. AFAILis a measuredmismatch, the lane demonstrably running something else; an
INDETERMINATEis anunreadable one. Treating the two alike would re-emit over a real mismatch.
is refused by name. A receipt whose health checks genuinely failed is never re-emitted,
under any combination.
re-emission.
The binding is re-established, never inherited
The converged run may itself have failed to bind — that is the case this widening exists
for — so its answer is not evidence for anybody else. Each candidate's binding is resolved
fresh, in the verify job that already holds the lane, across three surfaces tried in
order: the container's revision label, the deploy agent's
/healthloaded_code_sha,and the lane's
/readyversion.The first surface that establishes containment wins. A surface that is readable but
does not contain the candidate does not veto a later one: it is one lane fact
disagreeing, not a verdict, and a single stale label must not suppress a correct answer
the agent already has. If all three are silent the candidate is omitted and named in
the log rather than answered for — no receipt, the gate keeps refusing, and the refusal
is visible where a reader of that run will find it. Silence is not a pass.
The surface that won is written to the receipt as
converged_via, so a reader getswhich lane fact bound the sha rather than a bare assertion that something did.
Replaces #3915
#3915 was stacked on the part-one branch. When part one landed as a squash its commits
were re-minted, so the stacked branch conflicted with dev on
check_dev_lane_staleness.pyand could not be merged or rebased in place: the shared-clone guard refuses both inside a
worktree (OMN-18974, filed). Rebuilt on current dev by re-applying the part-two diff with
a three-way apply.
That apply is also the proof the two changes coexist: OMN-18990 landed its queue-lag
staleness arm in the same module while this branch was open, and it is present here
untouched --
MAX_QUEUE_LAG_AGE_SECONDSand_stale_lag_reasonare in the file besidethe window and the binding resolver. No version bump; no package behaviour changes.
Evidence-Ticket: OMN-18988
Evidence-Source: OCC#10667
The companion below belongs to #3915, which this pull request replaces. It is retained
only because the branch-protection guard refuses an edit that drops a change-control
evidence line, and the receipt gate reads the first such line, which is the one above.
Evidence-Source: OCC#10656