Slack doctor contract isolated to prevent config warmup crashes
Commands that read configuration before execution no longer crash when Slack channels are configured, thanks to a dedicated doctor-contract sidecar that prevents unnecessary contract code from loading during config warmup.
Some CLI commands like openclaw cron read and warm up configuration before executing. When Slack channels were present in that configuration, the system would attempt to load Slack's contract surface to check doctor compatibility rules. The problem: Slack didn't have a dedicated "doctor contract" sidecar, so the registry fell back to the broader Slack contract surface instead. That broader surface pulled in unrelated code that could crash during the config warmup phase.
The fix is straightforward. A new file at re-exports only the doctor contract rules from Slack's existing contract implementation. This gives the doctor-contract registry a narrow surface to load during config warmup, eliminating the fallback to the broader contract code.
The change lives in the Slack extension, where it sits alongside the existing contract implementations. Commands that warm config no longer encounter Slack-related code during initialization.
View Original GitHub DescriptionFact Check
Summary
- Problem: commands that warm config early, such as
openclaw cron, could crash during config read whenchannels.slackwas present. - Why it matters: the doctor-contract registry fell back to Slack's broad
contract-apisurface, which pulled in unrelated contract code during config warmup. - What changed: added a dedicated runtime sidecar at
extensions/slack/doctor-contract-api.tsand added a regression test that locks the registry preference to that sidecar. - What did NOT change (scope boundary): no Slack doctor rule logic changed, no config schema changed, and no general contract-registry behavior changed outside this missing Slack surface.
Change Type (select all)
- Bug fix
- Feature
- Refactor required for the fix
- Docs
- Security hardening
- Chore/infra
Scope (select all touched areas)
- Gateway / orchestration
- Skills / tool execution
- Auth / tokens
- Memory / storage
- Integrations
- API / contracts
- UI / DX
- CI/CD / infra
Linked Issue/PR
- Closes #
- Related #
- This PR fixes a bug or regression
Root Cause (if applicable)
- Root cause:
src/plugins/doctor-contract-registry.tsalready prefersdoctor-contract-api*overcontract-api*, but Slack did not expose a dedicateddoctor-contract-apisidecar. That forced runtime fallback to the broader Slack contract surface during config warmup. - Missing detection / guardrail: there was no regression test asserting that the registry prefers
doctor-contract-apiwhen both public surfaces exist. - Contributing context (if known): this showed up on CLI paths that read config before command execution, with
channels.slackpresent.
Regression Test Plan (if applicable)
- Coverage level that should have caught this:
- Unit test
- Seam / integration test
- End-to-end test
- Existing coverage already sufficient
- Target test or file:
src/plugins/doctor-contract-registry.test.ts - Scenario the test should lock in: when both
doctor-contract-api.jsandcontract-api.jsexist, the registry loads the doctor sidecar. - Why this is the smallest reliable guardrail: the bug is in doctor-contract resolution, so the registry seam is the narrowest place to prevent the bad fallback.
- Existing test that already covers this (if any): none.
- If no new test is added, why not: N/A
User-visible / Behavior Changes
Commands that warm config no longer need Slack's broad contract-api surface just to read doctor compatibility rules. That avoids the Slack-related config read crash on the affected CLI path.
Diagram (if applicable)
Before:
[openclaw cron] -> [config warmup] -> [Slack contract-api fallback] -> [unrelated Slack contract load] -> [crash]
After:
[openclaw cron] -> [config warmup] -> [Slack doctor-contract-api] -> [doctor rules only] -> [command continues]
Security Impact (required)
- New permissions/capabilities? (No)
- Secrets/tokens handling changed? (No)
- New/changed network calls? (No)
- Command/tool execution surface changed? (No)
- Data access scope changed? (No)
- If any
Yes, explain risk + mitigation:
Repro + Verification
Environment
- OS: macOS
- Runtime/container: local CLI checkout
- Model/provider: N/A
- Integration/channel (if any): Slack
- Relevant config (redacted):
channels.slackpresent in user config
Steps
- Keep a config with
channels.slackconfigured. - Run a command that warms config/context before execution, such as
openclaw cron --help. - Observe which public Slack surface the doctor registry resolves during config warmup.
Expected
- Config warmup loads only the Slack doctor-contract surface and does not fall back to unrelated Slack contract code.
Actual
- Before this patch, Slack fell back to the broad
contract-apisurface and could crash during config read.
Evidence
- Failing test/log before + passing after
- Trace/log snippets
- Screenshot/recording
- Perf numbers (if relevant)
Human Verification (required)
- Verified scenarios:
pnpm test src/plugins/doctor-contract-registry.test.tspasses with the new preference assertion; bundled plugin build entry resolution includesextensions/slack/doctor-contract-api.tsanddist/extensions/slack/doctor-contract-api.js. - Edge cases checked: verified the preference behavior when both sidecars exist.
- What you did not verify: a full
pnpm buildis still red in this checkout because of an unrelatedcanvas:a2ui:bundlefailure.
Review Conversations
- I replied to or resolved every bot review conversation I addressed in this PR.
- I left unresolved only the conversations that still need reviewer or maintainer judgment.
Compatibility / Migration
- Backward compatible? (Yes)
- Config/env changes? (No)
- Migration needed? (No)
- If yes, exact upgrade steps:
Risks and Mitigations
- Risk: another bundled channel plugin could still rely on the broad
contract-apifallback for doctor-only loading.- Mitigation: this change adds Slack's missing narrow sidecar and locks the intended preference with a regression test.