fix(skills): stale create proposals with existing targets#90493
fix(skills): stale create proposals with existing targets#90493sahibzada-allahyar wants to merge 2 commits into
Conversation
|
Codex review: needs maintainer review before merge. Reviewed June 4, 2026, 11:21 PM ET / 03:21 UTC. Summary PR surface: Source +64, Tests +156. Total +220 across 2 files. Reproducibility: yes. source-reproducible: current main's scoped list/inspect paths do not compare pending create targets with the workspace Review metrics: 1 noteworthy metric.
Merge readiness Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch. Rank-up moves:
Risk before merge
Maintainer options:
Next step before merge
Security Review detailsBest possible solution: Keep the reconciliation in the shared Skill Workshop service, guarded by the target lock and fresh record status checks, and merge after normal CI/maintainer gates are satisfied. Do we have a high-confidence way to reproduce the issue? Yes, source-reproducible: current main's scoped list/inspect paths do not compare pending create targets with the workspace Is this the best way to solve the issue? Yes. The PR fixes the shared service read paths used by UI, gateway, CLI, and agent tool callers, and the follow-up uses existing target locks plus fresh record reads instead of a UI-only warning or duplicate state path. AGENTS.md: found and applied where relevant. Codex review notes: model gpt-5.5, reasoning high; reviewed against f3abe61b78fa. Label changesLabel changes:
Label justifications:
Evidence reviewedPR surface: Source +64, Tests +156. Total +220 across 2 files. View PR surface stats
What I checked:
Likely related people:
What the crustacean ranks mean
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. How this review workflow works
|
|
@clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Addressed the stale reconciliation race follow-up in commit What changed:
Verification: @clawsweeper re-review |
|
🦞🧹 I asked ClawSweeper to review this item again. Re-review progress:
|
|
Checked the red CI locally at the current PR head (5b1e3b7). The failures appear stale now:\n\n |
Summary
createproposals when their target skill file already exists in the workspace.staleduring scoped list/inspect flows so the UI and tool surface stop presenting an already-installed skill as still waiting to be used.Fixes #90388
Verification
node scripts/run-vitest.mjs src/skills/workshop/service.test.tsnode scripts/run-vitest.mjs src/skills/workshop/service.test.ts src/gateway/server-methods/skills.proposals.test.ts src/agents/tools/skill-workshop-tool.test.tsgit diff --checkReal behavior proof
skills/<name>/SKILL.mdalready exists is reconciled tostaleinstead of remaining actionable/waiting.fastino-90388-skill-proposal-stale-c.pnpm exec tsx --eval 'async function main() { const fs = await import("node:fs/promises"); const os = await import("node:os"); const path = await import("node:path"); const stateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-proof-state-")); const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-proof-workspace-")); process.env.OPENCLAW_STATE_DIR = stateDir; const service = await import("./src/skills/workshop/service.ts"); const proposal = await service.proposeCreateSkill({ workspaceDir, name: "Manual Install Proof", description: "Shows stale reconciliation", content: "# Manual Install Proof\n\nProposed content.\n" }); console.log("before", (await service.inspectSkillProposal(proposal.record.id))?.record.status); await fs.mkdir(path.dirname(proposal.record.target.skillFile), { recursive: true }); await fs.writeFile(proposal.record.target.skillFile, "---\nname: manual-install-proof\ndescription: Already installed\n---\n\n# Manual Install Proof\n", "utf8"); await service.listSkillProposals({ workspaceDir }); const after = await service.inspectSkillProposal(proposal.record.id); console.log("after", after?.record.status, after?.record.statusReason); } main();'stale, and a later inspect call returns that stale status/reason.Platforms tested