Skip to content

chore(ci): fail CI when gateway events go unhandled by the mobile apps#100206

Merged
steipete merged 5 commits into
mainfrom
ci/protocol-event-coverage
Jul 5, 2026
Merged

chore(ci): fail CI when gateway events go unhandled by the mobile apps#100206
steipete merged 5 commits into
mainfrom
ci/protocol-event-coverage

Conversation

@steipete

@steipete steipete commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

Gateway server→client events (GATEWAY_EVENTS in src/gateway/server-methods-list.ts) and the event handling in the iOS and Android apps can silently drift apart: a new gateway event ships and no mobile client ever handles it, with no signal anywhere.

Closes #100198

Why This Change Was Made

There was no machine-checked link between the advertised event catalog and mobile event dispatch. The protocol package only types the event envelope (event: NonEmptyString), so nothing fails when a new event name is added server-side. This adds pnpm check:protocol-coverage, which derives the event inventory from the gateway catalog, extracts each client's handled events from Swift/Kotlin dispatch sites (switch/when case literals plus .event == "…" comparisons), and fails CI for any gateway event a client neither handles nor allowlists. scripts/protocol-event-coverage.allowlist.json documents currently-unhandled events per client with a one-line reason each; stale entries (event removed, or now handled) also fail, so the list self-maintains. Sentinel dispatch files fail the check loudly if they move.

User Impact

No runtime behavior change. Contributors adding a gateway event now get an explicit CI failure until each mobile client either handles it or records an intentional exemption, preventing silently-dropped events in the apps.

Evidence

  • node scripts/check-protocol-event-coverage.mjsProtocol event coverage OK: 29 gateway events; ios handles 10, allowlists 19; android handles 12, allowlists 17.
  • Negative probe (fake event appended to GATEWAY_EVENTS) → exit 1 listing the unhandled event for both clients.
  • node scripts/run-vitest.mjs test/scripts/check-protocol-event-coverage.test.ts — 8 passed.
  • Wired into the CI guards check shard in .github/workflows/ci.yml.
  • Codex autoreview on the commit: clean, no accepted findings.

Known limitation (documented in the allowlist): the extractor reads string-literal dispatch only, so iOS exec.approval.* handling routed through constants is allowlisted with a reason saying exactly that.

Follow-up commit 0b21f1b279d (from review): Kotlin extraction is now scoped to fun handle*Event(...) dispatch bodies so event == "…" literals in non-dispatch predicate helpers can't count as coverage — previously node.pair.requested/node.pair.resolved were marked handled via an uncalled helper (gatewayEventInvalidatesNodesDevices, no production caller). Those two events are now allowlisted with that reason. Regression test added (predicate-helper literals must not count); 9/9 vitest green; Codex autoreview on the follow-up commit clean.

Follow-up commit 2a7d537cf6e: The dead Android predicate and its unit test are removed. The node-pair events remain allowlisted with their actual delivery contract: the gateway gates them behind operator.pairing, while Android onboarding intentionally receives a bounded operator token without that scope and refreshes approval state through explicit node.list requests. Focused Android unit proof passed (GatewayNodeApprovalStateTest), protocol coverage reports 29 events / Android 12 handled + 17 allowlisted, and the final Codex autoreview is clean.

@openclaw-barnacle openclaw-barnacle Bot added scripts Repository scripts size: L maintainer Maintainer-authored PR labels Jul 5, 2026
@clawsweeper

clawsweeper Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs changes before merge. Reviewed July 5, 2026, 5:48 AM ET / 09:48 UTC.

Summary
The PR adds a gateway protocol event coverage checker, per-client allowlist, package script, CI guard hook, tests, and removes an unused Android node-pairing event predicate.

PR surface: Tests +189, Config +2, Other +466. Total +657 across 7 files.

Reproducibility: yes. The scheduling gap is source-reproducible by calling detectChangedScope for scanned native roots and observing runNode:false while the PR runs the checker only under run_check; the iOS approval gap is source-reproducible from the allowlist plus current constant-backed switch cases.

Review metrics: 1 noteworthy metric.

  • CI gate wiring: 1 package script, 1 check-shard hook. The new drift guard only runs when check-shard runs, so CI routing determines whether native-only mobile changes are covered.

Root-cause cluster
Relationship: fixed_by_candidate
Canonical: #100198
Summary: This PR is the active candidate fix for the open CI protocol/mobile coverage issue; related mobile approval PRs are adjacent implementation history rather than duplicate fixes.

Members:

Proposal only: this assessment does not dispatch repair, suppress jobs, mutate sibling items, close, or merge anything.

Merge readiness
Overall: 🦐 gold shrimp
Proof: 🦞 diamond lobster
Patch quality: 🦐 gold shrimp
Result: needs maintainer review before merge.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Rank-up moves:

  • Make protocol coverage run for native-only iOS/Android/shared dispatch changes.
  • Resolve or explicitly map the iOS exec approval constants so those handled events are not allowlisted gaps.

Risk before merge

  • [P1] Native-only iOS, Android, or shared mobile dispatch changes can still skip the new checker, so a handler removal or stale allowlist entry may not fail CI.
  • [P1] The current iOS allowlist treats handled exec approval events as intentional gaps, so drift or removal of those user approval handlers can remain invisible.
  • [P1] This PR changes maintainer automation, so the risk is a false sense of CI coverage rather than a runtime regression in this branch.

Maintainer options:

  1. Fix the guard coverage before merge (recommended)
    Update CI routing or native lanes so scanned iOS/Android roots run protocol coverage, and resolve iOS approval constants into handled events with focused tests.
  2. Accept a partial CI guard
    Maintainers could intentionally land the checker as partial coverage, but future native-only drift and iOS approval drift would remain possible.
Copy recommended automerge instruction
@clawsweeper automerge

Special instructions:
Update protocol-event coverage so native-only iOS/Android dispatch changes run the check, and treat ExecApprovalNotificationBridge.requestedKind/resolvedKind as handled rather than allowlisted; add focused tests for both.

Next step before merge

  • [P2] The remaining blockers are narrow and mechanically repairable; because the source branch is not maintainer-editable, automation may need an author update or a narrow replacement branch.

Security
Cleared: No concrete security or supply-chain regression was found; the diff adds a local repo-reading checker and CI invocation without new third-party code, secrets, permissions, or package-resolution changes.

Review findings

  • [P2] Run coverage when native dispatch changes — .github/workflows/ci.yml:1209
  • [P2] Track iOS approval constants as handled — scripts/protocol-event-coverage.allowlist.json:16-17
Review details

Best possible solution:

Wire the protocol coverage check into the CI paths that run for the scanned native app roots, and make constant-backed iOS exec approval events count as handled instead of allowlisted.

Do we have a high-confidence way to reproduce the issue?

Yes. The scheduling gap is source-reproducible by calling detectChangedScope for scanned native roots and observing runNode:false while the PR runs the checker only under run_check; the iOS approval gap is source-reproducible from the allowlist plus current constant-backed switch cases.

Is this the best way to solve the issue?

No. The checker is the right general layer, but the current PR is not the best fix until it covers native-only dispatch changes and treats existing iOS approval handlers as handled events instead of exemptions.

Full review comments:

  • [P2] Run coverage when native dispatch changes — .github/workflows/ci.yml:1209
    The new command runs only in check-shard, whose job-level if requires needs.preflight.outputs.run_check == 'true'. detectChangedScope() still returns runNode:false for the app roots this checker scans, so a native-only PR can remove a handler or make an allowlist entry stale without running this gate. Wire the check into the native lanes too, or make preflight set the relevant check output for those scanned roots.
    Confidence: 0.93
  • [P2] Track iOS approval constants as handled — scripts/protocol-event-coverage.allowlist.json:16-17
    These events are handled today by NodeAppModel through ExecApprovalNotificationBridge.requestedKind and .resolvedKind, but this allowlist exempts them because the extractor cannot see constants. Since allowlist entries suppress missing-coverage errors, removing or drifting those user approval cases would still pass; resolve these constants or add an explicit handled-constant map and keep them out of the allowlist.
    Confidence: 0.91

Overall correctness: patch is incorrect
Overall confidence: 0.92

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 78685aec4686.

Label changes

Label justifications:

  • P2: This is a normal-priority CI coverage improvement with limited blast radius, but the current implementation still misses important drift paths.
  • merge-risk: 🚨 automation: Merging as-is could leave maintainer automation reporting protocol coverage while native-only dispatch changes still bypass the check.
  • rating: 🦐 gold shrimp: Overall readiness is 🦐 gold shrimp; proof is 🦞 diamond lobster and patch quality is 🦐 gold shrimp.
  • status: ⏳ waiting on author: ClawSweeper has contributor-facing work open and is waiting for author action. Sufficient (terminal): The PR body includes terminal proof for the checker, a negative probe, focused Vitest output, Android unit proof, and exact-head CI evidence, which is sufficient for this CI-only behavior change.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes terminal proof for the checker, a negative probe, focused Vitest output, Android unit proof, and exact-head CI evidence, which is sufficient for this CI-only behavior change.
Evidence reviewed

PR surface:

Tests +189, Config +2, Other +466. Total +657 across 7 files.

View PR surface stats
Area Files Added Removed Net
Source 0 0 0 0
Tests 1 189 0 +189
Docs 0 0 0 0
Config 2 2 0 +2
Generated 0 0 0 0
Other 4 476 10 +466
Total 7 667 10 +657

Acceptance criteria:

  • [P1] node scripts/check-protocol-event-coverage.mjs.
  • [P1] node scripts/run-vitest.mjs test/scripts/check-protocol-event-coverage.test.ts src/scripts/ci-changed-scope.test.ts test/scripts/ci-workflow-guards.test.ts.
  • [P1] git diff --check.

What I checked:

  • Root policy read: Root AGENTS.md was read fully, including the review requirement to inspect whole decision surfaces and treat CI automation changes as merge-sensitive when they can affect maintainer workflows. (AGENTS.md:1, 78685aec4686)
  • Scoped policy read: Scoped apps/android, apps/ios, scripts, and test AGENTS.md files were read; scripts guidance prefers existing wrappers and changed-file verification seams. (scripts/AGENTS.md:1, 78685aec4686)
  • PR head and proof context: Live PR metadata shows head 2d525c3, proof-sufficient labeling, and body evidence for the checker, negative probe, focused Vitest, Android unit proof, and CI guard wiring. (2d525c3b9337)
  • Coverage command is gated by run_check: The PR adds pnpm check:protocol-coverage inside the guards task, but the check-shard job only runs when preflight output run_check is true. (.github/workflows/ci.yml:1209, 2d525c3b9337)
  • Native-only paths do not set runNode: A source-level probe of detectChangedScope returned runNode:false for Android, iOS, and shared native dispatch roots while setting only native lanes, so run_check remains false for those native-only changes. (scripts/ci-changed-scope.mjs:41, 78685aec4686)
  • iOS approvals are handled through constants: Current main handles exec.approval.requested and exec.approval.resolved in NodeAppModel through ExecApprovalNotificationBridge constants, and those constants resolve to the gateway event names. (apps/ios/Sources/Model/NodeAppModel.swift:969, 78685aec4686)

Likely related people:

  • vincentkoc: Recent live commit metadata shows repeated changes to scripts/ci-changed-scope.mjs and .github/workflows/ci.yml, including native CI routing work adjacent to the remaining scheduling blocker. (role: recent CI routing contributor; confidence: high; commits: 53bd4dde6c00, d71a24f3a4b7, 249d4904afac; files: .github/workflows/ci.yml, scripts/ci-changed-scope.mjs)
  • ngutman: GitHub commit metadata shows the exec approval notification flow and hardening commits created the bridge constants now used by iOS approval handling. (role: iOS exec approval feature contributor; confidence: medium; commits: 28955a36e7b0, 6f566585d84f; files: apps/ios/Sources/Push/ExecApprovalNotificationBridge.swift, apps/ios/Sources/Model/NodeAppModel.swift)
  • joshavant: Related merged work surfaced gateway exec approval events in the iOS foreground app, which is the constant-backed handling path the checker currently misses. (role: recent iOS approval routing contributor; confidence: medium; commits: 5d1e649aeadf, 0c7bac34ae68; files: apps/ios/Sources/Model/NodeAppModel.swift)
  • Solvely-Colin: Related merged Android approval work and recent ChatController history are adjacent to the Android dispatch surface covered by the new checker. (role: recent Android approval surface contributor; confidence: medium; commits: e39249100ebb, 5e342c774d8d; files: apps/android/app/src/main/java/ai/openclaw/app/chat/ChatController.kt)
  • steipete: Live commit metadata shows recent current-main work on the gateway event catalog and CI workflow, so this is not only a one-off proposal by the PR author. (role: gateway event catalog contributor and current PR author with prior area history; confidence: medium; commits: f083f35ddb46, 5ef2c81ed27a; files: src/gateway/server-methods-list.ts, .github/workflows/ci.yml)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (6 earlier review cycles)
  • reviewed 2026-07-05T07:25:45.472Z sha 9f9ac14 :: needs maintainer review before merge. :: none
  • reviewed 2026-07-05T07:31:39.991Z sha 9f9ac14 :: needs changes before merge. :: [P2] Limit Kotlin comparisons to dispatch handlers
  • reviewed 2026-07-05T08:22:16.916Z sha 0b21f1b :: needs changes before merge. :: [P2] Run protocol coverage for native-only app changes
  • reviewed 2026-07-05T08:53:16.945Z sha eb77e29 :: needs changes before merge. :: [P2] Run protocol coverage for native-only app changes | [P2] Track constant-handled iOS approvals as handled
  • reviewed 2026-07-05T09:15:01.938Z sha 8877e3f :: found issues before merge. :: [P2] Run protocol coverage for native app changes | [P2] Track handled iOS approvals as handled
  • reviewed 2026-07-05T09:21:02.960Z sha 8877e3f :: needs changes before merge. :: [P2] Run protocol coverage for native app changes | [P2] Track handled iOS approvals as handled

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9f9ac14b7f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const KOTLIN_EVENT_WHEN_RE = /\bwhen\s*\(\s*event\s*\)\s*\{/u;
const KOTLIN_CASE_LABEL_RE = /^\s*((?:"[^"]+"\s*,\s*)*"[^"]+")\s*->/u;
const SWIFT_EVENT_COMPARISON_RE = /\.event\s*==\s*"([^"]+)"/gu;
const KOTLIN_EVENT_COMPARISON_RE = /\bevent\s*==\s*"([^"]+)"/gu;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Narrow Kotlin comparisons to real dispatch handlers

