Skip to content

workspace disk guardrail blocks worktree add but recommended cleanup run --mode=artifacts does not reclaim disk — no working documented path to free artifact disk #871

Description

@chubes4

One-line

The workspace disk-budget guardrail refuses worktree add when free space is low and directs the operator to a cleanup path that only PLANS/SCHEDULES without reclaiming disk, while the actual delete path (cleanup apply <run-id>) is never mentioned and the --apply-plan escape hatch requires a plan file that no documented command produces — so there is no working documented path from "guardrail refused" to "disk reclaimed," and the operator is forced to rm -rf reconstructable artifacts by hand.

This is the sequel to (and reopening of the gap left by) #615 and #802, both closed:

So the redirect that closed #615 points at a command that does not reclaim; and the real reclaim command identified in #802 is not surfaced on the path an operator follows under disk pressure.

Reproduction (live VPS /var/www/extrachill.com, 2026-07-05)

All commands are the wp --allow-root --path=/var/www/extrachill.com datamachine-code workspace ... surface.

1. workspace worktree add wp-coding-agents <slug> was REFUSED:

Error: Refusing to create worktree before bootstrap/install because the workspace disk budget is unsafe.
Disk budget: ... 9.1 GiB (6.1%) free of 149.9 GiB total, 101 worktree-like dirs, status=refused.
Threshold: keep at least 10.0 GiB free and 10.0% free; effective floor on this filesystem is 10.0 GiB.
Recommended cleanup, in order:
1. review largest reconstructable artifacts: `... worktree cleanup-artifacts --dry-run --sort=size` (target reclaim: 888.2 MiB)
2. bounded cleanup-eligible worktrees: `... worktree bounded-cleanup-eligible-apply --dry-run --limit=25` ...
3. emergency cleanup report: `... worktree emergency-cleanup --format=json` ...

2. workspace worktree cleanup-artifacts --dry-run --sort=size correctly LISTED ~96 reconstructable artifacts (large node_modules/vendor dirs, several hundred MiB to 1.1 GiB each) with footer:

Success: 96 artifact(s) would be removed. Apply reviewed artifact cleanup with `... workspace cleanup run --mode=artifacts`; --apply-plan remains a low-level escape hatch.

3. workspace cleanup run --mode=artifacts RAN and printed a table of worktrees, but did NOT actually delete the artifacts. After it completed, du -sh on the exact node_modules it had listed showed them STILL PRESENT at full size:

807M  .../extrachill-studio@grid-adopt/node_modules
1.1G  .../wp-codebox@fix-mount-path-autoload/node_modules

Free disk was unchanged (~9.2 GiB). So cleanup run --mode=artifacts marked/reported but did not reclaim.

4. workspace worktree cleanup-artifacts --apply --sort=size ERRORED, redirecting BACK to the command that just did not reclaim:

Error: Artifact cleanup applies through the high-level cleanup runner for daily cleanup. Run `... workspace cleanup run --mode=artifacts` to apply reviewed artifact cleanup, or use --dry-run first and --apply-plan=<file> only as a low-level escape hatch.

This is circular: cleanup-artifacts --apply → "use cleanup run --mode=artifacts" → (does not reclaim) → back to cleanup-artifacts.

5. workspace worktree cleanup-artifacts --dry-run --apply-plan=/tmp/artifact-plan.json (attempting to generate a plan file to then apply) ERRORED:

Error: Cleanup plan is not readable: /tmp/artifact-plan.json

i.e. --apply-plan READS an existing plan file; nothing in the documented flow WRITES one. The "low-level escape hatch" is unreachable without an undocumented way to produce the plan file.

6. Only a manual rm -rf of the reconstructable node_modules dirs (group-writable) actually reclaimed disk — freeing ~6 GiB and moving free space from 9.1 GiB to 15 GiB, after which worktree add succeeded.

The defect, stated plainly

  • The guardrail refuses work AND recommends cleanup-artifactscleanup run --mode=artifacts, but that runner does not actually delete the reconstructable artifacts it reports (it schedules/plans only).
  • The alternative "escape hatch" --apply-plan=<file> requires a plan file that no documented command produces.
  • Result: there is NO working documented path to reclaim reconstructable artifact disk; the operator must bypass the tooling with rm -rf. A guardrail that blocks you and then offers a cleanup path that does not reclaim is worse than no guardrail.

