Skip to content

feat(evals): support multi-skill treatments - #291

Merged
slowdini merged 1 commit into
devfrom
feat/multi-skill-treatments
Aug 24, 2026
Merged

feat(evals): support multi-skill treatments#291
slowdini merged 1 commit into
devfrom
feat/multi-skill-treatments

Conversation

@slowdini

Copy link
Copy Markdown
Owner

Summary

  • accept an ordered, non-empty skill list as the treatment while retaining the scalar skill_name form
  • stage, snapshot, record, and promote the complete treatment roster in both comparison modes
  • grade invocation per treatment member across deterministic and LLM-fallback harnesses
  • preserve established scalar artifact shapes and document the multi-skill workflow

Closes #264.

Overall strategy

The implementation extends the existing treatment, source, and artifact contracts instead of adding a parallel suite abstraction. A list-authored skill_name resolves each member through the same source machinery used for a scalar skill, records the CLI-selected member as the eval owner, and carries the ordered roster through conditions.json, dispatch.json, each run.json, benchmark.json, and promoted BASELINE.md provenance.

For example, an eval can define a coordinated treatment as follows:

{
  "skill_name": ["review-workflow", "review-verification"],
  "evals": [
    {
      "id": "review-change",
      "prompt": "Review this change.",
      "expected_output": "A prioritized review."
    }
  ]
}

The --skill argument selects the eval owner. It supplies the eval definitions and fixtures and owns the workspace and promotion destination, so it must be a member of the authored list.

Staging and comparison modes

  • Mode A stages every treatment member in with_skill and none in without_skill.
  • Mode B snapshots the complete roster atomically and stages every member from revision A or revision B in the corresponding arm.
  • Skills under the shared skills directory that are not treatment members remain ambient siblings in both arms.
  • --no-stage inlines every treatment member from the iteration copy.
  • --stage-name rejects list-authored treatments because one override cannot name an entire roster.

Treatment names are validated as safe single directory components. Lists must be non-empty and unique, and the eval owner must be present. Failed set snapshots remove their pending directory instead of leaving a partial baseline.

Cross-harness invocation grading

The condition roster supplies each member's staged slug to grading. Harnesses whose descriptors expose a deterministic invocation signature produce one transcript result per member using indexed response files such as __skill_invoked__skill-1.json. Harnesses without deterministic evidence receive one LLM fallback task per member.

Each multi-skill meta result names its skill_name, and benchmark.json adds per-skill invocation counts and rates. The suite-level meta_summary.skill_invoked value uses the agreed any-member rule, so partial invocation satisfies the suite check while remaining distinguishable from complete invocation. Negative evals with skill_should_trigger: false continue to suppress invocation checks.

Compatibility and provenance

Scalar skill_name values retain the established single-skill paths, filenames, serialized fields, warnings, and grading behavior. Optional roster fields are absent from scalar artifacts. A one-member list deliberately uses the list-authored artifact and indexed grading forms rather than collapsing back to scalar behavior.

Multi-skill provenance records every member's resolved source, revision, and dirty state while retaining the eval owner's flattened source for compatibility. Live-source detection checks every recorded treatment source, and promotion renders every source in BASELINE.md.

Prompt-specific roster rendering, orchestration source records, and promotion-row formatting were extracted into focused modules to keep the shared files from accumulating the entire feature.

Before and after

Before, only a scalar treatment was valid:

{ "skill_name": "review-workflow" }

The list form treats the coordinated skills as one experimental treatment:

{ "skill_name": ["review-workflow", "review-verification"] }

Scalar input remains supported for existing evals.

Verification

  • cargo fmt --check
  • cargo build
  • cargo test --quiet — 926 library tests, 201 CLI tests, and 199 run tests passed
  • cargo clippy --all-targets -- -D warnings
  • git diff --check
  • cargo test --test run multi_skill — 12 focused multi-skill workflows passed

Reviewer notes

  • The suite-level invocation decision is intentionally any-member; the per-skill rollup preserves the partial-versus-complete distinction.
  • Ambient siblings exclude the complete treatment roster and remain present in both arms.
  • Existing scalar goldens and integration tests exercise the compatibility path alongside the list-specific coverage.

Stage ordered skill rosters as one treatment while preserving scalar artifact compatibility. Grade invocation per member and carry complete treatment provenance through reports and promotion.
@slowdini
slowdini merged commit e30a509 into dev Aug 24, 2026
7 checks passed
@slowdini
slowdini deleted the feat/multi-skill-treatments branch August 24, 2026 07:39
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.

Skill sets as the treatment (multi-skill evals)

1 participant