When any Kotlin source has a parameter/local named event and compares it to a gateway literal, this regex counts that event as handled even if the code is not a gateway dispatch handler. In the current tree, apps/android/app/src/main/java/ai/openclaw/app/NodeRuntime.kt:3717 compares node.pair.requested/node.pair.resolved inside an unused helper (repo-wide rg shows no production callers), so this new check reports Android coverage for those events and omits them from the allowlist even though the runtime never routes them; a real unhandled event can now pass CI merely by appearing in a predicate. Please restrict comparison extraction to known handler scopes or require these entries to stay allowlisted.

Useful? React with 👍 / 👎.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 automation 🚨 May affect CI, automerge, proof capture, label sync, or maintainer automation. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. and removed rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Jul 5, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 0b21f1b279

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread .github/workflows/ci.yml
case "$TASK" in
guards)
pnpm check:no-conflict-markers
pnpm check:protocol-coverage

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Run protocol coverage for native-only app changes

When the only changed files are mobile dispatch code, this new guard never runs: .github/workflows/ci.yml gates this check-shard on needs.preflight.outputs.run_check, while scripts/ci-changed-scope.mjs classifies paths like apps/android/.../ChatController.kt as runNode: false, runAndroid: true and apps/ios/.../IOSGatewayChatTransport.swift as runNode: false with native lanes only. That means a native-only PR can remove handling for a gateway event, or add handling for an allowlisted event, and CI will not run the new missing/stale coverage check; please invoke it from the native lanes too or make preflight set run_check for the app roots this script scans.

Useful? React with 👍 / 👎.

@steipete steipete self-assigned this Jul 5, 2026
@openclaw-barnacle openclaw-barnacle Bot added the app: android App: android label Jul 5, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2a7d537cf6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +16 to +17
"exec.approval.requested": "Handled in NodeAppModel via ExecApprovalNotificationBridge constants; the literal-only extractor cannot see constant case labels.",
"exec.approval.resolved": "Handled in NodeAppModel via ExecApprovalNotificationBridge constants; the literal-only extractor cannot see constant case labels.",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 Badge Track constant-handled iOS approvals as handled

Because allowlist entries suppress missing-coverage errors, putting the currently handled iOS exec approval events here means the new gate will still pass if the NodeAppModel cases using ExecApprovalNotificationBridge.requestedKind/resolvedKind are removed or drift from the gateway event names (apps/ios/Sources/Model/NodeAppModel.swift:969 and :974). For these user approval notifications, please resolve the Swift constants or add an explicit handled-constant map instead of exempting them as allowlisted gaps.

Useful? React with 👍 / 👎.

@steipete

