yf-change-validation
Runs a repo's recorded VALIDATION RECIPE (build/test/lint) over a change-set or merged tree by EXECUTING the commands an
Runs a repo's recorded VALIDATION RECIPE (build/test/lint) over a change-set or merged tree by EXECUTING the commands and reporting PASS / FAIL / INCONCLUSIVE + the first failing command; never auto-fixes and never auto-rewrites the manifest. Driven by a per-repo CHANGE-VALIDATION.md inferred from the toolchain, operator-approved, then re-proposed on drift.
At a glance
- Category:
utility - Invocation:
/yf-change-validation(user-invocable) - Requires tools:
uv - Source:
SKILL.md·README.md
When it fires
/yf-change-validation invoked; a file covered by an approved CHANGE-VALIDATION.md §3 glob is created or modified (run the FAST tier); a pre-push / land-the-plane FULL-tier validation; or a manifest is being bootstrapped on first install.
When to skip it
repos with no approved CHANGE-VALIDATION.md (silent no-op — no nag, no bootstrap on every edit); any request to FIX a failing command rather than report it; checking CONTENT AGREEMENT across docs/spec/impl edges — that is yf-drift-check (a prose/LLM trigger), an orthogonal axis this engine never invokes. Distinguishing axis: yf-change-validation proves a change-set is BEHAVIORALLY valid by running commands (exit code = verdict); yf-drift-check proves already-written artifacts AGREE. Neither invokes the other.
Prerequisites
- Claude Code (the skill loads as part of this repo's skill set).
uvon PATH (depends-on-tool: [uv]) — the engine is a PEP-723uv run --scriptPython script. No in-repo skill dependency (depends-on-skill: []).- A repo opts in by authoring (or bootstrapping) an approved
CHANGE-VALIDATION.mdat its repo root. With no approved manifest the engine is a silent no-op.
Usage
User-invocable (/yf-change-validation) with three subcommands:
init(bootstrap) — infer a draft manifest from the toolchain, present it, await operator approval (infer → approve → enforce).run --tier fast|full [--changed <paths>]— execute a tier over the approved manifest (affected-scoped when--changed) and report the result.check-drift— diff the live toolchain against the recorded fingerprint and emit a JSON re-proposal; never rewrites the manifest.
It also fires automatically from the always-loaded companion rule
(protocols/CHANGE-VALIDATION-TRIGGER.md): on edit of a path matching an approved manifest's
§3 glob it runs the FAST (affected) tier; on pre-push / land-the-plane it runs the FULL
tier on the merged tree.
The manifest
CHANGE-VALIDATION.md is markdown the engine reads by section (mirrors DRIFT-CHECK.md's
section-by-heading model, but the recipe is executable):
- §0 Status — the
approved: yes|nogate. Inert untilapproved: yes; setting it back tonois the one-edit rollback lever. - §1 Tiers — a
fastand afullordered command list, each a table of structured rows (id,cmd,cwd,timeout). FULL is a superset of CI ∪ repo-checks. - §2 Signal Fingerprint — the
{source-path, parsed-value-or-hash}of the toolchain signals the recipe was inferred from; whatcheck-driftre-reads. - §3 Trigger Scope — each changed-path glob → the FAST command ids it selects (affected-scoping).
Relationship to yf-plan
yf-plan's §6.1.5 merged-state validation (layer b) delegates to this skill via a prose
soft-dep: an approved CHANGE-VALIDATION.md present → delegate to change_validation.py run
--tier full; absent or approved: no → fall back to the static validate-cmd; absent that
too → the verbatim not-checked notice. This skill supersedes the static validate-cmd (the #27
migration), which it seeds the FULL tier from at inference time and keeps as a thin fallback.
Behavior model
edit / pre-push
│
▼
read approved CHANGE-VALIDATION.md ──(none / approved: no)──▶ silent no-op / clean refusal
│
▼
on-edit: match changed path against §3 globs ──▶ FAST tier (affected ids)
pre-push / land-the-plane ──────────▶ FULL tier (merged tree)
│
▼
execute commands in row order (sh -c, cwd, timeout)
│
├─ all exit 0 ──▶ PASS
├─ first non-zero exit ──▶ FAIL (first_failure + output tail); never auto-fix
└─ required tool absent ─▶ INCONCLUSIVE (fail-closed; never a false PASS)
- No auto-fix. The engine runs and reports; the operator repairs.
- No auto-rewrite.
check-driftproposes a tier delta; it never edits the manifest. - Fail-closed. A missing tool is INCONCLUSIVE, the deliberate contrast with a fail-open
validate-cmd.
Scope boundary
change-validation proves a change-set is behaviorally valid by executing build/test/lint.
Cross-edge content agreement (docs ↔ spec ↔ implementation) is yf-drift-check's axis — a
prose/LLM trigger with no runnable command, excluded from every tier. The two are
independent, non-recursive triggers; a shared .md edit firing both is expected. Neither
invokes the other.
Layout
skills/yf-change-validation/
├── SKILL.md # engine: invocation, manifest detection, dispatch, run-and-report
├── README.md # this file
├── SPEC.md # the requirement-numbered (REQ-CHGVAL-*) per-skill spec
├── spec/
│ ├── schema.md # the 4-section manifest schema + structured tier rows (REQ-SCHEMA-*)
│ ├── engine.md # no-op, infer→approve→enforce, run-and-report, fail-closed, re-propose, rollback (REQ-ENGINE-*)
│ └── inference.md # inference precedence, PEP-723 per-file idiom, FULL-superset, validate-cmd seed (REQ-INFER-*)
├── scripts/
│ └── change_validation.py # the Python engine: infer / run / check-drift
├── templates/
│ └── manifest.md # blank CHANGE-VALIDATION.md a repo fills in (inert approved: no)
└── protocols/
└── CHANGE-VALIDATION-TRIGGER.md # always-loaded on-edit (FAST) + pre-push (FULL) firing surface