# `simready_benchmark.PlanEditor` API Reference

`PlanEditor` wraps a `Plan` dataclass and appends `PlanOverride` entries to its
`plan.overrides` list. Plan I/O lives in `simready_benchmark.core.models`:
`load_plan(path) -> Plan` and `save_plan(plan, path) -> None`.

## Construction

```python
from simready_benchmark import PlanEditor

editor = PlanEditor(plan)   # plan is a `simready_benchmark.core.models.Plan` instance
editor.plan                 # access the wrapped Plan (read-only property)
```

## Profile Overrides

```python
editor.disable_profile(asset_path: str, profile_name: str) -> None
editor.enable_profile(asset_path:  str, profile_name: str) -> None
```

Apply to every version of the named profile that exists in the plan. Raises
`KeyError` if the asset or profile is not found.

## Feature Overrides

```python
editor.disable_feature(
    asset_path:    str,
    feature_name:  str,
    scope_profile: str | None = None,   # None = global; otherwise scoped to profile
) -> None

editor.enable_feature(asset_path: str, feature_name: str) -> None
```

When `scope_profile` is given on `disable_feature`, the disable applies only
within that profile (target `profile:<pid>/feature:<fid>`). Otherwise it is
global (target `feature:<fid>`).

```python
editor.only_features(asset_path: str, keep_patterns: list[str]) -> int
```

Disable every feature whose name does not match one of `keep_patterns`. Each
pattern is an exact name OR a prefix that matches `<prefix>_<suffix>`. Returns
the count of features disabled.

## Test Overrides

```python
editor.disable_test(asset_path: str, test_name: str) -> None
editor.enable_test(asset_path:  str, test_name: str) -> None
```

`test_name` is the test's exact registered name (not a `feature/name` path).

## Config Overrides

```python
editor.set_config_override(
    asset_path: str,
    test_name:  str,
    key:        str,
    value:      Any,
) -> None
```

Append a config override that the runner merges into the test's `ctx.config`
dict at execution time. Call it again for each key. The method takes one
key/value per invocation.

## Clear Edits

```python
editor.enable_all(asset_path: str) -> None
```

Remove every override entry targeting this asset from `plan.overrides`.

## Inspect Effective Tests

```python
editor.get_effective_tests(asset_path: str) -> list[int]
```

Returns the integer test IDs that resolve to enabled for this asset after the
full override log is applied. Backed by
`simready_benchmark.core.plan_resolver.resolve_effective_tests`.

## CLI Flag to API Mapping

| CLI flag | API call (after `editor = PlanEditor(plan)`) |
|---|---|
| `--asset PATH --disable-test NAME` | `editor.disable_test(PATH, NAME)` |
| `--asset PATH --enable-test NAME` | `editor.enable_test(PATH, NAME)` |
| `--asset PATH --disable-feature NAME` | `editor.disable_feature(PATH, NAME)` |
| `--asset PATH --enable-feature NAME` | `editor.enable_feature(PATH, NAME)` |
| `--asset PATH --disable-profile NAME` | `editor.disable_profile(PATH, NAME)` |
| `--asset PATH --enable-profile NAME` | `editor.enable_profile(PATH, NAME)` |
| `--asset PATH --only-features A,B,...` | `editor.only_features(PATH, ["A", "B", ...])` |
| `--asset PATH --set-config TEST:K=V` | `editor.set_config_override(PATH, "TEST", "K", V)` |
| `--asset PATH --enable-all` | `editor.enable_all(PATH)` |
| `--all-assets ...` | Iterate over `plan.assets` and call the corresponding method per asset path. |

## Override Semantics

Overrides compose in append order. A later override wins over an earlier one
for the same target. `enable_all(asset_path)` is the only operation that
*removes* prior overrides (everything else only appends).

The plan's `assets`, `features`, `profiles`, and `tests` lists are immutable;
runtime gating happens through `resolve_effective_tests(plan, asset_id)`,
which walks the override log.
