Phase 5 handoff: Zulip control-plane parity
Instruction to the next agent
Implement Phase 5 of the OpenClaw–Zulip integration: control-plane parity. Work from the existing repositories and live working tree; do not assume a clean checkout. Begin with a read-only recovery assessment in both repositories, then report the recovered state and a bounded implementation plan before making changes.
The owner has established a mandatory delegation workflow for large, divisible implementation work: use focused, non-overlapping gpt-5.6-luna sub-agents at xhigh reasoning, with strict file ownership and acceptance criteria. Keep cross-cutting decisions, shared integration surfaces, live operations, and final verification with the main agent. The main agent must inspect every actual diff and test result rather than accepting sub-agent summaries as proof.
Preserve every existing staged, unstaged, and untracked change. Never use git reset, git clean, destructive checkout/restore operations, broad overwrites, or deletion of runtime state. Do not commit, push, deploy, restart services, edit live jobs, or mutate production until the owner explicitly authorizes that exact operation.
Authoritative scope
The durable phase plan is:
/root/zulip/docs/openclaw-zulip-integration-plan.md
Its Phase 5 contract is:
- add or verify owner commands;
- enforce elevated-command authorization;
- deliver approval requests privately;
- support
/approvesafely; - keep
/diagnosticsprivate; - handle approval expiry and failure notices;
- improve or verify the Zulip channel status probe; and
- keep Discord working as fallback.
Phase 5 exit criteria:
- only the stable numeric owner identity can execute owner/elevated commands or approvals;
- approval requests and diagnostics remain private;
- expired or duplicate approvals fail safely; and
- Discord continues to work as the fallback.
Do not expand Phase 5 into RSS migration, semantic video support, a Zulip Server upgrade, an OpenClaw core fork, or Discord retirement. RSS is still deferred pending a MonitorRSS OPML export. Phase 6 owns the longer dual-channel canary and any optional Discord cutover.
Repository and production context
- OpenClaw deployment repository:
/root/openclaw, branchmaster. - Authoritative phase-plan / Zulip deployment repository:
/root/zulip, branchmain. - Production Zulip:
https://chat.yachint.com. - OpenClaw version remains
2026.7.1unless the owner separately authorizes an upgrade. - Zulip Server remains
12.1with a locally patched APNs badge image. Never bypass or replace that image while working on OpenClaw integration. - External channel plugin source:
/root/openclaw/plugins/openclaw-zulip. - Loaded plugin:
@yachint/openclaw-zulip0.4.2. - Generic bot Mue has numeric user ID
10. - Owner authorization identity is stable numeric Zulip user ID
8; never authorize by display name or synthetic email. - Owner/private default destination is
user:8. - Inbound test channel is numeric channel
4. - Curator output channel is numeric channel
5, topicstwitterandhacker-news. - Discord remains enabled and must not regress.
At handoff creation, /root/openclaw HEAD and origin/master are both commit 3f91b11 (docs: close phase 4 acceptance). The Phase 4 series has been committed and pushed. The working tree is still intentionally dirty with runtime-generated curator/config files and unrelated untracked artifacts. Treat the live tree as authoritative and preserve all of it.
The /root/zulip plan repository was clean at commit 3e2dfb0, but its top-level status prose still describes Phase 3-era runtime state. Use its Phase 5 definition as authoritative scope while using /root/openclaw/AGENTS.md, /root/openclaw/openclaw-data/workspace/MEMORY.md, and actual runtime inspection for current Phase 4 facts.
Phase 4 baseline that must remain intact
Phase 4 is complete and accepted:
- Zulip channel
4allowlist and mention gating passed. Unmentioned owner message142was ignored with no journal row; mentioned message146produced reply147exactly once. - Topic isolation passed.
phase-4-canaryused canonical session18a891bd-afb0-4739-b815-05d8f41c10e0;phase-4-betaused8ed305bd-6d10-409c-8c6f-9259e70a062c. - Authorized no-mention
/whoamimessage181produced private-safe identity reply182in its originating topic. - Media request
183produced uploaded image184and completion185inphase-4-media, canonical sessione2fbccbf-8ba7-45df-8b23-300e24beb38f. Message186was cosmetic tool noise after successful upload, not a transport failure. - Recovery message
188, queued while the gateway was stopped, produced exactly one reply189after startup. Its journal row completed withattempts=1, erased payload{}, and cursor189. - A second gateway restart plus a 20-second quiet window produced no duplicate reply. All 62 journal rows were completed and SQLite integrity was
ok. - Final plugin gate: clean TypeScript check/build, 69 tests across 9 files, and zero production dependency vulnerabilities.
- Twitter and HN rich delivery are accepted in Zulip. Do not disturb curator routing or acknowledgement behavior while implementing control-plane features.
The current control plane is still Discord-centric in important places. At handoff creation, read-only inspection showed:
commands.ownerAllowFromcontains onlydiscord:1361768145844703503;tools.elevated.allowFromcontains only the Discord owner ID;- Discord has
execApprovals.enabled, its Discord approver ID,target: "dm", and cleanup enabled; - Zulip is enabled with
defaultTo: "user:8",dmPolicy: "allowlist", and numericallowFrom: [8]; - the Zulip plugin already passes
CommandAuthorizedinto OpenClaw inbound context and recognizes control commands that may bypass channel mention gating for an authorized sender; and - the external-plugin status snapshot can say
healthState: not-runningeven while ingress is operating, so Phase 5 must investigate the SDK/status surface rather than merely relabeling an output.
These observations are starting points, not permission to copy Discord configuration keys blindly into Zulip. Verify the deployed OpenClaw schema and plugin SDK behavior first.
Required first pass: read-only recovery
Run bounded inspection first:
cd /root/openclaw
git status --short --branch
git diff --stat
git diff --cached --stat
git log --oneline --decorate -n 20
git -C /root/zulip status --short --branch
git -C /root/zulip log --oneline --decorate -n 10
Read completely before changing anything:
/root/openclaw/AGENTS.md/root/openclaw/CLAUDE.md/root/openclaw/openclaw-data/workspace/AGENTS.md/root/openclaw/openclaw-data/workspace/MEMORY.mdrelevant Zulip/approval sections/root/openclaw/plugins/openclaw-zulip/README.md/root/zulip/AGENTS.md/root/zulip/docs/openclaw-zulip-integration-plan.md, especially invariants, Phase 5, production acceptance, and upgrade policy
Then locate, with bounded rg searches:
- command detection and
CommandAuthorizedhandling; - owner/elevated authorization schema and identity normalization;
- approval request, resolve, expiry, duplicate, cleanup, and delivery adapters;
/approve,/deny,/stop,/whoami,/diagnostics, and any other protected commands;- private-route selection and failure-notice routing;
- channel status/probe data contracts and external-plugin SDK limitations;
- Discord approval behavior and regression tests;
- relevant local OpenClaw SDK/types in the plugin dependency, without editing vendored
node_modules.
Report exact relevant dirty files, staged state, current control-plane behavior, what is reusable, what is missing, and whether any required feature genuinely needs an OpenClaw core change. Core changes are out of scope unless the owner explicitly approves an architecture review.
Recommended non-overlapping delegation
Use Luna/xHigh agents only for bounded workstreams. A safe initial split is:
- Authorization audit — read-only mapping of owner commands, elevated authorization, sender normalization, and tests. No edits.
- Approval/private-routing audit — read-only mapping of approval adapters,
/approve, expiry/duplicate semantics, diagnostics privacy, and failure notices. No edits. - Status-probe audit — read-only mapping of external channel status lifecycle and the current
not-runningquirk. No edits.
After the main agent chooses an architecture, delegate implementation by disjoint file ownership. Do not let multiple agents edit openclaw.json, the same plugin source file, or shared docs concurrently. The main agent owns configuration integration, live operations, and final acceptance.
Implementation requirements
Numeric-owner authorization
- Use numeric Zulip user ID
8as the authorization identity. - Ensure DMs, channel messages, control commands, elevated commands, and approval decisions all converge on the same numeric-owner policy without weakening Discord's existing owner policy.
- Do not authorize via
@Yachint,user8@chat.yachint.com, display name, topic, channel membership alone, or message text. - Unauthorized and bot-authored attempts must not start privileged work or leak whether a pending approval exists.
Private approvals and /approve
- Approval requests must go to owner DM
user:8, never a public channel/topic. - Approval decisions must be accepted only from owner
8in an allowed private context, unless the verified OpenClaw contract deliberately permits an equally private mechanism. - Bind each decision to the intended approval/request identity. Duplicate resolution must be idempotent or fail closed, not execute twice.
- Expired, unknown, malformed, already-resolved, unauthorized, and replayed approvals must fail safely with a private, non-sensitive response.
- Preserve cleanup behavior only after the relevant resolve state is durable.
- Do not expose full sensitive commands, diagnostics, credentials, tokens, cookies, environment values, or private payloads in channel messages or logs.
Private diagnostics and failure notices
/diagnosticsmust deliver only to the owner DM. If invoked from a channel, either refuse privately or redirect through a verified private owner route without echoing sensitive output publicly.- Critical control-plane failures and approval expiry notices should use owner DM
user:8, retaining useful iOS badge behavior. - Keep notices bounded and redact secrets. A delivery failure must not falsely mark an approval or diagnostic response as delivered.
Channel status probe
- Determine whether the external plugin can accurately expose running/connected state through supported SDK lifecycle callbacks.
- Status must distinguish configured/authenticated from active event monitoring and should expose useful bounded failure/reconnect information where the SDK permits it.
- Do not fabricate health by hardcoding
running=true. If SDK limitations prevent accurate parity, document the limitation and retain authoritative evidence from startup logs plus advancing cursor/journal.
Compatibility and fallback
- Keep the work in the external Zulip plugin and tracked configuration unless an explicit architecture review authorizes otherwise.
- Preserve Phase 4 channel/topic sessions, recovery journal, message-ID receipts, long-text chunking, media limits, curator acknowledgement, and Discord failure-only curator fallback.
- Discord owner commands, elevated execution, approvals, and diagnostics must continue to work during Phase 5.
- Telegram and WhatsApp must remain disabled.
Test strategy
Add focused mocked tests before any live work. Cover at minimum:
- Zulip owner
8accepted for protected commands and elevated operations; - a different numeric user rejected;
- display-name/email spoofing rejected;
- bot/self messages rejected;
- allowed control command behavior with and without mention in the configured channel;
- approval request routed only to
user:8; - valid
/approveresolves exactly once; - duplicate, expired, unknown, malformed, unauthorized, and replayed decisions fail safely;
- diagnostics never appear in a channel/topic and secrets are redacted;
- failure/expiry notice delivery is private and receipt-aware;
- restart/recovery does not repeat a resolved approval or privileged execution;
- status transitions across start, authenticated probe, monitor start, reconnect/failure, and stop;
- existing Discord approval/diagnostic behavior remains unchanged; and
- all existing plugin tests continue to pass.
Run targeted tests and static checks first. Bound all output. Before presenting a candidate for promotion, require at least:
cd /root/openclaw/plugins/openclaw-zulip
npm run check
npm audit --omit=dev
Also use a clean disposable OpenClaw state to validate package installation, config schema, plugin inspection, doctor, and status behavior. Do not treat a source-tree build alone as deployable proof. Build an immutable artifact, record its version, SHA-256, npm integrity/shasum, file inventory, and compatibility result.
Controlled live acceptance plan
Do not execute this section without explicit owner authorization for the exact candidate and operations.
Use a dedicated private canary context and verify one case at a time:
- Owner DM command succeeds; same text from an unauthorized numeric user is rejected without privileged execution.
- A harmless elevated action produces one approval request only in owner DM.
- Valid owner
/approveexecutes exactly once and returns a bounded private result. - Repeating the same approval does not execute again.
- An intentionally expired approval fails safely and remains private.
/diagnosticsinvoked in DM remains private; invocation from channel4does not leak output there.- A controlled approval/failure notice reaches owner DM and exercises the expected iOS notification/badge path.
- Restart the gateway around a pending or resolved canary and verify no duplicate prompt, decision, or execution.
- Verify the Zulip status probe against logs and durable ingress state.
- Re-run Discord owner command, approval DM, and private diagnostics regression checks.
Record exact Zulip message IDs, sender IDs, destinations, approval IDs in safely redacted form, journal/session evidence, execution count, and gateway health. Screenshots are useful for physical privacy/UI acceptance, but API and durable-state evidence are also required.
Acceptance checklist
- Existing staged, unstaged, untracked, and runtime-generated work remains intact.
- Only numeric Zulip owner ID
8can invoke protected/elevated behavior or resolve approvals. - Approval prompts, diagnostic output, expiry notices, and sensitive failures stay in owner DM
user:8. - Valid approval executes once; duplicate, expired, unauthorized, replayed, and malformed decisions do not execute.
- Delivery state changes only after a concrete Zulip message receipt where applicable.
- Restart/recovery produces no duplicate approval prompt, decision, or privileged execution.
- Status is accurate within supported external-plugin APIs and any remaining SDK limitation is documented honestly.
- Discord fallback and its existing control plane still pass regression checks.
- Phase 1–4 text, media, topic, curator, and recovery contracts remain green.
- RSS, semantic video, platform upgrades, and Discord retirement remain untouched.
- Credentials and private diagnostics are absent from commits and logs.
- Targeted tests, full plugin check, production audit, clean packaging, and disposable install validation pass.
- No live promotion, commit, or push occurs without explicit owner approval.
Known operational cautions
/root/openclaw/openclaw-data/config/openclaw.jsonis a live-mounted tracked file and the gateway may normalize it at startup. Inspect any rewrite; do not commit runtime churn blindly.- The Zulip ingress SQLite database and WAL/SHM sidecars are runtime state under
openclaw-data/config/plugins/zulip/. Never hand-edit, broadly chmod, delete, clean, or add them to Git. Use SQLite's backup API for snapshots. - Preserve the Zulip APNs badge patch and follow
/root/zulip/OPERATIONS.mdfor any separately authorized Zulip lifecycle operation. Never rundocker composedirectly in/root/zulip; use./scripts/composethere. - In
/root/openclaw, do not start/restart services or run server-side tests unless the owner explicitly authorizes them. This handoff itself is not deployment authorization. - Bound searches, logs, diffs, screenshots, and model/tool payloads. The earlier Phase 4 thread suffered an OOM failure from excessive accumulated context.
Expected handoff from Phase 5
Before requesting promotion approval, provide the owner with:
- recovered working-tree attribution;
- chosen architecture and why it stays within the external plugin boundary;
- reviewed per-file diff summary;
- authorization/privacy threat-model notes;
- targeted and full test results;
- immutable artifact provenance;
- disposable-install/status evidence;
- exact controlled live test sequence and rollback plan; and
- an explicit list of anything still deferred to Phase 6 or later.