# llmlint config-lint plugin (bundled; referenced by URL,
# .../assets/config_lint.yml, and resolved offline from the embedded copy).
#
# Lints llmlint configuration files themselves: that rules are well-named and
# their descriptions yield a clear, unambiguous verdict in as few tokens as that
# takes. These are judgment checks; structural checks (unique names, valid
# identifiers, resolvable agents) are enforced deterministically by llmlint at
# load time and are intentionally not duplicated here.
#
# These rules use the **default agent** on purpose: a dedicated agent would force
# a separate judge invocation (batching is per-agent), doubling model usage for a
# small project that just wants these checks. Running on the default agent lets
# them batch with the consumer's own rules into one call — the per-file
# applicability keeps each rule scoped to its own files. Each rule scopes itself
# to config files with its own `files` filter (shared via the `&config_files`
# YAML anchor), so the plugin always lints configuration, not source, regardless
# of the CLI's target files.
#
# `version` is the published plugin version consumers pin with `@` (e.g. `@1`).
version: 1

# Every check localizes its findings to the offending rule (require_line_attribution:
# true) — a config-lint violation always points at a concrete file + line, which is
# itself the best practice these rules enforce for the configs they lint. Each rule
# below is phrased to pass its own checks (llmlint dogfoods this plugin).
rules:
  - name: description_yields_clear_verdict
    # The first rule anchors the config-file globs; the others alias it (`*config_files`)
    # so the scope stays in one place.
    files: &config_files
      include:
        - "**/llmlint.yml"
        - "**/llmlint.yaml"
        - "**/.llmlint.yml"
        - "**/.llmlint.yaml"
        - "**/*.llmlint.yml"
        - "**/*.llmlint.yaml"
    require_line_attribution: true
    description: |
      Every lint rule's `description` states one unambiguous property whose truth
      is the verdict — written as a plain invariant naming just the condition for
      the property to hold, or, only when the violating case adds detail the
      positive statement leaves unclear (such as concrete examples), as mutually
      exclusive holding and violating conditions.

  - name: name_describes_what_the_rule_checks
    files: *config_files
    require_line_attribution: true
    description: |
      Every lint rule `name` is a descriptive identifier that conveys the property
      its `description` checks — a meaningful token, never a placeholder or nonsense
      one (for example rule1, foo, bar, baz, test, todo, asdf, temp, thing, xxx),
      that names the same thing the description evaluates.

  - name: relevance_scopes_conditional_rules
    files: *config_files
    require_line_attribution: true
    description: |
      A lint rule that only applies in some situations expresses that with a
      `relevance:` condition rather than an inline applicability clause in its
      `description` (for example "or not applicable", "if present", "when
      relevant"), keeping the verdict a clean pass/fail.

  - name: description_and_relevance_are_concise
    files: *config_files
    require_line_attribution: true
    description: |
      Every rule's `description` and any `relevance` condition state their point in
      the fewest words that keep the verdict unambiguous — no filler, no restating
      the inverse, no narrative beyond the check. A judge call batches an agent's
      rules into one prompt, so tokens one rule wastes degrade the judge on the
      others; extra length is warranted only when it adds clarity the shorter form
      lacks, such as concrete examples or easily-confused edge cases.
