Skip to main content

poly-tiers-and-scope

poly's coverage tiers (native / tree-sitter / native-toolchain / quality / built-in ast-grep pack), the per-file tier vs the whole-project phase (--no-workspace), staged isolation (ADR 0019), and hierarchical poly.toml in monorepos (ADR 0018)

Quellinformationen

Repository
Goldziher/poly
Letzte Quellaktivität
14. Oktober 2026 um 17:24
Erkannte Sprache von SKILL.md
Englisch
Sterne
20
Forks
1

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

Datei-Explorer
2 Dateien

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
description
poly's coverage tiers (native / tree-sitter / native-toolchain / quality / built-in ast-grep pack), the per-file tier vs the whole-project phase (--no-workspace), staged isolation (ADR 0019), and hierarchical poly.toml in monorepos (ADR 0018)
name
poly-tiers-and-scope
license
MIT
metadata
{"author":"Goldziher","repository":"https://github.com/Goldziher/poly"}
<!-- AI-RULEZ :: GENERATED FILE — DO NOT EDIT Content-Hash: blake3:ad140ec77b0aed2a08b9c7261e16d5f9cde428c88e6e249a9891a24e6f35b964 Source-Hash: blake3:840c919261146454e2836095406355dd3ba8664fc068055c202682015400e43f Schema-Version: v1 --> # poly Tiers and Scope ## Coverage tiers inside the per-file pass Five mechanisms decide what actually inspects a file (`crates/poly-core/src/registry.rs`): 1. **Tier-1 native crate backends** — one in-process Rust crate registered for specific languages: ruff, oxc, taplo, rumdl, sqruff, yaml, malva (+ biome for CSS/SCSS), markup_fmt, mago, rubyfmt, graphql, nixfmt, hcl, dockerfile, dotenv, ini. 2. **Tier-2 tree-sitter generic tier** (ADR 0004) — the catch-all for every language with no native backend. Structural reindent for brace-family grammars, whitespace normalization otherwise, and a leave-untouched list where whitespace is significant. This is the coverage mechanism, not a fallback to avoid — but it is explicitly *not* gofmt/rustfmt parity. 3. **Native-toolchain tier** (ADR 0014, `engines/native_tool/`) — the language's canonical first-party CLI, run per file over stdin/stdout. `rustfmt`, `gofmt` and `shellcheck` are **on by default whenever the tool is found on PATH**; `zig fmt`, `shfmt`, `google-java-format`, `ktfmt`, `styler`, `swift-format`, `dart format`, and `gleam format` are opt-in via `[fmt.<lang>.<tool>] enabled = true` / `[lint.<lang>.<tool>] enabled = true`. Absent tool ⇒ the language falls through to tier 2 with an info-level notice, never an error. 4. **`quality`, the code-quality metric tier** (ADR 0027) — cross-cutting, appended to every language: file/function/type length, nesting depth, cyclomatic complexity, parameter count, and `lazy-ignore`. Warning severity, on by default, configured under `[lint.quality]`. The structural rules need a verified node-kind table and cover Python, Rust, Go, JavaScript, TypeScript, TSX, Java, Kotlin, C, C++, C#, and Ruby; `magic-number` and `law-of-demeter` are opt-in. A per-language deferral table keeps it from duplicating a tier-1 rule (ruff's `C901`/`PLR0913`, oxlint's `max-depth`/`max-params`). 5. **The built-in ast-grep rule pack** (ADR 0029) — 26 rules across 9 languages (C#, Elixir, Go, Java, Kotlin, Python, Ruby, Rust, Swift) embedded in the binary and on by default; 13 of them ship `severity: off`. It sits *beneath* `[rules] dirs` (default `.poly/rules`), so a user rule with the same `id` replaces a pack rule. `[rules] builtin = false` disables the pack wholesale. `typos` (spelling) and the opt-in `uncomment` engine are the other two cross-cutting backends appended to every language. **Every engine table also accepts a universal `enabled` key**, read by the runner's plan rather than by any backend. Whether narrowing a file out of a run charges against `--deny-skips` / `--max-skips` follows one rule: **a poly limitation is charged and names the limitation; a caller instruction is never charged and always names itself.** `--only`/`--skip` and `enabled = false` are caller instructions, so a file they remove is reported as skipped but not charged; `no matching engine for this file type`, `no lint rules for <language>`, and a generated-file skip are poly's own limitations, and are charged. ## Per-file tier vs whole-project phase `poly lint` runs in two phases: - **Per-file tier** — the parallel, cached engine pass over every discovered file, using the five mechanisms above. This is all `--no-workspace` runs. - **Whole-project phase** — invokes the same whole-workspace tools a pre-commit hook would (`cargo clippy` / `cargo-sort` / `cargo-machete` / `cargo-deny`, plus any inline `workspace = true` job such as a type checker) on the live worktree, folding their pass/fail into the report and exit code. It reuses the `[hooks]` config as its single source of truth. On by default; disable with `--no-workspace` or `[lint] workspace = false`. A repo with no `[hooks]` section has no whole-workspace hooks to lower, so it runs only the per-file tier. **Plain `poly lint` is not read-only.** poly applies no fixes without `--fix`, but the whole-project phase *executes* those tools against the live worktree, and their own side effects are not poly's to control (`cargo clippy` populating `target/` or refreshing `Cargo.lock`, a `go` invocation appending to `go.work.sum`, a type checker writing its cache). Only `--no-workspace` / `[lint] workspace = false` gives a run that cannot touch the tree. The phase is also **not path-scoped**: `poly lint <paths>` skips it when the paths *narrow* the run, and `--workspace` opts back in with the tools covering the whole repository — regardless of the named paths and of `[discovery] exclude`, which filters poly's own discovery only. Naming the directory poly is running in (`poly lint .`) is a request for the whole project, not a narrowing, so the phase still runs. A note on stderr says which branch was taken. Under `--fix`, the whole-project phase runs the tools in fix mode (`cargo sort` in place, `cargo-machete --fix`, `cargo clippy --fix --allow-dirty --allow-staged`; `cargo deny` stays check-only). Fix mode is what `--fix` adds — the phase runs either way, so `--no-workspace`, not the absence of `--fix`, is what skips it. The git-hook / commit-gate path never requests fix mode. `poly fmt` never runs this phase. ## Staged isolation (ADR 0019) On the commit-gate path, poly materializes a snapshot of the git **index** (via `git checkout-index`) and runs the stage from it, so unstaged edits neither hide nor cause failures. Details that matter: - The snapshot is the execution root for **every** hook in the run, not just the whole-workspace ones — a gate whose per-file hooks read the worktree while its whole-project hooks read the index would report on two different sets of bytes. - Isolation is active only for the index stages (`pre-commit`, `pre-merge-commit`) and is skipped for `--all-files` (which deliberately checks the whole tree) and for non-index stages such as `pre-push`. `[hooks] isolate = false` forces it off. - The snapshot lives in the per-user OS cache dir (`~/.cache/poly/<repo-key>/staged`), not in-repo, and is refreshed incrementally by index OID so tool caches stay warm. - `poly lint`'s whole-project phase is a *different* path: it runs against the live worktree, so no snapshot is built. - `[hooks] snapshot_include` opts named, git-untracked, repo-relative paths into the snapshot by symlinking them in from the live worktree, so a `workspace` hook whose build reads a gitignored input (a generated file, a local type-checker config, a downloaded fixture directory) does not fail under the gate for a reason the error message doesn't name. Three consequences follow from the symlink: a fix to an included file is withheld (there is no staged blob to write it into), an included file is never a per-file hook's own input (only index content is), and the symlink lets a hook write through into the live worktree. ## Hierarchical poly.toml in monorepos (ADR 0018) Config resolves hierarchically: every `poly.toml` from the repository root down to the file being processed is deep-merged, so a nested config layers on top of its ancestors and each package can tune rules while sharing a root baseline. The upward walk is bounded at the `.git` directory (or an explicit `[workspace] root = true`), so a stray `poly.toml` in `$HOME` is never picked up. `poly.local.toml` remains the final local override layer, and a top-level `extends` list (ADR 0020) shares sections from local or pinned-remote base configs — `poly config update` pins a symbolic remote ref into `poly-config.lock`, and `poly config show` prints the effective merged config.
Auf GitHub ansehen