Skip to content

fix(run): stop healthy retained runs from losing workspace ownership #1712

Description

@coygeek

Summary

An active retained local-container run using Crabbox 0.48.0 lost its workspace-owner renewal after a sustained dependency-install workload and before required artifact collection. Crabbox canceled the run lifecycle, refused collection and cleanup, and returned a workspace-owner failure even though the lease remained ready and later accepted reused runs successfully.

The reported exit status 75 is also too opaque to distinguish the remote protocol's MISMATCH and EXPIRED outcomes. A healthy active run should keep its owner renewed; a genuine ownership loss should remain fail-closed but identify the state that was actually observed.

Steps to reproduce

  1. Install Crabbox 0.48.0, initialize a small Git repository, and create a retained local-container lease:

    crabbox run --provider local-container --keep --slug owner-renew-repro --sync-only
  2. Reuse that lease for a setup command that spans many 10-second workspace-owner renewal intervals and requests a required artifact. The observed failing command used this shape:

    crabbox run \
      --provider local-container \
      --id owner-renew-repro \
      --preflight \
      --preflight-tools default,node,npm,python3 \
      --timing-json \
      --artifact-glob 'reports/**' \
      --require-artifact reports/summary.json \
      --download reports/summary.json=proof/summary.json \
      --keep-on-failure \
      --stop-after success \
      --script ./verify.sh
  3. In the observed run, verify.sh installed the clean Ubuntu runner's Node/npm and other test prerequisites, verified its source inputs, and entered npm ci. Instead of reaching artifact generation and collection, Crabbox ended with:

    warning: workspace owner release failed: remote workspace owner renewal failed closed: exit status 75
    remote command child ownership remains active; refusing collection and cleanup: remote workspace owner renewal failed closed: exit status 75
    
  4. Inspect the lease after the outer run exits. The observed lease remained ready, had no lingering npm, Node, make, or verification process, and accepted a subsequent synced run. That rerun completed the same source checks and later stopped the lease normally.

  5. The smallest missing reproduction fact is a deterministic way to produce the same active-owner exit status 75 without changing or tampering with workspace-owner state. Exact current-main controls using the same retained local-container route completed a 37-second dependency install and a separate 120-second command across twelve renewal intervals without failure.

Expected behavior

A retained or reused SSH-backed run should maintain workspace ownership while its controlling client and witnessed child remain active. A transient transport or response loss should not become terminal while the existing owner is still provably valid. A confirmed token mismatch, expiry, or ambiguous child state must continue to fail closed and prevent unsafe collection, cleanup, or reuse.

The regression is closed when a deterministic run-level test can hold or lose a renewal response during an active child, prove that still-valid ownership survives within its confirmed deadline, and prove that real mismatch or expiry remains exit 7 with the exact protocol state preserved. A live retained-run control should cross repeated renewal intervals, complete its workload, collect the required artifact, release ownership, and leave no owner or child residue.

Actual behavior

The renewal path returned only exit status 75, canceled the owner context, and caused the normal post-command ownership check to return before result and artifact collection. The output did not identify whether the remote owner reported MISMATCH or EXPIRED, and the still-usable lease required a separate manual inspection and rerun.

Affected area

  • Retained and reused SSH-backed crabbox run workflows that acquire a remote workspace owner.
  • Renewal response handling in internal/cli/workspace_owner.go.
  • Post-command child quiescence and the collection boundary in internal/cli/run.go.
  • Required artifacts, downloads, parsed results, failure capture, and cleanup that occur only after workspace ownership is confirmed.

Runtime or environment

  • Observed release: v0.48.0 at 26b735abbaf6571d5f46620e07514b7ce9ff0d7b.
  • Observed installed binary: Crabbox 0.48.0, SHA-256 44539a257aa51f885881aaf11f77ab800a48f72103e1072752f84dae3ab60a4e.
  • Observed failing direct run: run_9cdf95149d63, lease cbx_6f6398d094a5, provider local-container, Ubuntu 26.04 ARM64, Docker 29.7.2.
  • Exact current main: 697ac171ecca19ecdcad748ae9a66bd0d108ea28, three commits after the release baseline.
  • Current-main binary: dev, SHA-256 fc235b0216475c2748b44d2a426b8c703f4106d499c4faa07702e21ff04f19a8.
  • Build and control host: Go 1.26.5 on macOS 27.0 build 26A5421a, ARM64.
  • Current-main retained-run controls: dependency-install run run_4db22791540e and 120-second run run_4ae2c3d58f25, both exit 0 with workspace owner released.

Evidence

Current source maps a renewal token mismatch or expired owner state to remote exit 75. renewLoopWithTicks stores any non-RENEWED result as remote workspace owner renewal failed closed, cancels the lifecycle context, and does not retain the nonzero response token in the user-facing error. waitWorkspaceOwnerNoChild then returns that stored renewal error before results, required artifacts, downloads, or ordinary cleanup run.

The workspace-owner, run, and associated test paths are byte-identical between v0.48.0 and exact current main. The complete three-commit range from the release baseline through current main, all 20 live open issues, all 16 open issues authored by coygeek, all 28 live open pull requests, and relevant closed issues and merged pull requests were checked for duplicates, fixes, or supersession. No open item owns active-run renewal loss before evidence collection.

Closed issue #1390 and merged pull request #1392 fixed a different teardown race in which destructive --stop-after always cleanup killed an in-flight renewal after a successful command and evidence download. That fix deliberately preserves exit 7 for genuine renewal ambiguity before stop, which is the phase reached here.

Open issue #1691 concerns coordinator cleanup that preserves a claim while final output incorrectly reports the lease as stopped. This report instead concerns workspace-owner renewal loss during an active direct run, before collection and cleanup are allowed to begin.

Closed pull request #1442 implemented bounded same-token reconciliation for lost acquire and renewal responses, while keeping mismatch and expiry terminal. It was closed without merge because the broader patch's native-Windows authority proof remained incomplete. Current main does not contain that reconciliation; this report does not request the PR's Windows supervisor or POSIX descendant-containment scope.

The exact current-main focused workspace-owner tests pass, as do the retained 37-second dependency-install and 120-second live controls. Those controls establish that the failure is intermittent and that a deterministic trigger remains the evidence gap; they do not supersede the exact v0.48.0 observed failure or prove the unchanged renewal path cannot recur.

Impact

Long-running retained jobs can lose their final result, required artifacts, downloads, parsed test evidence, and automatic cleanup after paying the full setup and compute cost. Automation receives an infrastructure failure instead of the workload result, while the retained lease can remain usable but requires manual inspection. The opaque exit 75 also prevents operators from distinguishing a real token mismatch from expiry after delayed or lost renewal.

Additional context

The current-main control lease was explicitly stopped after testing, and local-container inventory confirmed its removal. This is an ordinary lifecycle and reliability bug under Crabbox's trusted-user model. It does not request a new preflight probe, weaken fail-closed ownership, or claim that artifact collection is safe after ownership is genuinely lost.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P1Urgent regression or broken agent/channel workflow affecting real users now.clawsweeper:bulk-filedClawSweeper detected a high recent issue-filing volume from this author.clawsweeper:needs-maintainer-reviewClawSweeper marked this issue as needing maintainer review before automation.clawsweeper:needs-product-decisionClawSweeper marked this issue as needing a product or behavior decision.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.impact:otherThis issue has meaningful maintainer-visible impact outside the owned taxonomy.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions