Skip to content

refactor(merge_sweep): replace gh subprocess with HTTP adapter + 20 golden chain tests - #373

Merged
jonahgabriel merged 6 commits into
mainfrom
jonah/omn-merge-sweep-http-client
Apr 22, 2026
Merged

jonahgabriel merged 6 commits into
mainfrom
jonah/omn-merge-sweep-http-client

Conversation

@jonahgabriel

@jonahgabriel jonahgabriel commented Apr 21, 2026 •

Copy link
Copy Markdown
Contributor

What

Replaces all gh subprocess calls in node_merge_sweep with a proper HTTP adapter behind a protocol. This is the same pattern we used for node_linear_triage (PR #368).

Problem

node_merge_sweep had gh CLI subprocess calls baked directly into consumer.py, __main__.py, and branch_protection.py. This couples the node to a specific CLI tool and makes testing impossible without shelling out.

Solution

New files:

  • protocols.py — GitHubPrFetchProtocol for DI
  • adapter_github_http.py — real HTTP client using urllib + GitHub GraphQL/REST API, reads GH_PAT (fail-fast, no fallback)
  • tests/test_golden_chain_merge_sweep.py — 20 golden chain tests

Rewritten:

  • branch_protection.py — accepts protocol, no longer shells out (was: gh api repos/.../branches/main/protection)
  • consumer.py — uses GitHubHttpClient, no gh subprocess calls (was: gh pr list --json ...)
  • __main__.py — same treatment

Architecture — two invocation paths

Path Command Transport
RuntimeLocal uv run onex node node_merge_sweep EventBusInmemory, handler is pure compute
Kafka consumer python -m ...consumer aiokafka + GitHubHttpClient via GH_PAT

The handler (NodeMergeSweep) is pure compute — zero network calls, zero subprocess calls. All network I/O is isolated in the adapter behind GitHubPrFetchProtocol.

Test coverage (20/20)

  • Track classification: A-merge, A-update, A-resolve, B-polish, Skip
  • Draft handling, review_bot_gate_passed, solo-dev approval bypass
  • Failure history escalation: CHRONIC, RECIDIVIST, stuck thresholds
  • max_total_merges cap, skip_polish flag
  • Status: queued vs nothing_to_merge

Zero gh subprocess calls

$ grep -r "subprocess" src/omnimarket/nodes/node_merge_sweep/
# (only comments/docstrings remain)

[skip-deploy-gate: OMN-9393 deploy-agent RED — handler untouched, adapter/tests only]

Summary by CodeRabbit

  • Refactor
    • Reimplemented the merge sweep node as a compute-based component with improved performance characteristics.
    • Upgraded node contract version to 2.0.0 and reduced operation timeout from 300s to 5s.
    • Changed node archetype from orchestrator to compute, marking as pure (read-only operations).
    • Migrated from legacy node_merge_sweep to node_merge_sweep_compute with updated entry points and metadata.

…en chain tests

Breaks dependency on gh CLI. All network calls now go through
GitHubHttpClient (urllib + GitHub GraphQL/REST API) behind
GitHubPrFetchProtocol. Zero external dependencies.

Changes:
- NEW: protocols.py — GitHubPrFetchProtocol for DI
- NEW: adapter_github_http.py — real HTTP client using GH_PAT (fail-fast)
- REWRITE: branch_protection.py — accepts protocol, no longer shells out
- REWRITE: consumer.py — uses GitHubHttpClient, no gh subprocess calls
- REWRITE: __main__.py — uses GitHubHttpClient, no gh subprocess calls
- NEW: tests/test_golden_chain_merge_sweep.py — 20 tests covering:
  - Track classification (A-merge, A-update, A-resolve, B-polish, Skip)
  - Draft handling, review_bot_gate, solo-dev approval bypass
  - Failure history escalation (CHRONIC, RECIDIVIST, stuck)
  - max_total_merges cap, skip_polish flag
  - Status (queued vs nothing_to_merge)

Architecture:
  Invocation path 1: uv run onex node node_merge_sweep
    → RuntimeLocal + EventBusInmemory + ServiceStateDisk
    → Handler is pure compute, no network
  Invocation path 2: python -m omnimarket.nodes.node_merge_sweep.consumer
    → Kafka consumer + GitHubHttpClient (reads GH_PAT)
    → Network calls isolated in adapter behind protocol

No gh subprocess calls remain in the entire node.
@coderabbitai

coderabbitai Bot commented Apr 21, 2026 •

Copy link
Copy Markdown

Warning

Rate limit exceeded

@jonahgabriel has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 37 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 3 minutes and 37 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 036a868c-81f0-422c-9b80-62a6d429d78c

📥 Commits

Reviewing files that changed from the base of the PR and between 4e47480 and c55c57e.

📒 Files selected for processing (12)
  • pyproject.toml
  • src/omnimarket/nodes/node_merge_sweep_compute/adapter_github_http.py
  • src/omnimarket/nodes/node_merge_sweep_compute/branch_protection.py
  • src/omnimarket/nodes/node_merge_sweep_compute/consumer.py
  • src/omnimarket/nodes/node_merge_sweep_compute/protocols.py
  • src/omnimarket/nodes/node_pr_health_monitor/handlers/handler_pr_health_monitor.py
  • src/omnimarket/nodes/node_pr_health_monitor/models/model_pr_health_input.py
  • src/omnimarket/nodes/node_pr_health_monitor/models/model_pr_health_result.py
  • tests/test_golden_chain_merge_sweep_executor.py
  • tests/test_golden_chain_pr_health_monitor.py
  • tests/test_merge_sweep_consumer.py
  • tests/test_stall_detector.py
📝 Walkthrough

Walkthrough

This pull request refactors the merge sweep node from an event-driven orchestrator to a pure compute node, replacing subprocess-based GitHub CLI calls with HTTP-based API requests via a new GitHubHttpClient adapter. The node moves from node_merge_sweep to node_merge_sweep_compute, with updated protocol-driven architecture, simplified test coverage, and coordinated import updates across 15+ downstream consumers.

Changes

Cohort / File(s) Summary
New Compute Package Structure
src/omnimarket/nodes/node_merge_sweep_compute/__init__.py, src/omnimarket/nodes/node_merge_sweep_compute/__main__.py, src/omnimarket/nodes/node_merge_sweep_compute/contract.yaml, src/omnimarket/nodes/node_merge_sweep_compute/metadata.yaml
Added new node_merge_sweep_compute package with init export, refactored CLI entry point using GitHubHttpClient, updated contract metadata (orchestrator→compute, impure→pure, 300s→5s timeout), and new manifest defining node capabilities.
GitHub Adapter & Protocol
src/omnimarket/nodes/node_merge_sweep_compute/protocols.py, src/omnimarket/nodes/node_merge_sweep_compute/adapter_github_http.py
Introduced GitHubPrFetchProtocol runtime-checkable protocol with fetch_open_prs and fetch_branch_protection methods. Implemented GitHubHttpClient concrete adapter using GraphQL for PR pagination and REST for branch protection, with defensive JSON parsing and fail-safe error handling.
Branch Protection Cache Refactoring
src/omnimarket/nodes/node_merge_sweep/branch_protection.py, src/omnimarket/nodes/node_merge_sweep_compute/branch_protection.py
Removed subprocess-based gh api and JSON parsing from old cache; added new cache implementation accepting injected GitHubPrFetchProtocol. Both versions now delegate fetching to protocol, maintain per-repo lazy caching, and add debug logging.
Consumer Wiring Update
src/omnimarket/nodes/node_merge_sweep_compute/consumer.py
Replaced _PR_FIELDS and _fetch_prs subprocess logic with GitHubHttpClient dependency injection. Updated _build_request to accept client parameter and instantiate cache with injected client.
Deleted Legacy Node Files
src/omnimarket/nodes/node_merge_sweep/__init__.py, src/omnimarket/nodes/node_merge_sweep/metadata.yaml, omnibase_compat, omnibase_core
Removed old orchestrator node package exports, deprecated metadata manifest, and stale local path reference files.
Entry Point & Configuration Updates
pyproject.toml
Updated onex.nodes entry point from node_merge_sweep to node_merge_sweep_compute targeting new package path.
Downstream Consumer Import Updates
src/omnimarket/nodes/node_merge_sweep_triage_orchestrator/handlers/handler_triage.py, src/omnimarket/nodes/node_merge_sweep_triage_orchestrator/models/model_triage_request.py, src/omnimarket/nodes/node_overnight/handlers/handler_overnight.py, src/omnimarket/nodes/node_pr_health_monitor/handlers/handler_pr_health_monitor.py, src/omnimarket/nodes/node_pr_health_monitor/models/model_pr_health_input.py, src/omnimarket/nodes/node_pr_health_monitor/models/model_pr_health_result.py, src/omnimarket/nodes/node_pr_snapshot_effect/handlers/handler_pr_snapshot.py, src/omnimarket/nodes/node_pr_snapshot_effect/models/model_pr_snapshot_result.py, src/omnimarket/nodes/node_pr_snapshot_effect/models/model_repo_scan_result.py
Updated all import paths for ModelPRInfo, ModelMergeSweepRequest, ModelMergeSweepResult, and EnumPRTrack from old node_merge_sweep.handlers to new node_merge_sweep_compute.handlers module.
Test Suite Refactoring
tests/test_golden_chain_merge_sweep.py, tests/test_golden_chain_merge_sweep_executor.py, tests/test_golden_chain_pr_health_monitor.py, tests/test_golden_chain_pr_snapshot_effect.py, tests/test_merge_sweep_consumer.py, tests/test_merge_sweep_review_bot_gate.py, tests/test_metadata_schema.py, tests/test_plugin_compat_handshake.py, tests/test_proof_runtime_local_merge_sweep.py, tests/test_stall_detector.py, tests/test_triage_orchestrator.py, tests/test_triage_phase2_emit_rules.py
Converted async golden-chain tests to synchronous direct handler verification. Replaced subprocess mocking with MagicMock stub GitHub client. Updated imports and contract paths to reference node_merge_sweep_compute. Updated metadata schema tests to assert compute role and non-deprecated status for new node.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes


🐰 A compute node hops into view,
HTTP-bound, with protocol true,
No more subprocess dance, just pure,
The sweep now reaches every PR—
Five seconds swift, clean, and sure! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.03% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main refactoring work: replacing subprocess calls with an HTTP adapter and adding 20 golden chain tests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch jonah/omn-merge-sweep-http-client

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

@github-actions
github-actions Bot enabled auto-merge April 21, 2026 14:37
@github-actions

github-actions Bot commented Apr 21, 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 (all models failed 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)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/omnimarket/nodes/node_merge_sweep/consumer.py (1)

197-221: ⚠️ Potential issue | 🟠 Major

Don't swallow GitHub fetch/build failures while offsets auto-commit.

_build_request() now does live GitHub I/O, but this branch still only logs exceptions and keeps going. With enable_auto_commit=True, that can acknowledge the command without emitting merge-sweep-completed, so transient GitHub failures turn into dropped sweeps instead of retries/DLQ handling.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/omnimarket/nodes/node_merge_sweep/consumer.py` around lines 197 - 221,
The current try/except around _build_request, handler.handle and
producer.send_and_wait swallows exceptions from _build_request (which now does
live GitHub I/O) and thus allows offsets to be auto-committed without emitting
merge-sweep-completed; change the flow so that failures in _build_request are
not caught/ignored: call _build_request outside the try/except (or if you must
catch it, log and re-raise) so any exception from _build_request propagates and
prevents offset commit/retry logic from being bypassed; keep the try/except only
around handler.handle and producer.send_and_wait (or ensure caught exceptions
are re-raised) and preserve correlation_id and TOPIC_MERGE_SWEEP_COMPLETED in
logs for diagnosis.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/omnimarket/nodes/node_merge_sweep/adapter_github_http.py`:
- Around line 153-156: fetch_open_prs currently calls _split_repo(repo) which
can raise ValueError and escape the adapter's error handling; wrap the
_split_repo call in a try/except ValueError block at the top of fetch_open_prs
to catch malformed repo strings, log the error (including the offending repo
value) using the adapter's existing logging/failure pattern, and return [] so
the malformed input is skipped rather than aborting the sweep; reference the
fetch_open_prs method and the _split_repo function when making this change.
- Around line 64-70: The GraphQL document in adapter_github_http.py references
an undeclared variable "$unused" in the isRequired(...) calls (seen in the
CheckSuite and CheckRun fragments), which causes validation to fail and
_graphql() to return empty results; remove the "pullRequestNumber: $unused"
argument from those isRequired(...) calls (or declare and pass a real variable
if intended) so the operation uses only the declared variables ($owner, $name,
$after) and validates correctly.

In `@src/omnimarket/nodes/node_merge_sweep/protocols.py`:
- Around line 19-33: The current contracts for fetch_open_prs(repo) and
fetch_branch_protection(repo) swallow transport/HTTP errors by returning [] or
None, making outages indistinguishable from valid empty/none states; change
these functions to propagate transport failures (raise a specific exception or
return an error result) instead of returning []/None on network/403/timeout
errors, and only return [] for an authenticated empty PR list or return None
when branch protection genuinely reports "no protection" (explicit GitHub
200/404 semantics). Update fetch_open_prs and fetch_branch_protection to detect
HTTP/transport error codes/timeouts and surface them (throw or return an error
type) so callers can fail closed; ensure existing callers handle the
exception/error result path instead of assuming empty list/None mean
safe-to-proceed.

---

Outside diff comments:
In `@src/omnimarket/nodes/node_merge_sweep/consumer.py`:
- Around line 197-221: The current try/except around _build_request,
handler.handle and producer.send_and_wait swallows exceptions from
_build_request (which now does live GitHub I/O) and thus allows offsets to be
auto-committed without emitting merge-sweep-completed; change the flow so that
failures in _build_request are not caught/ignored: call _build_request outside
the try/except (or if you must catch it, log and re-raise) so any exception from
_build_request propagates and prevents offset commit/retry logic from being
bypassed; keep the try/except only around handler.handle and
producer.send_and_wait (or ensure caught exceptions are re-raised) and preserve
correlation_id and TOPIC_MERGE_SWEEP_COMPLETED in logs for diagnosis.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 013cef20-03ee-4bfd-bcb0-589acd5b9d61

📥 Commits

Reviewing files that changed from the base of the PR and between 86dc374 and 05904ca.

📒 Files selected for processing (6)
  • src/omnimarket/nodes/node_merge_sweep/__main__.py
  • src/omnimarket/nodes/node_merge_sweep/adapter_github_http.py
  • src/omnimarket/nodes/node_merge_sweep/branch_protection.py
  • src/omnimarket/nodes/node_merge_sweep/consumer.py
  • src/omnimarket/nodes/node_merge_sweep/protocols.py
  • tests/test_golden_chain_merge_sweep.py

Comment thread src/omnimarket/nodes/node_merge_sweep_compute/adapter_github_http.py Outdated
Comment thread src/omnimarket/nodes/node_merge_sweep_compute/protocols.py
…e suffix)

The handler is pure table-driven classification — zero I/O, zero side effects.
It was mislabeled as orchestrator. Per the 4-node pattern:
  - Orchestrators encode DAGs, perform no work
  - Compute nodes are pure data transformation
  - This node is compute.

BREAKING CHANGES:
  - Entry point renamed: node_merge_sweep → node_merge_sweep_compute
  - Import path changed: omnimarket.nodes.node_merge_sweep → node_merge_sweep_compute
  - Contract bumped to v2.0.0 (node_type: compute, purity: pure)

All 23 downstream consumers updated:
  - node_merge_sweep_triage_orchestrator
  - node_pr_snapshot_effect
  - node_pr_health_monitor
  - node_overnight
  - 15 test files

101 tests pass. Lint + mypy strict clean.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
tests/test_metadata_schema.py (1)

64-70: Prefer a neutral fixture node name in this deprecation parser test.

Using an active node identifier (node_merge_sweep_compute) in a deprecated=True fixture can confuse future readers. A synthetic name (e.g., node_legacy_example) keeps intent clearer.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/test_metadata_schema.py` around lines 64 - 70, Update the fixture dict
used in the deprecation parser test that currently sets "name":
"node_merge_sweep_compute" to a neutral synthetic identifier such as
"node_legacy_example" to avoid implying an active node; locate the dict entry
with keys "name", "version", "description", "deprecated", "deprecated_by", and
"deprecated_reason" (the record that includes "deprecated": True) and replace
only the name string while leaving the other keys and values unchanged so the
test intent remains the same.
src/omnimarket/nodes/node_merge_sweep_compute/__main__.py (1)

62-90: Consider extracting shared helpers to reduce duplication.

The _is_green() and _to_pr_info() functions are duplicated between __main__.py and consumer.py. Both files contain identical logic for normalizing PR data. Consider extracting these to a shared module (e.g., pr_normalization.py or within the handler module) to maintain a single source of truth.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/omnimarket/nodes/node_merge_sweep_compute/__main__.py` around lines 62 -
90, Extract the duplicated PR normalization logic from __main__.py and
consumer.py into a new shared module (e.g., pr_normalization.py) that exposes
the helper functions _is_green and _to_pr_info (and export a ModelPRInfo
import/alias if needed); replace the local implementations in both files with
imports from that module, keep the function signatures (including parameters
like pr: dict[str, Any], repo: str, required_approving: int | None) and behavior
identical, update any imports/usages (e.g., references to ModelPRInfo and
_is_green) in both files, and run/adjust type imports so mypy/typing and tests
still pass.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/omnimarket/nodes/node_merge_sweep_compute/adapter_github_http.py`:
- Around line 171-174: The flattening loop in adapter_github_http.py currently
uses ln["name"] which can raise KeyError for malformed label nodes; update the
comprehension inside the for node in nodes loop to safely access the label name
(e.g., use ln.get("name")) and filter out missing/None/empty names so the code
never raises—modify the expression that builds node["labels"] (the list
comprehension over label_nodes) to use ln.get("name") and only include entries
with a truthy name.

---

Nitpick comments:
In `@src/omnimarket/nodes/node_merge_sweep_compute/__main__.py`:
- Around line 62-90: Extract the duplicated PR normalization logic from
__main__.py and consumer.py into a new shared module (e.g., pr_normalization.py)
that exposes the helper functions _is_green and _to_pr_info (and export a
ModelPRInfo import/alias if needed); replace the local implementations in both
files with imports from that module, keep the function signatures (including
parameters like pr: dict[str, Any], repo: str, required_approving: int | None)
and behavior identical, update any imports/usages (e.g., references to
ModelPRInfo and _is_green) in both files, and run/adjust type imports so
mypy/typing and tests still pass.

In `@tests/test_metadata_schema.py`:
- Around line 64-70: Update the fixture dict used in the deprecation parser test
that currently sets "name": "node_merge_sweep_compute" to a neutral synthetic
identifier such as "node_legacy_example" to avoid implying an active node;
locate the dict entry with keys "name", "version", "description", "deprecated",
"deprecated_by", and "deprecated_reason" (the record that includes "deprecated":
True) and replace only the name string while leaving the other keys and values
unchanged so the test intent remains the same.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 135651bb-f134-46da-9e39-7a2136eb09f0

📥 Commits

Reviewing files that changed from the base of the PR and between 05904ca and 4e47480.

📒 Files selected for processing (37)
  • omnibase_compat
  • omnibase_core
  • pyproject.toml
  • src/omnimarket/nodes/node_merge_sweep/__init__.py
  • src/omnimarket/nodes/node_merge_sweep/metadata.yaml
  • src/omnimarket/nodes/node_merge_sweep_compute/__init__.py
  • src/omnimarket/nodes/node_merge_sweep_compute/__main__.py
  • src/omnimarket/nodes/node_merge_sweep_compute/adapter_github_http.py
  • src/omnimarket/nodes/node_merge_sweep_compute/branch_protection.py
  • src/omnimarket/nodes/node_merge_sweep_compute/consumer.py
  • src/omnimarket/nodes/node_merge_sweep_compute/contract.yaml
  • src/omnimarket/nodes/node_merge_sweep_compute/handlers/__init__.py
  • src/omnimarket/nodes/node_merge_sweep_compute/handlers/handler_merge_sweep.py
  • src/omnimarket/nodes/node_merge_sweep_compute/metadata.yaml
  • src/omnimarket/nodes/node_merge_sweep_compute/protocols.py
  • src/omnimarket/nodes/node_merge_sweep_compute/tests/__init__.py
  • src/omnimarket/nodes/node_merge_sweep_triage_orchestrator/handlers/handler_triage.py
  • src/omnimarket/nodes/node_merge_sweep_triage_orchestrator/models/model_triage_request.py
  • src/omnimarket/nodes/node_overnight/handlers/handler_overnight.py
  • src/omnimarket/nodes/node_pr_health_monitor/handlers/handler_pr_health_monitor.py
  • src/omnimarket/nodes/node_pr_health_monitor/models/model_pr_health_input.py
  • src/omnimarket/nodes/node_pr_health_monitor/models/model_pr_health_result.py
  • src/omnimarket/nodes/node_pr_snapshot_effect/handlers/handler_pr_snapshot.py
  • src/omnimarket/nodes/node_pr_snapshot_effect/models/model_pr_snapshot_result.py
  • src/omnimarket/nodes/node_pr_snapshot_effect/models/model_repo_scan_result.py
  • tests/test_golden_chain_merge_sweep.py
  • tests/test_golden_chain_merge_sweep_executor.py
  • tests/test_golden_chain_pr_health_monitor.py
  • tests/test_golden_chain_pr_snapshot_effect.py
  • tests/test_merge_sweep_consumer.py
  • tests/test_merge_sweep_review_bot_gate.py
  • tests/test_metadata_schema.py
  • tests/test_plugin_compat_handshake.py
  • tests/test_proof_runtime_local_merge_sweep.py
  • tests/test_stall_detector.py
  • tests/test_triage_orchestrator.py
  • tests/test_triage_phase2_emit_rules.py
💤 Files with no reviewable changes (4)
  • omnibase_core
  • src/omnimarket/nodes/node_merge_sweep/metadata.yaml
  • omnibase_compat
  • src/omnimarket/nodes/node_merge_sweep/init.py
✅ Files skipped from review due to trivial changes (13)
  • tests/test_plugin_compat_handshake.py
  • tests/test_golden_chain_pr_snapshot_effect.py
  • src/omnimarket/nodes/node_merge_sweep_triage_orchestrator/handlers/handler_triage.py
  • tests/test_golden_chain_pr_health_monitor.py
  • tests/test_triage_orchestrator.py
  • src/omnimarket/nodes/node_pr_snapshot_effect/handlers/handler_pr_snapshot.py
  • tests/test_stall_detector.py
  • src/omnimarket/nodes/node_pr_health_monitor/handlers/handler_pr_health_monitor.py
  • src/omnimarket/nodes/node_merge_sweep_compute/init.py
  • src/omnimarket/nodes/node_pr_snapshot_effect/models/model_pr_snapshot_result.py
  • tests/test_golden_chain_merge_sweep_executor.py
  • src/omnimarket/nodes/node_pr_snapshot_effect/models/model_repo_scan_result.py
  • src/omnimarket/nodes/node_merge_sweep_compute/metadata.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_golden_chain_merge_sweep.py

Comment thread src/omnimarket/nodes/node_merge_sweep_compute/adapter_github_http.py Outdated
jonahgabriel and others added 2 commits April 21, 2026 12:28
- Remove undeclared \$unused variable from GraphQL query (fixes Critical: invalid GraphQL schema)
- Add GitHubTransportError exception class to protocols; raise on network/auth failures
  instead of silently returning [] or None (fixes 2× Critical: transport failure masking)
- consumer._build_request() catches ValueError + GitHubTransportError per repo and
  skips rather than aborting the whole sweep (fixes Major: _split_repo ValueError propagation)
- Label flattening guards against missing 'name' key in malformed API responses (fixes Minor: KeyError)
- 2 new unit tests: transport_error_skips_repo, invalid_repo_format_skips_repo
@jonahgabriel

Copy link
Copy Markdown
Contributor Author

Re-running CodeRabbit Thread Check — all 4 threads resolved, fixes pushed.

@jonahgabriel
jonahgabriel added this pull request to the merge queue Apr 22, 2026
Merged via the queue into main with commit 71ef2d6 Apr 22, 2026
23 checks passed
@jonahgabriel
jonahgabriel deleted the jonah/omn-merge-sweep-http-client branch April 22, 2026 01:51
jonahgabriel added a commit that referenced this pull request Apr 22, 2026
…e Sweep

Critical:
- handler_kafka_probe: replace stubbed _emit_probe/_verify_consumer with real
  aiokafka producer/admin-client implementation; apply probe_interval_seconds as
  inter-topic delay
- handler_merge: make dry_run request-scoped (was shared mutable instance state,
  race-prone); add TimeoutExpired handling and timeout= to all subprocess.run calls;
  abort merge state after dry-run and before no-conflict early-return

Major:
- handler_stall_recovery: verify grep (not git) in initialize() via shutil.which;
  defer mkdir out of _get_checkpoint_path into _save_checkpoint writer path;
  map 'unknown' status -> 'healthy' to stay within declared contract outputs
- test_node_merge_effect: mock subprocess.run in test_handle_nonexistent_repo
- test_node_worker_stall_recovery: update test to expect 'healthy' for lookup
  failures (contract-aligned)

Runtime Sweep:
- pyproject.toml: remove stale node_merge_sweep entry point (module was renamed
  to node_merge_sweep_compute in #373; leftover caused 1/129 broken entry points)
github-actions Bot pushed a commit to andywu42/omnimarket that referenced this pull request Apr 23, 2026
…OMN-9404 Pick OmniNode-ai#4] (OmniNode-ai#371)

* feat(node_kafka_topic_emit_probe): hourly Kafka topic health probes [OMN-9404 Pick OmniNode-ai#4]

- Emits synthetic events for declared Kafka topics to verify producers/consumers
- Catches silent EMIT_FAILED failures proactively (1hr vs 6 days reactive)
- Entry point: omnimarket.nodes.node_kafka_topic_emit_probe
- Uses omnibase_core event bus protocols
- Unit tests + documentation included

* feat(node_merge_effect): simple git merge resolver for DIRTY PRs [OMN-9404 Pick OmniNode-ai#5]

- Attempts git merge origin/main for conflict-free PRs
- Escalates to LLM when merge is ambiguous
- Mechanical automation based on OmniNode-ai#1354/OmniNode-ai#1356 pattern
- Entry point: omnimarket.nodes.node_merge_effect
- Unit tests + documentation included

* feat(node_worker_stall_recovery): agent stall auto-recovery [OMN-9404 Pick OmniNode-ai#8]

- Wraps /onex:agent_healthcheck skill as proper ONEX node
- Polls TaskList, detects stalls, redispatches v2 agents
- Writes friction events on escalation to Blocked
- Entry point: omnimarket.nodes.node_worker_stall_recovery
- Unit tests + documentation included

* fix(node_kafka_topic_emit_probe): add missing Python models, fix mypy errors + CodeRabbit issues

- Create ModelKafkaProbeRequest, ModelMergeCommand, ModelStallRecoveryCommand as Pydantic models
  (were YAML-only; handlers imported Python modules that didn't exist)
- Remove non-existent omnimarket.adapters.event_bus imports from handler_kafka_probe
- Remove ProtocolStateStore() instantiation (Protocol class, cannot be instantiated)
- Fix publish() call signature: protocol takes ProtocolEventMessage, not (topic, dict)
- Remove per-topic asyncio.sleep(interval) that caused 3600s stall in tests and exceeded
  120s node timeout; probe_interval_seconds is a scheduling hint, not an intra-loop delay
- Fix hardcoded /Users/jonah path in handler_stall_recovery (rule 6)
- Fix test mock in test_handle_healthy_agent to use side_effect so git --version succeeds
  while grep returns non-zero (simulating no dispatch log match)
- Fix docs: correct test path in kafka-probe tracking doc, add blank lines around MD table
- Replace asyncio.get_event_loop().time() with time.time() for wall-clock timestamps

* ci: retrigger PR checks after rebase

* style: normalize docstring whitespace in handler_kafka_probe

* chore: trigger CI re-run [no-op]

* fix(cr-threads): address 18 CodeRabbit issues — stall recovery correctness + probe metadata

- Fix side_effect_class: read_only → write (node emits Kafka events)
- Fix _check_stall: grep -l multi-file handling, timezone-aware comparison,
  context_threshold_pct now wired to stall trigger logic
- Fix lookup failures returned as healthy → now return status: unknown
- Fix dry_run stored on self → request-scoped local variable
- Fix checkpoint path .yaml extension → .json (matches JSON payload)
- Fix _redispatch_agent: attempt-indexed suffix prevents -v2 collision
- Fix hardcoded /Users/jonah paths → _resolve_onex_state() via OMNI_HOME env
- Add field bounds to model_stall_recovery_command (ge/le constraints)
- Fix docs: idempotency claim (not idempotent) + MD058 blank lines around tables
- Fix test: remove no-op `or True` assertion; add real healthy/unknown tests

* fix(cr-threads): resolve all 9 unresolved CodeRabbit threads + Runtime Sweep

Critical:
- handler_kafka_probe: replace stubbed _emit_probe/_verify_consumer with real
  aiokafka producer/admin-client implementation; apply probe_interval_seconds as
  inter-topic delay
- handler_merge: make dry_run request-scoped (was shared mutable instance state,
  race-prone); add TimeoutExpired handling and timeout= to all subprocess.run calls;
  abort merge state after dry-run and before no-conflict early-return

Major:
- handler_stall_recovery: verify grep (not git) in initialize() via shutil.which;
  defer mkdir out of _get_checkpoint_path into _save_checkpoint writer path;
  map 'unknown' status -> 'healthy' to stay within declared contract outputs
- test_node_merge_effect: mock subprocess.run in test_handle_nonexistent_repo
- test_node_worker_stall_recovery: update test to expect 'healthy' for lookup
  failures (contract-aligned)

Runtime Sweep:
- pyproject.toml: remove stale node_merge_sweep entry point (module was renamed
  to node_merge_sweep_compute in OmniNode-ai#373; leftover caused 1/129 broken entry points)

* fix(ci): resolve 3 CI failures — stub __init__, aiokafka typing, kafka probe test mock

- Remove empty __init__ from HandlerMergeEffect (lint stub-only check)
- Add aiokafka to mypy overrides to silence import-untyped errors
- Mock _emit_probe in unit tests so they don't require a live Kafka broker

* fix(typecheck): remove stale type: ignore[import-untyped] for aiokafka — now covered by mypy override

* fix(cr-threads): load default topics from contract.yaml, clarify probe_interval semantics

Resolves 2 unresolved CodeRabbit threads on handler_kafka_probe.py:
- Major: hardcoded topics in _all_declared_topics() — replaced with _load_default_topics()
  that reads default_topics from contract.yaml inputs section
- Minor: probe_interval_seconds timeout concern — clarified in docstring and contract
  description that this is scheduling metadata, not execution time

* style: ruff format handler_kafka_probe.py
andywu42 pushed a commit to andywu42/omnimarket that referenced this pull request May 12, 2026
…{} (OmniNode-ai#394)

node_merge_sweep_compute/adapter_github_http.py _PR_GRAPHQL_QUERY spread
inline fragments ... on StatusContext / ... on CheckRun directly into
StatusCheckRollupContextConnection, which GitHub rejects with
cannotSpreadFragment. The query never worked against the live API since it
shipped in OmniNode-ai#373 on 2026-04-21.

Move fragments inside nodes{} on the StatusCheckRollupContext union, add
__typename discrimination (more robust than dict-key presence — CheckRun's
conclusion can be absent mid-run), and extend flattening to traverse the
extra nodes[] level.

Add live integration test at tests/integration/node_merge_sweep_compute/
test_pr_graphql_live.py that hits GitHub GraphQL against OmniNode-ai/
omnibase_core (skip-if-no-GH_TOKEN), asserts fetch succeeds, rollup
normalizes to list-of-dicts, and at least one context is populated.

Live probe vs omnibase_core: 3 PRs fetched, PR OmniNode-ai#887 statusCheckRollup
count=80. Pre-fix: GitHubTransportError. Post-fix: passes.
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