Files
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

4.6 KiB

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:

@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.