steipete commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Land-ready review complete.

  • Best-fix decision: delete the dead Android pairing-event predicate. Gateway node.pair.requested / node.pair.resolved broadcasts require operator.pairing, while Android's bounded operator session intentionally lacks that scope and onboarding refreshes approval state with explicit node.list requests. Wiring the helper would therefore remain inert unless the authorization/product boundary widened.
  • Removed the helper and its orphaned unit test; retained both Android allowlist entries with the actual scope/polling rationale.
  • Local proof: node scripts/check-protocol-event-coverage.mjs; focused Android GatewayNodeApprovalStateTest; scoped oxlint; git diff --check.
  • Exact-head CI: eb77e299fac1e34fd91ff38e7bb7d71e41d9cf9a, run https://github.com/openclaw/openclaw/actions/runs/28735223935 — green, including Android Play/third-party tests, Android build, check-guards, lint, types, QA smoke, and Node shards.
  • Fresh autoreview: no actionable findings after removing the ineffective wiring approach and preserving caught-error cause in the new checker.

@steipete
steipete requested a review from a team as a code owner July 5, 2026 08:57
@openclaw-barnacle openclaw-barnacle Bot added docs Improvements or additions to documentation channel: discord Channel integration: discord channel: slack Channel integration: slack channel: voice-call Channel integration: voice-call app: macos App: macos app: web-ui App: web-ui gateway Gateway runtime cli CLI command changes commands Command implementations agents Agent runtime and tooling labels Jul 5, 2026
@blacksmith-sh

This comment has been minimized.

@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action. merge-risk: 🚨 automation 🚨 May affect CI, automerge, proof capture, label sync, or maintainer automation. labels Jul 5, 2026
@steipete
steipete force-pushed the ci/protocol-event-coverage branch from 8877e3f to 2d525c3 Compare July 5, 2026 09:43
steipete added 5 commits July 5, 2026 02:57
…00198)

Adds scripts/check-protocol-event-coverage.mjs, which derives the
server->client event catalog from GATEWAY_EVENTS in
src/gateway/server-methods-list.ts, extracts the events each mobile app
handles from Swift/Kotlin dispatch sites, and fails on gateway events no
client handles unless allowlisted with a reason in
scripts/protocol-event-coverage.allowlist.json. Wired as
pnpm check:protocol-coverage in the CI guards shard.
…ions (#100198)

Bare event == "..." literals in predicate helpers outside the dispatch
path (gatewayEventInvalidatesNodesDevices in NodeRuntime.kt, which has no
production caller) counted as Android coverage, silently masking that
node.pair.requested/resolved have no live handler. Kotlin extraction now
only reads when(event) labels and event comparisons inside fun
handle*Event(...) bodies; node.pair.* moved to the Android allowlist with
a truthful reason. Swift extraction stays tree-wide because consumption
there always reads .event off a received EventFrame.
@steipete
steipete force-pushed the ci/protocol-event-coverage branch from 2d525c3 to 5110ab5 Compare July 5, 2026 09:57
@steipete
steipete merged commit 5af24d1 into main Jul 5, 2026
104 checks passed
@steipete
steipete deleted the ci/protocol-event-coverage branch July 5, 2026 10:06
@steipete

steipete commented Jul 5, 2026

Copy link
Copy Markdown
Contributor Author

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 6, 2026
openclaw#100206)

* ci: guard gateway protocol event coverage for iOS/Android clients (openclaw#100198)

Adds scripts/check-protocol-event-coverage.mjs, which derives the
server->client event catalog from GATEWAY_EVENTS in
src/gateway/server-methods-list.ts, extracts the events each mobile app
handles from Swift/Kotlin dispatch sites, and fails on gateway events no
client handles unless allowlisted with a reason in
scripts/protocol-event-coverage.allowlist.json. Wired as
pnpm check:protocol-coverage in the CI guards shard.

* fix(ci): scope Kotlin event extraction to handle*Event dispatch functions (openclaw#100198)

Bare event == "..." literals in predicate helpers outside the dispatch
path (gatewayEventInvalidatesNodesDevices in NodeRuntime.kt, which has no
production caller) counted as Android coverage, silently masking that
node.pair.requested/resolved have no live handler. Kotlin extraction now
only reads when(event) labels and event comparisons inside fun
handle*Event(...) bodies; node.pair.* moved to the Android allowlist with
a truthful reason. Swift extraction stays tree-wide because consumption
there always reads .event off a received EventFrame.

* fix(android): remove dead node pairing event helper

* fix(ci): preserve protocol allowlist parse errors

* test(ci): align tooling import plan
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app: android App: android maintainer Maintainer-authored PR merge-risk: 🚨 automation 🚨 May affect CI, automerge, proof capture, label sync, or maintainer automation. P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. scripts Repository scripts size: L status: ⏳ waiting on author ClawSweeper has contributor-facing work open and is waiting for author action.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CI: gateway-protocol event coverage check for mobile clients

1 participant