Skip to content

feat: pause idle islo sandboxes instead of billing them - #1706

Open
zozo123 wants to merge 4 commits into
openclaw:mainfrom
zozo123:feat/islo-lifecycle-classes
Open

feat: pause idle islo sandboxes instead of billing them#1706
zozo123 wants to merge 4 commits into
openclaw:mainfrom
zozo123:feat/islo-lifecycle-classes

Conversation

@zozo123

@zozo123 zozo123 commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Problem

An islo sandbox held by a Crabbox lease keeps billing while nobody is using it. The adapter sent no lifecycle policy
on create, so an abandoned lease ran until someone noticed.

Change

Maps the lease idle timeout onto the provider's pause_after_idle, so an idle sandbox pauses instead of billing.
Deliberately narrow:

  • Opt-in (changed in this push — see below).
  • delete_after is never sent. Handing a deletion deadline to the provider lets it destroy a sandbox Crabbox
    still holds a claim on, possibly mid-run. internal/providers/daytona/lifecycle.go pins auto-delete off for the
    same reason.
  • auto_resume pinned to never, so resuming stays Crabbox's decision. Resuming is billable, so it is worth
    deciding deliberately rather than inheriting as a side effect.

Review fix (this push)

The reviewer was right that this should not be on by default. The previous version sent pause_after_idle
unconditionally from the 30-minute lease default, so existing users would get provider-side pausing they never
asked for — and, as these docs admit, what the provider counts as activity is not documented. A long-running
command or a lease serving a published share could be paused mid-flight.

Provider-side idle pausing is now opt-in. With the knob unset, no lifecycle object is sent and behaviour is
exactly as before this branch.

Evidence

go test ./internal/providers/islo/ ./internal/providers/all/   # ok, ok
gofmt / go build ./... / go vet                                # clean

Tests pin the exact JSON sent with the knob set, that nothing is sent with it unset, the omitted delete_after,
the pinned auto_resume, and the adoption-conflict path.

Live behaviour against real sandboxes (created and deleted for the test):

# opt-in default: create with no lifecycle
lifecycle echoed back = None
status: running at t+26 … t+228s      (no provider-side pause applied)

# opted in: pause_after_idle = 60s
status: running at t+75s, paused at t+90s   (policy is enforced, with scheduler lag)

The two together are the point: unset changes nothing, set actually pauses.

@clawsweeper

clawsweeper Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

🦞👀
ClawSweeper picked this up.

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

@clawsweeper clawsweeper Bot added merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P1 Urgent regression or broken agent/channel workflow affecting real users now. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. labels Aug 31, 2026
@clawsweeper

clawsweeper Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs real behavior proof before merge. Reviewed August 31, 2026, 2:07 PM ET / 18:07 UTC.

ClawSweeper review

What this changes

The PR maps Crabbox’s idle timeout to Islo’s create-time pause policy, resumes paused leases before reuse, rejects incompatible reclaims, and documents the lifecycle behavior.

Merge readiness

Blocked until real behavior proof is added - 5 items remain

Keep open: the adapter-local implementation is coherent, but it converts the existing 30-minute global default into a provider-enforced pause for every newly created Islo sandbox while the PR documents that active workloads may also be paused. This needs an explicit compatibility decision and real Crabbox-to-Islo behavior proof before merge.

Priority: P1
Reviewed head: 37127b6ea62371752743d512aa9cea114557a395
Owner decision: Required. See Decision needed.

Review scores

Measure Result What it means
Overall readiness 🦪 silver shellfish (2/6) The adapter change is focused and well tested at the request level, but the default compatibility break and missing end-to-end proof block merge readiness.
Proof confidence 🦪 silver shellfish (2/6) Needs real behavior proof before merge: The changed production owner is the lifecycle mapping passed to the Islo create client; the included httptest confirms serialized JSON, while the PR body narrates a real idle-policy observation but supplies no redacted Crabbox-to-Islo trace showing the changed CLI path and its after-fix result under an active workload. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Patch quality 🦐 gold shrimp (3/6) 1 actionable review finding remain.

Verification

Check Result Evidence
Real behavior Needs proof Needs real behavior proof before merge: The changed production owner is the lifecycle mapping passed to the Islo create client; the included httptest confirms serialized JSON, while the PR body narrates a real idle-policy observation but supplies no redacted Crabbox-to-Islo trace showing the changed CLI path and its after-fix result under an active workload. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
Evidence reviewed 6 items Introduced default policy: The introduced lifecycle helper always maps a positive idle timeout to Islo's pause field and pins automatic resume off; the normal CLI configuration supplies a positive default.
Existing global default: Current main config sets IdleTimeout to 30 minutes, so this is a semantic change for ordinary new Islo sandboxes without an added flag or migration.
Documented active-workload uncertainty: The introduced provider documentation says Islo does not document what counts as activity and that a run or share can be paused at the idle timeout.
Findings 1 actionable finding [P1] Keep idle pausing opt-in until active-workload safety is proven
Security None None.

How this fits together

Crabbox’s Islo provider adapter turns CLI lease settings into remote sandbox requests. It creates or reuses sandboxes, then supplies a running sandbox to workspace sync, command execution, SSH, and share-related flows.

flowchart LR
  A[CLI lease settings] --> B[Islo provider adapter]
  B --> C[Create lifecycle policy]
  C --> D[Islo sandbox]
  E[Reused lease] --> F[Check and resume sandbox]
  F --> G[Sync exec and SSH]
  D --> G
Loading

Decision needed

Question Recommendation
Should provider-enforced idle pausing apply by default to every newly created Islo sandbox through the existing 30-minute global timeout, or be introduced as an explicit opt-in policy? Keep the existing default: Make Islo idle pausing opt-in and prove that legacy default runs retain their previous behavior.

Why: The implementation deliberately changes an existing default into an external lifecycle action, and the provider's activity definition is unknown; this tradeoff cannot be resolved mechanically.

Before merge

  • Add real behavior proof - Needs real behavior proof before merge: The changed production owner is the lifecycle mapping passed to the Islo create client; the included httptest confirms serialized JSON, while the PR body narrates a real idle-policy observation but supplies no redacted Crabbox-to-Islo trace showing the changed CLI path and its after-fix result under an active workload. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.
  • Keep idle pausing opt-in until active-workload safety is proven (P1) - This unconditionally sends pause_after_idle for the existing 30-minute default. The new provider docs acknowledge that Islo does not define what activity is and that an active command or share can be paused, so ordinary upgraded users can lose a previously uninterrupted run without selecting new behavior. Preserve the prior default behind an explicit policy, or obtain maintainer approval with real upgrade and active-workload proof.
  • Resolve merge risk (P1) - Existing Islo users who rely on the established 30-minute default can have new long-running commands or shares paused without opting in; the PR itself states that activity semantics are undocumented.
  • Resolve merge risk (P1) - The new tests validate request serialization and recovery of a reused paused lease, but do not establish fresh-install and upgrade behavior through a real Crabbox-created sandbox under an active workload.

Findings

  • [P1] Keep idle pausing opt-in until active-workload safety is proven — internal/providers/islo/lifecycle.go:63-65
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Implementation and coverage delta production +145/-2, tests +288, docs/changelog +67 The patch adds substantial contract and documentation coverage, but its small behavioral core changes the default lifecycle of all new Islo sandboxes.

Merge-risk options

Maintainer options:

  1. Make idle pausing opt-in (recommended)
    Keep legacy Islo creation behavior by default and add focused fresh-install and upgrade coverage for an explicit pause policy.
  2. Accept the default-on lifecycle policy
    Approve the user-visible runtime change only after reviewing documented upgrade impact and real provider evidence for active workloads.

Technical review

Best possible solution:

Preserve current default runtime behavior until maintainers approve an explicit Islo idle-pause policy, then prove both an opted-in idle pause and uninterrupted active command/share behavior through the real CLI and provider.

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

No high-confidence reproduction of the active-workload regression is available. Source proves the default mapping, but the supplied real-provider observation covers an idle sandbox rather than a Crabbox-created sandbox executing a command or serving a share past the timeout.

Is this the best way to solve the issue?

No. The provider-specific mapping is at the correct adapter boundary, but unconditional default-on behavior is not the safest solution while Islo's activity semantics and upgrade impact remain unproven.

Full review comments:

  • [P1] Keep idle pausing opt-in until active-workload safety is proven — internal/providers/islo/lifecycle.go:63-65
    This unconditionally sends pause_after_idle for the existing 30-minute default. The new provider docs acknowledge that Islo does not define what activity is and that an active command or share can be paused, so ordinary upgraded users can lose a previously uninterrupted run without selecting new behavior. Preserve the prior default behind an explicit policy, or obtain maintainer approval with real upgrade and active-workload proof.
    Confidence: 0.95

Overall correctness: patch is incorrect
Overall confidence: 0.95

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 91f6710b04ad.

Labels

Label changes:

  • add P1: The introduced default can interrupt existing Islo command or share workflows after upgrade.
  • add merge-risk: 🚨 compatibility: A previously local idle-timeout default becomes an immutable provider lifecycle setting for new sandboxes.
  • add merge-risk: 🚨 availability: The PR documents that Islo may pause an active command or served share when its activity model does not treat it as active.
  • add rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦐 gold shrimp.
  • add status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The changed production owner is the lifecycle mapping passed to the Islo create client; the included httptest confirms serialized JSON, while the PR body narrates a real idle-policy observation but supplies no redacted Crabbox-to-Islo trace showing the changed CLI path and its after-fix result under an active workload. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Label justifications:

  • P1: The introduced default can interrupt existing Islo command or share workflows after upgrade.
  • merge-risk: 🚨 compatibility: A previously local idle-timeout default becomes an immutable provider lifecycle setting for new sandboxes.
  • merge-risk: 🚨 availability: The PR documents that Islo may pause an active command or served share when its activity model does not treat it as active.
  • rating: 🦪 silver shellfish: Overall readiness is 🦪 silver shellfish; proof is 🦪 silver shellfish and patch quality is 🦐 gold shrimp.
  • status: 📣 needs proof: The PR needs real behavior proof before ClawSweeper can clear the contributor ask. Needs real behavior proof before merge: The changed production owner is the lifecycle mapping passed to the Islo create client; the included httptest confirms serialized JSON, while the PR body narrates a real idle-policy observation but supplies no redacted Crabbox-to-Islo trace showing the changed CLI path and its after-fix result under an active workload. After adding proof, update the PR body; ClawSweeper should re-review automatically. If it does not, the PR author or someone with repository write access can comment @clawsweeper re-review.

Evidence

What I checked:

Likely related people:

  • Peter Steinberger: Suggested for follow-up; no historical authorship or introduction is verified. (role: unverified routing candidate; confidence: low)
  • Yossi Eliaz: 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.

  • Get a maintainer decision on default-on versus opt-in Islo pausing.
  • Preserve legacy default behavior or add explicit upgrade coverage for the approved policy.
  • Add a redacted real Crabbox create/reuse trace that shows the lifecycle request, idle pause, and active-workload behavior; updating the PR body should trigger re-review.

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.

zozo123 and others added 3 commits September 1, 2026 15:22
The islo create request never carried a lifecycle policy: before this change
createSandbox built CreateSandboxRequest with name/image/gateway/snapshot/
vcpus/memory/disk only (internal/providers/islo/backend.go:605-635 on
origin/main), so a sandbox ran under Islo's tenant defaults and the lease's own
--idle-timeout was local-only bookkeeping. Islo accepts a lifecycle object on
create and enforces it, so the non-destructive half of the lease intent can be
pushed down: IdleTimeout -> pause_after_idle (backend.go:645).

This is on by default, not opt-in: --idle-timeout defaults to 30m and
validateLeaseDurations (internal/cli/lease_flags.go:421-429) rejects a
non-positive one, so every CLI-driven islo create now carries an idle pause
where it previously sent nothing. The comment and both docs say so, and so does
the CHANGELOG entry.

--ttl is deliberately NOT mapped to delete_after. A provider-side deletion
deadline lets Islo destroy a sandbox Crabbox still holds a lease claim on,
possibly mid-run; the closest in-tree precedent makes the same call in the other
direction, pinning Daytona's auto-delete interval off
(internal/providers/daytona/lifecycle.go:48-51) so Crabbox stays the only thing
that deletes a Crabbox lease. pause_after stays unset because Crabbox has no
generic absolute pause deadline, so no flag was invented for it.

auto_resume is pinned to "never" and Crabbox does its own resuming, so that
resuming stays an explicit decision: a resume is billable, and an explicit
`crabbox pause` should not be undone by a background policy. The reuse run path
previously went from lease resolution straight to syncWorkspace/exec with no
status check, unlike the SSH resolve path, so it now goes through the same
resume-if-paused helper (backend.go:226, resolveRunningSandbox in ssh.go) -
which also fixes reusing a lease that an explicit `crabbox pause` had paused.

pause_after_idle is enforced, not merely recorded, and control-plane reads are
not activity: a sandbox created with pause_after_idle=60 still reported
"running" at 75s and "paused" at 90s while being polled with GET /sandboxes
every 15s throughout. What else Islo counts as activity is undocumented, and
that is not a safe unknown, so it is named with its consequence in
isloLifecycleForConfig and in docs/providers/islo.md rather than papered over:
if an in-flight exec or in-VM share/tailnet traffic does not hold the idle
clock off, a run longer than --idle-timeout can be paused mid-exec. Paths that
resolve a lease recover by resuming first; PublishPeer and fetchRunFileAs do not
resolve, which the ssh.go comment and the docs both state.

Islo fixes the policy at create time and exposes no lifecycle update, so a
reclaim that would change pause_after_idle fails with exit 2 at the one place a
sandbox is already fetched for adoption (backend.go:789) instead of adopting a
lease whose advertised idle timeout is not in force. A sandbox that reports no
lifecycle (created before this, or by another tool) stays reusable, and
auto_resume drift is not a conflict because Crabbox does not rely on it.

Verified against a fake API that captures the exact create JSON: lifecycle
arrives as {"auto_resume":"never","pause_after_idle":1800} for a 30m idle
timeout, delete_after is absent whatever --ttl says, sub-second values round up,
and a reused lease reported as paused is resumed once before sync and exec.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Supersedes the "on by default, not opt-in" decision in the previous commit on
this branch. That commit derived pause_after_idle from --idle-timeout
unconditionally, and --idle-timeout defaults to 30m with a non-positive one
rejected by validateLeaseDurations, so every existing islo user got a
provider-enforced pause on their next create without asking for it. The same
commit documents that Islo does not define what counts as activity, so an
in-flight exec or a lease serving a published share could be paused mid-flight
by a change nobody selected. That unknown is a reason for the feature to be
opt-in, not a caveat to hang on a default.

islo.idlePause / --islo-idle-pause / CRABBOX_ISLO_IDLE_PAUSE now gates it,
defaulting to off and following the existing islo flag wiring
(RegisterIsloProviderFlags / ApplyIsloProviderFlags, fileIsloConfig, applyEnv).
With the knob unset, isloLifecycleForConfig returns nil and the create request
carries no lifecycle object at all - byte-for-byte the request earlier releases
sent - and --idle-timeout stays local bookkeeping. With it set, the mapping is
unchanged: pause_after_idle seconds (rounded up), auto_resume pinned to "never",
delete_after never sent.

isloLifecycleConflict is gated on the same knob. With the knob off Crabbox makes
no provider-side lifecycle claim, so refusing to adopt a sandbox that carries a
pause policy from an Islo tenant default, another tool, or an opted-in run would
turn an opt-in feature into a reclaim regression for operators who never enabled
it. With the knob on the conflict is exactly as before.

The unconditional resume-if-paused check on the reuse path stays unconditional:
`crabbox pause` and an Islo tenant default can leave a reused lease paused
whether or not the knob is set, so that is a fix in its own right rather than
mitigation for this policy.

Tests cover both sides of the knob. The create wire contract asserts that a 30m
default idle timeout with the knob unset produces `{}` plus the generated name
and no lifecycle key, and that an opted-in one produces
{"auto_resume":"never","pause_after_idle":1800}; the conflict table and a
reclaim test assert that drifted and unwanted provider policies are adoptable
with the knob off and still conflict with it on; and flag/file/env tests assert
the shipped defaults leave it off while each opt-in path turns it on and
--islo-idle-pause=false / CRABBOX_ISLO_IDLE_PAUSE=0 turn it back off.

Mutation-checked: dropping the guard in isloLifecycleForConfig fails the
default-path wire test and TestIsloIdlePauseIsOptIn; dropping the guard in
isloLifecycleConflict fails the two knob-off conflict cases and
TestIsloReclaimWithoutIdlePauseAdoptsDriftedPolicy; dropping the flag, file, or
env wiring fails the corresponding opt-in assertion.

Docs and CHANGELOG now describe opt-in behaviour, and keep the honest "what
counts as activity is unknown" note as the stated reason the knob exists.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
clearConfigEnv unsets every CRABBOX_ISLO_* variable so a developer's exported
value cannot decide the outcome of a config test, but the new idle-pause knob
was missing from that list. With CRABBOX_ISLO_IDLE_PAUSE=1 exported, applyEnv
inside any config test flipped Islo.IdlePause on, which would make an
opt-in-is-off assertion pass or fail for a reason unrelated to the code under
test. Added it beside CRABBOX_ISLO_DISK_GB, and asserted the sealing directly in
TestIsloIdlePauseDefaultsOffAndOptsInExplicitly: after clearConfigEnv, applyEnv
over the shipped defaults must leave the pause off.

Also softened the docs claim about Islo's idle enforcement. docs/providers/islo.md
stated as bare fact that Islo enforces pause_after_idle and that control-plane
reads are not activity; both come from a single manual check against the live
API that nothing in this repository can reproduce - the tests here only pin the
request Crabbox sends. The paragraph now dates the check and reports what it
saw (pause_after_idle=60 still "running" at 75s, "paused" at 90s, polled with
GET /sandboxes every 15s throughout) as an observation at a point in time rather
than a documented contract, while keeping the load-bearing "what else counts as
activity is undocumented" note and its consequences intact - that unknown is
still the stated reason the knob is opt-in.

Mutation-checked: removing "CRABBOX_ISLO_IDLE_PAUSE" from clearConfigEnv fails
TestIsloIdlePauseDefaultsOffAndOptsInExplicitly under
CRABBOX_ISLO_IDLE_PAUSE=1 with "clearConfigEnv must unset
CRABBOX_ISLO_IDLE_PAUSE"; restoring it passes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@zozo123
zozo123 force-pushed the feat/islo-lifecycle-classes branch from dc1f047 to 288505e Compare September 1, 2026 20:23
@zozo123

zozo123 commented Sep 1, 2026

Copy link
Copy Markdown
Contributor Author

@clawsweeper re-review

Blocking findings addressed and the PR body updated with the fixes plus live behaviour proof.

@clawsweeper

clawsweeper Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

🦞🧹
ClawSweeper re-review requested.

I asked ClawSweeper to review this item again.
Action: item re-review queued (workflow sweep.yml, event exact_review_queue).
Result: when the review finishes, ClawSweeper will create the durable review comment if needed or update the existing comment in place.

Re-review progress:

TestProvidersDescribeBuiltBinaryContract pins a sha256 of `crabbox run --help`
so an unintended change to the CLI surface trips a test rather than shipping.
This branch adds --islo-idle-pause deliberately, so the baseline and byte
count are updated to match. Hand-maintained by design: there is no regeneration
script, which is what makes the tripwire meaningful.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 availability 🚨 Merging this PR could cause crashes, hangs, restart loops, stalls, or process outages. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P1 Urgent regression or broken agent/channel workflow affecting real users now. rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant