Skip to content

feat(framework): add cli suites, needsDocker, and CLI environment arms - #308

Closed
Coly010 wants to merge 3 commits into
mainfrom
columferry/cli-2398-cli-suite-and-arms
Closed

Coly010 wants to merge 3 commits into
mainfrom
columferry/cli-2398-cli-suite-and-arms

Conversation

@Coly010

@Coly010 Coly010 commented Sep 18, 2026

Copy link
Copy Markdown

Current Behavior

There is no home for CLI-team evals: the only suites are benchmark, regression, docs, other; an experiment can only opt out of evals by id; every local-stack experiment installs the pinned CLI into a sandbox where Docker always works. "One task, run across the environments the CLI actually meets" — Docker available / daemon unreachable / absent, stable vs beta CLI — cannot be expressed without touching other teams' experiments.

Expected Behavior

Infrastructure only — no evals in this PR (the first one, build-database-002-stack-lifecycle, is stacked on it in #281). Mirrors how the docs suite was added in #282.

Framework

  • A cli eval suite (evals/cli/, owned by @supabase/cli) and a cli experiment suite. Nightly runs cli × cli next to regression × regression; results export to apps/web/src/data/cli-eval-results.json with a cli-results.jsonl history on GitHub Pages. Pairing is by eval folder, so CLI experiment variants never pair with other teams' evals, and other teams' experiments never see CLI evals.
  • A needsDocker frontmatter field (default true). An eval that can run — and is meaningful — without a Docker daemon sets needsDocker: false; Docker-less experiments select on it with skipEval: (ev) => ev.metadata.needsDocker !== false instead of maintaining id lists.
  • README/CONTRIBUTING: suite list, the field, and a "CLI evals" section (ownership, experiments, add/refresh/history, unit-test command).

Experiment-land runtime — experiments/_lib/ (composes @supabase-evals/sandbox exports; no framework internals touched)

  • cli-channel.ts: resolves stable/beta to a concrete Supabase CLI version from npm dist-tags (latest/beta), lazily inside startSession (never on --dry/list), memoised, overridable via SUPABASE_CLI_STABLE_VERSION/SUPABASE_CLI_BETA_VERSION, and failing loudly rather than falling back to the pin.
  • docker-aware-local-stack.ts: dockerAwareLocalStackRuntime({ channel, docker }). docker: 'available' delegates to the stock runtime with the resolved CLI. 'no-daemon'/'absent' build the sandbox from the same exported pieces and make Docker unusable in layers the agent can't undo: DOCKER_HOST=tcp://127.0.0.1:1 on every command, a root-owned supabase PATH shim that re-exports it, removal of the real docker binaries, and for no-daemon a root-owned docker shim that still answers --version (so the CLI's managed-stack runtime probe, fix(cli): improve stack startup defaults and clean managed state cli#6563, selects Docker as on a real host) but fails everything else with the canonical daemon error. Permissions alone can't do this: the CI sandbox chmod 666s the socket. A marker at /tmp/supabase-eval-runtime.json records channel, resolved version, staged docker state and session start for scorers' metrics.

Experiments (Codex GPT-5.6 Luna, suite: ['cli'])

Experiment CLI installed Docker Selects evals by
codex-gpt-5.6-luna (existing; cli added to its suites) pinned available baseline column for CLI evals
-cli-stable latest stable available interface: cli, not hosted
-cli-beta latest beta (cut per merge to develop) available same
-cli-nodaemon latest beta client present, daemon unreachable needsDocker: false
-cli-absent latest beta no docker binary needsDocker: false

Docker-less arms use beta because the CLI's Docker-less path only exists in the managed stack, which ships there.

Review follow-ups (second commit)

Two independent reviews plus a triage of the first CI run of the cli suite (run 35274398896, via #307) led to:

  • CI root cause — npm/GitHub release skew. npm's beta dist-tag pointed at 2.118.0-beta.52 while its GitHub release was still a draft, so the .deb download 404'd and every beta-channel pair errored before the agent started (stable passed for exactly that reason). The resolver now HEAD-checks the release asset; for beta it walks back to the newest published -beta.N, for stable it throws — never falls back to the pin. The workflow resolves both channels once in prepare (overridable via new cli_stable_version / cli_beta_version dispatch inputs) and forwards the pins into the Vercel sandbox, so one nightly never mixes CLI versions across pairs. Each session logs the version it installed.
  • Structurally Docker-less. DockerSandboxOptions.mountDockerSocket (default true, 3 lines in packages/sandbox); the Docker-less arms create their sandbox without the socket and assert it's absent and that no docker resolves on the agent's PATH. DOCKER_HOST + shims stay as defence in depth.
  • CI gates the new code. apps/framework/tsconfig.json's experiments include pointed at a nonexistent directory (experiments were never typechecked); fixed, and pnpm check now runs the _lib and evals/cli unit tests (test:cli-lib).
  • Shared skipUnlessCli / skipUnlessDockerless predicates (mis-tagged evals skip instead of erroring), root-owned read-only runtime marker, single-quote shell escaping in the shims, needsDocker parse tests, CODEOWNERS for experiments/_lib/ and the arms, CONTRIBUTING spells out which evals each arm picks up.

Questions for the framework team, deliberately left as-is: needsDocker: boolean vs a requires: { docker } object (kanad's #307 already uses the boolean); moving the suite→experiment-suite mapping out of the workflow case; session facts (runtime id, resolved CLI version) on the scoring context to replace the marker file.

Related Issue(s)

Refs https://linear.app/supabase/issue/CLI-2398/add-a-docker-less-local-stack-e2e-eval-agent-cli-lifecycle (first eval: #281, stacked on this)
Context: #team-ai feedback thread → Linear project "Evals Driven Development".

Test plan

Give the CLI team the same home the docs team got in #282: a `cli` eval
suite (`evals/cli/`, owned by @supabase/cli, exported to
cli-eval-results.json with a cli-results.jsonl history) and a `cli`
experiment suite scheduled nightly next to regression. Pairing stays
folder-driven, so CLI experiment variants that force unusual environments
never pair with other teams' evals, and vice versa.

Add a `needsDocker` frontmatter field (default true). An eval that can run,
and is meaningful, without a Docker daemon sets it false; experiments that
stage Docker-less sandboxes select on it via `skipEval` instead of keeping
lists of eval ids.

Add the experiment-land runtime the CLI arms use: `resolveCliVersion`
turns `stable`/`beta` into a concrete CLI version from npm dist-tags
(lazily, memoised, overridable, never silently falling back to the pin), and
`dockerAwareLocalStackRuntime({ channel, docker })` installs that version
and, for `no-daemon`/`absent`, makes Docker unusable in layers the agent
cannot undo — DOCKER_HOST pointed at an unbound port on every command, a
root-owned `supabase` shim that re-exports it, the real docker binaries
removed, and (no-daemon) a `docker` shim that still answers --version so the
CLI's managed-stack runtime probe selects Docker as it would on a real host.
CI's sandbox makes the socket world-writable, so permissions alone cannot
stage this. Built only from `@supabase-evals/sandbox` exports.

Experiments: codex-gpt-5.6-luna-cli-{stable,beta,nodaemon,absent} in the
`cli` suite, and the pinned Codex Luna experiment tagged `cli` as the
baseline column. No evals yet; the first is stacked on this branch.

Refs: https://linear.app/supabase/issue/CLI-2398/add-a-docker-less-local-stack-e2e-eval-agent-cli-lifecycle
@vercel

vercel Bot commented Sep 18, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
evals Ready Ready Preview Sep 21, 2026 10:57am UTC

Request Review

@Coly010
Coly010 requested a review from a team as a code owner September 18, 2026 08:22
@Coly010 Coly010 self-assigned this Sep 18, 2026
The first CI run of the cli suite lost every beta-channel pair: npm's `beta`
dist-tag pointed at 2.118.0-beta.52 while its GitHub release was still a
draft, so the .deb download 404'd before the agent started. The resolver now
HEAD-checks the release asset and, for beta only, walks back to the newest
published `-beta.N`; a missing stable asset throws. The workflow resolves
both channels once in `prepare` (overridable via workflow_dispatch inputs)
and forwards the pins into the Vercel sandbox, so one nightly never mixes
CLI versions across pairs.

Make the Docker-less arms structurally Docker-less: `DockerSandboxOptions`
gains `mountDockerSocket` (default true) and the arms create their sandbox
without the socket, then assert it is absent and that no `docker` resolves
on the agent's PATH. DOCKER_HOST and the shims stay as defence in depth.

Gate the new code in CI: `apps/framework/tsconfig.json`'s `experiments`
include pointed at a nonexistent directory, so experiments were never
typechecked; `pnpm check` now typechecks them and runs the `_lib` and
`evals/cli` unit tests (`test:cli-lib`).

Also: shared `skipUnlessCli`/`skipUnlessDockerless` predicates so mis-tagged
evals skip instead of erroring; root-owned read-only runtime marker;
single-quote shell escaping in the shims; resolved version logged per
session; `needsDocker` frontmatter parse tests; CODEOWNERS for the CLI
runtime and arms; CONTRIBUTING spells out which evals each arm picks up.
Brings in #300 (native Grok Build CLI harness). Two conflicts, both
additive on each side, resolved by keeping both:

- `.github/workflows/eval-refresh.yml`: `XAI_API_KEY` alongside the
  `SUPABASE_CLI_{STABLE,BETA}_VERSION` run pins, in both the job `env`
  block and the `.env` heredoc.
- `apps/framework/scripts/run-vercel-evals.ts`: `XAI_API_KEY` added to
  `FORWARDED_ENV_NAMES` ahead of the CLI version pins.
@Coly010

Coly010 commented Sep 22, 2026

Copy link
Copy Markdown
Author

Closing in favour of the split agreed in the thread with @mattrossman and @Rodriguespn — this PR bundled sandbox plumbing, framework schema, CI wiring and CLI-team experiments into one 1365-line diff that couldn't be reviewed without CLI domain knowledge. It's now four PRs, each reviewable on its own:

#324 main CLI channel tags (cliVersion: 'stable' | 'beta') into @supabase-evals/sandbox, plus the per-run version pins
#325 #324 Docker-less staging (docker: 'no-daemon' | 'absent'), mountDockerSocket, needsDocker
#326 #315 typecheck experiments/ (it never has been)
next main after #315 the cli eval + experiment suites, CODEOWNERS, workflow wiring, and experiments/cli/

The substantive change versus what was here: dockerAwareLocalStackRuntime and experiments/_lib/ are gone entirely. Resolving a CLI channel and controlling Docker availability are now options on the shared localStackRuntime, per @mattrossman's suggestion, so they're available to everyone rather than living in CLI-team experiment land. What stays CLI-owned is just skipUnlessCli / skipUnlessDockerless and the experiment files themselves, which are ~6 lines each now that experiments/presets.ts exists.

Not deleting columferry/cli-2398-cli-suite-and-arms#281, #314 and #316 are still based on it. They'll be re-parented once the cli suite PR lands, and #307 follows #281. /cc @kanadgupta

This branch was successfully deployed

1 active deployment
Preview 91aee35b Deployed Sep 21, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant