exlab_wizard.validator.rules#

Validator rule functions. Backend Spec §8.1.

This module is a pure-function library: one function per §8.1 rule. Each function takes the rule-specific inputs and returns a list of finding-shaped dictionaries (the §11.8 wire shape, partially populated – the engine layer fills in run_path, synced_under_prior_policy and override_active once it knows the run context).

The rule functions are intentionally stateless and side-effect free so they can be exercised in isolation by the unit-test suite and re-used by both creation-time mode (single proposed path) and audit mode (whole subtree walk). The engine layer (validator/engine.py) orchestrates which rules fire on which inputs.

Each finding dict has the shape:

{
    "rule": "<rule_name>",
    "tier": "hard" | "soft",
    "matched_token": "<token>" | None,
    "rule_detail": "<human description>",
    "offending_kind": "directory_segment" | "file_name" | "file_content",
    "offending_path": "<path or filename>",
}

Functions

check_illegal_filesystem_character(*, ...)

§8.1.2: detect Windows-illegal characters in any segment / file name.

check_malformed_yaml_front_matter(*, content)

§8.1: detect malformed YAML front matter at the head of a Markdown file.

check_missing_required_field(*, ...)

§8.1.5: detect required README fields that are absent or empty.

check_mode_prefix_mismatch(*, leaf_dir_name, ...)

§8.1.3: detect three-way disagreement between run_kind, leaf prefix, and parent folder.

check_orphan(*, level, has_creation_json)

§8.1.4: detect missing creation.json at project / run level (NOT equipment).

check_reserved_filesystem_name(*, file_names)

§8.1.2: detect Windows reserved names (CON, PRN, AUX, NUL, COM1..COM9, LPT1..LPT9).

check_unresolved_placeholder(*, ...)

§8.1.1: detect angle-bracket and Jinja placeholder tokens.

exlab_wizard.validator.rules.check_illegal_filesystem_character(*, path_segments, file_names)[source]#

§8.1.2: detect Windows-illegal characters in any segment / file name.

Illegal set: NUL, <, >, :, ", /, \, |, ?, *, ASCII 0-31, trailing dot or trailing space.

The spec’s POSIX exception allows < / > in non-token positions on POSIX – but our app composes paths cross-platform, so we ALWAYS reject. Returns rule illegal_filesystem_character findings.

Parameters:
Return type:

list[dict[str, Any]]

exlab_wizard.validator.rules.check_malformed_yaml_front_matter(*, content)[source]#

§8.1: detect malformed YAML front matter at the head of a Markdown file.

Soft-tier. Returns rule malformed_yaml_front_matter finding when the first --- opens a block but no second --- closes it within the first 200 lines, OR when yaml.safe_load fails on the block.

Parameters:

content (str)

Return type:

list[dict[str, Any]]

exlab_wizard.validator.rules.check_missing_required_field(*, readme_fields, required_field_ids)[source]#

§8.1.5: detect required README fields that are absent or empty.

Soft-tier. Walks the readme_fields_json layer dicts (core_fields, template_fields, config_fields) for each id in required_field_ids. Returns rule missing_required_field findings.

Parameters:
Return type:

list[dict[str, Any]]

exlab_wizard.validator.rules.check_mode_prefix_mismatch(*, leaf_dir_name, parent_dir_name, creation_run_kind)[source]#

§8.1.3: detect three-way disagreement between run_kind, leaf prefix, and parent folder.

Hard-tier. Triple-agreement contract:

  • run_kind="experimental" <=> leaf prefix Run_ <=> parent != TestRuns/

  • run_kind="test" <=> leaf prefix TestRun_ <=> parent == TestRuns/

Returns rule mode_prefix_mismatch findings naming the conflict.

Parameters:
Return type:

list[dict[str, Any]]

exlab_wizard.validator.rules.check_orphan(*, level, has_creation_json)[source]#

§8.1.4: detect missing creation.json at project / run level (NOT equipment).

Soft-tier. Returns one rule orphan finding when level is DirectoryLevel.PROJECT or DirectoryLevel.RUN and has_creation_json is False.

Parameters:
  • level (DirectoryLevel | None)

  • has_creation_json (bool)

Return type:

list[dict[str, Any]]

exlab_wizard.validator.rules.check_reserved_filesystem_name(*, file_names)[source]#

§8.1.2: detect Windows reserved names (CON, PRN, AUX, NUL, COM1..COM9, LPT1..LPT9).

Case-insensitive; with or without extension. Uses WINDOWS_RESERVED_NAMES from constants. Returns rule reserved_filesystem_name findings.

Parameters:

file_names (list[str])

Return type:

list[dict[str, Any]]

exlab_wizard.validator.rules.check_unresolved_placeholder(*, path_segments, file_names, file_contents)[source]#

§8.1.1: detect angle-bracket and Jinja placeholder tokens.

Hard-tier. Uses PLACEHOLDER_ANGLE_BRACKET_PATTERN and the two Jinja patterns from constants. Returns one finding per match with rule unresolved_placeholder_token (angle-bracket) or leftover_jinja_marker (Jinja).

file_contents may be empty for path-only checks. Files larger than _CONTENT_SCAN_MAX_BYTES are skipped per §8.1.1.

Parameters:
Return type:

list[dict[str, Any]]