Code evidence (read-only investigation, not fixed)

wp-content/plugins/data-machine-code/inc/Cli/Commands/WorkspaceCommand.php:

  • Line 6079–6080cleanup-artifacts --dry-run success message defaults the advertised apply command to cleanup run --mode=artifacts:

    $apply_command = (string) ( $result['apply_command'] ?? $summary['apply_command'] ?? 'studio wp datamachine-code workspace cleanup run --mode=artifacts --format=json' );
    WP_CLI::success(sprintf('%d artifact(s) would be removed. Apply reviewed artifact cleanup with `%s`; --apply-plan remains a low-level escape hatch.', ..., $apply_command));

    This is the command the operator is told to "apply" with — and it does not reclaim.

  • Line 888 (run_cleanup_task)cleanup run --mode=artifacts dispatches to the datamachine-code/workspace-cleanup-run ability whose failure mode is "Failed to schedule cleanup run." It SCHEDULES a DB-backed run; it does not delete synchronously. Without --drain (or a follow-up cleanup apply <run-id>), nothing is reclaimed on the command the guardrail recommends.

  • Line 774–775 — the actual apply/delete path exists, but is never surfaced on the recommended path:

    case 'apply':
        $this->run_cleanup_control_ability('apply', (string) ( $args[1] ?? '' ), $assoc_args);

    i.e. wp datamachine-code workspace cleanup apply <run-id>. This is the command Artifact cleanup dry-run advertises confusing apply command #802 identified as the working apply — and it is exactly what the guardrail/dry-run flow fails to mention.

  • Line 3925--apply-plan only reads an existing file:

    $input['apply_plan'] = $this->read_worktree_cleanup_plan( (string) $assoc_args['apply-plan']);

    No command in the documented flow writes that file, so the escape hatch is unreachable.

wp-content/plugins/data-machine-code/inc/Workspace/WorkspaceWorktreeLifecycle.php:

  • Line 146 — the guardrail message emits cleanup_recommendations from WorktreeDiskBudget. The recommendations point at cleanup-artifacts --dry-run, bounded-cleanup-eligible-apply, emergency-cleanup — none of which, followed end-to-end, reclaim disk without hitting the broken cleanup run / --apply-plan rungs above.

Concrete evidence that the documented path did not reclaim

  • After cleanup run --mode=artifacts completed, du -sh on the listed node_modules showed them still present at full size (807M, 1.1G).
  • Free disk unchanged at ~9.2 GiB before and after the run.
  • Manual rm -rf of those same reconstructable dirs freed ~6 GiB → free space went 9.1 GiB → 15 GiB → worktree add then succeeded. That is the only thing that worked.

Suggested acceptance criteria

  • workspace cleanup run --mode=artifacts (or whatever the sanctioned command is) ACTUALLY deletes the reconstructable artifacts it reports and reclaims disk, OR clearly states the additional flag/step required (e.g. cleanup apply <run-id> / --drain) and THAT step is the one the guardrail/dry-run messages point at.
  • There exists ONE documented, non-rm -rf path that takes an operator from "guardrail refused, disk low" to "disk reclaimed" — and the guardrail's Recommended cleanup text points at THAT path end-to-end, not at a rung that schedules without applying.
  • If --apply-plan is the sanctioned apply mechanism, a documented command WRITES the plan file it consumes (or the dry-run writes it by default).
  • No circular redirect between cleanup-artifacts --apply and cleanup run --mode=artifacts.
  • The cleanup-artifacts --dry-run success message's advertised apply_command (WorkspaceCommand.php:6079) points at a command that actually applies (e.g. cleanup run --mode=artifacts --drain producing an applied run, or cleanup apply <run-id>), not at a schedule-only command.

Constraints noted

Filed as issue only — no code, no PR, no branch. Investigation was read-only grep against the plugin source to sharpen the report; nothing was changed.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Fields

    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