Skip to content

Commit 9bb23ac

Browse files
committed
Split matrix selection: run takes --interpreter, prepare-envs takes --env (ADR-0103)
`--env` and `--interpreter` both existed on `run` and `prepare-envs` and intersected in ways callers could not predict: a base named by `--env` silently ignored its `default_interpreters` policy, and `--interpreter` on prepare-envs widened or narrowed every matrix base at once. Each command now has one selector, matching what it operates on: `run` fans out over interpreters, `prepare-envs` over envs. - `run` selects by `--interpreter` only; `all` means the full axis and ignores `default_interpreters`. `--env` is removed from `run`. - `prepare-envs` selects by `--env` only, in four forms: `<base>` (the base's default subset), `<base>@<impl>-<version>` (one child), `<base>@all` (every child) and a non-matrix env name. `--interpreter` is removed from it. - With any `--env` given, a base not named contributes nothing; without one, every base applies its own default policy. Each base's policy selects its own children only. - Rework env_selection around resolve_run_selection, which yields concrete selected envs, and pass them down through matrix_runner and matrix_streaming via selected_variants so both fan-out paths filter identically. - Update CLI docs, the preparing-environments guide, WM internals and protocol docs, and the unit tests.
1 parent 2e32a89 commit 9bb23ac

29 files changed

Lines changed: 883 additions & 607 deletions

‎docs/cli.md‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,13 @@ python -m finecode run [options] <action> [<action> ...] [payload] [--config.<ke
6464
| `--no-save-results` | Do not write action results to the cache directory |
6565
| `--results-file=<path>` | Also write *this run's* results to `<path>`, unmerged, on every exit path, and report the path on stderr. See [Per-run results file](#per-run-results-file) |
6666
| `--dev-env=<env>` | Override the detected dev environment. One of: `ai`, `ci`, `cli`, `ide`, `precommit` (default: auto-detected — see [Dev environment detection](#dev-environment-detection)) |
67-
| `--env=<name>` | For a matrixed action (ADR-0047), restrict execution to the named interpreter environment(s) — a matrix base selects all of its children, a concrete child selects only itself. Repeatable. Non-matrix envs are unaffected. See [Preparing Environments — filtering by environment name](guides/preparing-environments.md#filtering-by-environment-name). |
68-
| `--interpreter=<impl>@<version>` | For a matrixed action, restrict execution to the named interpreter(s) across every matrix env the action touches. Repeatable; a bare version means `cpython`. See [Preparing Environments — filtering by interpreter](guides/preparing-environments.md#filtering-by-interpreter). |
67+
| `--interpreter=<impl>@<version>` | For a matrixed action, restrict execution to the named interpreter(s). Repeatable; a bare version means `cpython`; `all` means the full axis, ignoring `default_interpreters`. |
6968
| `--resource-usage[=SEC]` | Print one `[resources]` stderr line per interval plus a peaks summary at the end (see [Resource usage in CI](#resource-usage-in-ci)). Accepted range `0 < SEC ≤ 600`; bare flag polls every 15 s. |
7069
| `--no-resource-usage` | Disable the reporter, including its CI default. |
7170

7271
In a multi-project workspace, `run` fans out across every project that declares the action; spawned subprocesses are bounded by the machine-wide process budget (one slot per subprocess, leased per unit of work; default: derived from the machine's CPU budget). Fan-out is throttled, never refused — workspace size does not limit which actions you can run. See [Process budget](guides/wm-server-internals.md#process-budget).
7372

74-
`--env` and `--interpreter` on `run` use the same selector semantics as `prepare-envs` (ADR-0050): they compose by intersection, and a matrix env's config-declared `default_interpreters` policy (see [Preparing Environments — default interpreter subset](guides/preparing-environments.md#default-interpreter-subset)) applies as the default when neither is given — so a plain `run` can execute only a local subset of a matrix (e.g. the newest interpreter) while CI still runs the full axis, mirroring `prepare-envs`. The selection also decides which interpreter instances are *started*, not only which run: unselected matrix children are never started or repaired.
73+
`run` selects matrix variants with `--interpreter` alone (ADR-0103): no selector applies each base's `default_interpreters` policy (see [Preparing Environments — default interpreter subset](guides/preparing-environments.md#default-interpreter-subset)) — so a plain `run` can execute only a local subset of a matrix (e.g. the newest interpreter) while CI still runs the full axis; values select exactly those interpreters; `all` selects the full axis, ignoring the policy. The selection also decides which interpreter instances are *started*, not only which run: unselected matrix children are never started or repaired.
7574

7675
WAL environment variable and storage settings are shared with `start-wm-server` — see [`start-wm-server`](#start-wm-server) for details.
7776

@@ -192,11 +191,14 @@ python -m finecode --workdir=./finecode_extension_api run lint
192191
# Override ruff line length
193192
python -m finecode run lint --config.ruff.line_length=120
194193

195-
# Run a matrixed action's "testing" env only for its cpython@3.11 child
196-
python -m finecode run run_tests --env=testing@cpython-3.11
194+
# Run a matrixed action's 3.11 variant
195+
python -m finecode run run_tests --interpreter=3.11
197196

198197
# Run every matrix env's 3.12 interpreter
199198
python -m finecode run run_tests --interpreter=3.12
199+
200+
# Run the full declared axis, ignoring the default policy
201+
python -m finecode run run_tests --interpreter=all
200202
```
201203

202204
---
@@ -220,7 +222,7 @@ See [Preparing Environments](guides/preparing-environments.md) for a full explan
220222
| Option | Description |
221223
|---|---|
222224
| `--recreate` | Delete and recreate the venvs this run covers (all, or the `--env` selection) |
223-
| `--env=<name>` | Restrict `create_envs` and `install_envs` to the named env(s). Repeatable. See note below. |
225+
| `--env=<name>` | Restrict `create_envs` and `install_envs` to the named env(s): a name, a matrix base (its default interpreters), `<base>@<impl>-<version>`, or `<base>@all`. Repeatable. See note below. |
224226
| `--project=<name>` | Restrict preparation to the named project(s) (matched by `[project].name` from `pyproject.toml`). Repeatable. |
225227
| `--log-level=<level>` | Set log level: `TRACE`, `DEBUG`, `INFO`, `WARNING`, `ERROR` (default: `INFO`) |
226228
| `--verbose` / `-v` | Stream WM and ER diagnostic logs to stderr live over the protocol (`server/logRecords`). Auto-enabled in CI. |

‎docs/guides/developing-finecode.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,8 +195,8 @@ python -m finecode run --shared-server inspect_code --target=files \
195195
`--interpreter=3.13` restricts a matrixed `testing` run to that interpreter.
196196
Dropping `--interpreter` does **not** widen to the whole axis: it runs the env's
197197
`default_interpreters` policy, which for `testing` selects the newest
198-
downloadable interpreter only. To run more, pass `--dev-env=ci` (the full
199-
axis) or repeat `--interpreter=` for each one.
198+
downloadable interpreter only. To run more, pass `--interpreter=all` (the full
199+
axis), `--dev-env=ci`, or repeat `--interpreter=` for each one.
200200

201201
Payload fields are validated at the CLI against the action's schema before anything
202202
runs: an unknown field name is refused, and a value that cannot be the field's declared

‎docs/guides/preparing-environments.md‎

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -259,8 +259,8 @@ This is the only command most users need. It:
259259
1. Discovers all projects in the workspace
260260
2. Bootstraps `dev_workspace` for each subproject (`create_envs` + `install_envs`, using workspace root config)
261261
3. Starts Extension Runners
262-
4. Runs `create_envs` across all projects (only the selected envs when `--env`/`--interpreter` narrows the run — see Filtering by environment name)
263-
5. Runs `install_envs` across all projects (only the selected envs when `--env`/`--interpreter` narrows the run — see Filtering by environment name)
262+
4. Runs `create_envs` across all projects (only the selected envs when `--env` narrows the run — see Filtering by environment name)
263+
5. Runs `install_envs` across all projects (only the selected envs when `--env` narrows the run — see Filtering by environment name)
264264

265265
See [CLI reference — prepare-envs](../cli.md#prepare-envs) for available options.
266266

@@ -349,34 +349,33 @@ Useful when you've added a new handler in one env and want to update only that e
349349

350350
#### Matrix environments
351351

352-
For a matrix environment (ADR-0047 — one declaring an `interpreters` axis), the same rule applies, with base-name expansion: naming a concrete matrix child — or its base name, which expands to all of its children — restricts **both** `create_envs` and `install_envs` to the selected children (PRD-0003 AC8). Unselected children of that matrix are not created at all, since there is no point creating a venv for an interpreter nobody asked for in this run.
352+
For a matrix base (ADR-0047 — one declaring an `interpreters` axis), `--env` takes four forms (ADR-0103). With any `--env` given, a base not named in any form contributes nothing; with no `--env`, every base contributes its default and every non-matrix env is included:
353+
354+
| Form | Selects |
355+
|---|---|
356+
| `<base>` | that base's default interpreters |
357+
| `<base>@<impl>-<version>` | that one child |
358+
| `<base>@all` | every child |
359+
| `<non-matrix>` | that env |
360+
361+
The selection restricts **both** `create_envs` and `install_envs` (PRD-0003 AC8). Unselected children are not created at all. Each base's policy selects its own children only.
353362

354363
```bash
355-
# Select every child of the "testing" matrix env.
364+
# The "testing" base's default subset.
356365
python -m finecode prepare-envs --env=testing
357366

367+
# Every child of "testing".
368+
python -m finecode prepare-envs --env=testing@all
369+
358370
# Select only the cpython@3.11 child.
359371
python -m finecode prepare-envs --env=testing@cpython-3.11
360372
```
361373

362-
A matrix base named by `--env` is always expanded to *all* of its children, ignoring that base's own `default_interpreters` policy (see below) — `--env` is more specific than a config default. A sibling matrix base *not* named by `--env` is unaffected by this and keeps applying its own config default (or its full axis, if it has none).
363-
364-
### Filtering by interpreter
365-
366-
```bash
367-
python -m finecode prepare-envs --interpreter=3.11
368-
python -m finecode prepare-envs --interpreter=pypy@3.11
369-
```
370-
371-
Restricts every matrix environment's interpreter axis to the named interpreter(s), the same way for both `create_envs` and `install_envs`. `--interpreter` is repeatable to select more than one interpreter. Values may be the canonical `<impl>@<version>` form or a bare version, which is shorthand for `cpython@<version>`.
372-
373-
`--interpreter` can be combined with `--env`: the effective selection is the intersection of the two — e.g. `--env=testing --interpreter=3.12` selects only `testing`'s `cpython@3.12` child. An `--interpreter` value that doesn't exist in a given matrix env's axis simply contributes nothing for that env (it is not an error by itself — see below for when a selector *is* rejected).
374-
375-
Non-matrix envs are unaffected by `--interpreter`; they are created and installed unless an `--env` filter excludes them.
374+
`prepare-envs` has no `--interpreter`; name the child, or use `<base>@all`.
376375

377376
### Default interpreter subset
378377

379-
A matrix environment can declare a default interpreter subset per dev-env, so that a plain `prepare-envs` run (no `--env`/`--interpreter`) still narrows the axis automatically:
378+
A matrix base can declare a default interpreter subset per dev-env, so that a plain `prepare-envs` run (no `--env`) still narrows the axis automatically:
380379

381380
```toml
382381
[tool.finecode.env.testing]
@@ -395,11 +394,11 @@ Each key is either an exact dev-env (`ide`/`cli`/`ai`/`git_hook`/`ci`) or one of
395394

396395
Lookup for the active dev-env `D` (see [dev environment detection](../cli.md#dev-environment-detection) — `ide`/`cli`/`ai`/`git_hook`/`ci`) tries, in order: the exact key `D`, then the bucket key (`"ci"` if `D == "ci"`, otherwise `"local"`), then falls back to `"all"`. In the example above, `local = "newest"` covers `ide`/`cli`/`ai`/`git_hook`, while `ci = "all"` covers `ci` — a common pattern where local development only needs the newest interpreter, but CI verifies every interpreter in the matrix.
397396

398-
An explicit `--interpreter` selector always overrides the config default outright, for every matrix base. A config default (or its explicit-list policy) that names an interpreter outside the env's declared axis is rejected at resolution time with a clear error.
397+
An explicit `--env` selector always overrides the config default outright, for its own base; each base's policy selects its own children only. A config default (or its explicit-list policy) that names an interpreter outside the env's declared axis is rejected at resolution time with a clear error.
399398

400-
### `run` uses the same selection
399+
### `run` selects by interpreter
401400

402-
`python -m finecode run` accepts the same `--env`/`--interpreter` selectors, with identical semantics (ADR-0050), to restrict which interpreter variants of a matrixed action actually execute — see [CLI reference — `run`](../cli.md#run). The config-declared `default_interpreters` policy applies there too: a plain `run` (no selectors) executes only the dev-env's default subset of a matrix (e.g. just the newest interpreter locally), while `ci` runs the full axis by default, exactly mirroring `prepare-envs`. Selection is resolved once per project (via `env_selection.resolve_selected_interpreters`) and passed down to whichever fan-out site handles the request — `matrix_runner` (non-streaming) or `matrix_streaming` (CLI / IDE streaming) — so both paths filter identically.
401+
`python -m finecode run` selects with `--interpreter` (ADR-0103) to restrict which interpreter variants of a matrixed action actually execute — see [CLI reference — `run`](../cli.md#run). Values select exactly those interpreters, `all` selects the full axis, and no selector applies the `default_interpreters` policy. The config-declared `default_interpreters` policy applies there too: a plain `run` (no selectors) executes only the dev-env's default subset of a matrix (e.g. just the newest interpreter locally), while `ci` runs the full axis by default. Selection is resolved once per project (via `env_selection.resolve_run_selection` into selected concrete envs) and passed down to whichever fan-out site handles the request — `matrix_runner` (non-streaming) or `matrix_streaming` (CLI / IDE streaming) via `matrix_runner.selected_variants` — so both paths filter identically.
403402

404403
---
405404

‎docs/guides/wm-server-internals.md‎

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -165,19 +165,19 @@ Non-matrix actions use the existing single/multi-env dispatch path unchanged
165165
on both paths.
166166

167167
A matrixed action's fan-out can be restricted to a subset of its declared
168-
interpreter axis (PRD-0003 AC8): `run`'s `--env`/`--interpreter` selectors
169-
(and, absent those, a matrix env's config-declared `default_interpreters`
168+
interpreter axis (ADR-0103): `run`'s `--interpreter` selectors
169+
(and, absent those, each base's config-declared `default_interpreters`
170170
policy) are resolved ONCE per project by
171-
`run_service/run_selection.selected_interpreters_for_project` (a thin
172-
WM-side wrapper around the pure `config/env_selection.resolve_selected_interpreters`
173-
resolver — the same resolver `prepare_envs_service` uses for `prepare-envs`)
171+
`run_service/run_selection.selected_envs_for_project` (a thin
172+
WM-side wrapper around the pure `config/env_selection.resolve_run_selection`
173+
resolver into selected concrete envs)
174174
at each run entry point (`actions/run`, `actions/runBatch`, both with and
175175
without a `partialResultToken`), then threaded down as a
176-
`selected_interpreters: set[str] | None` argument to whichever fan-out site
176+
`selected_envs: set[str] | None` argument to whichever fan-out site
177177
handles the request — `matrix_runner.run_matrix_action` or
178-
`matrix_streaming.run_matrix_with_partial_results`. `None` (no selectors, no
179-
narrowing config default) runs the full axis, unchanged; an interpreter
180-
outside the resolved axis raises `ActionRunFailed`.
178+
`matrix_streaming.run_matrix_with_partial_results` — which derive variants
179+
via `matrix_runner.selected_variants`. `None` (no selectors, every base full)
180+
runs the full axis, unchanged.
181181

182182
### `runner/` — ER process lifecycle and JSON-RPC client
183183

‎docs/wm-protocol.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ converted before dispatch; MCP leaves it off so listing tools never starts
433433
environments.
434434

435435
`runOptions` (optional, honoured only with `startRunners`): the selection
436-
inputs the run itself will use — `devEnv`, `envSelectors` and
436+
inputs the run itself will use — `devEnv` and
437437
`interpreterSelectors` (same shapes as the `actions/runBatch` run options).
438438
The fetch computes the same per-project interpreter selection as the run, so
439439
it starts only the interpreter instances the run will select. Selectors are

‎src/finecode/cli_app/cli.py‎

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,6 @@ def run(ctx) -> None:
391391
dev_env: str = detect_dev_env()
392392
wal_enabled: bool | None = None
393393
verbose: bool = False
394-
env_selectors: list[str] = []
395394
interpreter_selectors: list[str] = []
396395
resource_usage_flag_value: str | float | None = None
397396
no_resource_usage: bool = False
@@ -455,8 +454,12 @@ def run(ctx) -> None:
455454
err=True,
456455
)
457456
sys.exit(1)
458-
elif arg.startswith("--env="):
459-
env_selectors.append(arg.removeprefix("--env="))
457+
elif arg == "--env" or arg.startswith("--env="):
458+
click.echo(
459+
"run selects matrix variants by interpreter: use --interpreter=<impl>@<version> (repeatable) or --interpreter=all. --env is a prepare-envs option (ADR-0103).",
460+
err=True,
461+
)
462+
sys.exit(1)
460463
elif arg.startswith("--interpreter="):
461464
interpreter_selectors.append(arg.removeprefix("--interpreter="))
462465
elif arg == "--resource-usage":
@@ -587,7 +590,6 @@ def run(ctx) -> None:
587590
dev_env=dev_env,
588591
wal_enabled=wal_enabled,
589592
verbose=verbose,
590-
env_selectors=env_selectors,
591593
interpreter_selectors=interpreter_selectors,
592594
resource_usage_interval=resource_usage_interval,
593595
)
@@ -704,14 +706,7 @@ def run(ctx) -> None:
704706
"env_names",
705707
multiple=True,
706708
metavar="ENV_NAME",
707-
help="Limit to specific environment(s). Can be specified multiple times.",
708-
)
709-
@click.option(
710-
"--interpreter",
711-
"interpreter_names",
712-
multiple=True,
713-
metavar="IMPL@VERSION",
714-
help="Limit to specific interpreter(s) of matrix environments. Repeatable; version-only form means cpython.",
709+
help="Limit to specific environment(s): a name, a matrix base (its default interpreters), <base>@<impl>-<version>, or <base>@all. Repeatable.",
715710
)
716711
@click.option(
717712
"--project",
@@ -750,7 +745,6 @@ def prepare_envs(
750745
dev_env: str | None,
751746
workspace_packages_mode: str | None,
752747
env_names: tuple[str, ...],
753-
interpreter_names: tuple[str, ...],
754748
project_names: tuple[str, ...],
755749
verbose: bool,
756750
resource_usage: float | None,
@@ -806,9 +800,6 @@ def prepare_envs(
806800
own_server=not shared_server,
807801
log_level=log_level,
808802
env_names=list(env_names) if env_names else None,
809-
interpreter_names=list(interpreter_names)
810-
if interpreter_names
811-
else None,
812803
project_names=list(project_names) if project_names else None,
813804
dev_env=dev_env or detect_dev_env(),
814805
workspace_packages_mode=workspace_packages_mode,

0 commit comments

Comments
 (0)