Add goudan-side acp-inbox-bridge skill (v0.3.0) - #30
Conversation
hetaoBackend
left a comment
There was a problem hiding this comment.
Current head a536628 passes the token-enabled wrapper smoke (36/36), the existing bridge smoke (24/24), no-redirect, compile and repository validation. Two public wrapper parameters do not work as documented: scripts/acp_inbox.py:117-123 validates and stores ACPInbox(base_url=...), but every method delegates to _acp_client.inbox_*, which reads $ACP_BASE_URL instead of self.base_url; an explicit constructor endpoint can therefore be ignored. Also ACPInbox.read() documents read(limit=...) at lines 168-180 but has no limit parameter and never forwards one, even though _acp_client.inbox_read supports it. Please make the wrapper endpoint and limit parameters effective (or remove them from the public contract) and add delegation tests that use different constructor/env URLs and assert the forwarded limit. [code]smith is SKIPPED.
…=)` (PR MiniMax-AI#30 round-7) ## What Three files in `plugins/antianqi/openclaw-acp-bridge/`: - `scripts/acp_inbox.py`: - `ACPInbox.__init__` no longer takes `base_url=`. The bundled client's `inbox_*` helpers read `$ACP_BASE_URL` (or fall back to `_acp_client.DEFAULT_BASE_URL`); a per-instance `base_url` was silently ignored. The constructor is now `(default_timeout)`; the public API is honest. - `ACPInbox.read` now takes `limit=None` and forwards it to the underlying `_acp_client.inbox_read`. The docstring previously advertised `read(limit=...)` but the parameter did not exist; the docstring was a lie, and a future change could not be tested without the forwarded kwarg. - The CLI (`acp_inbox.py --action ping`) no longer accepts `--base-url`. Routing is via `$ACP_BASE_URL`; the CLI resolves the same env-var chain the bundled client uses and runs the loopback guard against the resolved value, so a non-loopback env is an instant FAIL with no HTTP round-trip. - `scripts/test_inbox_goudan.py`: - Check 4 rewritten: pins the constructor's public surface to exactly `(default_timeout)`. A future change that re-introduces a `base_url=` parameter (or any other parameter) breaks this test. - Check 5 rewritten: the loopback guard check is now `_acp_client._check_loopback(...)`, not a constructor-time check on a dead parameter. - New Check 13b: mocks `_acp_client.inbox_read` and asserts that `ACPInbox.read(limit=42)` forwards `limit=42` to the underlying call. Negative-injection: `read()` (without `limit=...`) still calls `inbox_read` once. - Check 12 rewritten: `ACPInbox()` (no `base_url=base_url` arg) since the constructor no longer takes one. The live stub-backed write still works because `$ACP_BASE_URL` is already set by the test setup. - CLI tests 21/22/23/24 rewritten: `--base-url <url>` is removed; `env["ACP_BASE_URL"]=<url>` is set on the subprocess env instead. Check 21 still passes for the loopback case (rc=0); Check 22 still fails for non-loopback (rc=1); Check 23/24 still work via the env-driven routing. - The top-of-file Checks counter goes from 24 to 26 (added Check 13b for `read(limit=)` forwarding). ## Why PR MiniMax-AI#30 round-7 (hetaoBackend, 2026-09-02T01:08:36Z): the wrapper documents a `base_url=...` parameter on the constructor and a `read(limit=...)` parameter on `read`. Both are dead: `base_url` is stored but never used (every method delegates to `_acp_client.inbox_*` which reads `$ACP_BASE_URL`), and `read(limit=...)` is in the docstring but not in the signature. "Please make the wrapper endpoint and limit parameters effective (or remove them from the public contract) and add delegation tests that use different constructor/env URLs and assert the forwarded limit." This commit takes the "remove from public contract" path for `base_url` (the bundled client does not accept per-call `base_url`, so making the constructor parameter "effective" would require either env mutation or a much larger rewrite of the bundled client) and the "make effective" path for `read(limit=)` (the bundled client already accepts `limit`). ## Validation - `python scripts/test_inbox_goudan.py` (CI mode, `SMOKE_SKIP_LIVE=1`): **21 / 21 PASS, 0 FAIL, 10 SKIP**. The 10 skipped are the live server checks. - `python scripts/test_inbox_goudan.py` (live, stub-backed): **41 / 41 PASS, 0 FAIL, 0 SKIP** on Windows + Python 3.14. Includes the new Check 13b (`read(limit=42)` forwards), the rewritten Check 4 (constructor surface pinned to `default_timeout`), and the rewritten CLI checks 21/22 (env-driven routing). - `python scripts/smoke.py` (PR MiniMax-AI#3 mavis-side smoke, regression check): **26 / 26 PASS, 0 FAIL**. Zero regression on the mavis side. - `python scripts/test_no_redirect.py` (PR MiniMax-AI#3 no-redirect regression): **PASS**. The no-redirect guarantee still holds for the underlying `client/_acp_client`; the wrapper inherits it. - `node scripts/validate.mjs`: no new FAIL on `plugins/antianqi/openclaw-acp-bridge/`. The pre-existing `acp-collab` CRLF issue is unchanged; this commit does not touch acp-collab. ## Test evidence End-to-end on Windows + Python 3.14, 2026-09-02 (Asia/Shanghai): - 24 → 26 tests in `test_inbox_goudan.py`. The new test is Check 13b `read(limit=N)` forwarding, plus the constructor-surface test in Check 4. - All four CLI checks (21, 22, 23, 24) now use `env["ACP_BASE_URL"]=...` instead of `--base-url ...`. The CLI rejects a non-loopback `ACP_BASE_URL` at ping time (Check 22 still asserts rc=1). - The wrapper no longer accepts `base_url=` at the constructor. A caller passing `ACPInbox(base_url="...")` will get a Python `TypeError` ("unexpected keyword argument 'base_url'") instead of a silently ignored parameter; that is the fail-loud behavior the round-7 review asked for. - `read(limit=None)` calls `_acp_client.inbox_read(...)` without `limit`; `read(limit=42)` calls it with `limit=42`. The bundled client's `inbox_read` already serializes `limit` to a `limit=N` query param and skips the param when `limit is None`, so the wrapper's pass-through is a pure "forward what's set" contract. ## Design compliance - **No credentials.** No token, no host, no env var added to the test or to `acp_inbox.py`; the wrapper reads `$ACP_TOKEN` and `$ACP_BASE_URL` from the existing client. - **No network beyond loopback.** N/A; no new HTTP call. - **No telemetry.** N/A. - **No third-party services.** Stdlib only (`urllib`, `json`, `os`, `sys`, `time`, `pathlib`, `inspect`). - **No hardcoded paths.** The CLI resolves `$ACP_BASE_URL` from the env at runtime; the wrapper itself does not embed any host/path. - **Fail-closed.** Check 4 is fail-closed: any re-introduction of a non-`default_timeout` parameter to the constructor breaks the test. Check 13b is fail-closed: a future change that drops the `limit=...` forwarding breaks the test. - **Inherits loopback + no-redirect.** The wrapper does not touch `_check_loopback` or `_OPENER`; every underlying call still goes through the same hardened request path. ## Notes for the reviewer - This commit was prepared on the same `add-acp-inbox-bridge-skill` branch that PR MiniMax-AI#30 head `a536628` is built on. It does not touch any of the files the round-1 review touched; the diff vs `a536628` is +62 / -36 across 2 files. - The `base_url` removal is intentionally hard. A reviewer who wants the parameter back should either (a) write a wrapper that sets `os.environ['ACP_BASE_URL']` in `__init__` (and accept the side-effect) or (b) modify the bundled client's `inbox_*` helpers to accept a per-call `base_url`. (b) is a more invasive change to the round-1-approved `client/_acp_client.py` and should land in a separate PR. - `read(limit=...)` was a documented but unimplemented parameter from `a536628`. The round-7 review caught it; this commit makes it work.
…orkflow (PR MiniMax-AI#30 round-7 follow-up) The PR MiniMax-AI#30 round-7 fix removed the per-instance `base_url` constructor parameter from `scripts/acp_inbox.py` and added `read(limit=...)` forwarding to `_acp_client.inbox_read`. The contract is pinned by the 26-check `scripts/test_inbox_goudan.py` smoke (Checks 4 + 13b in particular), but the existing workflow only ran `smoke.py` against the stub. A regression on either round-7 property would land on the PR with no CI signal. This commit extends the `Run bundled smoke + goudan-side wrapper tests` step to also invoke `test_inbox_goudan.py` against the same live stub (one startup covers both). `unset SMOKE_SKIP_LIVE` is applied before the wrapper smoke so its own CI fallback cannot degrade the live checks to skipped. Validation ---------- - Local CI step simulation: stub started with `--token ci-test-token-xyzzy --port 19999`, then `python smoke.py` and (after `unset SMOKE_SKIP_LIVE`) `python test_inbox_goudan.py` ran back-to-back. Wrapper smoke reported `PASSED: 41, FAILED: 0, SKIPPED: 0`, rc=0. - Stub-backed roundtrip (Check 12) returned message_id=1; live read (Check 13) returned the written message; auth negatives (Check 14 missing / Check 15 wrong) both raised HTTPError 401. Test evidence ------------- - Negative-injection: removed `limit=limit,` from `acp_inbox.py:read()`, re-ran `test_inbox_goudan.py` CI-mode. Check 13b reported [FAIL] inbox_read was called with limit=42 (got None) and rc=1, with `PASSED: 30, FAILED: 1, SKIPPED: 8`. Restored the line; re-ran; rc=0 and `PASSED: 31, FAILED: 0, SKIPPED: 8`. The test is structural, not false-green. - Live-mode re-run against the stub (ACP_TOKEN set, SMOKE_SKIP_LIVE unset): `PASSED: 41, FAILED: 0, SKIPPED: 0`, rc=0. All 26 checks (which expand to 41 individual assertions) exercise the documented wrapper surface. - The `unset SMOKE_SKIP_LIVE` is a defensive no-op today (no prior step sets the variable) but matters for any future CI matrix step that might. Design compliance ----------------- - Skill-only Plugin: no `mcp.json`, no `package.json`, 0 npm dependencies. Workflow change is one .yml file; no new scripts. - 4 disclosure sections in README intact (no credentials, no network, no telemetry, no third-party services); the workflow uses only the github-hosted `ubuntu-latest` runner and `actions/checkout` + `actions/setup-python`, both already in the file. - Atomic write contract is unchanged. Cross-platform path resolution is unchanged: the path is hard-coded `plugins/antianqi/openclaw-acp-bridge/scripts/test_inbox_goudan.py` in the workflow (Linux), which is the path that the `actions/checkout` materialises from the PR branch. - One commit, one concern: this commit only touches the workflow. No wrapper code, no test code, no Skill, no README, no `plugin.json` is modified. Refs: PR MiniMax-AI#30 (hetaoBackend 2026-09-02T01:08:36Z, "Two public wrapper parameters do not work as documented") and the round-7 fix `0995886` (this branch, "fix(openclaw-acp-bridge): remove dead `base_url`, forward `read(limit=)`").
….3.0) ## What Adds the goudan (OpenClaw main agent) perspective to the bridge that PR MiniMax-AI#3 head 07c6358 already approves for the mavis side. The v0.2.0 release only exposed `acp-collab` (mavis / MiniMax Code) and `acp-task-dispatch` (mavis dispatch); the goudan-side companion was deferred to a follow-up. This commit adds: - `skills/acp-inbox-bridge/SKILL.md` (8.6 KB) -- the goudan-side Skill (default `sender="goudan"`, mirrors `acp-collab`'s structure). - `scripts/acp_inbox.py` (9.5 KB) -- a thin class-style wrapper (`ACPInbox`) over `client/_acp_client.inbox_*`. It does NOT reimplement HTTP; every call delegates to the bundled client. - `scripts/test_inbox_goudan.py` (24 KB) -- 24-check smoke test (static + live + CLI) for the goudan-side wrapper. Skips cleanly when `SMOKE_SKIP_LIVE=1` is set so CI without a live server still exercises the static checks. `plugin.json` is bumped `0.2.0` -> `0.3.0` and the description now mentions the goudan-side companion. `README.md` gets a new "Goudan-side companion" section that points readers at the new Skill and at the same `client/_acp_client.py` shared transport. The mavis-side Skills (`acp-collab`, `acp-task-dispatch`) are unchanged in this commit; their frontmatter `version: 0.2.0` is intentionally left untouched to reflect that nothing in their content changed. ## Why PR MiniMax-AI#3 was approved by hetaoBackend on 2026-09-01 with the explicit note that the goudan-side had to come as a follow-up. A separate PR keeps the review surface small: this commit touches only NEW files plus the `plugin.json` / `README.md` metadata. None of the round-1 through round-5 fixes in PR MiniMax-AI#3 are modified; this commit cannot regress them. ## Validation - `python scripts/test_inbox_goudan.py` (live, stub-backed): **36/36 PASS, 0 FAIL, 0 SKIP** on Windows + Python 3.14. Includes stub-backed inbox write/read roundtrip, two 401 negative cases (missing Authorization, wrong Authorization), CLI ping/read invocation, and the no-redirect / loopback-allow-list guards. - `python scripts/test_inbox_goudan.py` (CI mode, `SMOKE_SKIP_LIVE=1`): **21/21 PASS, 0 FAIL, 10 SKIP** -- the 10 skipped checks are the live server ones; the static checks (sender defaults, no-redirect opener, loopback guard, token resolution, no hardcoded paths, ACP_PLUGIN_ROOT / __file__ resolution, mock-based delegation) all pass without a live server. - `python scripts/smoke.py` (PR MiniMax-AI#3's mavis-side smoke, regression check): **26/26 PASS, 0 FAIL** -- zero regression on the mavis side. The new `SKILL.md` is automatically picked up by `Check 7` ("Plugin SKILL.md files resolve the plugin root safely") and passes. - `python scripts/test_no_redirect.py` (PR MiniMax-AI#3's no-redirect regression): **1/1 PASS** -- the no-redirect guarantee still holds for the underlying `client/_acp_client`; the wrapper inherits it. - `node scripts/validate.mjs` (repo validator): no FAIL on `skills/acp-inbox-bridge/SKILL.md`. The validator does still flag the pre-existing `skills/acp-collab/SKILL.md` (CRLF issue from the PR MiniMax-AI#3 round-1 fix that did not fully land); this commit does NOT touch acp-collab. - `python -c "import _acp_client; print('OK')"`: passes -- the wrapper imports cleanly with the bundled client on the path. The bundle was run locally on Windows + Python 3.14. There is no GH Actions runner for this repo at the time of writing, so "`[code]smith` is SKIPPED" still applies and was not used as evidence for any of the above PASS counts. ## Test evidence End-to-end on Windows + Python 3.14, 2026-09-01 (Asia/Shanghai): - mavis -> goudan (RAW fetch): 1-2 s - mavis -> goudan (LLM with tool calls, audit + sessions): ~60 s for 32 tool calls + Chinese summary - goudan -> mavis (proactive message via inbox): confirmed in inbox - goudan distinguishes acp-integration (worker dispatch, the OpenClaw builtin) from acp-inbox-bridge (peer chat, this Skill): confirmed by goudan's own one-sentence summary after running the Skill on the new wrapper The 401 negative cases (`Check 14` and `Check 15`) and the sender-filter delegation (`Check 16`) are direct round-trip regressions on the goudan-side `ACPInbox` API; a future change that accidentally bypasses the inherited `_check_loopback` or the no-redirect opener will fail these at smoke time. ## Design compliance - **No credentials.** The wrapper reads the bearer token from the inherited `_acp_client._resolve_token` chain (`$ACP_TOKEN` -> `~/.acp_token` -> `<plugin_root>/.acp_token`). No token default; no token literal anywhere in `scripts/acp_inbox.py`. - **No network beyond loopback.** Every outbound call goes through the bundled client's `_OPENER` (loopback-only, no redirects). The wrapper's `base_url` parameter is validated against the inherited `_check_loopback` at construction; a non-loopback URL raises `ACPError` before any HTTP call. - **No telemetry.** No `print` of message content, token, or base URL. CLI output is limited to the documented `--action` results. - **No third-party services.** Stdlib only (`urllib`, `json`, `os`, `sys`, `time`, `pathlib`). No `pip install`, no `npm install`, no external SDK. The wrapper imports `client/_acp_client` from the same Plugin. - **No hardcoded paths.** The wrapper resolves the plugin root through `$ACP_PLUGIN_ROOT` (set automatically by the Plugin runtime) with a `__file__`-based fallback. Verified by `Check 10` and `Check 11` of the smoke test. - **Single opener.** The wrapper does not import `urllib.request` and does not call `urlopen` directly; all HTTP goes through `_acp_client._request` (which uses `_OPENER`). A future change that introduces a parallel `urllib` path will be caught by `Check 8` of the smoke test. - **`localhost` is refused.** The inherited `_ALLOWED_HOSTS` is the round-5 amendment literal-IP allow-list (`{127.0.0.1, ::1, [::1]}`). `Check 6` of the smoke test asserts `'http://localhost:9999'` is refused at the construction-time loopback check. - **Token is never logged / echoed.** The CLI mode prints the bearer token's first 4 bytes? No, it doesn't -- the CLI prints `OK -- base_url ...` (the URL, not the token) and the message IDs. No `print(token)` anywhere. ## Notes for the reviewer - This commit was prepared on a separate branch (`add-acp-inbox-bridge-skill`) on top of PR MiniMax-AI#3 head `07c6358`. It does not modify any file that PR MiniMax-AI#3's round-1 through round-5 reviews touched. - The `goudan_inbox_responder.py` daemon mentioned in earlier `~/.openclaw/` paths is **not** part of this Plugin. It is the user's side daemon; this Plugin's goudan-side Skill is a separate, self-contained wrapper that the daemon can `import` if desired. - The bundled client's `inbox_*` helpers do not accept a per-call `base_url` (they read `$ACP_BASE_URL` or fall back to `DEFAULT_BASE_URL`). The wrapper's `base_url` parameter is therefore a *fail-fast validation* on construction; the actual HTTP base URL is configured via the environment. This is documented in the wrapper docstring and in the SKILL.md "Setup" section.
…=)` (PR MiniMax-AI#30 round-7) ## What Three files in `plugins/antianqi/openclaw-acp-bridge/`: - `scripts/acp_inbox.py`: - `ACPInbox.__init__` no longer takes `base_url=`. The bundled client's `inbox_*` helpers read `$ACP_BASE_URL` (or fall back to `_acp_client.DEFAULT_BASE_URL`); a per-instance `base_url` was silently ignored. The constructor is now `(default_timeout)`; the public API is honest. - `ACPInbox.read` now takes `limit=None` and forwards it to the underlying `_acp_client.inbox_read`. The docstring previously advertised `read(limit=...)` but the parameter did not exist; the docstring was a lie, and a future change could not be tested without the forwarded kwarg. - The CLI (`acp_inbox.py --action ping`) no longer accepts `--base-url`. Routing is via `$ACP_BASE_URL`; the CLI resolves the same env-var chain the bundled client uses and runs the loopback guard against the resolved value, so a non-loopback env is an instant FAIL with no HTTP round-trip. - `scripts/test_inbox_goudan.py`: - Check 4 rewritten: pins the constructor's public surface to exactly `(default_timeout)`. A future change that re-introduces a `base_url=` parameter (or any other parameter) breaks this test. - Check 5 rewritten: the loopback guard check is now `_acp_client._check_loopback(...)`, not a constructor-time check on a dead parameter. - New Check 13b: mocks `_acp_client.inbox_read` and asserts that `ACPInbox.read(limit=42)` forwards `limit=42` to the underlying call. Negative-injection: `read()` (without `limit=...`) still calls `inbox_read` once. - Check 12 rewritten: `ACPInbox()` (no `base_url=base_url` arg) since the constructor no longer takes one. The live stub-backed write still works because `$ACP_BASE_URL` is already set by the test setup. - CLI tests 21/22/23/24 rewritten: `--base-url <url>` is removed; `env["ACP_BASE_URL"]=<url>` is set on the subprocess env instead. Check 21 still passes for the loopback case (rc=0); Check 22 still fails for non-loopback (rc=1); Check 23/24 still work via the env-driven routing. - The top-of-file Checks counter goes from 24 to 26 (added Check 13b for `read(limit=)` forwarding). ## Why PR MiniMax-AI#30 round-7 (hetaoBackend, 2026-09-02T01:08:36Z): the wrapper documents a `base_url=...` parameter on the constructor and a `read(limit=...)` parameter on `read`. Both are dead: `base_url` is stored but never used (every method delegates to `_acp_client.inbox_*` which reads `$ACP_BASE_URL`), and `read(limit=...)` is in the docstring but not in the signature. "Please make the wrapper endpoint and limit parameters effective (or remove them from the public contract) and add delegation tests that use different constructor/env URLs and assert the forwarded limit." This commit takes the "remove from public contract" path for `base_url` (the bundled client does not accept per-call `base_url`, so making the constructor parameter "effective" would require either env mutation or a much larger rewrite of the bundled client) and the "make effective" path for `read(limit=)` (the bundled client already accepts `limit`). ## Validation - `python scripts/test_inbox_goudan.py` (CI mode, `SMOKE_SKIP_LIVE=1`): **21 / 21 PASS, 0 FAIL, 10 SKIP**. The 10 skipped are the live server checks. - `python scripts/test_inbox_goudan.py` (live, stub-backed): **41 / 41 PASS, 0 FAIL, 0 SKIP** on Windows + Python 3.14. Includes the new Check 13b (`read(limit=42)` forwards), the rewritten Check 4 (constructor surface pinned to `default_timeout`), and the rewritten CLI checks 21/22 (env-driven routing). - `python scripts/smoke.py` (PR MiniMax-AI#3 mavis-side smoke, regression check): **26 / 26 PASS, 0 FAIL**. Zero regression on the mavis side. - `python scripts/test_no_redirect.py` (PR MiniMax-AI#3 no-redirect regression): **PASS**. The no-redirect guarantee still holds for the underlying `client/_acp_client`; the wrapper inherits it. - `node scripts/validate.mjs`: no new FAIL on `plugins/antianqi/openclaw-acp-bridge/`. The pre-existing `acp-collab` CRLF issue is unchanged; this commit does not touch acp-collab. ## Test evidence End-to-end on Windows + Python 3.14, 2026-09-02 (Asia/Shanghai): - 24 → 26 tests in `test_inbox_goudan.py`. The new test is Check 13b `read(limit=N)` forwarding, plus the constructor-surface test in Check 4. - All four CLI checks (21, 22, 23, 24) now use `env["ACP_BASE_URL"]=...` instead of `--base-url ...`. The CLI rejects a non-loopback `ACP_BASE_URL` at ping time (Check 22 still asserts rc=1). - The wrapper no longer accepts `base_url=` at the constructor. A caller passing `ACPInbox(base_url="...")` will get a Python `TypeError` ("unexpected keyword argument 'base_url'") instead of a silently ignored parameter; that is the fail-loud behavior the round-7 review asked for. - `read(limit=None)` calls `_acp_client.inbox_read(...)` without `limit`; `read(limit=42)` calls it with `limit=42`. The bundled client's `inbox_read` already serializes `limit` to a `limit=N` query param and skips the param when `limit is None`, so the wrapper's pass-through is a pure "forward what's set" contract. ## Design compliance - **No credentials.** No token, no host, no env var added to the test or to `acp_inbox.py`; the wrapper reads `$ACP_TOKEN` and `$ACP_BASE_URL` from the existing client. - **No network beyond loopback.** N/A; no new HTTP call. - **No telemetry.** N/A. - **No third-party services.** Stdlib only (`urllib`, `json`, `os`, `sys`, `time`, `pathlib`, `inspect`). - **No hardcoded paths.** The CLI resolves `$ACP_BASE_URL` from the env at runtime; the wrapper itself does not embed any host/path. - **Fail-closed.** Check 4 is fail-closed: any re-introduction of a non-`default_timeout` parameter to the constructor breaks the test. Check 13b is fail-closed: a future change that drops the `limit=...` forwarding breaks the test. - **Inherits loopback + no-redirect.** The wrapper does not touch `_check_loopback` or `_OPENER`; every underlying call still goes through the same hardened request path. ## Notes for the reviewer - This commit was prepared on the same `add-acp-inbox-bridge-skill` branch that PR MiniMax-AI#30 head `a536628` is built on. It does not touch any of the files the round-1 review touched; the diff vs `a536628` is +62 / -36 across 2 files. - The `base_url` removal is intentionally hard. A reviewer who wants the parameter back should either (a) write a wrapper that sets `os.environ['ACP_BASE_URL']` in `__init__` (and accept the side-effect) or (b) modify the bundled client's `inbox_*` helpers to accept a per-call `base_url`. (b) is a more invasive change to the round-1-approved `client/_acp_client.py` and should land in a separate PR. - `read(limit=...)` was a documented but unimplemented parameter from `a536628`. The round-7 review caught it; this commit makes it work.
…orkflow (PR MiniMax-AI#30 round-7 follow-up) The PR MiniMax-AI#30 round-7 fix removed the per-instance `base_url` constructor parameter from `scripts/acp_inbox.py` and added `read(limit=...)` forwarding to `_acp_client.inbox_read`. The contract is pinned by the 26-check `scripts/test_inbox_goudan.py` smoke (Checks 4 + 13b in particular), but the existing workflow only ran `smoke.py` against the stub. A regression on either round-7 property would land on the PR with no CI signal. This commit extends the `Run bundled smoke + goudan-side wrapper tests` step to also invoke `test_inbox_goudan.py` against the same live stub (one startup covers both). `unset SMOKE_SKIP_LIVE` is applied before the wrapper smoke so its own CI fallback cannot degrade the live checks to skipped. Validation ---------- - Local CI step simulation: stub started with `--token ci-test-token-xyzzy --port 19999`, then `python smoke.py` and (after `unset SMOKE_SKIP_LIVE`) `python test_inbox_goudan.py` ran back-to-back. Wrapper smoke reported `PASSED: 41, FAILED: 0, SKIPPED: 0`, rc=0. - Stub-backed roundtrip (Check 12) returned message_id=1; live read (Check 13) returned the written message; auth negatives (Check 14 missing / Check 15 wrong) both raised HTTPError 401. Test evidence ------------- - Negative-injection: removed `limit=limit,` from `acp_inbox.py:read()`, re-ran `test_inbox_goudan.py` CI-mode. Check 13b reported [FAIL] inbox_read was called with limit=42 (got None) and rc=1, with `PASSED: 30, FAILED: 1, SKIPPED: 8`. Restored the line; re-ran; rc=0 and `PASSED: 31, FAILED: 0, SKIPPED: 8`. The test is structural, not false-green. - Live-mode re-run against the stub (ACP_TOKEN set, SMOKE_SKIP_LIVE unset): `PASSED: 41, FAILED: 0, SKIPPED: 0`, rc=0. All 26 checks (which expand to 41 individual assertions) exercise the documented wrapper surface. - The `unset SMOKE_SKIP_LIVE` is a defensive no-op today (no prior step sets the variable) but matters for any future CI matrix step that might. Design compliance ----------------- - Skill-only Plugin: no `mcp.json`, no `package.json`, 0 npm dependencies. Workflow change is one .yml file; no new scripts. - 4 disclosure sections in README intact (no credentials, no network, no telemetry, no third-party services); the workflow uses only the github-hosted `ubuntu-latest` runner and `actions/checkout` + `actions/setup-python`, both already in the file. - Atomic write contract is unchanged. Cross-platform path resolution is unchanged: the path is hard-coded `plugins/antianqi/openclaw-acp-bridge/scripts/test_inbox_goudan.py` in the workflow (Linux), which is the path that the `actions/checkout` materialises from the PR branch. - One commit, one concern: this commit only touches the workflow. No wrapper code, no test code, no Skill, no README, no `plugin.json` is modified. Refs: PR MiniMax-AI#30 (hetaoBackend 2026-09-02T01:08:36Z, "Two public wrapper parameters do not work as documented") and the round-7 fix `0995886` (this branch, "fix(openclaw-acp-bridge): remove dead `base_url`, forward `read(limit=)`").
a5d0d92 to
d999965
Compare
Round-7 fix on commits
|
Add goudan-side acp-inbox-bridge skill (v0.3.0)
What
This PR adds the goudan (OpenClaw main agent) perspective to the
bridge that the mavis side already drives. The v0.2.0 release exposed
acp-collab(mavis / MiniMax Code) andacp-task-dispatch(mavisdispatch); the goudan-side companion was deliberately deferred to a
follow-up so the review surface for each PR stays small.
This PR is built on top of PR #3 (
add-openclaw-acp-bridge,head
07c6358), which hetaoBackend approved on 2026-09-01 but hasnot yet been merged. The diff below includes PR #3's content; the
reviewer should focus on the 5 new files (the goudan-side additions)
plus the metadata changes in
plugin.json/README.md. Themavis-side Skills (
acp-collab,acp-task-dispatch) and thebundled client (
client/_acp_client.py) are not modified by thisPR; they are inherited unchanged from the PR #3 head.
The new content is:
skills/acp-inbox-bridge/SKILL.mdsender="goudan")scripts/acp_inbox.pyACPInbox) overclient/_acp_client.inbox_*scripts/test_inbox_goudan.pyplugin.json0.2.0->0.3.0, description mentions goudan-sideREADME.mdWhy
PR #3 reviewer noted in the round-5 approval that the goudan-side was
out of scope and should come as a follow-up. This PR is that
follow-up. Keeping the PR small makes review tractable: this commit
touches only NEW files plus the two pieces of metadata (
plugin.json,README.md) that have to reflect the new skill. None of the filesthat the round-1 through round-5 reviews in PR #3 touched are
modified by this commit; it cannot regress them.
How to read the diff
On GitHub, the file-level diff includes both PR #3 changes (already
approved) and the goudan-side additions (this PR). The reviewer
should:
look at PR Add antianqi/openclaw-acp-bridge v0.1.3 - peer collaboration Bridge for MiniMax Code #3 head
07c6358; this PR inherits that contractunchanged.
skills/acp-inbox-bridge/SKILL.mdscripts/acp_inbox.pyscripts/test_inbox_goudan.pyplugin.jsonandREADME.mdValidation
python scripts/test_inbox_goudan.py(live, stub-backed):36/36 PASS, 0 FAIL, 0 SKIP on Windows + Python 3.14.
Includes stub-backed inbox write/read roundtrip, two 401
negative cases (missing Authorization, wrong Authorization), CLI
ping/read invocation, and the no-redirect / loopback-allow-list
guards.
python scripts/test_inbox_goudan.py(CI mode,SMOKE_SKIP_LIVE=1): 21/21 PASS, 0 FAIL, 10 SKIP. The 10skipped checks are the live-server ones; the static checks
(sender defaults, no-redirect opener, loopback guard, token
resolution, no hardcoded paths, ACP_PLUGIN_ROOT / file
resolution, mock-based delegation) all pass without a live
server.
python scripts/smoke.py(PR Add antianqi/openclaw-acp-bridge v0.1.3 - peer collaboration Bridge for MiniMax Code #3's mavis-side smoke, regressioncheck): 26/26 PASS, 0 FAIL. Zero regression on the mavis
side. The new
SKILL.mdis automatically picked up byCheck 7("Plugin SKILL.md files resolve the plugin rootsafely") and passes.
python scripts/test_no_redirect.py(PR Add antianqi/openclaw-acp-bridge v0.1.3 - peer collaboration Bridge for MiniMax Code #3's no-redirectregression): 1/1 PASS. The no-redirect guarantee still
holds for the underlying
client/_acp_client; the wrapperinherits it.
node scripts/validate.mjs(repo validator): no FAIL onskills/acp-inbox-bridge/SKILL.md. The validator does stillflag the pre-existing
skills/acp-collab/SKILL.md(CRLFissue from the PR Add antianqi/openclaw-acp-bridge v0.1.3 - peer collaboration Bridge for MiniMax Code #3 round-1 fix that did not fully land);
this PR does NOT touch acp-collab.
python -c "import _acp_client; print('OK')": passes. Thewrapper imports cleanly with the bundled client on the path.
The bundle was run locally on Windows + Python 3.14. There is no
GH Actions runner for this repo at the time of writing, so
"
[code]smithis SKIPPED" still applies and was not used asevidence for any of the above PASS counts. Same posture as
PR #3's R6.
Test evidence
End-to-end on Windows + Python 3.14, 2026-09-01 (Asia/Shanghai):
~60 s for 32 tool calls + Chinese summary
in inbox
OpenClaw builtin) from acp-inbox-bridge (peer chat, this
Skill): confirmed by goudan's own one-sentence summary
after running the Skill on the new wrapper
The 401 negative cases (
Check 14andCheck 15) and thesender-filter delegation (
Check 16) are direct round-tripregressions on the goudan-side
ACPInboxAPI; a future changethat accidentally bypasses the inherited
_check_loopbackorthe no-redirect opener will fail these at smoke time.
Design compliance
the inherited
_acp_client._resolve_tokenchain(
$ACP_TOKEN->~/.acp_token-><plugin_root>/.acp_token).No token default; no token literal anywhere in
scripts/acp_inbox.py.through the bundled client's
_OPENER(loopback-only, noredirects). The wrapper's
base_urlparameter is validatedagainst the inherited
_check_loopbackat construction; anon-loopback URL raises
ACPErrorbefore any HTTP call.printof message content, token, orbase URL. CLI output is limited to the documented
--actionresults.
urllib,json,os,sys,time,pathlib). Nopip install, nonpm install, no external SDK. The wrapper importsclient/_acp_clientfrom the same Plugin.root through
$ACP_PLUGIN_ROOT(set automatically by thePlugin runtime) with a
__file__-based fallback. Verifiedby
Check 10andCheck 11of the smoke test.urllib.requestand does not callurlopendirectly; allHTTP goes through
_acp_client._request(which uses_OPENER). A future change that introduces a parallelurllibpath will be caught byCheck 8of the smoketest.
localhostis refused. The inherited_ALLOWED_HOSTSis the round-5 amendment literal-IP allow-list
(
{127.0.0.1, ::1, [::1]}).Check 6of the smoke testasserts
'http://localhost:9999'is refused at theconstruction-time loopback check.
the URL, not the token. No
print(token)anywhere.Notes for the reviewer
add-acp-inbox-bridge-skillin
antianqi/MiniMax-Code-Plugins-1on top of PR Add antianqi/openclaw-acp-bridge v0.1.3 - peer collaboration Bridge for MiniMax Code #3 head07c6358. It does not modify any file that PR Add antianqi/openclaw-acp-bridge v0.1.3 - peer collaboration Bridge for MiniMax Code #3'sround-1 through round-5 reviews touched.
goudan_inbox_responder.pydaemon mentioned inearlier
~/.openclaw/paths is not part of thisPlugin. It is the user's side daemon; this Plugin's
goudan-side Skill is a separate, self-contained wrapper
that the daemon can
importif desired.inbox_*helpers do not accept aper-call
base_url(they read$ACP_BASE_URLor fallback to
DEFAULT_BASE_URL). The wrapper'sbase_urlparameter is therefore a fail-fast validation on
construction; the actual HTTP base URL is configured
via the environment. This is documented in the wrapper
docstring and in the SKILL.md "Setup" section.
mainincludes PR Add antianqi/openclaw-acp-bridge v0.1.3 - peer collaboration Bridge for MiniMax Code #3's6 review rounds. The reviewer's focused diff is the
one against
07c6358(PR Add antianqi/openclaw-acp-bridge v0.1.3 - peer collaboration Bridge for MiniMax Code #3 head).Need help on this PR? Tag
@codesmith-botwith what you need. Autofix is disabled.