Back to blog

OpenClaw PR #80773: config unset Dry-run Feature Implementation

Adding --dry-run, --json, and --allow-exec flags to openclaw config unset command, passing ClawSweeper eight-round review. Includes SecretRef fallout detection, detailed use cases, and review process analysis.

#OpenClaw#PR#Open Source#TypeScript

Feature Overview

This PR adds three flags to the openclaw config unset command:

FlagFunction
--dry-runPreview deletion result without modifying config file
--jsonStructured output for script parsing (requires --dry-run)
--allow-execAllow 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 why

With 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 first

Functional Value

ScenarioProblem Solved
Production changesPre-deletion validation, avoiding config errors causing service interruption
CI/CD integrationAutomated 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 directly

Case 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
fi

Case 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:

ReasonExplanation
PerformanceExternal commands may be slow (e.g., network requests)
Side effectsCommands may have side effects (logging, sending requests)
CostCloud 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 availability

This 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:

StageDescription
TriggerPR creation, new commit push, or @clawsweeper re-review comment
Fetch CodeFetch PR diff + generate merge result based on main branch
Model AnalysisGPT-5.5 analyzes code changes, checks schema, types, logic
Publish ResultIn-place edit of original comment (not adding new comment)
Label SyncAutomatically 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:

  1. Receives repository_dispatch event
  2. Re-runs complete review
  3. Edits original review comment (preserving comment ID)
  4. 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

RoundFindingFix
1[P2] Need post-change config validationAdded schema validation and SecretRef fallout check
2[P1] Unused variable isSecretsPathRemoved unused variable
3[P2] Incomplete SecretRef fallout checkExtended to all secrets-related paths
4[P2] Warning references non-existent flagAdded --allow-exec support
5[P3] Missing documentationUpdated docs/cli/config.md
6[P2] Test assertion order mismatchCheck __exit__:1 first, then error message
7[P2] Redundant setSnapshot in testRemoved 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 TypeUnit Tests Can FindReal 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 issue

Situation encountered in this PR:

CheckStatusReason
check-lintfailPre-existing lint errors in main branch
check-prod-typesfailPre-existing type errors in main branch
security-dependency-auditfail@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:

LabelSource
proof: suppliedCI automatic check
proof: sufficientClawSweeper label sync
triage: needs-real-behavior-proofAuto-added when proof missing

When ClawSweeper says "Sufficient" but label isn't updated, manually request re-review to sync.


Modified Files

FileChanges
src/cli/config-cli.tsConfigUnsetOptions type, dry-run logic, SecretRef fallout check, --allow-exec
src/cli/config-cli.test.ts4 test cases
docs/cli/config.mdconfig 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