Skip to content

feat(OMN-14168): RUNTIME_OPS_READBACK no-PR runtime-ops evidence gate - #1651

Merged
jonahgabriel merged 4 commits into
devfrom
jonah/omn-14168-runtime-ops-readback-gate
Jul 9, 2026
Merged

jonahgabriel merged 4 commits into
devfrom
jonah/omn-14168-runtime-ops-readback-gate

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Jul 9, 2026 •

Copy link
Copy Markdown
Contributor

OMN-14168 — RUNTIME_OPS_READBACK no-PR runtime-ops evidence gate (omnimarket half)

Implements the omnimarket half of the RUNTIME_OPS_READBACK class per docs/plans/2026-07-08-nopr-runtime-ops-evidence-class.md. Gives a genuine, independently-verified, no-source-change runtime-ops fix (kubectl patch / live restart / config repair — no repo diff, no PR) a closure path without re-opening the self-attestation hole.

Surface A — DurableEvidenceGate (node_dod_verify)

  • EnumDurableEvidenceCheck += RUNTIME_OPS_READBACK.
  • New pure services/runtime_ops_readback.py verifies a PASS receipt set against every guardrail: G1 verifier≠runner, G2a no PR + no RUNTIME_OPS/PR mixing, G2b mutation_verb in the governed omnibase_core allowlist, G3 no_source_change + non-empty readback (+ optional freshness), G4 prod target fails closed (OMN-13418 grant NOT waived; UNKNOWN fails closed), G5 prevention_followup linked.
  • evaluate() branches at the Check-2 boundary: when every PASS receipt is evidence_class == RUNTIME_OPS and none carries a pr_number, it runs the readback check instead of CONTRACT_CITES_MERGE_COMMIT; a mixed set is refused. Check 1 (RECEIPT_TRACKED) and Check 5 (DONE_CLASS_LABEL requiring RUNTIME_OBSERVED) are unchanged. Check 3 uses the runtime-ops contract stub's runtime_readback key so the contract-on-governance-ref invariant stays intact.

Surface B — close gate (node_linear_triage)

  • EnumCloseEvidenceKind += RUNTIME_OPS_READBACK.
  • RuntimeOpsReadbackProbe + RuntimeOpsReadbackSubprocessProbe (fail-closed, git-backed) verify a tracked typed receipt end-to-end.
  • _apply_runtime_ops_readback constructs the close evidence and routes through the single _mark_done chokepoint — never from a Linear label alone (G6).

Deferred (out of scope, per operator)

  • Surface C (L1 hook linear_done_verify.py) → OMN-13856; runtime-observed is NOT added to is_exempt().
  • Autogen-tick caller wiring left as a hook point (probe + _apply staged and directly tested).

dod_evidence (verified locally against the sibling omnibase_core 0.46.7 worktree)

Because omnimarket CI pulls the released omnibase_core (0.46.6), local proof used the sibling 0.46.7 build (UV_NO_SYNC=1 + extracted wheel):

  • mypy --strict — Success, 5 changed files, 0 errors.
  • ruff format / ruff check — clean.
  • pre-commit on changed files — all hooks pass, incl. the DurableEvidenceGate regression hook.
  • 289 tests pass across tests/unit/nodes/node_dod_verify + tests/nodes/node_linear_triage + tests/integration/node_dod_verify, incl. 30 new Surface-A acceptance/abuse (test_runtime_ops_readback_gate.py) and 22 new Surface-B probe/apply (test_omn_14168_runtime_ops_readback_close.py) tests. Acceptance regression accepts a well-formed readback and rejects each abuse (self-attested, PR-bearing, git-verb, label-only/untracked, prod-without-grant, stale, mixed, missing prevention).

⚠️ BLOCKED-ON-CORE-RELEASE (cross-repo sequencing)

