Skip to content

feat(examples): add experiment authoring-input templates for runs and studies - #1458

Open
doublewhy wants to merge 3 commits into
devfrom
380-experiment-template-catalog
Open

doublewhy wants to merge 3 commits into
devfrom
380-experiment-template-catalog

Conversation

@doublewhy

@doublewhy doublewhy commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Builds on #1445 (issue #378), which builds on #1438 (issue #381). Until those merge, this PR also shows their commits, 6fa7bdab and f397d0a4. This PR's own change is commit 88975279.

The library's task, run, and study templates were SDL wrappers only. This PR adds two templates whose bodies are experiment-authoring-input-v1 documents, the pre-run experiment design that ADR-074 defines:

  • seeded-run-plan (run surface): the run count, a seed, and episode controls for one task.
  • two-condition-study-design (study surface): a treatment factor, two compared conditions, and the runs per condition.

Their catalog entries declare contract: experiment-authoring-input-v1. The gate validates such a body with raes_contracts.experiment_spec.parse_experiment_spec, the authoring-input loader, instead of the SDL parser.

How this meets the issue's points:

  • Separate entries when the repository can validate them. Run and study gain experiment templates. The task surface keeps only its SDL template. An experiment task is an experiment-task-v1 document, and the repository has no task loader. raes semantic validate --contract experiment-task-v1 exits 2 with "The contract selector is not supported." ExperimentTaskModel also requires at least one artifact reference with a checksum, size, and creation time, which a template would have to invent. An experiment-authoring-input-v1 document cannot serve as a task template either: it names a task in task_ref and must not repeat task meaning (ADR-074 §2). The README lists the task template under "Outside current support".
  • Validation commands and expected results. Each experiment template's validation list gives the gate command and a loader command for an adapted copy. The README gives the copy command too.
  • SDL, guidance, and outside current support. A new README subsection, "Task, run, and study entries", separates the SDL templates, the experiment templates, the prose patterns, and what is not supported.
  • Distinct concerns and no lifecycle coverage claims. The run template declares no factors or conditions, only a flat target_run_count. The study template has factors and a condition allocation. The limits state that each is pre-run design only and not an experiment-run-v1 or experiment-study-v1 record, and they name the checks the loader does not make.

Requirement UIDs

Related Issues

Closes #380

ADR Impact

  • No ADR required, and no ADR text changes. The templates follow ADR-074: the authoring input is pre-run design, names its task by reference, and is neither a run nor a study record. They also follow ADR-055's separation of tasks, runs, and studies.

Changes

  • examples/library/templates/run/seeded-run-plan.yaml and examples/library/templates/study/two-condition-study-design.yaml (new): each holds an experiment-authoring-input-v1 body and two validation entries.
  • examples/library/catalog.yaml: catalogs both templates with contract, validation_status: validated, intended_user: experiment-author, and limits, and no sdl_sections. The catalog description now mentions experiment templates.
  • tools/example_library_checks.py:
    • An entry may declare contract. The allowed values are sdl-yaml/v1, the default, and experiment-authoring-input-v1, which only templates may use.
    • Each contract has one intended user: sdl-author or experiment-author.
    • sdl_sections is required for SDL entries and must be absent for experiment entries.
    • A new rule id, example-library-entry-contract, reports a contract the entry may not use.
    • check_template_body sends an experiment body to the loader, and a loader failure is reported as example-library-template-body. A body under an unknown contract is not validated, because the contract rule already fails the entry.
  • tools/check_example_library.py: unchanged, at 478 lines (wc -l).
  • examples/README.md: adds the experiment templates to the library table. Adds a contract row to the metadata table and updates the validation_status, sdl_sections, and intended_user rows. Adds the "Task, run, and study entries" subsection and the adapted-copy command, and updates the gate paragraph and the template-boundary paragraphs.
  • docs/explain/getting-started.md: one table cell now says a template body validates as current SDL or as an experiment-authoring-input-v1 document.
  • implementations/python/tests/test_issue_380_experiment_templates.py (new): a passing experiment template, and 8 violations that must each produce exactly one rule id:
    • a contract on a worked example, an unknown contract, sdl_sections on an experiment entry, and sdl-author on an experiment entry;
    • experiment-author on an SDL template;
    • an experiment body without task_ref, an SDL body under the experiment contract, and an experiment body without a contract.

