# Test fixture: exercises every v0.1 rule kind with both required and
# optional fields. Used by `schema_tests::schema_accepts_all_kinds_fixture`.
version: 1

ignore:
  - "target/**"
respect_gitignore: true

vars:
  copyright_year: "2026"
  org: "Acme Corp"

facts:
  - id: is_rust
    any_file_exists: [Cargo.toml]
  - id: has_license_and_readme
    all_files_exist: [LICENSE, README.md]
  - id: n_rs_files
    count_files: "**/*.rs"

rules:
  - id: readme-exists
    kind: file_exists
    paths: README.md
    root_only: true
    level: error
    message: "README required"
    policy_url: "https://example.com/policies/readme"

  - id: no-lockfile-bak
    kind: file_absent
    paths: ["**/Cargo.lock.bak", "**/package-lock.json.bak"]
    level: error
    fix:
      file_remove: {}

  - id: has-editorconfig
    kind: file_exists
    paths: .editorconfig
    root_only: true
    level: warning
    fix:
      file_create:
        content: |
          root = true
          [*]
          end_of_line = lf
          indent_style = space
          indent_size = 2

  - id: docs-dir
    kind: dir_exists
    paths: "docs/**"
    level: warning

  - id: no-target
    kind: dir_absent
    paths: "**/target"
    level: error

  - id: license-header
    kind: file_content_matches
    paths:
      include: "**/*.java"
      exclude: "**/generated/**"
    pattern: "(?i)copyright"
    level: error
    fix:
      file_append:
        content: "\n// Copyright (c) 2026\n"

  - id: no-todos
    kind: file_content_forbidden
    paths:
      include: ["src/**/*.rs"]
      exclude: ["src/**/*_test.rs"]
    pattern: '\b(TODO|FIXME|XXX)\b'
    level: warning

  - id: java-header
    kind: file_header
    paths: "**/*.java"
    pattern: "(?s)Copyright"
    lines: 20
    level: error
    fix:
      file_prepend:
        content: |
          // Copyright (c) 2026 Acme Corp
          // SPDX-License-Identifier: Apache-2.0

  - id: max-size
    kind: file_max_size
    paths: "**"
    max_bytes: 10485760
    level: warning

  - id: all-text
    kind: file_is_text
    paths: "src/**"
    level: error

  - id: components-pascal
    kind: filename_case
    paths: "components/**/*.tsx"
    case: PascalCase
    level: error
    fix:
      file_rename: {}

  - id: tests-pattern
    kind: filename_regex
    paths: "tests/**/*.rs"
    pattern: "^test_[a-z0-9_]+$"
    stem: true
    level: error

  - id: c-requires-h
    kind: pair
    primary: "**/*.c"
    partner: "{dir}/{stem}.h"
    level: error
    message: "{{ctx.primary}} has no matching header at {{ctx.partner}}"

  - id: fips-sum-pins-sources
    kind: pair_hash
    source: "src/fips/**/*.go"
    target: "src/fips/fips140.sum"
    algorithm: sha256
    format: sums-line
    level: error

  - id: workspace-members-resolve
    kind: registry_paths_resolve
    source: Cargo.toml
    extract: { toml: "$.workspace.members[*]" }
    base: registry_dir
    entries_are_globs: false
    expect: dir
    must_contain: Cargo.toml
    exclude_query: "$.workspace.exclude[*]"
    orphans: { space: "crates/*/Cargo.toml", unreferenced: warn }
    level: error

  - id: workspace-versions-coherent
    kind: cross_file_value_equals
    source:
      file: Cargo.toml
      extract: { toml: "$.workspace.package.version" }
    targets:
      files: "crates/*/Cargo.toml"
      extract: { toml: "$.package.version" }
    normalize: none
    allow_missing_target: false
    level: error

  - id: keep-sorted
    kind: ordered_block
    paths: ["**/.gitignore", "CODEOWNERS"]
    start: "# keep-sorted start"
    end: "# keep-sorted end"
    comparator: lexical
    unique: false
    level: warning

  - id: bindings-fresh
    kind: generated_file_fresh
    file: docs/rules.md
    command: ["true"]
    workdir: "."
    normalize: final-newline
    timeout: 30
    level: error

  - id: no-internal-imports
    kind: import_gate
    paths: "src/**/*.rs"
    language: rust
    forbid: "^crate::secrets"
    allow: ["src/secrets/**"]
    level: error

  - id: fmt-is-idempotent
    kind: command_idempotent
    command: ["true"]
    workdir: "."
    files_from: stderr
    files_pattern: "Diff in (.+) at"
    timeout: 30
    level: error

  - id: every-module-has-mod
    kind: for_each_dir
    select: "src/*"
    require:
      - kind: file_exists
        paths: "{path}/mod.rs"
    level: error

  - id: unit-has-snapshot
    kind: for_each_file
    select: "tests/unit/*.rs"
    require:
      - kind: file_exists
        paths: "tests/snapshots/{stem}.snap"
    level: warning

  - id: src-only-rs
    kind: dir_only_contains
    select: "src/*"
    allow: ["*.rs", "mod.rs"]
    level: error
    message: "{{ctx.basename}} not allowed under {{ctx.dir}}"

  - id: unique-rs-stems
    kind: unique_by
    select: "**/*.rs"
    key: "{stem}"
    level: warning
    message: "{{ctx.count}} files share stem {{ctx.key}}: {{ctx.paths}}"

  - id: packages-have-readme-and-license
    kind: dir_contains
    select: "packages/*"
    require: ["README.md", "LICENSE*"]
    level: error

  - id: every-pkg-has-src
    kind: every_matching_has
    select: "packages/*"
    require:
      - kind: file_exists
        paths: "{path}/package.json"
    level: error

  # ─── Content-family coverage (one entry per kind) ──────────────
  - id: content-matches-spdx
    kind: file_content_matches
    paths: "src/**/*.rs"
    pattern: "SPDX-License-Identifier"
    level: warning
  - id: content-matches-alias
    kind: content_matches
    paths: "**/*.toml"
    pattern: "version\\s*="
    level: info
  - id: content-forbidden-todo
    kind: file_content_forbidden
    paths: "src/**/*.rs"
    pattern: '\bTODO\b'
    level: warning
  - id: content-forbidden-alias
    kind: content_forbidden
    paths: "src/**/*.rs"
    pattern: 'unimplemented!'
    level: warning
  - id: rust-spdx-header
    kind: file_header
    paths: "src/**/*.rs"
    pattern: "^// SPDX-License-Identifier:"
    level: warning
  - id: header-alias
    kind: header
    paths: "src/**/*.toml"
    pattern: "^# "
    level: info
  - id: rust-final-line
    kind: file_footer
    paths: "src/**/*.rs"
    pattern: "}$"
    level: info
  - id: footer-alias
    kind: footer
    paths: "**/*.txt"
    pattern: "$"
    level: info
  - id: scripts-have-shebang
    kind: file_shebang
    paths: "scripts/**"
    shebang: "^#!/(usr/)?bin/(env )?(ba)?sh"
    level: warning
  - id: shebang-alias
    kind: shebang
    paths: "scripts/**/*.py"
    shebang: "^#!/(usr/)?bin/(env )?python3?"
    level: warning
  - id: rust-starts-with-spdx
    kind: file_starts_with
    paths: "src/**/*.rs"
    prefix: "// SPDX-License-Identifier"
    level: warning
  - id: text-files-end-with-newline
    kind: file_ends_with
    paths: "src/**/*.rs"
    suffix: "\n"
    level: warning
  - id: max-content-size
    kind: file_max_size
    paths: "**"
    max_bytes: 10485760
    level: warning
  - id: max-size-alias
    kind: max_size
    paths: "**/*.png"
    max_bytes: 5242880
    level: warning
  - id: readme-min-size
    kind: file_min_size
    paths: "README.md"
    min_bytes: 200
    level: warning
  - id: min-size-alias
    kind: min_size
    paths: "LICENSE"
    min_bytes: 100
    level: warning
  - id: readme-min-lines
    kind: file_min_lines
    paths: "README.md"
    min_lines: 5
    level: info
  - id: min-lines-alias
    kind: min_lines
    paths: "CHANGELOG.md"
    min_lines: 3
    level: info
  - id: changelog-max-lines
    kind: file_max_lines
    paths: "CHANGELOG.md"
    max_lines: 10000
    level: info
  - id: max-lines-alias
    kind: max_lines
    paths: "**/*.md"
    max_lines: 5000
    level: info
  - id: rust-fixture-hash
    kind: file_hash
    paths: "vendor/frozen.bin"
    sha256: "0000000000000000000000000000000000000000000000000000000000000000"
    level: error
  - id: rust-must-be-text
    kind: file_is_text
    paths: "src/**/*.rs"
    level: warning
  - id: is-text-alias
    kind: is_text
    paths: "**/*.md"
    level: warning
  - id: rust-must-be-ascii
    kind: file_is_ascii
    paths: "src/**/*.rs"
    level: info

  # ─── Structured-query family ───────────────────────────────────
  - id: package-name-matches
    kind: json_path_equals
    paths: "package.json"
    path: "$.name"
    equals: "demo"
    level: error
  - id: package-version-matches
    kind: json_path_matches
    paths: "package.json"
    path: "$.version"
    matches: "^\\d+\\.\\d+\\.\\d+$"
    level: warning
  - id: workflow-uses-checkout
    kind: yaml_path_equals
    paths: ".github/workflows/*.yml"
    path: "$.jobs.build.steps[0].uses"
    equals: "actions/checkout@v4"
    level: warning
    if_present: true
  - id: workflow-permissions-shape
    kind: yaml_path_matches
    paths: ".github/workflows/*.yml"
    path: "$.permissions['contents']"
    matches: "^read$"
    level: warning
    if_present: true
  - id: cargo-edition-2024
    kind: toml_path_equals
    paths: "Cargo.toml"
    path: "$.package.edition"
    equals: "2024"
    level: error
  - id: rust-version-floor
    kind: toml_path_matches
    paths: "Cargo.toml"
    path: "$.package['rust-version']"
    matches: "^1\\.\\d+$"
    level: warning
    if_present: true
  - id: csproj-targets-net8
    kind: xml_path_equals
    paths: "**/*.csproj"
    path: "$.Project.PropertyGroup.TargetFramework"
    equals: "net8.0"
    level: error
    if_present: true
  - id: packageref-has-version
    kind: xml_path_matches
    paths: "**/*.csproj"
    path: "$.Project.ItemGroup.PackageReference[*]['@Version']"
    matches: "^\\d"
    level: warning
    if_present: true
  - id: package-conforms-to-schema
    kind: json_schema_passes
    paths: "package.json"
    schema_path: "schemas/package.schema.json"
    level: error

  # ─── Markdown / commented-code / blame-age ─────────────────────
  - id: agents-md-paths-resolve
    kind: markdown_paths_resolve
    paths: "AGENTS.md"
    prefixes: ["src/", "crates/", "docs/"]
    level: warning
  - id: no-commented-code
    kind: commented_out_code
    paths: "src/**/*.rs"
    min_lines: 3
    threshold: 0.5
    level: warning
  - id: stale-todos
    kind: git_blame_age
    paths: "src/**/*.rs"
    pattern: '\b(TODO|FIXME)\b'
    max_age_days: 180
    level: warning

  # ─── Git-aware family ──────────────────────────────────────────
  - id: no-tracked-secrets
    kind: git_no_denied_paths
    denied: ["*.env", "*.pem", "id_rsa"]
    level: error
  - id: conventional-commit
    kind: git_commit_message
    pattern: "^(feat|fix|chore): "
    level: warning

  # ─── Plugin tier 1 ─────────────────────────────────────────────
  - id: shellcheck-shell-scripts
    kind: command
    paths: "scripts/**/*.sh"
    command: ["shellcheck"]
    level: warning

  # ─── Text hygiene ──────────────────────────────────────────────
  - id: no-trailing-ws
    kind: no_trailing_whitespace
    paths: "src/**/*.rs"
    level: warning
  - id: rust-final-newline
    kind: final_newline
    paths: "src/**/*.rs"
    level: warning
  - id: line-endings-lf
    kind: line_endings
    paths: "src/**/*.rs"
    target: lf
    level: warning
  - id: line-max-width
    kind: line_max_width
    paths: "src/**/*.rs"
    max_width: 100
    level: warning
  - id: indent-style
    kind: indent_style
    paths: "src/**/*.rs"
    style: spaces
    width: 4
    level: warning
  - id: max-blank-lines
    kind: max_consecutive_blank_lines
    paths: "src/**/*.rs"
    max: 2
    level: warning

  # ─── Security / Unicode sanity ─────────────────────────────────
  - id: no-merge-conflict-markers
    kind: no_merge_conflict_markers
    paths: "src/**/*.rs"
    level: error
  - id: no-bidi-controls
    kind: no_bidi_controls
    paths: "src/**/*.rs"
    level: error
  - id: no-zero-width
    kind: no_zero_width_chars
    paths: "src/**/*.rs"
    level: error

  # ─── Encoding ──────────────────────────────────────────────────
  - id: no-bom
    kind: no_bom
    paths: "src/**/*.rs"
    level: warning

  # ─── Structure / layout ────────────────────────────────────────
  - id: max-dir-depth
    kind: max_directory_depth
    paths: "**"
    max_depth: 12
    level: warning
  - id: max-files-per-dir
    kind: max_files_per_directory
    paths: "**"
    max_files: 500
    level: warning
  - id: no-empty-files
    kind: no_empty_files
    paths: "src/**/*.rs"
    level: warning

  # ─── Portable metadata ─────────────────────────────────────────
  - id: no-case-conflicts
    kind: no_case_conflicts
    paths: "**"
    level: warning
  - id: no-illegal-windows-names
    kind: no_illegal_windows_names
    paths: "**"
    level: warning

  # ─── Unix metadata + git ───────────────────────────────────────
  - id: no-symlinks
    kind: no_symlinks
    paths: "**"
    level: warning
  - id: scripts-executable
    kind: executable_bit
    paths: "scripts/**"
    require: true
    level: warning
  - id: exec-needs-shebang
    kind: executable_has_shebang
    paths: "**"
    level: warning
  - id: shebang-needs-exec
    kind: shebang_has_executable
    paths: "**"
    level: warning
  - id: no-submodules
    kind: no_submodules
    level: warning