This PR imports omnibase_core EnumEvidenceClass.RUNTIME_OPS + validation.runtime_ops_verb_loader, which ship in omnibase_core 0.46.7 (companion PR OmniNode-ai/omnibase_core#1405). omnimarket CI resolves the released omnibase_core, so verify / mypy will be RED until 0.46.7 is released. This is expected — Codex sequences core release → then this merges.

occ-preflight will also be RED pending a NET-NEW OCC evidence companion (Evidence-Source: OCC#); per feedback_no_self_authored_evidence I do not author my own companion. Do NOT merge from here; Codex owns the queue.

Closes OMN-14168

Evidence-Source: OCC#3740

Evidence-Ticket: OMN-14168

@coderabbitai

coderabbitai Bot commented Jul 9, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 1 minute

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 75fdf602-b17f-4ac2-8b3a-0b7829f490ac

📥 Commits

Reviewing files that changed from the base of the PR and between 865ea21 and 1fc0110.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (11)
  • .github/workflows/cr-thread-gate-caller.yml
  • .github/workflows/cr-thread-gate.yml
  • pyproject.toml
  • src/omnimarket/nodes/node_dod_verify/models/model_durable_evidence_gate.py
  • src/omnimarket/nodes/node_dod_verify/services/durable_evidence_gate.py
  • src/omnimarket/nodes/node_dod_verify/services/runtime_ops_readback.py
  • src/omnimarket/nodes/node_linear_triage/handlers/handler_linear_triage.py
  • src/omnimarket/nodes/node_linear_triage/services/close_evidence_gate.py
  • tests/nodes/node_linear_triage/test_omn_14168_runtime_ops_readback_close.py
  • tests/test_omn_13836_no_git_url_overrides.py
  • tests/unit/nodes/node_dod_verify/test_runtime_ops_readback_gate.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jonah/omn-14168-runtime-ops-readback-gate

Comment @coderabbitai help to get the list of available commands.

jonahgabriel added a commit that referenced this pull request Jul 9, 2026
…fresh-ticket companion)

Residual follow-up to #1653. The residual hypothesis ("OccAutobindAdapter only
SYNCS an existing OCC contract and no-ops when a fresh ticket has none — the
contract-CREATION path may be required to mint for a first-time ticket") is
REFUTED against live code:

- The create-if-absent branch already exists and is wired live:
  adapter_occ_autobind.py:285-294 writes contracts/<ticket>.yaml via
  _CONTRACT_TEMPLATE when `not contract_path.is_file()`, and the live
  OccAutobindAdapter has been injected in _ensure_sub_handlers since facb4e8
  (2026-06-24, #1392) — never a noop on the merge_sweep path.
- run_git uses check=True and re-raises CalledProcessError (occ_git_transport.py),
  so a failed OCC push RAISES → fix_applied=False → not counted. A mint failure
  can never surface as "fixed but no companion". The only clean-return-no-mint
  path is the idempotency guard (already Evidence-Source: OCC#-bound), which
  #1653 now gates via the OccCompanionVerifier read-back.

The genuine gap was COVERAGE: the sole end-to-end test (TestAutobindForcePush)
forced Path.is_file=True (the SYNC path), so nothing locked the CREATE path and
the residual could not be falsified empirically.

This adds TestAutobindCreatesContractForFreshTicket (2 tests), verifying the
EFFECT (not the call), mirroring the #1653 mutation-test style:
- fresh ticket (is_file=False) → a net-new contracts/<ticket>.yaml is WRITTEN
  with the contract template, the auto/* companion branch is force-pushed, and
  Evidence-Source: OCC#<n> is patched back onto the product PR.
- existing contract (is_file=True) → SYNC binds receipts + pushes + patches
  WITHOUT rewriting the contract (OMN-13888 whole-file-hash: never modify an
  existing contract).

Mutation-verified: reverting the `if not contract_path.is_file()` create guard
(→ `if False`) makes test_fresh_ticket_mints_net_new_contract_and_pushes fail
(only receipts written, no contract) — the test genuinely guards the mint.

dod_evidence:
- env -u PYTHONPATH uv run pytest tests/unit/nodes/node_pr_lifecycle_fix_effect/test_adapter_occ_autobind.py: 26 passed
- node_pr_lifecycle_fix_effect + node_pr_lifecycle_orchestrator test dirs: 274 passed
- mutation (revert create guard): fresh-ticket test FAILS as designed, source restored clean
- ruff format + ruff check: clean; mypy --strict fix_effect node: Success (19 files)
- pre-commit (changed file, OMNIBASE_INFRA_PATH set): all hooks pass
- Live fresh-mint end-to-end proof: NOT run — no safe target (only open omnimarket
  PRs are #1653 [self-authoring prohibited] and #1651 [already bound OCC#3740]);
  merge_sweep has no single-PR scope. proof_class=code-only for the live link;
  create path is code-verified + mutation-locked. OCC companion for OMN-14173 is
  owed (Codex authors it per feedback_no_self_authored_evidence).
jonahgabriel added a commit that referenced this pull request Jul 9, 2026
… — verify pushed companion, not the call (#1653)

* fix(OMN-14173): fail-closed prs_fixed accounting for OCC autobind arm — verify pushed companion, not the call

merge_sweep --fix-only reported prs_fixed=2 on omnimarket #1651/#1652 while
authoring ZERO OCC companions (a false-success of the exact class the tooling
targets). Root cause = accounting, not routing.

Trace:
- Routing (STEP 2A): _block_reason_for_fix already routes occ-preflight/eligibility
  (with a ticket) to RECEIPT_EVIDENCE_SOURCE_AUTOBIND via the OCC-evidence subset
  match (handler_pr_lifecycle_orchestrator.py) — the OMN-13990 follow-up already
  landed this. The pre-supplied "{verify/verify}-only" diagnosis was stale vs HEAD.
  Re-asserted with a fresh witness test.
- Accounting (STEP 2B, the real defect): HandlerPrLifecycleFix.handle set
  fix_applied=True whenever _route() returned without raising — including the
  no-op / idempotent / short-circuit paths that push NO companion. _fix_one then
  counted prs_dispatched=1 off fix_applied.

Fix (fail-closed, verify the effect not the call):
- New OccCompanionVerifier reads GitHub back after the autobind adapter runs and
  confirms all three: product PR body carries Evidence-Source: OCC#<n>, that OCC
  PR is OPEN, and the auto/* companion branch exists on the OCC remote. Any
  missing evidence / resolution error fails CLOSED (verified=False).
- HandlerPrLifecycleFix gains occ_companion_verified on its result, set from the
  verifier for the autobind arm. Default verifier is fail-closed (proves nothing
  → counts nothing); the merge-sweep orchestrator injects the live verifier.
- _fix_one gates prs_fixed for RECEIPT_EVIDENCE_SOURCE_AUTOBIND on
  occ_companion_verified, never on fix_applied. All other block reasons keep
  their existing dispatch semantics (no regression to merge arm, OccContractAdapter,
  CI/code/coderabbit arms).

Tests (STEP 3, executed): new test_occ_companion_fail_closed_accounting.py proves
(i) occ-preflight/eligibility routes to the autobind arm, (ii) an unverified /
unpushed companion is NOT counted in prs_fixed (fail-closed), and the happy path
counts exactly once. Live-adapter wiring test asserts the orchestrator injects the
real OccCompanionVerifier.

dod_evidence:
- env -u PYTHONPATH uv run pytest tests/ -m "not kafka": 12271 passed, 60 skipped
- new test_occ_companion_fail_closed_accounting.py: 7 passed
- ruff format + ruff check --fix: clean
- mypy --strict on both nodes: Success, no issues in 27 files
- pre-commit (scoped to changed files, OMNIBASE_INFRA_PATH set): all hooks pass

* test(OMN-14173): lock OccAutobindAdapter create-if-absent mint path (fresh-ticket companion)

Residual follow-up to #1653. The residual hypothesis ("OccAutobindAdapter only
SYNCS an existing OCC contract and no-ops when a fresh ticket has none — the
contract-CREATION path may be required to mint for a first-time ticket") is
REFUTED against live code:

- The create-if-absent branch already exists and is wired live:
  adapter_occ_autobind.py:285-294 writes contracts/<ticket>.yaml via
  _CONTRACT_TEMPLATE when `not contract_path.is_file()`, and the live
  OccAutobindAdapter has been injected in _ensure_sub_handlers since facb4e8
  (2026-06-24, #1392) — never a noop on the merge_sweep path.
- run_git uses check=True and re-raises CalledProcessError (occ_git_transport.py),
  so a failed OCC push RAISES → fix_applied=False → not counted. A mint failure
  can never surface as "fixed but no companion". The only clean-return-no-mint
  path is the idempotency guard (already Evidence-Source: OCC#-bound), which
  #1653 now gates via the OccCompanionVerifier read-back.

The genuine gap was COVERAGE: the sole end-to-end test (TestAutobindForcePush)
forced Path.is_file=True (the SYNC path), so nothing locked the CREATE path and
the residual could not be falsified empirically.

This adds TestAutobindCreatesContractForFreshTicket (2 tests), verifying the
EFFECT (not the call), mirroring the #1653 mutation-test style:
- fresh ticket (is_file=False) → a net-new contracts/<ticket>.yaml is WRITTEN
  with the contract template, the auto/* companion branch is force-pushed, and
  Evidence-Source: OCC#<n> is patched back onto the product PR.
- existing contract (is_file=True) → SYNC binds receipts + pushes + patches
  WITHOUT rewriting the contract (OMN-13888 whole-file-hash: never modify an
  existing contract).

Mutation-verified: reverting the `if not contract_path.is_file()` create guard
(→ `if False`) makes test_fresh_ticket_mints_net_new_contract_and_pushes fail
(only receipts written, no contract) — the test genuinely guards the mint.

dod_evidence:
- env -u PYTHONPATH uv run pytest tests/unit/nodes/node_pr_lifecycle_fix_effect/test_adapter_occ_autobind.py: 26 passed
- node_pr_lifecycle_fix_effect + node_pr_lifecycle_orchestrator test dirs: 274 passed
- mutation (revert create guard): fresh-ticket test FAILS as designed, source restored clean
- ruff format + ruff check: clean; mypy --strict fix_effect node: Success (19 files)
- pre-commit (changed file, OMNIBASE_INFRA_PATH set): all hooks pass
- Live fresh-mint end-to-end proof: NOT run — no safe target (only open omnimarket
  PRs are #1653 [self-authoring prohibited] and #1651 [already bound OCC#3740]);
  merge_sweep has no single-PR scope. proof_class=code-only for the live link;
  create path is code-verified + mutation-locked. OCC companion for OMN-14173 is
  owed (Codex authors it per feedback_no_self_authored_evidence).
…gate

Adds the omnimarket half of the RUNTIME_OPS_READBACK no-PR runtime-ops evidence
class (design docs/plans/2026-07-08-nopr-runtime-ops-evidence-class.md). Gives a
genuine, independently-verified, no-source-change runtime-ops fix (kubectl patch
/ live restart / config repair — no repo diff, no PR) a closure path that does
NOT re-open the self-attestation hole.

Surface A — DurableEvidenceGate (node_dod_verify):
- EnumDurableEvidenceCheck += RUNTIME_OPS_READBACK.
- New pure services/runtime_ops_readback.py verifies a PASS receipt set:
  G1 verifier!=runner, G2a no PR + no mixing, G2b mutation_verb in the GOVERNED
  omnibase_core allowlist, G3 no_source_change + non-empty readback (+optional
  freshness), G4 prod target fails closed (OMN-13418 grant not waived), G5
  prevention_followup linked.
- evaluate() branches at the Check-2 boundary: when every PASS receipt is
  evidence_class==RUNTIME_OPS and none carries a pr_number, it runs the readback
  check instead of CONTRACT_CITES_MERGE_COMMIT; a mixed RUNTIME_OPS+PR set is
  refused. Check 1 (RECEIPT_TRACKED) and Check 5 (DONE_CLASS_LABEL, requiring
  RUNTIME_OBSERVED) are unchanged; Check 3 uses the runtime-ops contract stub's
  runtime_readback key so the contract-on-governance-ref invariant stays intact.

Surface B — close gate (node_linear_triage):
- EnumCloseEvidenceKind += RUNTIME_OPS_READBACK.
- RuntimeOpsReadbackProbe + RuntimeOpsReadbackSubprocessProbe (fail-closed,
  git-backed, reuses the OCC probe plumbing) verify a tracked typed receipt.
- _apply_runtime_ops_readback constructs the close evidence and routes through
  the single _mark_done chokepoint — never from a Linear label alone (G6).

Surface C (L1 hook) is DEFERRED to OMN-13856; the autogen-tick caller wiring is
also deferred. runtime-observed is NOT added to is_exempt().

BLOCKED-ON-CORE-RELEASE: this PR imports omnibase_core RUNTIME_OPS enum +
runtime_ops_verb_loader, which ship in omnibase_core 0.46.7 (companion PR).
omnimarket CI pulls the RELEASED omnibase_core, so verify/mypy stay RED until
0.46.7 is released; Codex sequences core release + merge.

dod_evidence: built + fully gated locally against the sibling omnibase_core
0.46.7 worktree (UV_NO_SYNC + extracted wheel). mypy --strict clean (5 files);
ruff format+check clean; pre-commit all hooks pass incl. DurableEvidenceGate
regression; 289 node_dod_verify + node_linear_triage tests pass, incl. 30 new
Surface-A acceptance/abuse + 22 new Surface-B probe/apply tests.
@jonahgabriel
jonahgabriel force-pushed the jonah/omn-14168-runtime-ops-readback-gate branch from 2596d91 to 6c114d2 Compare July 9, 2026 12:20
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown

✅ Architectural Review — PASSED

Errors: 0
Warnings: 0

What this checks

Rule Description
ARCH-TOPIC-001 No hardcoded Kafka topic strings in handler code
ARCH-DI-001 No event_bus=None bypass
ARCH-DI-002 No direct Handler instantiation outside workflow_runner/adapters
ARCH-DI-003 No reinvented DI containers
ARCH-TOPIC-002 contract.yaml topics follow `onex.{cmd

No architectural violations found.

Static architectural lint — no model inference (OMN-14176).

@github-actions

github-actions Bot commented Jul 9, 2026 •

Copy link
Copy Markdown

⚠️ Hostile Reviewer — DEGRADED (informational)

Blocking findings (critical/error): 0
Total findings: 0
Models succeeded: none

Note: All reviewer models failed or were unavailable. Degraded results are informational during the pilot phase (OMN-8524) and do not block merge. Error: cli_review exited non-zero (fewer than 2 models succeeded — partial/total outage — or binary error)


Gate semantics (pilot phase)

Verdict Meaning Blocks merge?
passed No critical/error findings No
blocked CRITICAL or ERROR findings found Yes
degraded All models unavailable (infra) No (pilot)

Powered by omniintelligence.review_pairing.cli_review — node-based adversarial review via HandlerLlmCliSubprocess (OMN-8524)

@jonahgabriel
jonahgabriel merged commit 02dfd33 into dev Jul 9, 2026
79 of 108 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-14168-runtime-ops-readback-gate branch July 9, 2026 13:00
jonahgabriel added a commit that referenced this pull request Jul 10, 2026
…t dispatch

Piece 5/5 of the OMN-14180 canonical OCC stamp-model. Generalizes the OMN-14173
OccCompanionVerifier read-back to EVERY fix arm and closes OMN-14174's
dispatch-vs-effect over-count.

The merge-sweep orchestrator (node_pr_lifecycle_orchestrator) used to increment
prs_fixed on the strength of the fix handler returning fix_applied=True — i.e. on
DISPATCH. Only the OCC-autobind arm was hardened (OMN-14173); every other arm
(CODE_FAILURE / CI_FAILURE / CODERABBIT / CONFLICT), which mixed-failure PRs fall
through to, still counted on dispatch. That over-counted prs_fixed for PRs where
nothing landed (OMN-14174: omnimarket #1651/#1652 reported prs_fixed=2 with zero
companions).

Change: after a fix arm runs, the orchestrator performs an INDEPENDENT read-back
over live gh/remote state — never the handler's self-reported flag (CLAUDE.md
Rule 3) — and counts the fix ONLY when the OCC stamp is confirmed landed:
  (a) the referenced OCC companion PR exists on the remote and is open, and
  (b) the product PR body carries the Evidence-Source: OCC#<n> + Evidence-Ticket
      stamp, parsed via the Piece-2 canonical parser
      (omnibase_compat.contracts.pr_occ_stamp — the SAME parser the gate uses,
      no second extraction path).
Fail-closed: a noop / dispatched-but-not-landed / dry-run arm counts 0. Arms that
legitimately land no OCC stamp under-count (the safe direction OMN-14174
requires; full per-arm effect read-back is the deferred generalization).

- New: node_pr_lifecycle_orchestrator/handlers/occ_stamp_readback.py
  (ProtocolOccStampReadback + live OccStampReadback via gh pr view + fail-closed
  _UnverifiedOccStampReadback default). Wired live in _ensure_sub_handlers.
- _dispatch_fix_parallel._fix_one gates counted_as_fixed on the read-back for
  every arm (replaces the autobind-only occ_companion_verified special case).
- metadata.yaml: declare the new omnibase_compat schema dependency.
- pyproject: bump omnibase-compat ==0.5.5 -> ==0.5.6 (dependencies +
  override-dependencies); omnibase-core stays >=0.46.7,<0.47.0.

Tests (all green locally):
  - false-success regression: noop dispatch, nothing landed -> prs_fixed == 0
  - true-success: real companion landed (body stamped + OCC PR open) -> counted
  - over-count regression: mixed-failure CODE_FAILURE dispatch, no stamp -> 0
  - read-back unit coverage (real Piece-2 parser, in-memory gh fetcher)

Gate evidence: ruff format+check clean; mypy clean on the orchestrator node +
new module + touched tests; node-metadata dependency check PASS; runtime
import-check clean; /onex:compliance_sweep parity (0 new violations); node
orchestrator/fix + integration + golden-chain + merge_sweep/e2e suites green.

OCC companion: pending independent verifier (implementer does not self-author
evidence).

The Piece-2 OCC-stamp schema was relocated from omnibase_core to omnibase_compat
under OMN-14223 (lowest shared layer). Depends on omnibase-compat 0.5.6
(omnibase_compat#173) being published to PyPI; validated locally against the
compat-dev build.
jonahgabriel added a commit that referenced this pull request Jul 10, 2026
…t dispatch

Piece 5/5 of the OMN-14180 canonical OCC stamp-model. Generalizes the OMN-14173
OccCompanionVerifier read-back to EVERY fix arm and closes OMN-14174's
dispatch-vs-effect over-count.

The merge-sweep orchestrator (node_pr_lifecycle_orchestrator) used to increment
prs_fixed on the strength of the fix handler returning fix_applied=True — i.e. on
DISPATCH. Only the OCC-autobind arm was hardened (OMN-14173); every other arm
(CODE_FAILURE / CI_FAILURE / CODERABBIT / CONFLICT), which mixed-failure PRs fall
through to, still counted on dispatch. That over-counted prs_fixed for PRs where
nothing landed (OMN-14174: omnimarket #1651/#1652 reported prs_fixed=2 with zero
companions).

Change: after a fix arm runs, the orchestrator performs an INDEPENDENT read-back
over live gh/remote state — never the handler's self-reported flag (CLAUDE.md
Rule 3) — and counts the fix ONLY when the OCC stamp is confirmed landed:
  (a) the referenced OCC companion PR exists on the remote and is open, and
  (b) the product PR body carries the Evidence-Source: OCC#<n> + Evidence-Ticket
      stamp, parsed via the Piece-2 canonical parser
      (omnibase_compat.contracts.pr_occ_stamp — the SAME parser the gate uses,
      no second extraction path).
Fail-closed: a noop / dispatched-but-not-landed / dry-run arm counts 0. Arms that
legitimately land no OCC stamp under-count (the safe direction OMN-14174
requires; full per-arm effect read-back is the deferred generalization).

- New: node_pr_lifecycle_orchestrator/handlers/occ_stamp_readback.py
  (ProtocolOccStampReadback + live OccStampReadback via gh pr view + fail-closed
  _UnverifiedOccStampReadback default). Wired live in _ensure_sub_handlers.
- _dispatch_fix_parallel._fix_one gates counted_as_fixed on the read-back for
  every arm (replaces the autobind-only occ_companion_verified special case).
- metadata.yaml: declare the new omnibase_compat schema dependency.
- pyproject: bump omnibase-compat ==0.5.5 -> ==0.5.6 (dependencies +
  override-dependencies); omnibase-core stays >=0.46.7,<0.47.0.

Tests (all green locally):
  - false-success regression: noop dispatch, nothing landed -> prs_fixed == 0
  - true-success: real companion landed (body stamped + OCC PR open) -> counted
  - over-count regression: mixed-failure CODE_FAILURE dispatch, no stamp -> 0
  - read-back unit coverage (real Piece-2 parser, in-memory gh fetcher)

Gate evidence: ruff format+check clean; mypy clean on the orchestrator node +
new module + touched tests; node-metadata dependency check PASS; runtime
import-check clean; /onex:compliance_sweep parity (0 new violations); node
orchestrator/fix + integration + golden-chain + merge_sweep/e2e suites green.

OCC companion: pending independent verifier (implementer does not self-author
evidence).

The Piece-2 OCC-stamp schema was relocated from omnibase_core to omnibase_compat
under OMN-14223 (lowest shared layer). Depends on omnibase-compat 0.5.6
(omnibase_compat#173) being published to PyPI; validated locally against the
compat-dev build.
jonahgabriel added a commit that referenced this pull request Jul 10, 2026
…t dispatch (#1664)

* fix(OMN-14191): merge-sweep counts prs_fixed only after read-back, not dispatch

Piece 5/5 of the OMN-14180 canonical OCC stamp-model. Generalizes the OMN-14173
OccCompanionVerifier read-back to EVERY fix arm and closes OMN-14174's
dispatch-vs-effect over-count.

The merge-sweep orchestrator (node_pr_lifecycle_orchestrator) used to increment
prs_fixed on the strength of the fix handler returning fix_applied=True — i.e. on
DISPATCH. Only the OCC-autobind arm was hardened (OMN-14173); every other arm
(CODE_FAILURE / CI_FAILURE / CODERABBIT / CONFLICT), which mixed-failure PRs fall
through to, still counted on dispatch. That over-counted prs_fixed for PRs where
nothing landed (OMN-14174: omnimarket #1651/#1652 reported prs_fixed=2 with zero
companions).

Change: after a fix arm runs, the orchestrator performs an INDEPENDENT read-back
over live gh/remote state — never the handler's self-reported flag (CLAUDE.md
Rule 3) — and counts the fix ONLY when the OCC stamp is confirmed landed:
  (a) the referenced OCC companion PR exists on the remote and is open, and
  (b) the product PR body carries the Evidence-Source: OCC#<n> + Evidence-Ticket
      stamp, parsed via the Piece-2 canonical parser
      (omnibase_compat.contracts.pr_occ_stamp — the SAME parser the gate uses,
      no second extraction path).
Fail-closed: a noop / dispatched-but-not-landed / dry-run arm counts 0. Arms that
legitimately land no OCC stamp under-count (the safe direction OMN-14174
requires; full per-arm effect read-back is the deferred generalization).

- New: node_pr_lifecycle_orchestrator/handlers/occ_stamp_readback.py
  (ProtocolOccStampReadback + live OccStampReadback via gh pr view + fail-closed
  _UnverifiedOccStampReadback default). Wired live in _ensure_sub_handlers.
- _dispatch_fix_parallel._fix_one gates counted_as_fixed on the read-back for
  every arm (replaces the autobind-only occ_companion_verified special case).
- metadata.yaml: declare the new omnibase_compat schema dependency.
- pyproject: bump omnibase-compat ==0.5.5 -> ==0.5.6 (dependencies +
  override-dependencies); omnibase-core stays >=0.46.7,<0.47.0.

Tests (all green locally):
  - false-success regression: noop dispatch, nothing landed -> prs_fixed == 0
  - true-success: real companion landed (body stamped + OCC PR open) -> counted
  - over-count regression: mixed-failure CODE_FAILURE dispatch, no stamp -> 0
  - read-back unit coverage (real Piece-2 parser, in-memory gh fetcher)

Gate evidence: ruff format+check clean; mypy clean on the orchestrator node +
new module + touched tests; node-metadata dependency check PASS; runtime
import-check clean; /onex:compliance_sweep parity (0 new violations); node
orchestrator/fix + integration + golden-chain + merge_sweep/e2e suites green.

OCC companion: pending independent verifier (implementer does not self-author
evidence).

The Piece-2 OCC-stamp schema was relocated from omnibase_core to omnibase_compat
under OMN-14223 (lowest shared layer). Depends on omnibase-compat 0.5.6
(omnibase_compat#173) being published to PyPI; validated locally against the
compat-dev build.

* chore(OMN-14191): regen uv.lock for omnibase-compat 0.5.6

pyproject.toml already pinned omnibase-compat==0.5.6; uv.lock still
resolved 0.5.5 (published before the pin landed). compat 0.5.6 is now
live on PyPI — regenerate the lock so frozen sync resolves cleanly.
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