Test Plan

  • Unit tests pass
  • Integration tests pass if applicable
  • Full completion suite required in CI before merge
  • No coverage regression

All commands ran from the worktree root under CPython 3.12.13 on head 88975279. The base is origin/dev at 35122105 plus the #1438 and #1445 commits, 6fa7bdab and f397d0a4.

I ran each documented command. uv run --project implementations/python --frozen python tools/check_example_library.py exited 0 and printed nothing. For each experiment template, I saved its body as my-experiment.exp.yaml in the worktree root and ran the README's load_experiment_spec command. It printed library-seeded-run-plan and library-two-condition-study-design, each with exit 0. On a copy that declares both target_run_count and allocation, the same command exited 1, and its traceback ended with ExperimentSpecValidationError reporting /run_plan: value_error. I removed the file after each run.

On a scratch copy where seeded-run-plan declares both run-count sources, the gate with --repo-root exited 1. It reported [example-library-template-body] examples/library/templates/run/seeded-run-plan.yaml: template body is not a valid experiment-authoring-input-v1 document, followed by the loader's /run_plan: value_error.

For the task decision, uv run --project implementations/python --frozen raes semantic validate --contract experiment-task-v1 contracts/fixtures/experiment-core/experiment-task-v1/valid/reference.json printed validate: usage and error [cli.selector] The contract selector is not supported., and exited 2.

The limits rest on these loader probes against edited copies of the two bodies:

  • The loader rejected both run-count sources together, a condition key that differs from its condition_id, and a duplicate YAML key.
  • It accepted a condition level that the factor does not declare, a condition that names an undeclared factor, and a task_ref to a task that does not exist.

uv run --project implementations/python --frozen --all-extras python -m pytest on test_issue_380_experiment_templates.py, test_issue_378_scenario_templates.py, test_issue_381_example_catalog_metadata.py, and test_example_library_policy.py with -q -p no:cacheprovider passed: 39 passed (9 new). None of the modules has integration-marked cases, and -m integration deselects all 39.

Regression check: I restored #1445's tools/example_library_checks.py from f397d0a4 and kept the new tests and templates. 8 of the 9 new cases failed. The ninth, experiment-body-without-contract, passes on both versions, because a template without a contract is validated as SDL in both. With the change restored, all 39 pass. A coverage.py branch report from the same modules covers every line and branch of tools/example_library_checks.py, including every line this commit adds.

nox -s verify-fast-feedback -- --base-rev origin/dev passed, with every non-skipped stage green. policy / requirement governance was skipped because the branch resolves no requirement UID and no scope file exists for #380. nox -s lint and make policy passed.

This head is rebased onto #1445's head f397d0a4, which is rebased onto #1438's head 6fa7bdab. The previous head, 3ad4b312, was on 6b467ebc. Three hunks conflicted. In tools/example_library_checks.py, the sdl_sections message keeps #1438's new wording, "names values that are not SDL sections". In examples/README.md, the gate paragraph keeps #1445's new sentence that the gate does not run the validation commands. The "Template Boundary" placeholder sentence from #1445 now says "an SDL template body", because parse_experiment_spec accepted a seeded-run-plan body whose title or description contains ${undeclared}. The templates and tests are unchanged. CI on 3ad4b312 (run 37918467275) passed.

CI on head 88975279: run 37933867593 (CI) passed, and all 32 checks pass, with deploy skipped. The SonarCloud quality gate is OK on 88975279: 0 new issues, 99.3% coverage on new code, and 0.0% duplication.

Ground Control Checks

  • Repository policy command passes: make policy exited 0, and the only skipped stage was requirement governance.
  • Regression check fails without the checker change and passes with it, as described in the Test Plan.

