Skip to content

fix(OMN-19050): a FAIL supersede receipt no longer latches a receipt key forever - #1736

Merged
jonahgabriel merged 2 commits into
devfrom
jonah/omn-19050-supersede-fail-latch
Sep 21, 2026
Merged

jonahgabriel merged 2 commits into
devfrom
jonah/omn-19050-supersede-fail-latch

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

What this fixes

OMN-19050. A FAIL supersede record latched a receipt key forever, so a pull request could not clear OCC preflight by fixing the very test that failed.

Measured end to end on omnimarket#2751 on 2026-09-21. The product-repo receipt runner executed the declared behaviour-proof check at head bab99887, it failed for an environment reason (a depth-1 checkout with no tags), and it filed test_passes.supersede.2751.yaml with status: FAIL. The cause was fixed at 668a565b1 and the check re-executed green. The key still resolved FAIL, preflight still reported a non-pass receipt, the OCC companion could not merge, and the product PR could not merge behind it. The supersede filename was keyed on the PR number alone, so one pull request got exactly one executed attempt, ever.

Root cause, in this module

Two independent defects, both in validator_receipt_supersession.py.

  1. The correction record was invisible. The suffix pattern was \.supersede\.([^./]+)\.yaml$, which excludes a dot. An attempt-scoped record named <check>.supersede.<pr>.<n>.yaml matched nothing and never entered resolution. It was not outranked by the record it corrected; it was dropped silently, with no error. That is the same failure the previous widening of this pattern was written to fix, one character narrower.
  2. Ordering favoured the record being corrected. The tiebreak was int(suffix) if suffix.isdigit() else -1, so a dotted suffix ranked below every bare one. Even once visible, a correction lost to the record it was filed against whenever the two shared a created_at — which is reachable, because the runner stamps that field to whole seconds.

What this changes

Ordering is total by construction. Records sort by created_at first and by a dotted-numeric sequence key second. "2751" keys to (2751,) and "2751.0002" to (2751, 2), so a correction sorts strictly after the record it extends by plain tuple comparison. Two records for one key now resolve identically in any filesystem order, and a same-second pair still has exactly one winner.

A PASS supersedes a FAIL only as an independent observation. Ordering alone would let any later PASS erase any earlier FAIL, which turns an append-only chain into a retry-until-green channel: re-file often enough and the gate stops biting. So a PASS clears the latest prior FAIL only when its replacement carries a different commit_sha — something actually changed between the two runs. A PASS re-filed at the FAIL's own head is the same observation restated and clears nothing.

The two-actor rule is unchanged and deliberately not re-implemented here. ModelDodReceipt already downgrades a PASS whose verifier equals its runner to ADVISORY, so an author still cannot pass their own receipt through a supersession. A second copy of that rule in this module would be a second place for it to drift.

Deferral vocabulary is unchanged. The honesty validator still refuses a PASS carrying it, and a supersede record is not a route around that. Pinned by a test here so the claim is falsifiable rather than asserted.

Backward compatibility

A single-component suffix keys to a 1-tuple and compares exactly as the prior int(suffix) did, so every existing single-consumer chain — the overwhelming majority — resolves as before. Non-numeric suffixes such as 2010-head stay out of the sequence tiebreak, matching pre-OMN-16432 behaviour. The 86 existing tests across the four receipt and eligibility modules pass unchanged.

Verification

Red first. Before the fix, seven of the eight new tests failed, and the two failure shapes were the two defects exactly: assert None is not None where the dotted record was invisible, and FAIL is not PASS where the bare-suffix FAIL outranked the dotted PASS.

tests/unit/validation/test_receipt_supersession_fail_latch_omn_19050.py  8 passed
tests/unit/validation/test_occ_per_entry_hashing.py                      25 passed
tests/unit/validation/test_occ_merge_eligibility.py                      44 passed
tests/unit/validation/test_occ_awaiting_runner_receipt_omn_16859.py      10 passed

86 passed in 8.09s. mypy --strict clean on the changed module, ruff format and ruff check clean, full pre-commit chain green on commit and push.

The new tests cover the ticket's acceptance criteria 1 through 3 plus three negative controls: a PASS at the same head stays FAIL, a self-attested PASS stays non-PASS, two FAILs stay FAIL, and a PASS carrying deferral vocabulary stays refused.

Scope note

The ticket spans three repositories and this is one of three PRs. This one repairs the resolver. The runner that mints the record (omnimarket) still refuses to write a second attempt, and the OCC receipt-hardening gate's active-candidate selection skips a non-digit token for the same reason this module did. Those are the other two. Merge order is this PR first, then the OCC gate, then the runner — the readers must accept the new shape before a producer emits it.

Evidence-Ticket OMN-19050

Evidence-Ticket: OMN-19050
Evidence-Source: OCC#10719

Measured on omnimarket#2751. The product-repo receipt runner executed a
declared check, it failed for an environment reason, and it filed a
supersede record with status FAIL. The cause was fixed and the check
re-executed green, but the key kept resolving FAIL and the OCC companion
could not merge. One pull request got exactly one executed attempt, ever.

Two defects in this module produced that, and both are fixed here.

The suffix pattern excluded a dot, so an attempt-scoped record named
<check>.supersede.<pr>.<n>.yaml matched nothing and never entered
resolution. It was not outranked by the record it corrected -- it was
invisible, with no error. That is the same silent-drop failure the prior
widening of this pattern was written to fix, one character narrower.

Ordering then ranked a non-numeric suffix at -1, below every bare one, so
even once visible an attempt-scoped record lost to the record it was filed
to correct. Records now sort by created_at first and by a total
dotted-numeric sequence key second, so two records for one key resolve
identically in any filesystem order, and a same-second pair -- reachable,
since created_at is stamped to whole seconds -- still has one winner.

Ordering alone would let any later PASS erase any earlier FAIL, which turns
an append-only chain into a retry-until-green channel. So a PASS supersedes
the latest prior FAIL only as an independent observation: its replacement
must carry a different commit_sha. A PASS re-filed at the FAIL's own head
changes nothing and clears nothing.

The other half of the two-actor rule is unchanged and deliberately not
re-implemented here: ModelDodReceipt already downgrades a PASS whose
verifier equals its runner to ADVISORY, so an author still cannot pass
their own receipt through a supersession.

Single-component suffixes key to a 1-tuple and compare exactly as the prior
int(suffix) did, so every existing single-consumer chain resolves as before.

Evidence-Ticket OMN-19050
@jonahgabriel
jonahgabriel enabled auto-merge (squash) September 21, 2026 19:13
jonahgabriel pushed a commit to OmniNode-ai/onex_change_control that referenced this pull request Sep 21, 2026
…ibase_core#1736 (#10719)

* evidence(OMN-19050): author OCC companion for OmniNode-ai/omnibase_core#1736

OCC companion by node_pr_lifecycle_fix_effect (OMN-13317 F1 / OMN-13990 / OMN-14285). Product PR head 5e96acb7c71f85d7decbfae6ee1b64416179625d.

* evidence(OMN-19050): self-bind OCC#10719 + rebind contract_sha256

---------

Co-authored-by: omnimarket-bot <bot@omninode.ai>
…his side

onex_change_control's receipt-hardening gate must order supersede records
identically to this resolver: it decides which record is VALIDATED and this
module decides which record is ACTIVE for merge eligibility. The two
disagreeing means a record passes the gate and is not the one that decides
the merge.

That repo pins omnibase-core from the registry, so it cannot import
_sequence_key until the pin carries this release. Both ends therefore assert
the same literal table from their own repository. Replacing the duplication
with a direct import once the pin moves is a named follow-up in that repo's
test module.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant