Skip to content

feat(orb): dispatch config_push relay rows separately from the GitHub-webhook queue#7615

Merged
JSONbored merged 3 commits into
feat/config-push-write-pathfrom
feat/config-push-dispatch
Jul 21, 2026
Merged

feat(orb): dispatch config_push relay rows separately from the GitHub-webhook queue#7615
JSONbored merged 3 commits into
feat/config-push-write-pathfrom
feat/config-push-dispatch

Conversation

@JSONbored

Copy link
Copy Markdown
Owner

Summary

  • Stacked on feat(orb): add a typed config-push write path to the relay pipeline #7611 (the write-side/kind-column PR) — this diffs against that branch, not main, since the kind column and ConfigPushPayload shape are prerequisites. Please merge feat(orb): add a typed config-push write path to the relay pipeline #7611 first; this PR's base will need to move to main afterward (or GitHub will auto-retarget once feat(orb): add a typed config-push write path to the relay pipeline #7611 merges and its branch is deleted).
  • drainOrbRelayWithMonitor's per-event loop (src/selfhost/monitored-work.ts) now branches on ev.kind before calling args.enqueue (which unconditionally does JSON.parse(rawBody) as GitHubWebhookPayload): a 'config_push' row routes to a new dedicated handleConfigPushRelayEvent — receive-and-log only, per Add a config-push/rollout-control write path to fleet analytics #4902's explicit v1 scope (no capability-toggle or config-mutation side effect implemented here). Everything else — 'github_webhook' and any old-shaped/missing kind — falls through to the existing path unchanged.
  • Threads kind through the whole read side that feeds the drain loop: pullRelayPending's SELECT + RelayPendingEvent type (src/orb/relay.ts), the /v1/orb/relay/pull response (no code change needed there — it just returns pullRelayPending's output), and drainOrbRelay's HTTP response parsing (src/orb/broker-client.ts) — defaults a missing kind to 'github_webhook' so a rolling deploy against an older Orb server never misroutes an old-shaped event.
  • New metric loopover_orb_config_push_received_total, registered in src/selfhost/metrics.ts.

Test plan

  • npm run typecheck
  • npm run ui:openapi:check
  • Updated existing test/integration/orb-relay.test.ts / test/unit/orb-broker-client.test.ts fixtures for the new required kind field (all existing assertions otherwise byte-identical — the webhook path's behavior did not change)
  • New tests in test/unit/selfhost-monitored-work.test.ts (describe("kind-based dispatch (#7523)")): a 'github_webhook' row AND a legacy row with no kind at all both still dispatch through the exact existing enqueueWebhookByEnv path unchanged; a 'config_push' row reaches the new handler instead and never calls enqueue; malformed JSON in a config_push rawBody surfaces as payload: null instead of throwing; a throwing handler doesn't abort the rest of the batch and isn't acked (mirrors the existing enqueue-isolation regression test); default console.log sink parity with the webhook path
  • 100% patch-line coverage on this PR's own diff (verified via lcov cross-reference against the write-path base branch)
  • Full unsharded suite green aside from the same known, pre-existing, unrelated flakes noted on the companion write-side PR

Closes #7523

…-webhook queue

drainOrbRelayWithMonitor's per-event loop now branches on kind BEFORE
calling args.enqueue (which unconditionally does JSON.parse(rawBody) as
GitHubWebhookPayload): a 'config_push' row routes to a new dedicated
handleConfigPushRelayEvent (receive-and-log only, per #4902's v1 scope --
no capability-toggle or config-mutation side effect implemented here).
Everything else -- 'github_webhook' and any old-shaped/missing kind --
falls through to the existing path unchanged.

Threads `kind` through the read side that feeds the drain loop:
pullRelayPending's SELECT + RelayPendingEvent, the /v1/orb/relay/pull
response (no code change needed there -- it just returns pullRelayPending's
output), and drainOrbRelay's HTTP response parsing (defaults a missing
`kind` to 'github_webhook' for a rolling deploy against an older Orb
server).

Closes #7523
selfhost-metrics.test.ts scans every incr() call site for its metric
name and cross-checks it against DEFAULT_METRIC_META -- loopover_orb_config_push_received_total
(introduced in the previous commit) was missing.
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@codecov

codecov Bot commented Jul 21, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 88.49%. Comparing base (ef90ee2) to head (1cd0ff2).
✅ All tests successful. No failed tests found.

Additional details and impacted files
@@                       Coverage Diff                       @@
##           feat/config-push-write-path    #7615      +/-   ##
===============================================================
- Coverage                        88.51%   88.49%   -0.02%     
===============================================================
  Files                              724      724              
  Lines                            75976    75990      +14     
  Branches                         22614    22616       +2     
===============================================================
+ Hits                             67247    67250       +3     
- Misses                            7681     7688       +7     
- Partials                          1048     1052       +4     
Flag Coverage Δ
shard-1 34.13% <0.00%> (-0.01%) ⬇️
shard-2 39.40% <0.00%> (-0.11%) ⬇️
shard-3 35.27% <83.33%> (+0.01%) ⬆️
shard-4 38.12% <16.66%> (-0.03%) ⬇️
shard-5 29.69% <0.00%> (-0.01%) ⬇️
shard-6 28.46% <0.00%> (-0.11%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
src/orb/broker-client.ts 99.14% <100.00%> (ø)
src/orb/relay.ts 100.00% <100.00%> (ø)
src/selfhost/metrics.ts 100.00% <ø> (ø)
src/selfhost/monitored-work.ts 100.00% <100.00%> (ø)

... and 2 files with indirect coverage changes

@loopover-orb loopover-orb Bot added the gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. label Jul 21, 2026
@loopover-orb

loopover-orb Bot commented Jul 21, 2026

Copy link
Copy Markdown

Tip

✅ LoopOver review result - approve/merge recommended

Review updated: 2026-07-21 03:22:13 UTC

7 files · 1 AI reviewer · no blockers · readiness 82/100 · CI green · clean

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
This threads a new `kind` field through the entire Orb relay pull-mode path (relay.ts SELECT/type, broker-client.ts HTTP parsing with a safe default, monitored-work.ts's drain loop) and branches config_push rows to a dedicated receive-and-log handler before they ever hit `args.enqueue`'s unconditional `JSON.parse(rawBody) as GitHubWebhookPayload`, which is exactly the right place to intercept — a config_push row would otherwise be misinterpreted as a webhook payload. The per-event try/catch around the new handler correctly isolates a throwing config_push event from the rest of the batch (mirrors the existing webhook-path isolation) and doesn't ack it, so a redelivery is retried. The old-shaped/missing-`kind` fallback to `'github_webhook'` in broker-client.ts is the right rolling-deploy safety net, and the new tests cover the dispatch split, the malformed-JSON-payload case, and the throw-doesn't-abort-batch regression.

Nits — 5 non-blocking
  • codecov/patch is failing at 94.44% vs the 99% target — worth checking which changed branch (likely the malformed-JSON catch in `handleConfigPushRelayEvent` or the `kind` default fallback arm in broker-client.ts) is uncovered before merge.
  • src/orb/relay.ts:427's `pullRelayPending` now selects `kind` and maps it straight through with no fallback — confirm the feat(orb): add a typed config-push write path to the relay pipeline #7611 migration backfills/defaults `kind` for pre-existing rows so this SELECT never returns a null/undefined value that would violate the new non-optional `kind: string` type.
  • The external size-smell flag on src/orb/relay.ts (431 lines) is only a threshold nudge, not something this diff should be blocked on splitting.
  • Add a unit test asserting `pullRelayPending` handles a legacy row where the DB `kind` column is NULL (if that's possible pre-backfill), to close the coverage gap flagged by codecov/patch.
  • Consider linking this PR's description more explicitly to the tracking issue number (feat(orb): dispatch config_push relay rows separately from the GitHub-webhook queue #7523) in the title/body for the deterministic issue-scope gate, since the description currently references it inline rather than as a formal closes/fixes link.

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ✅ Gate result — Passing (No configured blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #7523
Related work ⚠️ 1 scoped overlap Top overlaps are listed below; lower-confidence bulk is hidden.
Change scope ❌ 8/20 High review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 23 registered-repo PR(s), 16 merged, 358 issue(s).
Contributor context ✅ Confirmed Gittensor contributor JSONbored; Gittensor profile; 23 PR(s), 358 issue(s).
Improvement ✅ Minor risk: clean · value: minor
Review context
  • Author: JSONbored
  • Role context: owner (maintainer lane)
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: Python, TypeScript, Ruby, Go, JavaScript, MDX, Shell, Solidity
  • Official Gittensor activity: 23 PR(s), 358 issue(s).
  • Related work: Titles/paths share 9 meaningful terms. (PR #7611)
Contributor next steps
  • Start here: Treat this as maintainer-lane context rather than normal contributor-lane activity.
  • Then work through the remaining 3 steps in the Signals table above.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.

  • @loopover ask &lt;question&gt; answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat &lt;question&gt; answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.

  • Re-run LoopOver review

@loopover-orb loopover-orb Bot added the manual-review Gittensor contributor context label Jul 21, 2026
Closes the codecov/patch gap on PR #7615 -- handleConfigPushRelayEvent's
error-logging ternary (error instanceof Error ? error.message : String(error))
only had its Error-instance arm exercised.
@JSONbored
JSONbored merged commit 3a08939 into feat/config-push-write-path Jul 21, 2026
15 checks passed
@JSONbored
JSONbored deleted the feat/config-push-dispatch branch July 21, 2026 03:48
JSONbored added a commit that referenced this pull request Jul 21, 2026
…-webhook queue (#7615)

* feat(orb): dispatch config_push relay rows separately from the GitHub-webhook queue

drainOrbRelayWithMonitor's per-event loop now branches on kind BEFORE
calling args.enqueue (which unconditionally does JSON.parse(rawBody) as
GitHubWebhookPayload): a 'config_push' row routes to a new dedicated
handleConfigPushRelayEvent (receive-and-log only, per #4902's v1 scope --
no capability-toggle or config-mutation side effect implemented here).
Everything else -- 'github_webhook' and any old-shaped/missing kind --
falls through to the existing path unchanged.

Threads `kind` through the read side that feeds the drain loop:
pullRelayPending's SELECT + RelayPendingEvent, the /v1/orb/relay/pull
response (no code change needed there -- it just returns pullRelayPending's
output), and drainOrbRelay's HTTP response parsing (defaults a missing
`kind` to 'github_webhook' for a rolling deploy against an older Orb
server).

Closes #7523

* chore(orb): register the config_push relay metric

selfhost-metrics.test.ts scans every incr() call site for its metric
name and cross-checks it against DEFAULT_METRIC_META -- loopover_orb_config_push_received_total
(introduced in the previous commit) was missing.

* test(selfhost): cover the non-Error branch of the config_push throw log

Closes the codecov/patch gap on PR #7615 -- handleConfigPushRelayEvent's
error-logging ternary (error instanceof Error ? error.message : String(error))
only had its Error-instance arm exercised.
JSONbored added a commit that referenced this pull request Jul 21, 2026
…7611)

* feat(orb): add a typed config-push write path to the relay pipeline

Adds a discriminator column (kind, default 'github_webhook') to
orb_relay_pending so the pull-drain loop can eventually tell an
operator-addressed config-push notice apart from a GitHub webhook row
(dispatch side is a separate follow-up). A new POST /v1/app/fleet/config-push
endpoint lets an operator enqueue a typed { pushId, message, capability?,
deprecatesAt? } payload for an explicit list of installation_ids, mirroring
the kill-switch endpoint's requireAppRole + zod-validated-body pattern.

Placed under /v1/app/*, not /v1/internal/* (the latter's own middleware
requires a bearer INTERNAL_JOB_TOKEN and never resolves a session identity
for it, which would make requireAppRole's session-role branch unreachable
for a control-panel caller -- see the route's own doc comment).

Write side only: enqueueConfigPushRelay shares the same TTL pruning and
per-installation backlog cap as the existing webhook path, but skips its
GitHubWebhookPayload-shaped coalesce-key derivation entirely (a config_push
payload never has that shape). No existing webhook-row behavior changes.

Closes #7522

* feat(orb): dispatch config_push relay rows separately from the GitHub-webhook queue (#7615)

* feat(orb): dispatch config_push relay rows separately from the GitHub-webhook queue

drainOrbRelayWithMonitor's per-event loop now branches on kind BEFORE
calling args.enqueue (which unconditionally does JSON.parse(rawBody) as
GitHubWebhookPayload): a 'config_push' row routes to a new dedicated
handleConfigPushRelayEvent (receive-and-log only, per #4902's v1 scope --
no capability-toggle or config-mutation side effect implemented here).
Everything else -- 'github_webhook' and any old-shaped/missing kind --
falls through to the existing path unchanged.

Threads `kind` through the read side that feeds the drain loop:
pullRelayPending's SELECT + RelayPendingEvent, the /v1/orb/relay/pull
response (no code change needed there -- it just returns pullRelayPending's
output), and drainOrbRelay's HTTP response parsing (defaults a missing
`kind` to 'github_webhook' for a rolling deploy against an older Orb
server).

Closes #7523

* chore(orb): register the config_push relay metric

selfhost-metrics.test.ts scans every incr() call site for its metric
name and cross-checks it against DEFAULT_METRIC_META -- loopover_orb_config_push_received_total
(introduced in the previous commit) was missing.

* test(selfhost): cover the non-Error branch of the config_push throw log

Closes the codecov/patch gap on PR #7615 -- handleConfigPushRelayEvent's
error-logging ternary (error instanceof Error ? error.message : String(error))
only had its Error-instance arm exercised.

* fix(review): prune the relay backlog once per config-push request, not per target

enqueueConfigPushRelay pruned the shared orb_relay_pending table on every
call, but the route fans it out over up to 500 installationIds via
Promise.all -- turning one request into up to 500 redundant global
TTL-prune scans/deletes. Moves the prune to the route handler, called
once before the fan-out; enqueueConfigPushRelay no longer prunes itself.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. manual-review Gittensor contributor context

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant