* fix(gateway): restart channels after secret reload
* fix(gateway): serialize secrets.reload and isolate channel restart errors
Address review feedback from Greptile (P1), Codex (P2), and Aisle (Medium,
CWE-362) on #70720:
- Serialize the entire secrets.reload path through a promise tail lock so
concurrent callers cannot overlap the stop/start loop or diff against a
stale pre-activation snapshot.
- Wrap each channel's stop/start pair in a try/catch so one channel failing
to restart does not leave other changed channels unrestarted.
- Register slack/zalo/discord channel plugins with reload.configPrefixes in
the test setup so channels.<id>.* diff paths actually match a restart rule
(without this, the diff falls through to restart-gateway and the handler
never enters the per-channel restart branch).
- Add tests covering concurrent-reload serialization and per-channel
restart-failure isolation.
* fix(gateway): surface channel restart failures from secrets.reload
Address review feedback on the previous commit:
- Codex P1: `secrets.reload` swallowed per-channel restart failures and
still returned `{ ok: true }`, so a rotation that left a channel on the
old secret looked successful to the caller. The handler now collects
restart failures during the loop and throws an aggregate error after
attempting every channel, so the client-side RPC response surfaces the
partial failure while unaffected channels still restart (preserving the
original Greptile P1 non-cascading semantic).
- Greptile P2: test mock-call assertions sorted the captured channel
arguments so they no longer depend on `Set`/object-key iteration order,
which is not a stable contract of the handler.
* fix(gateway): harden secrets reload followups
* docs(changelog): note secret-backed channel restart on secrets.reload
* test(gateway): align secrets reload snapshot activation
* test(gateway): reset plugin runtime state in aux handlers
* fix(gateway): refresh reload rules and roll back channels
* fix(gateway): harden secrets.reload rollback tests
* test(gateway): inject aux handler reload plan
* test(gateway): avoid resettable reload-plan mocks
* test(gateway): isolate aux handler tests from skip env-var leakage
test-helpers.mocks.ts and test-helpers.server.ts set
OPENCLAW_SKIP_CHANNELS=1 / OPENCLAW_SKIP_PROVIDERS=1 at module load. When
a shared vitest worker imports those helpers before this file's tests
run, the leaked env vars route the secrets.reload skip-mode branch and
the channel restart loop never fires. Add a beforeEach that clears both
env vars so the suite is independent of worker import order.
* fix(gateway): restore required generation on secrets.reload rollback
setCurrentSharedGatewaySessionGeneration can clear `required` as a side
effect of activating a new generation. The previous rollback path
restored only `current`, leaving `required` cleared and weakening
shared-gateway auth-generation enforcement after a failed reload (Aisle
CWE-287). Capture both fields before activation and restore both in the
catch block. Add a focused regression test that locks in the contract.
* fix(gateway): track restart channels for rollback before stopChannel awaits
Pushing to stoppedChannels only after `await stopChannel` succeeded meant
that if stopChannel rejected mid-call (for example, a plugin stopAccount
hook throws after the runtime already closed the socket), the rollback
loop skipped that channel entirely. A failed secrets.reload could then
leave the channel down. Track the channel before awaiting so rollback
always attempts to bring it back, and add a regression test.