Pre-push code review and test-quality review: not run for this lane.

Traceability

  • IMPLEMENTS: examples/library/templates/run/seeded-run-plan.yaml, examples/library/templates/study/two-condition-study-design.yaml, examples/library/catalog.yaml, tools/example_library_checks.py, examples/README.md, docs/explain/getting-started.md
  • TESTS: implementations/python/tests/test_issue_380_experiment_templates.py

Checklist

  • Code follows the project coding standards: the contract logic sits in the support module from feat(examples): add machine-checked metadata to example library entries #1438, with at most three returns per function, and the main checker is unchanged.
  • FM: not applicable. There is no SDL, contract, schema, or runtime semantic change; contracts/schemas/ and the experiment loader are untouched, and the templates only use them. No executable or runtime adoption is claimed: nothing admits, schedules, or runs these plans.
  • Published contract schemas regenerated if models changed: no model or schema changed.
  • PR title is a Conventional Commit: feat(examples), because the new templates are user-visible.
  • Architectural docs updated if applicable: there is no architecture change. examples/README.md and docs/explain/getting-started.md describe the templates.

Documentation

Updated: examples/README.md (library table, metadata table, "Task, run, and study entries", template boundary) and one table cell in docs/explain/getting-started.md.

@doublewhy
doublewhy force-pushed the 380-experiment-template-catalog branch 2 times, most recently from 64ddfd7 to 3ad4b31 Compare October 9, 2026 10:34
Every worked example, template, and pattern entry in
examples/library/catalog.yaml now records validation_status (validated
or guidance), sdl_sections, intended_user, and limits. The gate rejects
an entry whose status is outside the vocabulary, a template that is not
validated, a pattern that claims validation, an unknown intended user,
empty limits, or a section name that is not a current SDL section.
Validated worked examples are now parsed through parse_sdl_file and
fail on errors or advisories, and a validated entry must list only
sections present in its SDL. The catalog description no longer calls
every entry validated.

The entry checks live in a new support module,
tools/example_library_checks.py, because tools/check_example_library.py
was already 488 lines against SonarCloud's 500-line file limit. The
template-body SDL check moves there with its rule ids and messages. The
module imports raes at module level, so the
example-library-template-import rule for a failed raes import is
removed. The top-level fields that are not sections come from
tools/sdl_catalog_parity/_paths.py, which the SDL catalog parity gate
keeps in step with specs/sdl/sections.md.

examples/README.md documents the fields and what the gate checks.
…laim notes

Add two scenario templates beside minimal-validated-scenario:
segmented-network-scenario (two switched segments, linked hosts with
fixed addresses, HTTPS-only access rules, a service feature) and
parameterized-scenario (host operating system, size, instance count, and
service port from declared variables). Both bodies pass the SDL parser
and semantic validator with no advisories, and both are cataloged with
the entry metadata.

Every template now carries a validation list of commands with their
expected results. The gate requires the field, and
tools/example_library_checks.py checks that it is a non-empty list of
mappings with non-empty command and expected strings. The gate does not
run the commands.

examples/README.md lists what each scenario template shows and does not
claim, how to validate an adapted copy with raes semantic validate, and
that a ${name} placeholder in a template body must name a declared
variable.
… studies

Add two templates whose bodies are experiment-authoring-input-v1
documents: seeded-run-plan (run count, seed, and episode controls for one
task) and two-condition-study-design (a treatment factor, two compared
conditions, and the runs per condition). Both pass the experiment
authoring-input loader, parse_experiment_spec.

A template's catalog entry may now declare contract:
experiment-authoring-input-v1. Such an entry names experiment-author as
its intended user and lists no sdl_sections, and the gate validates its
body with the experiment loader instead of the SDL parser. Worked
examples and patterns stay SDL.

examples/README.md separates the SDL, experiment-input, guidance, and
unsupported parts of the task, run, and study surfaces, including why
there is no experiment-task-v1 template, and gives a command to validate
an adapted experiment template.

This branch has not been deployed

No deployments
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.

1 participant