Repository navigation
feat(OMN-20415): landing decision groups red PRs by shared cause and dispatches one bounded cause worker - #3342
Conversation
✅ 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) |
|
Lab: head=05d5b2ce25af7000d4ec657c3d8b75c0cc5bfc73 |
Adds test_landing_decision_causes.py with 46 unit tests for node PR landing decision compute. Includes fixtures/replay_2026_10_03_omnimarket.json containing the landing controller facts snapshot of tick 725. The fixture is trimmed to 25 omnimarket PRs, 5 live leases and their records. Author handles in annotation text are replaced by role names. Test selectors cover replay_tick722, cause_permutation, cause_scenario S21 to S28, budget, fixer_hold, and escalated_singleton. These tests fail against the current node because it lacks shared-cause rules.
The landing decision node groups red PRs in one repo that fail one check with one normalized failure signature into a shared cause. One cause worker lease is issued per cause key and dispatched before per-PR workers. Fleet-wide caps limit cause workers to 4, with 2 per repo and 6 workers of any kind per repo. No per-PR worker is created for a member of a cause with a live lease, an open fix PR, or a park. Two attempts are allowed per park episode, followed by a 12-hour park with one escalate_operator action. A fixer HOLD revokes every live lease in its scope and dispatches nothing, while merges and reruns continue. All new fields are optional, so existing S1 to S20 scenarios pass unchanged, and the contract version is 0.2.0.
…r dedupe (omnibase_internal#144)
… exit spends an attempt (LC-F2, pinned)
05d5b2c to
c76bbdf
Compare
Parked shared causes no longer suppress member PRs. Members now take the normal per-PR path (rung 0 ladder, per-PR caps, worker pool, load pause) as a fallback so red PRs keep getting fixed during the park. The cause itself still does not redispatch while parked and still escalates once per park episode. A RELEASE row or the 12 h expiry still ends the park. A live cause lease, an open fix PR, or an owner CLAIM on a parked cause still holds the members. A fixer HOLD still dispatches nothing. Per-PR parked PRs stay parked. cause_not_shared unchanged. Implemented with a new helper _holds_members; _active is unchanged for dispatch. Tests: 12 new fallback tests; s23, three-spawn-failure, lc_f2 and lc_f3 assertions updated from "members suppressed while parked" to the fallback. Handler docstring and contract.yaml 0.2.0 header updated; contract version stays 0.2.0.
…n 0.4.291 for the release-identity gate Onex-Lane: market-3342-land Onex-Session: 64e92e670889474bb15ab2c7d6ebc530
…nimarket#3342 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/37195884603
…e; S1 to S20 stay evidence Onex-Lane: market-3342-land Onex-Session: 64e92e670889474bb15ab2c7d6ebc530
) * evidence(OMN-20415): author OCC companion for OmniNode-ai/omnimarket#3342 OCC companion by node_pr_lifecycle_fix_effect (OMN-13317 F1 / OMN-13990 / OMN-14285). Product PR head 2299ddfaf1eb505f2e57e7c48991672b1ae0272f. * evidence(OMN-20415): self-bind OCC#12778 + rebind contract_sha256 * evidence(OMN-16859): executed test_passes receipts for OmniNode-ai/omnimarket#3342 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/37195884603 --------- Co-authored-by: omnimarket-bot <bot@omninode.ai> Co-authored-by: onexbot-occ-writer[bot] <307849072+onexbot-occ-writer[bot]@users.noreply.github.com>
…caller test requires Onex-Lane: market-3342-land Onex-Session: 64e92e670889474bb15ab2c7d6ebc530
|
Lab: head=9a5be7658e9d5f7eaef242e6c5e75760c249908b |
Summary
Ticket OMN-20415, epic OMN-20413. Node: src/omnimarket/nodes/node_pr_landing_decision_compute (pure definition-B compute, handle(ModelLandingFacts) -> ModelLandingDecision). Contract version 0.1.0 -> 0.2.0. metadata.yaml unchanged (no new dependencies or capabilities).
Seam-first declaration
T1 verdict (OMN-20414, omnibase_internal#144, merged): TLC on h201, all 82 configurations meet their expectation, P1 to P14 and L1 to L6 hold, scenarios S21 to S28 are reached and every mutant is caught (tla/landing_controller/README.md, "TLC results"). The model found two gaps in plan section 2.4, LC-F2 and LC-F3; this PR implements both (below), and the plan is amended in knowledge-base-internal#1006. Plan R8 (T1 verdict cited) is met; the PR was a draft until the park-vs-fallback ruling below; it is ready for review on this head and handed to the landing controller.
What changed
Rules added: normalize_signature (timestamps, shas, #n, bracketed id lists and digit runs replaced; the generic "Process completed with exit code N" line skipped; whitespace collapsed; cut to 300 chars; signature = first 12 hex of sha256 over check name + normalized text; no annotation = unread, never clusters). Clusters: same repo + check + signature, >=3 members (>=2 in a floor-breached repo), follower checks and the hostile reviewer checks excluded, largest first, a cluster >=80% inside a chosen cause is absorbed carrying its pairs, cause key cause:/:, a PR in at most one cause, an unused rerun reruns first. A gate-suspended PR joins once gate_since is more than 60 minutes old; hold, draft, do_not_land never join; new owned suspension may join but never gets a per-PR worker and is never merged. A parked (escalation_exhausted) PR with a readable annotation and no covering cluster becomes a cause of one. One lease per cause key in the shared lease-id space; caps 4 cause workers fleet-wide, 2 per repo, 6 workers of any kind per repo (also applied to per-PR dispatch), inside max_workers; causes dispatch before per-PR workers, floor-breached repos first, then member count, then key. Member suppression: no per-PR worker for a member of a cause with a live lease, an open fix PR or a park. Budget: 2 attempts per park episode (a recorded result, or the deadline passing with the process alive); an exit with no result inside 600 s is a spawn failure, three park the cause. Park 12 h with exactly one escalate_operator action, dedupe key @<parked_until>; a RELEASE row dated after the park start ends it early. Fixer HOLD (repo or all): no dispatch in scope, every live lease in scope revoked through the existing kill sequence; merges, reruns, branch updates continue. cause_fix_submitted verified against the fix PR in the facts (open at fix_head, or merged), else invalid and the attempt is spent; after the fix merges one rerun per member head; cause_not_shared returns members to per-PR at rung 0 and blocks that key for that member set until a member head moves. A cause is owned only by a cause_owners entry (a CLAIM naming the cause), never by per-PR CLAIMs. All new facts, state and policy fields are optional with defaults.
Replay fixture derivation
Fixture tests/unit/nodes/node_pr_landing_decision_compute/fixtures/replay_2026_10_03_omnimarket.json: the landing controller facts snapshot of tick 725 (observed 2026-10-03T21:00:21Z, the input of that tick, same cluster as tick 722), trimmed to 25 omnimarket PRs, 5 live leases with probes, 27 records. Source sha256: landing-facts-snapshot.json 31610a0cd3c6052361126554bdb162bc9df33f0eb63da7b6a4faefbde83784ec, omnimarket-red-annotations.json 776bbf7a1624c24e883ae9c37bb5ad24240c734b976e31ddfbf5e85ea1b3d303. red_annotations: per red check, the failure-level annotations (at most 3, newline-joined) of the latest failing check-run copy on the snapshot head from the GitHub rollup read at about 21:05Z; red checks with no failing copy in that read are absent (unread). gate_since (not in the snapshot): completed_at of the first-created (lowest id) failing check-run copy of the PR's red checks on the snapshot head, read from the GitHub check-runs API by the ids the rollup read carries. The controller tick tail shows every such PR in its companion wait from tick 721 (20:36:14Z), so the true wait is between 24 minutes and that bound; test_replay_tick722_lower_bound_gate_still_forms_a_cause replays the lower bound (one cause, #3321 inside, only the 3 PRs with no gate). omnimarket#3340 (gate_since 20:12:19Z, 48 minutes) stays out. Author handles in annotation text replaced by role names; floor alarm state not in the snapshot, read as no breach.
RED proof
Replay result: one cause, key cause:OmniNode-ai/omnimarket:52c59723e1ad, 19 members, omnimarket#3321 a member, no per-PR dispatch_worker for any member, the cause dispatch is the first action, cause lease 5400 s on claude_opus; the live per-PR lease on member omnimarket#3338 runs to its own deadline.
Commits 7dfcbd5 (tests) then 1791b89 (implementation). With the node files checked out from origin/dev (identical to b10129a), test_landing_decision_causes.py: 45 failed, 1 passed (the passing one is the static fixture hygiene check). Per selector at b10129a: replay_tick722 3 failed 1 passed; cause_permutation 10 failed; cause_scenario 8 failed; budget 5 failed; fixer_hold 3 failed; escalated_singleton 3 failed. test_landing_decision_scenarios.py (S1-S20) 60 passed at b10129a and unchanged.
At head 1791b89 on h201: replay_tick722 4 passed; cause_permutation 10 passed; cause_scenario 8 passed; budget 5 passed; fixer_hold 3 passed; escalated_singleton 3 passed; whole node suite 235 passed (S1-S20 file unchanged); ruff format and ruff check clean; mypy --strict on the node clean; check_node_metadata_dependencies PASS.
Acceptance selectors
Lab
Lab: host h201, lane rpf-OMN-20415, head 1791b89, command
uv run pytest tests/unit/nodes/node_pr_landing_decision_compute -qin the lane worktree on h201, exit 0, observed 235 passed. onex-lab-run --via bus placement refused with exit 75 twice (h200 over the 0.75 load/core bar at 0.83 and 0.87; h201 at load 26.9 on 32 cores not offered).Lab: host h201, lane rpf-OMN-20415, head 1791b89, command
landing_decide_bridge.py --node-src src < replay facts(bridge fetched read-only from omniclaude-internal main 54d19db4b71d), exit 0, observed: actions = [dispatch_worker cause:OmniNode-ai/omnimarket:52c59723e1ad, 19 members, claude_opus, attempt 1], cause lease id 2254, no per-PR dispatch, degraded escalation_exhausted omnimarket#3321.LC-F2 and LC-F3 (T1 model findings)
lc_f2pin it. RED proof is by mutation, since the behavior pre-exists: changing the attempt branch of_cause_lease_endedto the plan text (deadline only) fails all 3lc_f2tests; restored, they pass. Selector:-k lc_f2.<cause key>@<parked_until>. New facts fieldcause_escalations(cause, at, dedupe_key; optional, empty by default). A park covered by a row younger than a park, with no later RELEASE row, adopts the row's time and key and emits noescalate_operator; otherwise it escalates as before. A crash between the MSG append and the state write therefore sends no second MSG. RED first: commit 6fee85c holds the tests, 5 of the 6lc_f3tests failing on the unknown facts field (the sixth, the no-row positive control, passes); commit 05d5b2c implements it, all pass. Selector:-k lc_f3. The controller half (read the ledger for the key before appending) is T4, OMN-20417.Park versus fallback (operator ruling 2026-10-04T10:11:35Z, ledger line 44477: fixed and landed, not parked)
Before: a cause that spent its budget parked for 12 hours and its members stayed suppressed, so no fixer ran for any of those red PRs for 12 hours. Now: a park alone releases its members to per-PR dispatch (rung 0, under the per-PR caps, pool and load pause). The cause itself still does not redispatch while parked, still escalates exactly once per park episode (dedupe and the LC-F3 ledger-row dedupe unchanged), and a RELEASE row or the 12 h expiry ends the park as before. A live cause lease, an open fix PR, or a park that an owner CLAIM covers still hold the members; a fixer HOLD still dispatches nothing; cause_not_shared still returns members to per-PR; a per-PR parked PR stays parked. Code:
_holds_membersin handler_pr_landing_decision.py (_activeunchanged, so_try_dispatch_causestill refuses a parked cause). Tests: 12test_fallback_*tests in test_landing_decision_causes.py, written RED first (10 failed, 275 passed on the unchanged handler), plus the s23, three-spawn-failure and lc_f2/lc_f3 assertions moved from "members stay suppressed" to the fallback. Mutating_holds_membersto always return False fails 46 tests. The plan text "Members stay suppressed while parked" (knowledge-base-internal beta/plans/2026-10-03-red-pr-fixer-plan.md, Park and escalation) is superseded by this ruling.Lab: host h202, lane market-3342-fallback, head 2299ddf, command
uv run pytest tests/unit/nodes/node_pr_landing_decision_compute -qvia onex_lab_run.py --via ssh (the bus path refused with exit 69), exit 0, observed 285 passed. Host h201 ruff format, ruff check and mypy --strict on the node exit 0; pre-commit on the three touched files exit 0. The tick-725 replay (cause:OmniNode-ai/omnimarket:52c59723e1ad, 19 members, no per-PR member dispatch while the lease is live) is among the 285.Install and rebase
This is a decision-node change (node_pr_landing_decision_compute, contract 0.2.0). It installs through the same G-ACT re-pin of the landing controller's pinned decision node as omnimarket#3359 (OMN-20508), which landed first as 2332f6f on dev; this branch is rebased onto dev acd8a5b and resolved against it (the stale-refresh docstring item and the STALE_REFRESH_EXHAUSTED and CAUSE_EXHAUSTED degraded reasons coexist). One G-ACT re-pin installs both.
Model questions for OMN-20414
Each item is what this node does today, chosen from the plan text where the plan does not decide the interleaving; the model should confirm or overrule it.
Evidence-Ticket: OMN-20415
Evidence-Source: OCC#12778