Property Tests
SkillDocs & knowledgeUse when adding or changing a rule's detection or safe-fix behaviour, or editing any property-test suite (safe-fix / rule-checker / markdown-fix / config). Covers what each generator must be extended with, the ~1000x pre-commit run, and which rules intentionally have no safe `--fix`.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Property Tests skill
What this skill tells your AI
The instructions your AI receives, as published by owenlamont/ryl in .agents/skills/property-tests/SKILL.md and read by ahel’s review.
When implementing a new rule or changing an existing one, extend the relevant
property-test generator(s) so the new/updated syntax is actually exercised (each suite
below lists exactly what to extend and the deterministic guard to add), then do a
one-off ~1000× thorough run before committing: e.g.
PROPTEST_CASES=512000 cargo test --release --test property_check (the suites'
in-CI default is 512 cases — tuned for speed, not exhaustiveness). Build --release
and run it in the background; it routinely flushes rare interleavings the small count
misses. Commit only once it is green, and keep any newly-persisted seeds in
tests/proptest-regressions/.
Property Tests For Safe Fixes
tests/property_safe_fix.rs runs generated YAML through apply_safe_fixes and asserts
three soundness invariants (a safe fix must never change meaning, but need not be
complete — so it does not assert "no diagnostics remain"): idempotence, parse
preservation (parses to an equal YamlOwned), and a leading # ryl disable making the
fix a byte-for-byte no-op. It runs a matrix of named configs — five YAML
(yamllint-default, best-practice, strict-single, strict-double, consistent)
plus one TOML-backed (best-practice-toml, covering ryl-only options like
allow-double-quotes-for-escaping). Deterministic siblings pin known-dirty /
production-bug inputs through the same checks (and assert the fixer clears them) so the
property can't pass vacuously.
When you add a new FixSafety::Safe rule:
- Add its rule id to
SAFE_FIX_RULESand toCOMMON_SAFE_FIX_RULES_YAMLintests/property_safe_fix.rs. If the new rule introduces meaningful config axes, add a variant toQUOTED_STRINGS_VARIANTS(or a peer constant for that rule) so the matrix exercises each regime; ryl-only options must go through the TOML slot rather than YAML. - Extend the AST / renderer in that file so generated documents exercise the syntax the new fixer targets. Skipping this leaves the property tests green for the wrong reason — the fixer has nothing to do.
- Run
cargo test --test property_safe_fixand resolve any failures before landing the rule. - Add a focused CLI-level regression test in
tests/cli_fix.rs(or the rule-specific file) for any production bug discovered along the way, so the property suite is backed by a deterministic guard.
Failing inputs are persisted at tests/proptest-regressions/property_safe_fix.txt
and replayed first on every run. That file is committed to git so the regression
follows the codebase, not the developer's machine.
Property Tests For Rule Checkers
tests/property_check.rs property-tests the detection path: it runs every rule's
check() over generated YAML and asserts oracle-free invariants — check() never
panics, every span is in-bounds and character-aligned (1 <= line <= line_count,
1 <= column <= chars_on_line + 1), a leading # ryl disable mutes every rule (only a
syntax error survives), and block-disabling a firing rule removes its diagnostics. It
targets ryl's fragile byte↔char offset arithmetic rather than semantic correctness (the
fast complement to the slow yamllint_compat_* differential suite).
property_check/strategy.rs generates documents biased to trigger every rule (truthy
words, octal/float scalars, duplicate/unordered keys, flow spacing, anchors, long lines,
odd indentation, trailing spaces, %YAML version directives) interleaved with multibyte
chars, raw NEL/LS/PS, and mixed LF/CRLF/bare-CR (a bare \r is a YAML 1.2 line break
everywhere, so the oracle line_char_lengths is CR-aware too). harness.rs holds the
trigger-all config and the per-rule dispatch, which calls each check() directly (not
lint_str, which drops rule spans on a parse error) so spans are bounds-checked even on
input that fails to parse.
When you add a new rule, extend collect_spans in harness.rs to call its
check() and add a (rule-id, triggering-input) row to RULE_TRIGGERS in
property_check.rs. The deterministic each_rule_triggers_and_reports_in_bounds_spans
test asserts each rule fires on its crafted input, so the property assertions
cannot silently pass vacuously if the generator drifts. Failing inputs persist
to the committed tests/proptest-regressions/property_check.txt. Run with
cargo test --test property_check.
Property Tests For Markdown --fix
tests/property_markdown_fix.rs property-tests fix::fix_markdown_str (write-back into
embedded YAML). It reuses the safe-fix generator via #[path] and wraps the documents
into a Markdown host (property_markdown_fix/wrap.rs), asserting four oracle-free
invariants across the config matrix: host bytes outside regions stay byte-identical
(region count/kinds stable), each region's parsed value is preserved, each region is
untouched or rewritten to exactly its apply_safe_fixes_filtered form, and it's
idempotent. Deterministic siblings pin known-dirty / CRLF / ragged /
fence-crossing-front-matter cases.
Extend this suite only when the Markdown extractor/wrapper grows new region shapes:
add a wrap.rs variant and a deterministic sibling. Failing inputs persist to the
committed tests/proptest-regressions/property_markdown_fix.txt; run with
cargo test --test property_markdown_fix.
Property Tests For Config Parsing
tests/property_config.rs property-tests configuration robustness:
property_config/strategy.rs generates randomized configs (random rule subsets, levels,
and options, mixing valid with hostile values — invalid regexes, ill-typed/out-of-range
scalars, bogus locales) rendered to both YAML and TOML. The oracle-free invariant: the
pipeline errors or succeeds but never panics — YAML via YamlLintConfig::from_yaml_str
(then linting samples, to drive the .expect()s in key-ordering/quoted-strings
resolve()), TOML via parse_toml_config_str -> validate_toml_config -> normalize_toml_config. Deterministic siblings pin empty-config, invalid-regex,
billion-laughs, and rich-valid cases. When a rule gains a config-compiled regex or typed
option, add its key(s) to CATALOG in strategy.rs.
Rules Without A Safe --fix
These rules are intentionally not part of SAFE_FIX_RULES. Each entry is the
one-sentence reason --fix cannot rewrite the rule without risking changed
parsed values or unintended user-visible behaviour. Revisit this list when
considering a partial safe fix — if you can satisfy the property-test
invariants for some subset, move the rule into SAFE_FIX_RULES and document
the unsafe-trigger subset in that rule's module-level doc comment instead.
anchors— Fixing requires choosing which anchor an undeclared alias should point at, which duplicate to keep, or whether an "unused" anchor is actually referenced from a template the linter cannot see.block-scalar-chomping— YAML has no explicit clip indicator (only-strip and+keep exist), so a bare|/>cannot be annotated without switching it to strip or keep, which changes the scalar's trailing newlines and resolved value; the choice is the author's intent.colons— Collapsing extra space around colons safely needs precise parser context tracking (plain scalars, alias keys, explicit?/:mappings) equivalent to re-implementing the YAML mapping scanner.empty-values— The rule's intent is to force the user to choose between~,null, or restructuring; auto-inserting a literal contradicts the rule's purpose and would silently change downstream behaviour.float-values— Rewrites such as0.5 → .5,.5 → 0.5, expanding1e3 → 1000, or replacing.nan/.infall change the scalar's string representation and, in tagged or string-typed consumers, its semantic value.hyphens— Collapsing trailing spaces after-in a block sequence changes the indent of any nested block mapping/sequence that follows on subsequent lines and so can change the parsed structure; thedash-on-own-lineoption is likewise no-fix, since breaking the-onto its own line re-indents the mapping body.indentation— Re-indenting alters the block-structure boundaries the YAML grammar uses to delimit mappings, sequences, and scalars; any non-trivial fix risks changing the parsed value.key-duplicates— Resolving a duplicate requires deciding which key (and value) to keep; both choices alter the parsed mapping and need user intent.key-ordering— Reordering a mapping silently disassociates any comment the user placed above or beside a key from that key, losing information the YAML grammar does not carry.line-length— Splitting an over-long line requires line-folding decisions that depend on whether the scalar is plain, quoted, or block-styled, and on whether folding is semantically allowed; no single rewrite is universally safe.merge-keys— Removing a<<merge requires inlining the merged mapping's resolved keys/values (which the source text alone does not carry) and would change the document's structure; quoting the<<silently drops the merge, so no rewrite is universally safe.octal-values— Resolving010requires knowing whether the user meant the integer8, the integer10, or the string"010"; the YAML source alone cannot disambiguate.tags— Rewriting or removing a flagged tag changes the node's resolved type (!!omapto a plain mapping,!envto a string, …) or requires guessing the intended value, so no rewrite is universally safe.truthy— RewritingYes/No/On/Offrequires choosing between quoting them (preserves the string), normalising totrue/false(changes type), or rewording — all of which depend on the user's intent.unicode-line-breaks— The\N/\L/\Pescape is valid only inside a double-quoted scalar; rewriting a raw NEL/LS/PS in a plain or single-quoted scalar, a comment, or a block scalar would require changing the quoting style or guessing intent, so no rewrite is universally safe.
Signals
- GitHub stars
- 72
- Forks
- 3
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
property-tests- Source
- github.com/owenlamont/ryl