Feature Overview
This PR adds three flags to the openclaw config unset command:
| Flag | Function |
|---|---|
--dry-run | Preview deletion result without modifying config file |
--json | Structured output for script parsing (requires --dry-run) |
--allow-exec | Allow command execution for SecretRef resolution in dry-run mode |
These features bring config unset to parity with sibling commands config set and config patch.
Issue: #80721 PR: #80773(opens in a new tab)
Core Value
Safety: SecretRef Fallout Detection
OpenClaw uses SecretRef to reference secrets in configuration, not storing plaintext directly:
{
"channels": {
"slack": {
"appToken": {
"source": "env",
"provider": "env-provider",
"id": "SLACK_APP_TOKEN"
}
}
},
"secrets": {
"providers": {
"env-provider": { "type": "env" }
}
}
}If you delete env-provider, the reference in channels.slack.appToken becomes invalid—this is fallout: one deletion affects other configurations.
Without fallout detection:
openclaw config unset secrets.providers.env-provider
# Deletion succeeds, but silent failure
# Later Slack integration breaks, user doesn't know whyWith fallout detection:
openclaw config unset secrets.providers.env-provider --dry-run
# Output:
# Dry run failed: 1 SecretRef could not be resolved
# - env:env-provider:SLACK_APP_TOKEN -> provider not found
#
# User knows to modify channels.slack.appToken reference firstFunctional Value
| Scenario | Problem Solved |
|---|---|
| Production changes | Pre-deletion validation, avoiding config errors causing service interruption |
| CI/CD integration | Automated verification via --json output |
| Exec Provider | --allow-exec controls whether to execute external commands for validation |
Use Cases
Case 1: Pre-deletion Validation
# Verify if deletion affects other configurations
openclaw config unset secrets.providers.env-provider --dry-run
# Output:
# Dry run failed: 1 SecretRef assignment(s) could not be resolved.
# - env:env-provider:API_KEY -> SecretProviderResolutionError
#
# This means channels.slack.appToken still references env-provider, cannot delete directlyCase 2: Script Automation
result=$(openclaw config unset tools.alsoAllow --dry-run --json)
if echo "$result" | jq -e '.ok' > /dev/null; then
openclaw config unset tools.alsoAllow # Validation passed, execute real deletion
else
echo "Deletion failed: $(echo "$result" | jq -r '.error')"
exit 1
fiCase 3: Exec Provider Check Control
How exec-type SecretRef works:
Exec-type providers execute external commands to retrieve secrets:
{
"secrets": {
"providers": {
"aws-provider": {
"type": "exec",
"command": ["aws", "secretsmanager", "get-secret-value", "--secret-id", "MY_KEY"]
}
}
}
}OpenClaw runtime executes this command and extracts the secret value from output.
Why dry-run skips by default:
| Reason | Explanation |
|---|---|
| Performance | External commands may be slow (e.g., network requests) |
| Side effects | Commands may have side effects (logging, sending requests) |
| Cost | Cloud API calls may incur charges |
Usage:
# Default: skip exec type validation (don't execute external commands)
openclaw config unset secrets.providers.aws-provider --dry-run
# Output: skipped 1 exec SecretRef resolvability check(s)
# Explicit authorization to execute:
openclaw config unset secrets.providers.aws-provider --dry-run --allow-exec
# Will actually execute the aws command to verify secret availabilityThis follows the principle of least privilege—default to not executing, only execute on user confirmation.
ClawSweeper Review: Eight Rounds
ClawSweeper is OpenClaw's dedicated code review bot using GPT-5.5 model.
Working Mechanism:
| Stage | Description |
|---|---|
| Trigger | PR creation, new commit push, or @clawsweeper re-review comment |
| Fetch Code | Fetch PR diff + generate merge result based on main branch |
| Model Analysis | GPT-5.5 analyzes code changes, checks schema, types, logic |
| Publish Result | In-place edit of original comment (not adding new comment) |
| Label Sync | Automatically adds proof: sufficient and other labels |
Review Scope:
- Schema validation correctness
- Type matching (runs type check)
- SecretRef fallout handling
- Test code convention compliance
- Documentation updates
Re-review Flow:
gh pr comment <number> --body "@clawsweeper re-review"When triggered, ClawSweeper:
- Receives
repository_dispatchevent - Re-runs complete review
- Edits original review comment (preserving comment ID)
- Updates label status
Special Case We Encountered:
ClawSweeper's re-review workflow failed multiple times (status: Failed), due to ClawSweeper GitHub App token authentication issue:
fatal: could not read Username for 'https://github.com': terminal prompts disabled
This is ClawSweeper's own infrastructure issue, not PR code issue. Solution: update commit SHA in PR body to trigger automatic re-review, bypassing the problematic workflow.
Review Classification:
- P1: Critical issues (type check failures)
- P2: Important functional defects
- P3: Suggestive improvements
Review Iteration Log
| Round | Finding | Fix |
|---|---|---|
| 1 | [P2] Need post-change config validation | Added schema validation and SecretRef fallout check |
| 2 | [P1] Unused variable isSecretsPath | Removed unused variable |
| 3 | [P2] Incomplete SecretRef fallout check | Extended to all secrets-related paths |
| 4 | [P2] Warning references non-existent flag | Added --allow-exec support |
| 5 | [P3] Missing documentation | Updated docs/cli/config.md |
| 6 | [P2] Test assertion order mismatch | Check __exit__:1 first, then error message |
| 7 | [P2] Redundant setSnapshot in test | Removed uncalled mock in mode-error test |
| 8 | [P2] Redundant .toString() | parsedPath[2] is already string, removed conversion |
Round 6 Detail: Test Assertion Order
Issue: Test code assertion order didn't match project convention.
Principle: exit() doesn't actually exit, it throws a sentinel exception
OpenClaw's CLI test framework uses runtime.exit(code) to simulate process exit. In test environment, exit() doesn't actually exit the process, but throws a special exception:
// src/cli/test-runtime-capture.ts
exit(code: number): never {
throw `__exit__:${code}`; // Throws sentinel exception
}When CLI command encounters an error, execution flow is:
Command logic → runtime.error("error message") → runtime.exit(1) → throws __exit__:1
Before fix:
await expect(
runConfigCommand(["config", "unset", "tools.alsoAllow", "--json"]),
).rejects.toThrow("config unset mode error: --json requires --dry-run.");Problem: toThrow("error message") expects exception content to contain "error message", but actual throw is __exit__:1, assertion fails.
After fix:
await expect(
runConfigCommand(["config", "unset", "tools.alsoAllow", "--json"]),
).rejects.toThrow("__exit__:1");
expect(mockWriteConfigFile).not.toHaveBeenCalled();
expectErrorIncludes("config unset mode error: --json requires --dry-run.");Error message is written to stderr via runtime.error(), captured by test framework. Must first let __exit__:1 exception be properly caught, then check stderr content.
Real Behavior Proof Format
Why this requirement exists:
OpenClaw is a complex AI Agent system involving:
- Multi-process architecture (main process + worker processes)
- External tool execution (bash, file operations, network requests)
- Multi-platform support (Windows, Linux, macOS)
Unit tests and CI checks only verify code logic, cannot verify:
- Real environment process communication works correctly
- File system operations are correct
- Network API calls succeed
- Cross-platform compatibility is effective
External PR contributors typically aren't inside OpenClaw team, cannot access internal test environments. Real behavior proof requires contributors to run code in their own real environment, provide actual output proof, ensuring functionality actually works.
What problems this prevents:
| Problem Type | Unit Tests Can Find | Real behavior proof Can Find |
|---|---|---|
| Logic errors | ✅ | ✅ |
| Type errors | ✅ | ✅ |
| Process communication issues | ❌ | ✅ |
| File path issues (Windows vs Linux) | ❌ | ✅ |
| Real API call failures | ❌ | ✅ |
| Config parsing fails in real environment | ❌ | ✅ |
Required fields:
const requiredProofFields = [
{ key: "behavior", names: ["Behavior or issue addressed", ...] },
{ key: "environment", names: ["Real environment tested", ...] },
{ key: "steps", names: ["Exact steps or command run after this patch", ...] },
{ key: "evidence", names: ["Evidence after fix", ...] },
{ key: "observedResult", names: ["Observed result after fix", ...] },
{ key: "notTested", names: ["What was not tested", ...] },
];Correct format:
## Real behavior proof
- **Behavior or issue addressed**: ...
- **Real environment tested**: Windows 10, Node.js v22.12.0, OpenClaw 2026.5.12-beta.1 (commit SHA)
- **Exact steps or command run after this patch**: ...
- **Evidence after fix**: ...
- **Observed result after fix**: ...
- **What was not tested**: ...Key points:
- Must use
- **Key**:format, not heading - Evidence must have real terminal output (code block)
- commit SHA must match PR head
CI Check Responsibility Boundary
CI checks the complete codebase after merge, not just PR-modified files.
Judgment method:
# Locally check modified files
pnpm test src/cli/config-cli.test.ts
# If your file-related tests pass, other CI failures are not your issueSituation encountered in this PR:
| Check | Status | Reason |
|---|---|---|
check-lint | fail | Pre-existing lint errors in main branch |
check-prod-types | fail | Pre-existing type errors in main branch |
security-dependency-audit | fail | @mistralai/mistralai supply chain security issue |
Conclusion: Above failures are unrelated to files modified in this PR.
Label System
PR labels come from different systems:
| Label | Source |
|---|---|
proof: supplied | CI automatic check |
proof: sufficient | ClawSweeper label sync |
triage: needs-real-behavior-proof | Auto-added when proof missing |
When ClawSweeper says "Sufficient" but label isn't updated, manually request re-review to sync.
Modified Files
| File | Changes |
|---|---|
src/cli/config-cli.ts | ConfigUnsetOptions type, dry-run logic, SecretRef fallout check, --allow-exec |
src/cli/config-cli.test.ts | 4 test cases |
docs/cli/config.md | config unset dry-run documentation |
Key Code
SecretRef fallout check:
const affectsSecretResolution =
path === "secrets" ||
path.startsWith("secrets.providers") ||
path.startsWith("secrets.defaults");
if (affectsSecretResolution) {
const refs = collectAffectedRefsForProviderRemoval(nextConfig, providerAlias);
const { refsToResolve, skippedExecRefs } = selectDryRunRefsForResolution({
refs,
allowExecInDryRun: Boolean(cliOptions.allowExec),
});
// Verify refs can be resolved
}Mode validation:
if (cliOptions.json && !cliOptions.dryRun) {
throw new Error("config unset mode error: --json requires --dry-run.");
}
if (cliOptions.allowExec && !cliOptions.dryRun) {
throw new Error("config unset mode error: --allow-exec requires --dry-run.");
}Final Status
✅ Real behavior proof: pass
✅ ClawSweeper review: "needs maintainer review before merge" (highest rating)
✅ All tests pass (89 tests)
✅ All P1/P2/P3 findings resolved
"needs maintainer review before merge" indicates ClawSweeper found no further issues, awaiting human review.
Key Learnings
1. ClawSweeper is Iterative Review
Request re-review after each fix, updates comments in-place.
2. Real Behavior Proof Format is Strict
Must use - **Key**: format, must have real terminal output.
3. CI Failure May Not Be Your Problem
Check if it involves files you modified, if not, no action needed.
4. Documentation is Equally Important
P3 level findings also need attention, documentation update is required.
Related Links
Last updated: 2026-05-14