Skip to content

feat(gateway): live-switch src/remote-gateway-bridge.py to the canonical ag2-sparrow client - #2083

Merged
qingyun-wu merged 1 commit into
mainfrom
feat/sparrow-live-switch-shim
Jul 13, 2026
Merged

qingyun-wu merged 1 commit into
mainfrom
feat/sparrow-live-switch-shim

Conversation

@qingyun-wu

Copy link
Copy Markdown
Collaborator

Box: remote-task transport (Source: remote gateway × Mode: task-file bridge) — follow-up to #2082, the "Task#3 live-switch" it scoped.

What

src/remote-gateway-bridge.py (895-line monolith) becomes an ~80-line loader shim over the canonical package client (packages/ag2-sparrow/ag2_sparrow/remote_gateway_bridge.py, published to PyPI as ag2-sparrow 0.2.0). The shim:

  1. injects the workspace dirs (tasks/, results/, state/) via set_dirs() before the client evaluates import-time config;
  2. pins REMOTE_MEDIA_DIR to <workspace>/data/remote-media (transport default is STATE_DIR/remote-media);
  3. registers the sutando extra send-allowed roots on top of the transport's result-dir-only default;
  4. execs the canonical source into its own namespace with __package__ pinned.

Step 4 is deliberate (not import): the mock-gateway test harness loads this file several times under different env (tier fail-closed parsing) and mutates module attributes (rtc._ack_disabled = False); a cached module import satisfies neither. exec gives per-load config re-evaluation and a single shared namespace — the pre-switch module contract, unchanged for every loader (startup.sh, runpy via remote-relay-bridge.py, tests).

Two static design-guard tests that grep the src file for implementation strings (parse_markers wiring, interaction_type whitelist) now read the canonical package source instead — the constraints still guard the real implementation.

Why

Single source of truth: after #2082 (option A) the transport is package-canonical. Keeping a 895-line verbatim copy in src/ reintroduces the drift the package removed.

Reversibility

Revert this one file (git) and restart the bridge — no other consumer changes in either direction.

Verification

  • src/remote-gateway-bridge.test.py (mock gateway): all green — multi-load tier fail-closed, ack/heartbeat disable-on-404, task-id path-traversal rejection, newline-forge clamp, marker archiving, media saves, reconcile ledger, one full main() loop iteration.
  • tests/remote-gateway-file-attach, gateway-writeside-platform-card, gateway-writeside-attachments, remote-gateway-interaction-type, interaction-type-header, bridge-marker-no-leak, ag2-sparrow-drift, local-task-protocol: all pass.
  • Live E2E: the running gateway bridge was swapped to the shim (single instance, healthy startup line, identical config to the monolith instance it replaced; results delivering to the owner room through it).

🤖 Generated with Claude Code

…cal ag2-sparrow client (#2082 Task#3)

src/remote-gateway-bridge.py becomes a thin loader shim: it injects sutando's
workspace dirs (set_dirs), pins MEDIA_DIR to <workspace>/data/remote-media,
registers the sutando send-allowed roots, then executes the canonical package
source (packages/ag2-sparrow/ag2_sparrow/remote_gateway_bridge.py) in its own
namespace with __package__ pinned — so every existing loader (startup.sh,
runpy via remote-relay-bridge, the mock-gateway test harness with its
multi-load env parsing and module-attribute mutation) sees the same
module-level API as the pre-switch monolith. Reversible: revert this file.

The two static design-guard tests that grep the src file for implementation
strings (parse_markers wiring, interaction_type whitelist) now read the
canonical package source instead.

Verified: full mock-gateway harness green (tier fail-closed multi-load,
reconcile, markers, media, a live loop iteration), file-attach + writeside +
interaction-type + marker-no-leak + drift suites green, and the live bridge
swapped to the shim (single instance, healthy startup, results delivering).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Coverage Gate

✅ Diff coverage PASSES the 95% bar. Whole-tree (informational): 40%.

Diff Coverage

Diff: origin/main...HEAD, staged and unstaged changes

  • src/remote-gateway-bridge.py (100%)

Summary

  • Total: 18 lines
  • Missing: 0 lines
  • Coverage: 100%

@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Review signal: no blocking findings.

The live-switch shape looks sound to me: the src/remote-gateway-bridge.py entrypoint now injects Sutando's workspace dirs before the package bridge evaluates its import-time config, pins REMOTE_MEDIA_DIR back to the existing workspace data path, and keeps the scoped local REMOTE_TASK_*/media credentials in the local bridge process. I did not see Sutando-local taking AppService namespace credentials or moving membership enforcement into the local client.

I also checked the compatibility-sensitive parts: owner tier remains a local REMOTE_TASK_TIER decision with invalid values failing closed to team; task ids are still path-slug gated; gateway-provided headers are newline-clamped; result markers still route through parse_markers; attachment send paths still use the allowlist and room-side upload endpoint; and the deprecated onboarding aliases remain honored through the package implementation.

Validation run in an isolated /private/tmp checkout:

  • python3 src/remote-gateway-bridge.test.py and python3 tests/remote-gateway-bridge.test.py passed with loopback permission for the mock gateway.
  • python3 tests/remote-gateway-file-attach.test.py
  • python3 tests/gateway-writeside-platform-card.test.py
  • python3 tests/gateway-writeside-attachments.test.py
  • python3 tests/remote-gateway-interaction-type.test.py
  • python3 tests/bridge-marker-no-leak.test.py
  • python3 tests/interaction-type-header.test.py
  • python3 tests/ag2-sparrow-drift.test.py
  • python3 tests/local-task-protocol.test.py
  • python3 tests/local-task-protocol-attachments.test.py
  • git diff --check origin/main...HEAD
  • PYTHONPYCACHEPREFIX=/private/tmp/... python3 -m py_compile ...

One minor note, not a blocker: importing the shim under a custom importlib spec emits ImportWarning: __package__ != __spec__.parent in some tests because the shim pins __package__ = "ag2_sparrow" before exec. Normal script launch and the focused tests still pass; only a warnings-as-errors test mode would trip on it.

Reviewed by Qingyun's Codex.

@qingyun-wu
qingyun-wu merged commit 3507db4 into main Jul 13, 2026
7 checks passed
@qingyun-wu
qingyun-wu deleted the feat/sparrow-live-switch-shim branch July 13, 2026 01:45

@liususan091219 liususan091219 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Triage change-request (owner-approved batch, 2026-07-12 sweep). Conditions found on the current diff:

  • inadequate tests (source-grep, not behavior) — Replace bridge read_text/substring checks with behavioral import/header assertions

Justification: kb triage standards (source-grep test standard, voice/vision live-test policy, necessity/coverage checks) — see sonichi/sutando-pr-triage kb/.
Address the asks and push; re-request review to clear this. If a finding is wrong, reply with the counter-evidence and it will be withdrawn.

cr-reason: source_grep_tests

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ag2product AG2 product related (packages, PyPI, task-relay) request-change Has an active CHANGES_REQUESTED review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants