Files
boshu2__agentops/docs/contracts/scenario-test-linkage.md
T
Bo 5908024782 Structural: goals→fitness rename with compatibility alias; shared retired to a declared contract owner (#1007)
## Summary

The two structural beads deferred from the skill-overhaul reboot waves.

**goals → fitness** (`age-skill-overhaul-reboot-sjv7v.10`, 07-24 plan
D9)
- The semantic skill is now `fitness`; the `ao goals` CLI command family
deliberately keeps its name.
- `skills/goals` remains a thin, non-advertised compatibility alias:
user-invocable, zero capabilities/effects/dependencies, an `alias-of`
context_rel edge to fitness, physical deletion only under the
observed-zero policy.
- `alias-of` added to the frontmatter schema enums (the catalog schema's
own description already listed it as valid relationship vocabulary; the
enum lagged).
- bootstrap's `goals` consumer edge migrated; the stale `goals.feature`
row in the scenario-linkage contract corrected (the feature file was
already absent).

**shared retirement** (`age-skill-overhaul-reboot-sjv7v.11`, 07-24 plan
D9)
- The runtime-neutrality contract moved to its declared owner:
`docs/contracts/runtime-neutrality.md`, scope unchanged (shared
references a consuming skill loads).
- The last consumer edge (`bootstrap consumes: shared`) removed;
`skills/shared` reduced to a non-routable tombstone pending the
observed-zero deletion window; the ports-and-adapters contract updated
to executed state.

## Validation

- Strict frontmatter 50/50 (fitness + alias); regen byte-current;
scenario-linkage PASS; liveness/anti-spiral/desc-budget bats green;
`cli/internal/skills` Go tests pass
- Cross-family review, two rounds: round 1 — one finding fixed
(unintended contract-scope broadening reverted), one rejected with
evidence (the reviewer inferred a pre-existing feature file from a stale
doc row; the pre-change tree had none); round 2 delta **VERDICT: PASS**

Tracker: `age-skill-overhaul-reboot-sjv7v.10`, `.11`
2026-07-29 05:03:12 +00:00

105 lines
4.6 KiB
Markdown

# Scenario → Test Linkage Contract
Status: active · Bead: soc-63xfx · Gate: `validate-scenario-test-linkage` (T1, blocking)
## Why
71 `.feature` files live under `skills/*/references/`. Historically only a
handful had an executing test; the rest were documentation that drifted free of
any check. A scenario nobody runs is a claim nobody verifies.
This contract makes the scenario→test relationship **mechanical**: every Gherkin
`Scenario:` in the corpus must either name the test that covers it, or its
feature file must be explicitly declared documentation-only. The gate fails on
any scenario that is neither.
It is the sibling of `executable-spec-link-integrity` (soc-58nt / soc-m8tdn),
which links **GOALS directives → scenarios**. This one links **scenarios →
tests**. Together they close the spec-to-evidence chain:
```
GOALS directive ──► scenario (.feature) ──► test (executing)
(exec-spec-link-integrity) (scenario-test-linkage)
```
## The convention
Tag a scenario with the test that covers it using a Gherkin tag:
```gherkin
@covered-by:tests/e2e/rpi-phased-domain.sh
Scenario: Phases run in order and never compress
When /rpi executes
Then it runs Research, then Plan, then Implement in order
```
Rules:
- The tag goes on its **own line directly above** the `Scenario:` it covers —
the standard Gherkin tag position. A non-tag line (a step, `Background`, a
blank then content) between the tag and the scenario breaks the association.
- **File-level tags** (placed above the `Feature:` line) apply to **every**
scenario in the file.
- Multiple tags may stack; a scenario passes only if **all** of its
`@covered-by:` targets resolve.
- Two target forms:
- `@covered-by:<test-path>` — the test file must exist (repo-relative path).
- `@covered-by:<test-path>::<Name>` — additionally, `<Name>` (a Go func, a
bats `@test` label, a bash function, etc.) must appear in that file. The
name match is a substring check, which keeps the gate language-agnostic
while still catching a renamed or deleted test.
`<test-path>` is any executing test surface: `tests/e2e/*.sh`,
`tests/scripts/*.bats`, `cli/**/*_test.go`, and so on. The gate only asserts the
target **exists** — it does not run it. Running is the test suite's job; linking
is this gate's job.
## The allowlist
`scripts/.scenario-linkage-allow` lists feature files (repo-relative, one per
line, `#` comments allowed) that are intentionally documentation-only for now.
The gate skips scenarios in those files.
The allowlist is a **draining backlog**, not a parking lot:
- When a skill gains an executing test, tag its scenarios with `@covered-by:`
and **remove the file from the allowlist**.
- A file may **not** be both allowlisted **and** carry `@covered-by:` tags —
that ambiguous intent is a gate error. Pick one.
- A stale allowlist entry (a listed file that no longer exists) is a gate error.
- Do not add new feature files to the allowlist without a tracking bead; new
specs should ship with their covering test.
## Gate behavior
`scripts/check-scenario-test-linkage.sh`:
| Condition | Result |
|---|---|
| Scenario has a resolving `@covered-by:` tag | pass |
| Scenario's feature file is allowlisted | pass (counted as doc-only) |
| Scenario has no tag and file is not allowlisted | **FAIL** |
| `@covered-by:` path does not exist | **FAIL** (dangling) |
| `@covered-by:...::Name` not found in the file | **FAIL** (dangling) |
| Allowlisted file also carries a `@covered-by:` tag | **FAIL** (ambiguous) |
| Allowlist entry points at a deleted file | **FAIL** (stale) |
Flags: `--warn-only` (advisory, exit 0), `--json` (machine-readable summary),
`-h`/`--help`. Exit codes: `0` pass, `1` fail, `2` misuse.
Wired into `.github/workflows/validate.yml` as `validate-scenario-test-linkage`,
gated on changes to `skills/**`, `**/*.sh`, or `.github/**`.
## Starter set (at landing, soc-63xfx)
3 of 71 feature files are fully linked to executing tests:
| Feature | Covering test |
|---|---|
| `skills/rpi/references/rpi.feature` | `tests/e2e/rpi-phased-domain.sh` |
| ~~`skills/goals/references/goals.feature`~~ (stale row: the feature file was already absent from the tree before 2026-07-29; the skill is now `fitness`) | `tests/e2e/goals-measure-scenarios.sh`, `goals-trace-chain.sh` still execute against the `ao goals` CLI |
| ~~`skills/scenario/references/scenario.feature`~~ (removed: `scenario` folded into `eval-outcomes`, 2026-06-12) | `tests/e2e/goals-scenarios-link.sh` |
The remaining 68 files are allowlisted in `scripts/.scenario-linkage-allow` with
the standing goal to drain the list as skills gain executing tests.