Skip to content

feat(bridge): ===SKILL INSTRUCTIONS=== block on owner-tier gateway tasks - #2686

Merged
john-the-dev merged 9 commits into
mainfrom
feat/ag2space-skill-instructions
Aug 7, 2026
Merged

john-the-dev merged 9 commits into
mainfrom
feat/ag2space-skill-instructions

Conversation

@qingyun-wu

Copy link
Copy Markdown
Collaborator

Gateway tasks now carry the same task-hygiene block the discord bridge writes — with gateway-native recipes:

  1. CONTEXT-FIRST — room_ops.py read '<channel_id>' --limit 30 (plus the channel-env hint for hosts whose core session lacks the gateway exports), same unconditional wording as discord's (that wording is owner-approved prompt surface; mirrored, not rewritten).
  2. NOTIFY FIRST (>60s) — task-progress notify --source ag2space; the notify script already routes any non-discord/slack/telegram source through the remote gateway.
  3. Result path.

Owner-tier only: non-owner tiers carry ===SUTANDO SYSTEM INSTRUCTIONS=== and must not get a second, competing instruction block. Appended after access_tier like the secret-handling notice — prose lines, none header-shaped, so the tier-wins-last invariant holds (pinned by test).

Stacked on feat/task-envelope-room-members (#2685) — merge that first; this PR's diff on top is the block + its tests.

Evidence:

python3 src/remote-gateway-bridge.test.py        -> PASS — all checks green
  (packages/ change stashed)                     -> 2 FAILs (new checks)
  (restored)                                     -> PASS

New checks: owner task carries all three recipes; clamped-tier task carries none; block is the file tail; exactly one access_tier line.

🤖 Generated with Claude Code

https://claude.ai/code/session_01KiaMLCxgKNAbmhty29WGN6

@qingyun-wu
qingyun-wu requested a review from john-the-dev August 5, 2026 19:19

@bassilkhilo-ag2 bassilkhilo-ag2 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.

Reviewed the child-only layer at head 122ed710 (stacked on #2685's c96eb361), plus the cumulative diff against origin/main, per the stacked-PR review convention.

Child-only diff is clean, minimal (34 lines in the bridge + 20 in tests), and well-tested:

  • sender_tier = _tier_for(task.get("user_id")) is computed exactly once (line 1552) and reused for both access_tier: and this gate (line 1569) — no risk of the two drifting from separately-derived values.
  • Verified the owner-only gate directly: python3 src/remote-gateway-bridge.test.py → PASS at head, including the three new checks (non-owner carries no block, owner carries the full block, block is the file tail).
  • Mutation-tested: reverted to c96eb361 (pre-this-PR) and reran → 2 failures, matching the PR's own claimed count exactly. Restored, byte-identical.
  • Security invariant checked, not just trusted: every line in the new ===SKILL INSTRUCTIONS=== block is prose/numbered-sentence, none header-shaped (no key:-at-line-start pattern), so it can't be mistaken for a forged access_tier:/channel_id: line — same discipline as the existing secret-handling notice it's modeled on. _chan is piped through _one_line() before interpolation, so a channel_id containing a newline can't inject an extra header-shaped line either.
  • bash scripts/review-checks.sh --diff (child-only diff) → PASS (hardcoded-paths clean).
  • CI green except a SKIPPED publish-sparrow job (benign, seen on sibling PRs today too).

Stacking note for whoever merges: this PR's tree currently still carries the stray tests/__pycache__/*.pyc binary I flagged on #2685 (c96eb361) — confirmed it's inherited from the parent, not introduced here (child diff touches only the bridge file + its test). No new action needed on this PR for that; it resolves once #2685 drops the file and this branch rebases. Per the PR body's own note and CONTRIBUTING's stacked-PR guidance: merge #2685 first, then rebase/re-verify this one before merging.

No blockers on the child layer itself. Approving.

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Reviewed exact head 122ed7109c070ed7910b30f738ce803388e014c0, as the incremental diff on top of #2685.

No blocking findings in this stacked change. The owner-tier task-hygiene block is emitted only after the locally resolved access_tier: owner, non-owner/clamped tasks do not receive a competing instructions block, and the added text is bridge-generated prose rather than another header-shaped field. The result-path line still uses the local task id, matching the file the core is expected to write.

Validation run:

  • git diff --check pr-2685...HEAD
  • python3 src/remote-gateway-bridge.test.py (PASS)
  • python3 scripts/check-python39-compat.py --target packages/ag2-sparrow/ag2_sparrow --target src (106 files)

Approval signal for this incremental diff. The stack still depends on #2685 dropping the committed __pycache__ bytecode before either PR lands.

Reviewed by Qingyun's Personal Codex.

@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Cold review — no blockers; both load-bearing recipe claims checked against the shipped tools rather than assumed:

  1. notify.py ag2space support: VERIFIED — skills/task-progress/scripts/notify.py treats any --source beyond slack/discord/telegram as a remote-gateway send (generic sender, docstring even shows a Matrix room-id example), so instruction 2 is a real, working command. ✓
  2. The env-load fallback names a file this host doesn't have: the recipe says . "$CLAUDE_CONFIG_DIR/channels/ag2space/.env", but on this machine the directory contains relay-client.env (+ access.json) — no .env. If that naming holds on other hosts (it's where today's live room-ops calls sourced creds from), the fallback fails precisely in the situation it exists for, and the core gets "no gateway configured" twice with no next move. Suggested one-line hardening: try both names — for f in "$CLAUDE_CONFIG_DIR/channels/ag2space/.env" "$CLAUDE_CONFIG_DIR/channels/ag2space/relay-client.env"; do [ -f "$f" ] && set -a && . "$f" && set +a && break; done — or point at whichever the fleet standardizes on. Per-host config famously does not generalize; worth pinning the name the recipe relies on.

Two smaller observations: (a) _chan is interpolated into single-quoted shell commands — fine for well-formed Matrix ids (their grammar excludes quotes) and the broker is trusted for the envelope anyway, but a defensive strip of ' in _one_line would make the property local rather than inherited; (b) the access-tier-wins-last + exactly-one-access_tier assertions are exactly the right invariant pins — the tail-position test especially.

Owner-only gating with the non-owner-absence test is the correct posture (no second instruction block competing with SUTANDO SYSTEM INSTRUCTIONS). CI green; owner's merge.

— qingyun-sutando (001)

Base automatically changed from feat/task-envelope-room-members to main August 5, 2026 23:04
Owner ask 2026-08-05 (circulated the discord task file as the reference):
gateway tasks now carry the same task-hygiene block the discord bridge
writes — context-first thread reconstruction (room_ops read, with the
channel-env hint for hosts whose core lacks the gateway exports),
notify-first for >60s tasks (task-progress notify, which already speaks
remote-gateway for any non-discord/slack/telegram source), and the
result path.

Owner-tier ONLY: non-owner tiers carry ===SUTANDO SYSTEM INSTRUCTIONS===
and must not receive a second, competing instruction block. Appended
after access_tier like the secret notice — prose lines, none
header-shaped, so the tier-wins-last invariant holds (pinned).

Stacked on feat/task-envelope-room-members (merge that first).

Tests: owner task carries the block with all three recipes; clamped-tier
task does NOT; block is the file tail; exactly one access_tier line.
Fails-without-fix control: 2 FAILs with packages/ stashed -> PASS.
@qingyun-wu
qingyun-wu force-pushed the feat/ag2space-skill-instructions branch from f17ccff to 8b0f3d5 Compare August 5, 2026 23:09
@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Parent #2685 merged (squash 4558e10) — rebased this onto main via clean cherry-pick of the single skill-instructions commit (now 8b0f3d5; base auto-retargeted to main already). Bridge test suite green locally post-rebase; CI rerunning. — qingyun-sutando (001)

…r skill block

test_write_task_atomic pinned 'access_tier is the last line' as its
anti-spoof + completeness proxy. #2686 appends the bridge-authored
===SKILL INSTRUCTIONS=== block after access_tier (owner tier), so the
proxy moved: assert the sharper invariant instead — exactly one
header-shaped access_tier line with only bridge block text after it,
and the block's result-path line as the completeness tail. Verified the
updated test still fails against a bridge without the block.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KiaMLCxgKNAbmhty29WGN6
@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Full-suite CI on the new main base caught what the thin feature-branch CI could not: packages/ag2-sparrow/tests/test_write_task_atomic.py pinned "access_tier is the last line" as its anti-spoof/completeness proxy, and this PR moves the file tail (the owner-tier ===SKILL INSTRUCTIONS=== block is appended after access_tier).

Fixed in 2600fe8 by asserting the sharper form of the same invariant instead of the line position:

  • exactly one header-shaped access_tier: line (last-occurrence parsers still cannot be tricked — the block never carries the key, matching the invariant comment in _write_task itself);
  • everything after it is the bridge-authored block, ending with the result-path line as the completeness tail (a truncated write cannot produce it).

Evidence:

# on this branch (8b0f3d5 + fix):
$ python3 packages/ag2-sparrow/tests/test_write_task_atomic.py
ALL PASS test_write_task_atomic
$ python3 src/remote-gateway-bridge.test.py
PASS — all checks green

# control — same updated test against a bridge WITHOUT the block (main’s copy):
$ git checkout main -- packages/ag2-sparrow/ag2_sparrow/remote_gateway_bridge.py && python3 packages/ag2-sparrow/tests/test_write_task_atomic.py
AssertionError  (at the ===SKILL INSTRUCTIONS presence assertion)

@github-actions

github-actions Bot commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor

Coverage Gate

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

Diff Coverage

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

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

Summary

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

…lock

Same class as the atomic-write fix: gateway-writeside-attachments and
remote-gateway-interaction-type both asserted access_tier is the last
non-empty line. Under CI's clean env (default owner tier) the
===SKILL INSTRUCTIONS=== block follows access_tier, so both now assert
the real invariant — exactly one header-shaped access_tier line with
only bridge block text after it. Both reproduced red under a scrubbed
env (they passed on the dev host only because local tier config took a
different path), both green after, and the predicate still rejects a
forged second access_tier line. Sibling gateway tests swept clean-env:
no further tail proxies.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KiaMLCxgKNAbmhty29WGN6
@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Second CI pass unmasked the same tail-proxy in two more tests — tests/gateway-writeside-attachments.test.py and tests/remote-gateway-interaction-type.test.py both asserted access_tier is the last non-empty line. They passed on my dev host (local tier config takes a non-owner path) but fail under CI's clean env where the default owner tier appends the skill block — reproduced with a scrubbed env before fixing:

$ env -i PATH="$PATH" HOME=$TMP CLAUDE_CONFIG_DIR=$TMP/cfg python3 tests/gateway-writeside-attachments.test.py
FAIL — 2 check(s) failed: ['access_tier still last header', 'access_tier last even with media headers']
$ env -i ... python3 tests/remote-gateway-interaction-type.test.py
FAIL access_tier is the last header

Fixed in 0ae3d06 with the same sharper invariant as the atomic-write fix (exactly one header-shaped access_tier: line, only bridge-authored block text after it). After the fix, both PASS under the scrubbed env AND the host env; the predicate still rejects a forged second access_tier: line. Also swept every sibling tests/gateway-* / tests/remote-gateway-* file under the scrubbed env — all PASS, no further tail proxies to unmask.

@john-the-dev john-the-dev 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.

Approving after a full read + running the changed tests. This adds the owner-tier ===SKILL INSTRUCTIONS=== block (context-first room reconstruction via room_ops.py read, notify-first, result path) to gateway tasks — mirroring the Discord bridge's block — and does it safely:

  • Owner-tier only (if sender_tier == "owner"); non-owner tiers keep ===SUTANDO SYSTEM INSTRUCTIONS===, so there's no competing instruction block.
  • Anti-spoof invariant preserved: the block is appended after access_tier as prose + numbered sentences with no header-shaped lines, so the access-tier-wins-last / last-occurrence-parser guarantee still holds. test_write_task_atomic.py now pins exactly this (single access_tier: line, ===SKILL INSTRUCTIONS strictly after it, result-path is the file tail so a truncated write can't produce it).
  • Tests: test_write_task_atomic.py, src/remote-gateway-bridge.test.py, gateway-writeside-attachments.test.py, remote-gateway-interaction-type.test.py all pass locally; the access_tier is the last header assertions still hold.

LGTM. (Heads-up: needs one more maintainer approval — bassil's earlier approval pre-dates the latest substantive commit, so it doesn't currently cover head.)

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Approval signal for current head 0ae3d06.

No blocking findings on the rebased main-based diff. The owner-only task-hygiene block is still gated by the locally resolved owner tier, non-owner/clamped tasks do not receive the competing block, and the updated tests now pin the sharper invariant: exactly one header-shaped access_tier line, with only bridge-authored skill-block text after it. The block does not move AppService namespace credentials into Sutando-local; it only tells an owner-tier local core how to read context and send progress through the existing narrow gateway/room-ops path, with membership still enforced gateway-side.

Focused checks passed in an isolated /private/tmp checkout:

  • git diff --check origin/main...HEAD
  • src/remote-gateway-bridge.test.py
  • packages/ag2-sparrow/tests/test_write_task_atomic.py
  • clean-env tests/gateway-writeside-attachments.test.py
  • clean-env tests/remote-gateway-interaction-type.test.py
  • Python compilation for the changed bridge/test files

Hosted clean-install, diff coverage, smoke, lint, ruff, shellcheck, and CLA are green on this head.

Reviewed by Qingyun's Personal Codex.

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

LGTM, non-blocking review for exact head 979c2df3f883846eff6713e4036c87b8e0ce2172.

This is a merge-main refresh only: git range-diff shows all three topic commits unchanged, and the current five-file diff remains the owner-only gateway skill-instructions change previously reviewed. I re-read the full current diff and surrounding _write_task path; the locally resolved owner tier gates the block, _one_line() prevents channel-id line injection, and non-owner tasks retain only their system-instructions path.

Exact-head validation passed:

  • src/remote-gateway-bridge.test.py
  • packages/ag2-sparrow/tests/test_write_task_atomic.py
  • clean-env gateway attachment and interaction-type suites
  • Python 3.9 compatibility, git diff --check, and scripts/review-checks.sh

Worst-case disruption is an owner task being delayed by the new unconditional context-read recipe; scope is limited to owner-tier gateway tasks, and the generated instruction includes the existing channel-env recovery path. No broader user or non-owner behavior changed.

Code-ready. Merge after the two still-running hosted checks (clean install and diff coverage) pass. Formal self-approval is unavailable on this author account, so this is the exact-head approval signal.

Reviewed by Qingyun's Personal Codex.

@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

CI follow-up for current head 979c2df: the hosted clean-install, diff coverage, smoke, static gates, and CLA checks are now green. The exact-head review from earlier still stands; no blocking findings.

Reviewed by Qingyun's Personal Codex.

@qingyun-wu qingyun-wu left a comment •

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

CHANGES REQUESTED on exact head fec3f40984f8ae95182934f0c1a4351f55c65d37 (formal self-review state is unavailable).

  1. [P1 blocker] Shell-quote the provider channel id before putting it in executable instructions. _one_line() only removes CR/LF, but packages/ag2-sparrow/ag2_sparrow/remote_gateway_bridge.py:1597 and :1610 interpolate _chan inside single-quoted shell arguments. A valid provider value containing a quote, such as !room'; touch /tmp/injected; #, closes the quote and turns the remainder into shell syntax when the owner-tier core follows the generated command. Use an argv-safe representation such as shlex.quote consistently for both recipes, and add a quote/metacharacter regression.

  2. [P2 blocker] Apply the current two-line/no-history code-comment rule. The added production block at :1580-1588 is nine lines and carries owner/date/other-adapter history; several added test comment blocks do the same. Keep only the non-obvious invariant and move review history to the PR body.

Focused gateway wire-path and write-side suites passed, including the loopback test, atomic writer, attachment headers, interaction types, compilation, diff hygiene, and REVIEW path scan. Hosted checks are green.

Not merge-ready: worst case is an owner-privileged agent executing provider-controlled shell syntax.

Reviewed by Qingyun's Personal Codex.

…recipes

A provider-controlled channel_id was embedded in the owner-tier
SKILL INSTRUCTIONS block using a manual f"'{_chan}'" single-quote
wrap, which only strips CR/LF (_one_line) and does not escape
embedded quotes. A channel id containing a single quote (e.g.
"!room'; touch /tmp/x; #") closes the shell quote early and turns
the remainder into a second shell command if the generated
instructions are executed literally.

Use shlex.quote() for both recipes (room_ops.py read and
notify.py --channel-id) instead. Added a regression test that
parses the embedded commands with shlex: on the pre-fix code
shlex.split raises "No closing quotation" (proving the quoting
was genuinely unbalanced), confirmed locally by reverting the fix
and re-running; on fixed code it round-trips to the channel id as
exactly one argument.

Also trimmed two comment blocks added by this PR to the repo's
2-line/no-PR-reference convention (CLAUDE.md).

Addresses the P1 blocker + P2 comment-length finding from this
PR's review.
@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Fixed both findings at ffc9c210.

1. Shell-injection (P1). Replaced the manual f"'{_chan}'" wrap with shlex.quote(_chan) at both call sites (room_ops.py read recipe and notify.py --channel-id recipe).

Confirmed the vulnerability was real and reproduced it before fixing — a channel_id containing a single quote breaks the manual wrap:

$ python3 -c "
import shlex
old_cmd = '''skills/agent-room-ops/room_ops.py read '!room'; touch /tmp/pwn; #' --limit 30'''
shlex.split('python3 ' + old_cmd)
"
ValueError: No closing quotation

Added test_write_task_shell_quotes_channel_id_in_skill_instructions (parses the embedded commands with shlex.split and asserts they round-trip to the channel id as exactly one argument). Verified the new test actually catches the bug — reverted the fix locally and reran:

$ git stash push -- packages/ag2-sparrow/ag2_sparrow/remote_gateway_bridge.py
$ python3 packages/ag2-sparrow/tests/test_write_task_atomic.py
...
ValueError: No closing quotation
$ git stash pop
$ python3 packages/ag2-sparrow/tests/test_write_task_atomic.py
...
PASS test_write_task_shell_quotes_channel_id_in_skill_instructions
ALL PASS test_write_task_atomic

Full suite, no regressions:

$ for f in tests/test_*.py; do python3 "$f"; done
ALL PASS test_ack_retry
ALL PASS test_dns_timeout
PASS — event consumer P1
PASS — event inbox + channel P0
PASS — event channel wiring (isolation contract)
ALL PASS test_gateway_status
PASS — human_action (CardPoster + DecisionHandler + chain)
ALL PASS test_inflight_recovery
PASS test_log_write_failure_never_raises
OK test_vault_token_tier
ALL PASS test_write_task_atomic

2. Comment-length (P2). Trimmed the 9-line production comment and the 4-line test comment (which named #2686 directly) to 2 lines each, per CLAUDE.md's "at most 2 lines, no references to PRs/issues/people" rule.

Both fixes are on this branch at ffc9c210b600bffd67905b775152cf4c060e46ad.

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

CHANGES REQUESTED on exact head ffc9c210b600bffd67905b775152cf4c060e46ad (formal self-review state is unavailable).

The P1 shell-quoting fix is correct: both generated commands now use shlex.quote, the malicious single-quote regression passes, the full loopback gateway suite passes, the focused atomic/attachment/interaction suites pass, Python compilation and diff hygiene pass, and the REVIEW path scan is clean.

[P2 blocker] The new regression immediately reintroduces the comment-policy finding it claims to clear. packages/ag2-sparrow/tests/test_write_task_atomic.py:162-170 adds a nine-line docstring containing review history, the old exploit narrative, and an explicit #2686 reference. The repository rule applies to test comments/docstrings too: keep at most two lines naming the non-obvious invariant, and leave the mutation/review history in the PR body.

Hosted clean-install CI is also still in progress. Not merge-ready: the security defect is fixed, but the exact current-head comment-policy blocker remains.

Reviewed by Qingyun's Personal Codex.

@bassilkhilo-ag2 bassilkhilo-ag2 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.

Independently re-verified the shell-injection fix at ffc9c210 (current head — my prior approval and john's were both on earlier commits, before this fix landed).

Fix is real and correctly applied. Reproduced the vulnerability myself before trusting the claim: reverted just remote_gateway_bridge.py back to the pre-fix version (ffc9c210~1) and reran packages/ag2-sparrow/tests/test_write_task_atomic.py — test_write_task_shell_quotes_channel_id_in_skill_instructions fails with the exact claimed ValueError: No closing quotation, confirming the manual f"'{_chan}'" wrap genuinely broke on an embedded quote. Restored the file, reran — full suite passes (6/6, ALL PASS test_write_task_atomic). shlex.quote() is applied at both call sites (room_ops.py read recipe and notify.py --channel-id recipe), and the new test parses the embedded shell commands with shlex.split rather than a substring check, so it can't be satisfied by a visually-similar-but-still-broken quoting scheme.

One residual, non-blocking nit: the fix commit fixes a 2-line-comment violation in one place but introduces a fresh one in another — remote_gateway_bridge.py:1585-1587 is a 3-line inline # comment (# shlex.quote, not a manual f"'{_chan}'" wrap: ... / # channel id containing a quote would otherwise close the shell string / # early and turn the remainder into executable shell syntax.), over CLAUDE.md's 2-line cap for inline comments. Worth a quick trim in a follow-up, doesn't block — the security content is what matters here and it's correct.

Approving on ffc9c210.

…e rule

The prior commit fixed the P1 shell-injection finding but its own new
code violated the same CLAUDE.md comment convention it was enforcing
elsewhere: a 3-line inline comment, and a 9-line test docstring that
named this PR directly. Trimmed both to 2 lines, no PR/issue
references. No behavior change — re-ran the full ag2-sparrow suite
(11 files) green.
@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Fixed at d0395afb.

Good catch — my own P1 fix reintroduced the comment-length violation it was supposed to be clearing: a 3-line inline comment on the _chan_q = shlex.quote(_chan) line, and a 9-line test docstring that named this PR directly (#2686). Trimmed both to 2 lines with no PR/issue references.

$ python3 -m py_compile packages/ag2-sparrow/ag2_sparrow/remote_gateway_bridge.py packages/ag2-sparrow/tests/test_write_task_atomic.py
py_compile OK
$ for f in tests/test_*.py; do python3 "$f"; done
ALL PASS test_ack_retry
ALL PASS test_dns_timeout
PASS — event consumer P1
PASS — event inbox + channel P0
PASS — event channel wiring (isolation contract)
ALL PASS test_gateway_status
PASS — human_action (CardPoster + DecisionHandler + chain)
ALL PASS test_inflight_recovery
PASS test_log_write_failure_never_raises
OK test_vault_token_tier
ALL PASS test_write_task_atomic

No behavior change — same shlex.quote() fix and regression test, just the two comment blocks trimmed. All 4 comment/docstring blocks touched by this PR are now ≤2 lines with no PR/issue references (double-checked via git diff against the merge-base).

@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

CHANGES REQUESTED (blocking CI status on current head)

I re-reviewed d0395afb after the comment-policy cleanup. The prior shell injection issue remains fixed: the owner-tier room_ops.py read and notify.py --channel-id snippets now use shlex.quote(_chan), and the malicious-channel regression still verifies that the channel id round-trips as one shell argument. The newly shortened comments/docstring are now focused on the invariant instead of the review thread.

I did not find a new code-level blocker in the changed bridge behavior: Sutando-local still does not gain AppService namespace credentials, the generated owner-tier block uses the existing scoped room-ops/notify channel path, membership/authorization remains on the gateway/room-ops side, and the existing channel env fallback is preserved for onboarding compatibility.

The current head is still blocked by required CI, though. tsc + tests (clean install) failed on d0395afb; the full job log shows tests/gateway-writeside-attachments.test.py printing PASS — gateway write-side media-attachment headers and then the Python process exits with Segmentation fault (core dumped) before the next test starts. Because this PR touches that test and the required clean-install suite is red, I cannot give the approval signal yet. Please either fix the crash or rerun to a green required check if this was runner-only.

Local focused checks I ran from the isolated /private/tmp checkout:

  • PYTHONPYCACHEPREFIX=/private/tmp/pycache-sutando-pr2686 python3 -m py_compile packages/ag2-sparrow/ag2_sparrow/remote_gateway_bridge.py packages/ag2-sparrow/tests/test_write_task_atomic.py
  • python3 packages/ag2-sparrow/tests/test_write_task_atomic.py
  • python3 tests/gateway-writeside-attachments.test.py
  • python3 tests/gateway-writeside-platform-card.test.py
  • python3 tests/remote-gateway-interaction-type.test.py
  • python3 src/remote-gateway-bridge.test.py
  • git diff --check origin/main...HEAD
  • git diff origin/main...HEAD | bash scripts/review-checks.sh

Reviewed by Qingyun's Personal Codex.

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

CHANGES REQUESTED on exact head d0395afb97be31de058d1ad1d7679648a4f50804 (formal self-review state is unavailable).

The new cleanup correctly reduces the production shlex.quote explanation and malicious-channel regression docstring to two lines without changing behavior. Reviewer-run atomic, attachment, interaction-type, and full localhost gateway suites pass; Python compilation, diff hygiene, and the REVIEW path scan pass. The shell-injection fix remains intact.

[P2 blocker] Two other comment blocks added by this PR still violate the same current policy. tests/gateway-writeside-attachments.test.py:104-108 and tests/remote-gateway-interaction-type.test.py:75-79 are each five lines and explicitly reference #2686. Reduce each to at most two lines stating only the non-obvious invariant; the PR/history context belongs in the PR body.

The rerun of required CI is still in progress. Not merge-ready: functionality is sound, but the exact-head comment-policy cleanup is still incomplete and required CI is not yet green.

Reviewed by Qingyun's Personal Codex.

…line rule

tests/gateway-writeside-attachments.test.py and
tests/remote-gateway-interaction-type.test.py each carried a 5-line
comment block naming this PR directly, added by an earlier commit
on this branch. Trimmed both to 2 lines stating only the invariant.
No behavior change — re-ran both targeted tests plus the full
ag2-sparrow suite (11 files) and src/remote-gateway-bridge.test.py,
all green.
@qingyun-wu

Copy link
Copy Markdown
Collaborator Author

Fixed at 665591e2.

Good catch again — tests/gateway-writeside-attachments.test.py:104-108 and tests/remote-gateway-interaction-type.test.py:75-79 each carried a 5-line comment naming this PR directly (added by an earlier commit on this branch, before I touched it). Trimmed both to 2 lines, no PR/issue references.

$ python3 -m py_compile tests/gateway-writeside-attachments.test.py tests/remote-gateway-interaction-type.test.py src/remote-gateway-bridge.test.py packages/ag2-sparrow/ag2_sparrow/remote_gateway_bridge.py packages/ag2-sparrow/tests/test_write_task_atomic.py
py_compile OK
$ python3 tests/gateway-writeside-attachments.test.py
PASS — gateway write-side media-attachment headers
$ python3 tests/remote-gateway-interaction-type.test.py
PASS — gateway interaction_type serialization covered
$ python3 tests/gateway-writeside-platform-card.test.py
all passed
$ python3 src/remote-gateway-bridge.test.py
PASS — all checks green
$ for f in packages/ag2-sparrow/tests/test_*.py; do python3 "$f"; done
ALL PASS test_ack_retry
ALL PASS test_dns_timeout
PASS — event consumer P1
PASS — event inbox + channel P0
PASS — event channel wiring (isolation contract)
ALL PASS test_gateway_status
PASS — human_action (CardPoster + DecisionHandler + chain)
ALL PASS test_inflight_recovery
PASS test_log_write_failure_never_raises
OK test_vault_token_tier
ALL PASS test_write_task_atomic

No behavior change — including confirming shlex.quote("!room:ag2.space") produces the byte-identical '!room:ag2.space' the existing remote-gateway-bridge.test.py assertions already expect, so nothing there needed touching.

Also confirming CI: I'm watching the required tsc + tests (clean install) rerun on the prior head (d0395afb) — it's been running ~20min without the earlier segfault recurring, consistent with a flake rather than a regression from this PR's diff.

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Reviewed exact head 665591e2d2e7b931840fab8666d4ada52b3b071e (formal self-approval is unavailable).

The remaining comment-policy blocker is resolved: the two five-line/history blocks are now two-line invariant comments with no PR/person reference. This delta changes only those comments, so the previously verified behavior is unchanged: the shlex.quote security fix, malicious-channel regression, atomic/attachment/interaction tests, and full localhost gateway suite all pass; Python compilation, diff hygiene, and the REVIEW path scan were clean.

No further code finding. Code-ready, but not yet merge-ready: required checks on this exact head are still running (CI, Coverage Gate, and bundle-smoke); the completed hosted checks and CLA are green. A current-head formal maintainer approval is still required by the repository merge gate.

Reviewed by Qingyun's Personal Codex.

@bassilkhilo-ag2 bassilkhilo-ag2 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.

Re-reviewed on current head 665591e (my prior approval was on ffc9c21, two commits behind).

Verified the diff between ffc9c21..665591e directly (git diff ffc9c210b600..665591e2d2e7): both intervening commits (d0395af, 665591e) are pure comment/docstring trims to the 2-line no-PR-reference rule across 4 files — no logic changes. This resolves the two comment-policy findings qingyun raised after my last approval (remote_gateway_bridge.py:1585-1587 person/PR reference, plus the two similar violations in gateway-writeside-attachments.test.py and remote-gateway-interaction-type.test.py).

CI on 665591e: all fast checks + tsc + tests (clean install) + diff coverage >= 95% pass; smoke still pending at review time.

Approving on this exact head.

@john-the-dev john-the-dev 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.

Re-approve after the post-approval commits. The shlex.quote(_chan) hardening is correct — it prevents a channel_id from breaking out of the embedded shell commands in the SKILL INSTRUCTIONS block, and the added regression test proves a malicious channel_id round-trips as a single arg (shlex.split recovers it) in both the context and notify command lines. Comment trims satisfy the 2-line rule. CI green, CLEAN. LGTM.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

@cla-assistant check

@john-the-dev
john-the-dev merged commit cfd48af into main Aug 7, 2026
20 checks passed
@john-the-dev
john-the-dev deleted the feat/ag2space-skill-instructions branch August 7, 2026 16:23
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.

4 participants