feat(cli): expand fast_provision experiment to images + docker + sandbox (#3398)
Some checks failed
CLI Release / Build and release CLI (push) Has been cancelled
Lint / ShellCheck (push) Has been cancelled
Lint / Biome Lint (push) Has been cancelled
Lint / macOS Compatibility (push) Has been cancelled

* feat(cli): expand fast_provision experiment to images + docker + sandbox

Builds on the existing PostHog `fast_provision` flag (already wired via
shared/feature-flags.ts). The `test` variant now bundles the full
provisioning-speed stack rather than images alone:

  - images:  pre-built DO marketplace images (cloud-side faster boot)
  - docker:  Docker CE host image on Hetzner/GCP (cloud-side faster boot)
  - sandbox: local agents run in a Docker container (local-side faster boot)

Users who explicitly pass --beta or --fast still take precedence and skip
the experiment bucket. Exposure events are still captured for both
variants so PostHog can compute conversion across the broader bundle.

Bumps CLI to 1.0.39.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(cli): extract fast_provision bundle helper, align --fast

Addresses review feedback on the fast_provision experiment expansion:

- Align `--fast` with the experiment `test` variant. Previously --fast
  pushed [tarball, images, parallel, docker] (no sandbox) while the
  silent A/B pushed [images, docker, sandbox]. Add `sandbox` to --fast
  so the explicit user opt-in exercises the same surface as the silent
  experiment, plus the speed-ups outside the experiment scope.

- Extract the experiment bundle into a pure `expandFastProvisionVariant`
  helper in shared/feature-flags.ts so the bundle composition is
  testable in isolation from main() arg parsing. Drop-in replacement
  in index.ts; behavior unchanged for the test variant.

- Add unit tests pinning the bundle: test variant -> [images, docker,
  sandbox], control -> [], unknown variants -> [] (fail-closed). These
  guard against silent drift when someone tweaks the list later.

- Bump CLI 1.0.39 -> 1.0.40 per the version-on-every-cli-change rule.

Verified: bunx biome check src/ clean, bun test 2188/2188 pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* revert(cli): leave --fast untouched, experiment owns its own bundle

My previous commit (8f87330) added `sandbox` to --fast to align it with
the experiment test variant. That was wrong — --fast is unrelated to
the fast_provision experiment, has its own meaning, and shouldn't
inherit changes from the experiment bundle.

- Restore --fast to [tarball, images, parallel, docker] (its prior set).
- Drop the cross-reference comments tying --fast to the experiment.
- Keep expandFastProvisionVariant() and its unit tests intact — the
  experiment bundle still lives in feature-flags.ts as a testable
  pure helper, just no longer claims alignment with --fast.
- Bump CLI 1.0.40 -> 1.0.41.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Claude <claude@anthropic.com>
This commit is contained in:
Ahmed Abushagur 2026-05-08 22:47:02 -07:00 committed by GitHub
parent 070be392f5
commit af8ac24506
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 54 additions and 6 deletions

View file

@ -1,6 +1,6 @@
{
"name": "@openrouter/spawn",
"version": "1.0.38",
"version": "1.0.41",
"type": "module",
"bin": {
"spawn": "cli.js"

View file

@ -6,6 +6,7 @@ import { dirname, join } from "node:path";
import {
_awaitBackgroundRefreshForTest,
_resetFeatureFlagsForTest,
expandFastProvisionVariant,
getFeatureFlag,
initFeatureFlags,
} from "../shared/feature-flags.js";
@ -225,4 +226,28 @@ describe("feature flags", () => {
expect(getFeatureFlag("unknown", "default")).toBe("default");
});
});
describe("expandFastProvisionVariant", () => {
// These tests pin the experiment bundle composition so that tweaking the
// list in feature-flags.ts forces an explicit test update — drifting the
// bundle silently is the failure mode we're guarding against.
it("returns the full provisioning-speed bundle for the test variant", () => {
expect(expandFastProvisionVariant("test")).toEqual([
"images",
"docker",
"sandbox",
]);
});
it("returns an empty bundle for the control variant", () => {
expect(expandFastProvisionVariant("control")).toEqual([]);
});
it("returns an empty bundle for unknown variants (fail-closed)", () => {
expect(expandFastProvisionVariant("")).toEqual([]);
expect(expandFastProvisionVariant("rollout")).toEqual([]);
expect(expandFastProvisionVariant("totally-made-up")).toEqual([]);
});
});
});

View file

@ -40,7 +40,7 @@ import {
} from "./commands/index.js";
import { expandEqualsFlags, findUnknownFlag } from "./flags.js";
import { agentKeys, cloudKeys, getCacheAge, loadManifest } from "./manifest.js";
import { getFeatureFlag, initFeatureFlags } from "./shared/feature-flags.js";
import { expandFastProvisionVariant, getFeatureFlag, initFeatureFlags } from "./shared/feature-flags.js";
import { getInstallRefPath } from "./shared/paths.js";
import { asyncTryCatch, asyncTryCatchIf, isFileError, isNetworkError, tryCatch, tryCatchIf } from "./shared/result.js";
import { captureError, initTelemetry, setTelemetryContext } from "./shared/telemetry.js";
@ -969,13 +969,16 @@ async function main(): Promise<void> {
// fast_provision experiment: if the user did NOT pass --beta or --fast,
// bucket them on the PostHog `fast_provision` flag. The `test` variant
// turns on images by default; control behaves as before.
// turns on images + docker + sandbox by default; control behaves as before.
// - images: pre-built DO marketplace images (cloud-side faster boot)
// - docker: Docker CE host image on Hetzner/GCP (cloud-side faster boot)
// - sandbox: local agents run in a Docker container (local-side faster boot)
// Exposure is captured for both variants so PostHog can compute conversion.
// Bundle composition lives in expandFastProvisionVariant() for unit testing.
if (!userOptedIntoBeta) {
const variant = getFeatureFlag("fast_provision", "control");
if (variant === "test") {
betaFeatures.push("images");
}
const variantStr = isString(variant) ? variant : "control";
betaFeatures.push(...expandFastProvisionVariant(variantStr));
}
if (betaFeatures.length > 0) {

View file

@ -201,6 +201,26 @@ export function getFeatureFlag<T extends string | boolean>(key: string, fallback
return value;
}
/**
* Beta features bundled by the `fast_provision` PostHog experiment for a given
* variant. Returns an empty array for `control` or any unknown variant the
* caller is responsible for de-duping against features the user already passed.
*
* Kept as a pure, named export so the bundle composition is testable in
* isolation from `main()` arg parsing. This is the experiment surface only
* unrelated to `--fast`, which is its own user-facing flag and stays as-is.
*/
export function expandFastProvisionVariant(variant: string): readonly string[] {
if (variant === "test") {
return [
"images",
"docker",
"sandbox",
];
}
return [];
}
/** Test-only: reset module state between tests. */
export function _resetFeatureFlagsForTest(): void {
_flags = null;