Skip to content

Commit 4e253b7

Browse files
committed
feat(providers): recommend warm starts
1 parent 3f27387 commit 4e253b7

5 files changed

Lines changed: 113 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
- Added `crabbox providers recommend offline-validation` for credentialless local, BYO SSH, and external-provider validation guidance.
1212
- Added normalized provider lifecycle capabilities and `--lifecycle` filters to `crabbox providers` and `crabbox providers recommend`.
1313
- Added `crabbox providers recommend failure-diagnostics` for failed-run triage guidance across proof, session, artifact, download, preview, and SSH-debuggable providers.
14+
- Added `crabbox providers recommend warm-start` for low-latency repeated-run guidance across local runtimes, cache volumes, retained sessions, pause/resume, and workspace-state providers.
1415

1516
### Fixed
1617

docs/commands/providers.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ crabbox providers recommend run-session
125125
crabbox providers recommend team-cloud
126126
crabbox providers recommend workspace-reuse
127127
crabbox providers recommend versioned-workspace
128+
crabbox providers recommend warm-start
128129
crabbox providers recommend forkable-workspace --workspace fork
129130
crabbox providers recommend versioned-workspace --target macos --workspace fork
130131
crabbox providers recommend worker-runtime --json
@@ -192,6 +193,11 @@ Supported use cases:
192193
- `versioned-workspace`: providers with native checkpoint, fork, restore, or
193194
snapshot-reference capabilities. Aliases include `workspace-reuse`,
194195
`forkable-workspace`, `durable-workspace`, and `stateful-workspace`.
196+
- `warm-start`: providers with local runtimes, reusable cache volumes, retained
197+
sessions, pause/resume, or workspace-state features that can reduce repeated
198+
setup overhead. Aliases include `warm-pool`, `prewarm`, and
199+
`low-latency-start`. This is provider selection guidance over existing
200+
reuse signals, not a guarantee of native warm-pool APIs.
195201
- `windows`: native Windows and WSL2 targets.
196202
- `worker-runtime`: Worker/module-runtime execution.
197203

docs/features/provider-landscape.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,9 @@ Ship these in small PRs:
138138
cloud API-backed leases.
139139
Use `crabbox providers recommend pause-resume` when long-running sandbox or
140140
dev-environment state needs to be parked and resumed.
141+
Use `crabbox providers recommend warm-start` when repeated runs should
142+
prefer local runtimes, reusable caches, retained sessions, pause/resume, or
143+
workspace-state signals before paying remote cold-start cost.
141144
4. Strengthen workspace reuse before runtime-specific forking.
142145
Checkpoint, fork, restore, and provider snapshot semantics should be testable
143146
through the CLI before adding live microVM fan-out. Use

internal/cli/providers.go

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,7 @@ func providerRecommendationUseCases() []string {
470470
"self-hosted",
471471
"team-cloud",
472472
"versioned-workspace",
473+
"warm-start",
473474
"windows",
474475
"worker-runtime",
475476
}
@@ -555,6 +556,10 @@ func normalizeProviderRecommendationUseCase(value string) (string, bool) {
555556
"forkable-workspaces", "durable-workspace", "stateful-workspace",
556557
"snapshot-fork":
557558
return "versioned-workspace", true
559+
case "warm-start", "warm", "warmup", "warm-up", "warm-pool", "warm-pools",
560+
"prewarm", "pre-warm", "prewarmed", "pre-warmed", "low-latency-start",
561+
"low-latency", "reuse-state", "reuse-runtime":
562+
return "warm-start", true
558563
case "windows", "win":
559564
return "windows", true
560565
case "worker", "worker-runtime", "module", "module-run":
@@ -1251,6 +1256,44 @@ func scoreProviderRecommendation(entry providerMatrixEntry, useCase string) (int
12511256
if hasFeature(FeatureCleanup) {
12521257
add(8, "can clean up provider-owned workspace resources")
12531258
}
1259+
case "warm-start":
1260+
hasWarmSignal := hasFeature(FeatureCacheVolume) || hasFeature(FeatureRunSession) ||
1261+
hasFeature(FeaturePauseResume) || hasFeature(FeatureCheckpoint) ||
1262+
hasFeature(FeatureFork) || hasFeature(FeatureRestore) ||
1263+
hasFeature(FeatureSnapshot) || strings.HasPrefix(category, "local-")
1264+
if !hasWarmSignal {
1265+
break
1266+
}
1267+
if strings.HasPrefix(category, "local-") {
1268+
add(45, "local runtime avoids cloud cold starts")
1269+
}
1270+
if hasFeature(FeatureCacheVolume) {
1271+
add(35, "can reuse dependency/cache volumes")
1272+
}
1273+
if hasFeature(FeatureRunSession) {
1274+
add(30, "can reuse retained run sessions")
1275+
}
1276+
if hasFeature(FeaturePauseResume) {
1277+
add(28, "can pause and resume warm runtime state")
1278+
}
1279+
if hasFeature(FeatureCheckpoint) || hasFeature(FeatureFork) || hasFeature(FeatureRestore) || hasFeature(FeatureSnapshot) {
1280+
add(25, "can reuse prepared workspace state")
1281+
}
1282+
if hasFeature(FeatureCrabboxSync) || hasFeature(FeatureArchiveSync) {
1283+
add(16, "can seed warm state from the current checkout")
1284+
}
1285+
if category == "ci-proof-runner" {
1286+
add(14, "CI proof runner is optimized for repeated validation loops")
1287+
}
1288+
if hasFeature(FeatureCleanup) {
1289+
add(12, "can clean up warm-start resources")
1290+
}
1291+
if hasFeature(FeatureRunProof) || hasFeature(FeatureRunArtifacts) || hasFeature(FeatureRunDownloads) {
1292+
add(8, "can retain evidence without keeping every run hot")
1293+
}
1294+
if hasTarget(targetLinux) || hasTarget(targetWorkerRuntime) {
1295+
add(6, "supports common warm-start targets")
1296+
}
12541297
case "windows":
12551298
if hasTarget(targetWindows + "/" + WindowsModeNormal) {
12561299
add(80, "supports native Windows")
@@ -1340,6 +1383,7 @@ func printProviderRecommendationUseCases(out io.Writer) {
13401383
fmt.Fprintln(out, " crabbox providers recommend run-session")
13411384
fmt.Fprintln(out, " crabbox providers recommend team-cloud")
13421385
fmt.Fprintln(out, " crabbox providers recommend versioned-workspace")
1386+
fmt.Fprintln(out, " crabbox providers recommend warm-start")
13431387
fmt.Fprintln(out, " crabbox providers recommend forkable-workspace --workspace fork")
13441388
}
13451389

internal/cli/providers_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,7 @@ func TestProvidersRecommendListsUseCases(t *testing.T) {
511511
"run-session",
512512
"team-cloud",
513513
"versioned-workspace",
514+
"warm-start",
514515
"worker-runtime",
515516
} {
516517
if !strings.Contains(text, want) {
@@ -738,6 +739,64 @@ func TestProvidersRecommendFailureDiagnosticsPrefersInspectableEvidence(t *testi
738739
}
739740
}
740741

742+
func TestProvidersRecommendWarmStartPrefersReusableState(t *testing.T) {
743+
recommendations := recommendProvidersForUseCase(providerMatrix(), "warm-start", 12)
744+
if len(recommendations) == 0 {
745+
t.Fatal("expected warm-start recommendations")
746+
}
747+
if recommendations[0].Provider != "local-container" && recommendations[0].Provider != "parallels" {
748+
t.Fatalf("top warm-start provider=%q recommendations=%v", recommendations[0].Provider, recommendations)
749+
}
750+
foundCache := false
751+
foundSession := false
752+
foundPause := false
753+
foundWorkspaceState := false
754+
foundLocalRuntime := false
755+
for _, recommendation := range recommendations {
756+
hasCache := providerRecommendationHasFeature(recommendation.Features, FeatureCacheVolume)
757+
hasSession := providerRecommendationHasFeature(recommendation.Features, FeatureRunSession)
758+
hasPause := providerRecommendationHasFeature(recommendation.Features, FeaturePauseResume)
759+
hasWorkspaceState := providerRecommendationHasFeature(recommendation.Features, FeatureCheckpoint) ||
760+
providerRecommendationHasFeature(recommendation.Features, FeatureFork) ||
761+
providerRecommendationHasFeature(recommendation.Features, FeatureRestore) ||
762+
providerRecommendationHasFeature(recommendation.Features, FeatureSnapshot)
763+
hasLocalRuntime := strings.HasPrefix(recommendation.Category, "local-")
764+
if !hasCache && !hasSession && !hasPause && !hasWorkspaceState && !hasLocalRuntime {
765+
t.Fatalf("warm-start recommendation lacks warm-state signal: %#v", recommendation)
766+
}
767+
foundCache = foundCache || hasCache
768+
foundSession = foundSession || hasSession
769+
foundPause = foundPause || hasPause
770+
foundWorkspaceState = foundWorkspaceState || hasWorkspaceState
771+
foundLocalRuntime = foundLocalRuntime || hasLocalRuntime
772+
}
773+
if !foundCache || !foundSession || !foundPause || !foundWorkspaceState || !foundLocalRuntime {
774+
t.Fatalf("warm-start should include cache, session, pause/resume, workspace-state, and local-runtime signals: %#v", recommendations)
775+
}
776+
}
777+
778+
func TestProvidersRecommendWarmPoolAlias(t *testing.T) {
779+
var stdout, stderr bytes.Buffer
780+
err := (App{Stdout: &stdout, Stderr: &stderr}).providers(context.Background(), []string{
781+
"recommend", "warm-pool",
782+
"--limit", "1",
783+
"--json",
784+
})
785+
if err != nil {
786+
t.Fatalf("providers recommend warm-pool error=%v stderr=%q", err, stderr.String())
787+
}
788+
var entries []providerRecommendationEntry
789+
if err := json.Unmarshal(stdout.Bytes(), &entries); err != nil {
790+
t.Fatalf("invalid json: %v\n%s", err, stdout.String())
791+
}
792+
if len(entries) != 1 {
793+
t.Fatalf("warm-pool alias entries=%#v", entries)
794+
}
795+
if entries[0].Provider != "local-container" && entries[0].Provider != "parallels" {
796+
t.Fatalf("warm-pool alias should prefer local reusable state providers: %#v", entries)
797+
}
798+
}
799+
741800
func TestProvidersRecommendFailedRunAlias(t *testing.T) {
742801
var stdout, stderr bytes.Buffer
743802
err := (App{Stdout: &stdout, Stderr: &stderr}).providers(context.Background(), []string{

0 commit comments

Comments
 (0)