Skip to content

Target validation hard-codes pnpm + check:changed; bun-based target repos (e.g. openclaw/clawhub) always no-op with validation_script_missing #239

@momothemage

Description

@momothemage

Target validation hard-codes pnpm + check:changed; bun-based target repos (e.g. openclaw/clawhub) always no-op with validation_script_missing

Summary

ClawSweeper's target-validation pipeline is built around a single assumption — the target repo is an OpenClaw pnpm monorepo that exposes a check:changed script. Any target repo that uses a different toolchain (in our case openclaw/clawhub, which is bun-based) cannot get past preflight: every automerge/autofix attempt ends as a no-op with

Executor outcome: validation_script_missing: required pnpm check:changed is
unavailable in target checkout.

even though the PR itself is perfectly mergeable.

Repro

  1. Open a PR against openclaw/clawhub and opt it into ClawSweeper automerge/autofix.
  2. Worker runs preflightTargetValidationPlan against the clawhub checkout.
  3. clawhub/package.json does not define a check:changed script (it ships check, check:peers, check:secrets only) and uses bun (bun.lock, no pnpm-lock.yaml, packageManager is not pnpm@…).
  4. Worker exits as no-op with the message above. No push, rebase, replacement PR, merge, or re-review happens.

Root cause

Three places in src/repair/target-validation.ts hard-wire the pnpm + OpenClaw shape:

  1. prepareTargetToolchain (line ~57) throws unsupported target package manager: ${packageManager} whenever package.json#packageManager doesn't start with pnpm@. Bun targets are rejected up front.

  2. requiredValidationCommands (~line 211) unions the fixArtifact's validation_commands with the OpenClaw changed-gate. The gate itself is guarded by requiresOpenClawChangedGate (only fires when targetRepo === "openclaw/openclaw" and the repo defines check:changed), but the prompts in prompts/repair/autonomous.md and docs/repair/automerge-flow.md describe pnpm check:changed as the default local gate, so the LLM-generated fixArtifact.validation_commands carries pnpm check:changed for every OpenClaw-org repo, not just openclaw/openclaw.

  3. preflightTargetValidationPlan (~line 197-225) then resolves each command to its required npm script via packageScriptRequirement, compares it against the keys of the target's package.json#scripts, and emits

    {
      status: "blocked",
      code: "validation_script_missing",
      required: missing.command,        // "pnpm check:changed"
      missing_script: missing.name,     // "check:changed"
      reason: `validation_script_missing: required ${missing.command} is unavailable in target checkout`,
    }
    

    execute-fix-artifact.ts treats this as a non-retryable terminal outcome (see the /no merge base|validation_script_missing/i short-circuit around line 2681), so the worker no-ops.

The net effect: any PR targeting a non-pnpm / non-check:changed repo is unreachable by automerge, even when the PR has zero validation problems.

Why config alone is not enough

config/target-repositories.json doesn't currently override the validation command set or the package-manager check — prepareTargetToolchain and preflightTargetValidationPlan ignore it for these decisions. So even if we add an entry for openclaw/clawhub there, the pnpm/check:changed assumptions still fire.

Proposed fix (config-driven, smallest blast radius)

Make the validation pipeline read three things per target repo from config/target-repositories.json:

  • packageManager: "pnpm" | "bun" | "npm" — gate prepareTargetToolchain (corepack vs. bun install) on this instead of a hard pnpm@ prefix check.
  • validationCommands: string[] — used as the base set inside requiredValidationCommands; default still adds pnpm check:changed for openclaw/openclaw, but other repos opt in explicitly (e.g. clawhub → ["bun run check"]).
  • changedGate: { command, requiredScript } | null — replaces the hard-coded pnpm check:changed / check:changed pair in requiresOpenClawChangedGate and resolveAllowedValidationCommands. Repos with null skip the gate normalization entirely.

Then:

  • prepareTargetToolchain: branch on packageManager. For bun, run bun install --frozen-lockfile (with --no-frozen-lockfile fallback on lockfile drift), no corepack.
  • requiredValidationCommands: add the configured changedGate.command instead of literal "pnpm check:changed".
  • preflightTargetValidationPlan / packageScriptRequirement: keep the existing logic, just consult the per-repo config when deciding which script name a given command implies.
  • validationFallbackCommands / shouldRetryValidationCommand: parameterize the parts[0] !== "pnpm" || parts[1] !== "check:changed" check on the configured changed-gate so bun targets also get the merge-base recovery and the single transient retry.

Tests in test/repair/target-validation.test.ts already drive the pnpm path end-to-end via packageFixture — adding a sibling bunPackageFixture plus a clawhub-shaped repo entry should give us the new path coverage cheaply.

Workaround until fixed

Maintainers have to merge clawhub PRs manually; ClawSweeper automerge cannot reach them.

References

  • src/repair/target-validation.tsprepareTargetToolchain (≈ L43-L100), requiredValidationCommands (≈ L211-L219), preflightTargetValidationPlan (≈ L168-L225), requiresOpenClawChangedGate (≈ L455).
  • src/repair/execute-fix-artifact.ts — terminal-outcome detector at if (/no merge base|validation_script_missing/i.test(message)) return false;
  • prompts/repair/autonomous.md L69, docs/repair/automerge-flow.md L108 — prompt-side source of the pnpm check:changed default that bleeds into non-openclaw target repos.

Suggested labels

area: repair, area: target-validation, bug, multi-repo

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions