Skip to content

fix(cli): preserve plugin manifests when builds fail - #140740

Merged
obviyus merged 4 commits into
openclaw:mainfrom
zenglingbiao:fix/plugins-build-atomic-package-json
Sep 7, 2026
Merged

obviyus merged 4 commits into
openclaw:mainfrom
zenglingbiao:fix/plugins-build-atomic-package-json

Conversation

@zenglingbiao

@zenglingbiao zenglingbiao commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

What Problem This Solves

A failed openclaw plugins build could truncate the user's hand-maintained package.json, leaving invalid JSON and breaking the plugin project.

Solution

Publish the complete package manifest through the existing atomic replacement helper. Preserve the file mode and all parent-directory permission bits, including setgid and sticky bits. A failed rename reports an error without falling back to an in-place copy. Resolve a selected symlink project root for publication while preserving entry validation and displayed paths.

User Impact

A failed package-manifest write preserves the original file. After the filesystem problem is resolved, the same build can succeed. Successful JSON output, --check, initialization, and adjacent plugin-manifest publication retain their behavior.

Evidence

  • Real CLI under a file-size limit: the original publication code truncated a 5,164-byte manifest to 2,048 bytes of invalid JSON. The repaired runtime preserved all 5,164 bytes through both physical and symlink project roots.
  • Partial disk-full writes and rename permission failures preserve bytes, file mode 02640, directory mode 03770, and remove owned temporary files. Successful retries generate both manifests.
  • Staged-file symlink, hardlink, file replacement, and directory replacement are rejected without modifying unrelated targets. Entries whose ownership can no longer be verified remain protected instead of being deleted.
  • Fresh independent CLI checks cover relative and absolute entries through symlink roots, failures, retries, modes, checks, and initialization. Adjacent-manifest failure remains visible; no transaction across both manifests is promised.
  • All 28 focused authoring tests passed, including the symlink-root regression. The two interrupted-publication cases fail against the original publication code for the expected reasons.
  • Formatting, lint, source review, build, type checks, and CI for this head passed. Runtime proof used the compiled merge artifact from that run.

@clawsweeper

clawsweeper Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

Pull request received. I will update this pull request when review starts.

ClawSweeper review complete

ClawSweeper finished reviewing this revision. The review result is being finalized.

View the workflow run.

@openclaw-barnacle openclaw-barnacle Bot added cli CLI command changes size: S labels Sep 7, 2026
@clawsweeper clawsweeper Bot added P2 Normal backlog priority with limited blast radius. merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. labels Sep 7, 2026
@clawsweeper

clawsweeper Bot commented Sep 7, 2026 •

Copy link
Copy Markdown
Contributor

Codex review: blocked before merge. Reviewed September 7, 2026, 1:45 AM ET / 05:45 UTC.

ClawSweeper review

What this changes

The PR publishes plugin package metadata atomically, preserves permission bits, and adds failure-preservation and symlink-root coverage.

Merge readiness

⛔ Blocked before merge - 5 items remain

The repair remains necessary: current main and v2026.9.2 still overwrite package.json directly. The earlier permission-mask concern is addressed, and no concrete introduced defect was established.

Priority: P2
Reviewed head: 757df0c090bdf7013401f1874b10bba33b2463de
Owner decision: Required. See Decision needed.

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) A focused repair with useful real CLI evidence and meaningful failure coverage; the latest symlink-root correction would benefit from the announced runtime confirmation.
Proof confidence 🐚 platinum hermit (4/6) Sufficient (live_output): The retained real CLI file-size-limit comparison exercises package-manifest publication and records preserved bytes after the repair, with failure and retry observations supplementing it. That evidence remains valid for ordinary roots; fresh symlink-root coverage is pending, and unavailable dependency inspection is a reviewer limitation.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Verified Sufficient (live_output): The retained real CLI file-size-limit comparison exercises package-manifest publication and records preserved bytes after the repair, with failure and retry observations supplementing it. That evidence remains valid for ordinary roots; fresh symlink-root coverage is pending, and unavailable dependency inspection is a reviewer limitation.
Evidence reviewed 9 items Current main still has the defect: The build command calls writeJsonFile for package.json before publishing the plugin manifest; writeJsonFile uses fs.writeFileSync directly.
Latest supplied release retains direct publication: v2026.9.2 also calls writeJsonFile(packagePath, nextPackageManifest); the proposed protection is not present in that release.
Introduced publication boundary: The complete host-pinned introduction patch resolves the project root, preserves all four permission digits with 0o7777, and enables temporary-file and parent-directory synchronization. It does not opt into a copy fallback.
Findings None None.
Security None None.

How this fits together

The plugin authoring CLI reads a plugin entry and existing package metadata, then generates manifests used to load the plugin. This change protects the package manifest when filesystem publication fails.

flowchart TD
  A[Plugin entry] --> C[Build plugin metadata]
  B[Existing package manifest] --> C
  C --> D[Stage package manifest]
  D --> E{Publication succeeds?}
  E -->|Yes| F[Publish plugin manifest]
  E -->|No| G[Preserve original and report error]
Loading

Decision needed

Question Recommendation
Does the recorded runtime evidence adequately resolve the prior fs-safe contract concern, or should exact 0.8.1 inspection be completed before landing? Complete pinned dependency verification: Confirm directory-mode application and disabled copy fallback against fs-safe 0.8.1 before landing.

Why: The permission-mask correction is visible and runtime evidence is positive, but this environment could not inspect the exact dependency version requested by the previous review.

Before merge

  • Resolve merge risk (P1) - Exact fs-safe 0.8.1 directory-mode and rename-failure behavior remains unverified by this reviewer; the available upstream snapshot is a different version.
  • Resolve merge risk (P1) - The retained real CLI proof covers ordinary roots; the PR body explicitly leaves fresh symlink-root CLI verification pending.
  • Complete next step (P2) - Resolve the exact fs-safe 0.8.1 verification limitation through source inspection or an explicit maintainer acceptance of the recorded runtime evidence.
  • Resolve review confidence - ClawSweeper must reach high confidence before merge readiness is known.
  • Resolve maintainer decision - Resolve the maintainer decision shown above before merge.
Agent review details

Security

None.

PR surface

Source +9, Tests +107. Total +116 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 11 2 +9
Tests 1 117 10 +107
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 128 12 +116

Review metrics

None.

Merge-risk options

Maintainer options:

  1. Decide the mitigation before merge
    Keep atomic publication at the existing manifest writer boundary, preserving project permissions and symlink-root behavior, with the pinned helper contract confirmed before landing.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Technical review

Best possible solution:

Keep atomic publication at the existing manifest writer boundary, preserving project permissions and symlink-root behavior, with the pinned helper contract confirmed before landing.

Do we have a high-confidence way to reproduce the issue?

Yes: current main directly overwrites package.json, and the supplied real CLI comparison demonstrates truncation under a file-size limit. This review inspected source without executing the reproduction.

Is this the best way to solve the issue?

Yes: reusing the existing atomic replacement helper is a focused repair that preserves generated JSON semantics; exact dependency verification remains the outstanding review limitation.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning medium; reviewed against 6ca2ef59b8e5.

Labels

Label changes:

  • remove merge-risk: 🚨 compatibility: Current PR review selected no merge-risk labels.

Label justifications:

  • P2: This is a bounded plugin-authoring repair that protects a project manifest when filesystem writes fail.
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🐚 platinum hermit and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (live_output): The retained real CLI file-size-limit comparison exercises package-manifest publication and records preserved bytes after the repair, with failure and retry observations supplementing it. That evidence remains valid for ordinary roots; fresh symlink-root coverage is pending, and unavailable dependency inspection is a reviewer limitation.
  • proof: sufficient: Contributor real behavior proof is sufficient. The retained real CLI file-size-limit comparison exercises package-manifest publication and records preserved bytes after the repair, with failure and retry observations supplementing it. That evidence remains valid for ordinary roots; fresh symlink-root coverage is pending, and unavailable dependency inspection is a reviewer limitation.

Evidence

What I checked:

  • Current main still has the defect: The build command calls writeJsonFile for package.json before publishing the plugin manifest; writeJsonFile uses fs.writeFileSync directly. (src/cli/plugins-authoring-command.ts:331, 6ca2ef59b8e5)
  • Latest supplied release retains direct publication: v2026.9.2 also calls writeJsonFile(packagePath, nextPackageManifest); the proposed protection is not present in that release. (src/cli/plugins-authoring-command.ts:331, 3928bad9badf)
  • Introduced publication boundary: The complete host-pinned introduction patch resolves the project root, preserves all four permission digits with 0o7777, and enables temporary-file and parent-directory synchronization. It does not opt into a copy fallback. (src/cli/plugins-authoring-command.ts:332, 757df0c090bd)
  • Dependency contract applies: The introduced call reaches an unmodified re-export from @openclaw/fs-safe/atomic, pinned to 0.8.1 in package.json. Its directory-mode and rename-failure semantics therefore determine correctness. The installed dependency is unavailable in this checkout. (src/infra/replace-file.ts:3, 757df0c090bd)
  • Upstream inspection has a version limitation: The inspected upstream implementation applies directory modes and enables copy fallback only when explicitly requested. Repository ownership is confirmed by its package metadata. However, the fetched metadata reports 0.7.2, and the 0.8.1 source could not be retrieved; this is corroboration, not verification of the pinned dependency. Sources: https://raw.githubusercontent.com/openclaw/fs-safe/main/src/replace-file.ts and https://raw.githubusercontent.com/openclaw/fs-safe/main/package.json. (src/replace-file.ts:326)
  • Retained real CLI evidence: The supplied complete PR body at sourceRevision 53412d998d2252c2b01db57582be285133917cbe7a20e982178d0f88936e73c1 records a real CLI file-size-limit comparison: the original truncated 5,164 bytes to 2,048 bytes, while the compiled repair preserved all 5,164 bytes. It also records failure preservation and successful retries. This evidence predates the latest symlink-root correction, whose fresh CLI proof is explicitly pending. The linked hosted build could not be opened from this reviewer environment.

Likely related people:

  • Peter Steinberger: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Complete the announced symlink-root CLI build/check verification and retain its result in the PR body.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

History

Review history (1 earlier review cycle)
  • reviewed 2026-09-07T03:46:08.637Z sha 6ddafae :: blocked before merge. :: none

@obviyus obviyus self-assigned this Sep 7, 2026
@obviyus obviyus changed the title fix(cli): publish plugins build package.json atomically fix(cli): preserve plugin manifests when builds fail Sep 7, 2026
@clawsweeper clawsweeper Bot removed the merge-risk: 🚨 compatibility 🚨 May break existing users, config, migrations, defaults, or upgrade paths. label Sep 7, 2026
@obviyus
obviyus merged commit 3be8b66 into openclaw:main Sep 7, 2026
110 checks passed
@obviyus

obviyus commented Sep 7, 2026

Copy link
Copy Markdown
Collaborator

Thanks @zenglingbiao! Merged as 3be8b66.

Proof: Real openclaw plugins build runs preserve the original manifest and full permission bits after partial writes or rejected renames, then succeed on retry through physical and symlink project roots.

  • Bug: An interrupted in-place write could corrupt the user's package manifest.
  • Fix: Stage and publish through the existing atomic helper, preserve full modes, disable copy fallback, and resolve symlink roots at publication.
  • Validation: All 28 authoring tests and CI for the merged head passed. Fresh independent behavior validation and final source review found no remaining defect.
Before After
A file-size limit truncated a 5,164-byte manifest to 2,048 invalid bytes. The same failure preserves all 5,164 bytes; removing the limit allows a successful build.
Unsafe options could strip directory special bits or permit an in-place copy after rename failure. File mode 02640 and directory mode 03770 remain intact, and rename failures remain visible.

Owned temporary files are removed. Substituted, multiply linked, or relocated entries remain protected when cleanup ownership cannot be verified. The original literal cleanup failure, its clarified ownership interpretation, and all raw observations are retained; no unconditional deletion or cross-manifest transaction is claimed.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Sep 8, 2026
## What Problem This Solves

A failed `openclaw plugins build` could truncate the user's hand-maintained `package.json`, leaving invalid JSON and breaking the plugin project.

## Solution

Publish the complete package manifest through the existing atomic replacement helper. Preserve the file mode and all parent-directory permission bits, including setgid and sticky bits. A failed rename reports an error without falling back to an in-place copy. Resolve a selected symlink project root for publication while preserving entry validation and displayed paths.

## User Impact

A failed package-manifest write preserves the original file. After the filesystem problem is resolved, the same build can succeed. Successful JSON output, `--check`, initialization, and adjacent plugin-manifest publication retain their behavior.

## Evidence

- Real CLI under a file-size limit: the original publication code truncated a 5,164-byte manifest to 2,048 bytes of invalid JSON. The repaired runtime preserved all 5,164 bytes through both physical and symlink project roots.
- Partial disk-full writes and rename permission failures preserve bytes, file mode `02640`, directory mode `03770`, and remove owned temporary files. Successful retries generate both manifests.
- Staged-file symlink, hardlink, file replacement, and directory replacement are rejected without modifying unrelated targets. Entries whose ownership can no longer be verified remain protected instead of being deleted.
- Fresh independent CLI checks cover relative and absolute entries through symlink roots, failures, retries, modes, checks, and initialization. Adjacent-manifest failure remains visible; no transaction across both manifests is promised.
- All 28 focused authoring tests passed, including the symlink-root regression. The two interrupted-publication cases fail against the original publication code for the expected reasons.
- Formatting, lint, source review, build, type checks, and [CI for this head](https://github.com/openclaw/openclaw/actions/runs/34087634188) passed. Runtime proof used the compiled merge artifact from that run.

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cli CLI command changes P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. size: S status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants