Using TopMark with pre-commit¶
Pre-commit integration allows TopMark to participate in:
- local Git workflows
- CI validation
- staged-file checks
- repository-wide mutation workflows
- runtime policy enforcement during commits and pushes
Note
The canonical vocabulary used throughout the documentation is defined in Terminology and Canonical Vocabulary.
TopMark ships a hook manifest so you can run header checks in Git workflows and CI. This page covers setup, recommended patterns, and troubleshooting.
Hook execution uses the same runtime-resolution, filtering, policy-evaluation, and runtime configuration semantics as normal CLI execution.
Hook execution also uses the same filesystem-identity evaluation and processing-path selection semantics as normal CLI execution. If multiple path spellings resolve to the same filesystem target (for example a symlink and its target), TopMark processes the selected processing target once.
Hard-link policy is evaluated separately. If multiple selected paths refer to the same filesystem object through hard links, TopMark reports each affected path independently and blocks processing for the entire hard-link group.
Quick start (consumer repos)¶
For canonical file type identity semantics and runtime configuration behavior, see Configuration discovery, precedence, and policy.
Add TopMark to a project's .pre-commit-config.yaml:
repos:
- repo: https://github.com/shutterfreak/topmark
rev: v1.0.1 # pin to a released tag
hooks:
- id: topmark-check
# Optional: limit scope to supported text types
# files: '\.(py|md|toml|ya?ml|sh|Makefile)$'
args: ["--report", "actionable", "--summary"]
Install and run:
Hooks provided by TopMark¶
TopMark provides three pre-commit hooks to help manage, mutate, and diagnose file headers:
topmark-check- non-destructive validation. Fails if headers need changes.- Entry:
topmark check topmark-apply- destructive fix; requires--apply. Markedmanualso it only runs when explicitly invoked.- Entry:
topmark check --apply topmark-probe- read-only runtime-resolution diagnostics. Explains which file type and processor TopMark selects for each input. Markedmanualbecause it is intended for troubleshooting and investigation rather than routine commit validation.- Entry:
topmark probe
Hook policy¶
By default:
topmark-checkruns automatically atpre-commitandpre-push.\ It validates headers and fails if changes are needed.topmark-applyis restricted to the manual stage.\ It may modify files and should only be invoked explicitly by developers.topmark-probeis restricted to the manual stage.\ It is diagnostic-only and read-only, but its output is primarily useful when investigating file runtime discovery, filtering, file-type selection, or processor resolution.
This policy ensures safety in CI and everyday workflows. Teams that want formatter-like behavior
(similar to Black or Prettier) may choose to enable topmark-apply at pre-commit once the
repository is clean.
TopMark intentionally defaults to non-destructive behavior unless --apply is explicitly enabled.
The hook manifest intentionally exposes minimal runtime defaults. All runtime behavioral flags (such
as --summary, --report, policy options, probe verbosity, file-type filters, or output modes)
should be supplied by consuming repositories via the hook's args: configuration.
TopMark performs staged whole-source TOML validation during hook execution; TOML-source diagnostics are included in the reported runtime configuration diagnostics.
Consumers can control configuration-loading validation strictness using --strict / --no-strict.
This overrides the effective strict setting resolved from TOML sources for the duration of the
hook run.
Note
[config].strict is a TOML-source-local strictness preference controlling staged
configuration-loading validation for the current TOML source.
Effective strictness is evaluated across:
- TOML-source diagnostics;
- merged-config diagnostics;
- runtime applicability diagnostics.
When strict validation fails, TopMark exits with CONFIG_ERROR. The diagnostics that triggered
the failure remain visible in human-readable and machine-readable output formats.
strict is resolved during TOML loading and does not become a layered configuration field.
In the current implementation, this strictness is applied across staged configuration-loading validation (TOML-source, merged-config, and runtime applicability diagnostics), while the reported diagnostics remain the flattened compatibility view derived from staged validation logs. For the stable 1.x line, this boundary is intentional: staged validation remains primarily internal, while hook output exposes only the flattened compatibility view.
For the topmark-check hook (which runs topmark check), consumers may also
pass policy options such as --header-mutation-mode, --allow-header-in-empty-files, or
--empty-insert-mode when they need command-specific behavior on top of the resolved config.
These options follow the same runtime policy-resolution and file type identity semantics as normal CLI execution.
Filesystem-processing hooks also follow the same filesystem-identity semantics as normal CLI execution. Runtime processing operates on selected processing paths rather than preserving the original filename spelling supplied by pre-commit.
Hard-link processing policy is applied before runtime processing. Affected paths are reported independently and are not reduced to a preferred source, target, winner, or loser path.
Invoke the manual hook locally:
# Apply headers on the whole repo
pre-commit run topmark-apply --all-files --hook-stage manual
# Or apply headers to specific files
pre-commit run topmark-apply --files path/to/file1 path/to/file2 --hook-stage manual
# Explain file-type and processor resolution for selected files
pre-commit run topmark-probe --files README.md pyproject.toml --hook-stage manual
Pre-commit and files¶
Pre-commit batches filenames to avoid OS argument-length limits (ARG_MAX). Your hook may run multiple times per invocation (for different batches). This is expected.
TopMark applies the same layered runtime filtering pipeline during hook execution:
- filesystem-identity evaluation and processing-path selection
- path filtering
- file-type filtering
- runtime-resolution and probe evaluation
- runtime policy evaluation
Filesystem-identity evaluation includes processing-path selection for equivalent path spellings (such as symlinks) and processing-target eligibility checks (such as hard-link detection).
Note
- Verbosity (
-v/--verbose) affects only TEXT rendering. - Quiet mode (
-q/--quiet) suppresses TEXT rendering for commands that support it. - Markdown and machine-readable output are not affected by TEXT verbosity controls.
For filesystem-backed hook execution, machine-readable path fields and generated filesystem-related header metadata describe the selected processing target. If a file is reached through a symlink, output and generated metadata reflect the resolved target TopMark reads and writes rather than the symlink spelling.
If selected paths are hard links to the same filesystem object, hook execution still produces one result per selected path. Each affected path is reported independently as a policy-blocked processing target.
Run once per repository by setting pass_filenames: false in the hook manifest and letting
TopMark perform its own file discovery from config:
About args¶
Arguments passed through args: behave exactly like normal CLI arguments.
Pre-commit supports an args: list in consumer repos (in .pre-commit-config.yaml). Because
TopMark's hook manifest uses minimal runtime defaults, consumer args: are the primary mechanism
for configuring TopMark runtime behavior when run under pre-commit.
Example (consumer repo):
repos:
- repo: https://github.com/shutterfreak/topmark
rev: v1.0.1
hooks:
- id: topmark-check
args: ["--report", "noncompliant", "--output-format=ndjson"]
For the manual hook:
For the diagnostic probe hook:
Examples using canonical qualified identifiers:
Notes:
-
args:is appended to the hook'sentry. -
Prefer
args:over copying a fullentry:in the consumer config; it stays compatible when the hook entry changes. -
If you need TopMark to run once per repo (self-discovery), combine
pass_filenames: falsewithargs:as needed. -
TEXT-oriented human-readable controls such as
-v/--verboseand-q/--quietaffect only human TEXT output; Markdown and machine-readable JSON/NDJSON output ignore these flags.
File-type identifier behavior¶
TopMark accepts file type identifiers in local form, such as python, or qualified form, such as
topmark:python.
Local identifiers are accepted only when unambiguous. Internally, TopMark normalizes identifiers to canonical qualified file type identities before filtering, runtime resolution, policy evaluation, diagnostics, and registry lookup.
See file-type filtering for the full identifier contract.
Pre-commit hook arguments, TOML configuration, and runtime policy evaluation all share the same canonical file type identity semantics.
Recommended patterns¶
CI-friendly checks¶
These patterns are especially useful for repository-wide validation workflows in CI.
During these runs, staged configuration-loading validation includes per-source TOML validation before layered runtime configuration merging, so schema issues are surfaced alongside normal check runtime diagnostics.
# Enforce strict config validation in CI
# (warnings are treated as errors)
topmark check --report actionable --strict
You can also pass --summary to receive only a summary instead of per-file diagnostics.
Machine-readable output emitted from pre-commit hooks follows the same processing-path contract as normal CLI execution. JSON and NDJSON path fields therefore report selected processing paths rather than preserving original pre-commit filename spellings.
Narrow file scope in consuming repos¶
Exit codes in pre-commit hooks¶
TopMark hooks rely on the stable CLI exit-code contract to signal success or failure to pre-commit and CI.
-
topmark-check(non-destructive): -
Exits with
SUCCESS (0)when all headers are up-to-date. - Exits with
WOULD_CHANGE (3)when headers would be added/updated in dry-run mode (this causes the hook to fail as intended). -
May exit with other non-zero codes for errors, for example:
FILE_NOT_FOUND (66)for explicit missing input pathsIO_ERROR (74)orPERMISSION_DENIED (77)for filesystem issuesCONFIG_ERROR (78)for configuration problems
-
topmark-apply(manual, destructive): -
Exits with
SUCCESS (0)on successful application of changes. -
May exit with the same error codes as above if issues occur during processing.
-
topmark-probe(manual, read-only diagnostic): -
Exits with
SUCCESS (0)when all explicit inputs resolve successfully. - Exits with
UNSUPPORTED_FILE_TYPE (69)when one or more inputs produce unsupported, filtered, unavailable, or unresolved semantic runtime outcomes. - May exit with other non-zero codes for hard input, filesystem, usage, or configuration errors.
Notes:
- Pre-commit treats any non-zero exit code as a failure; this is expected for
topmark-checkwhen changes are needed (WOULD_CHANGE (3)). - Use
--quietin CI to suppress human-readable TEXT output and rely solely on exit status. - Use
topmark-probewhen a hook appears to skip, include, or classify files with unexpected semantic runtime outcomes. - Filesystem-identity evaluation occurs before runtime processing and may affect processing-target eligibility. Equivalent path spellings contribute to the same runtime processing outcome. Hard-link policy participates through normal processing outcomes and diagnostics rather than through dedicated pre-commit hook exit codes.
- For full details and edge cases (mixed-result runs, precedence), see:
Exit codescheckcommandstripcommand
hooks:
- id: topmark-check
files: '\.(py|md|toml|ya?ml|sh|Makefile)$'
args: ["--report", "actionable"]
Runtime hook model¶
TopMark intentionally separates:
- staged configuration-loading validation
- layered runtime configuration resolution
- workspace-root and configuration-discovery evaluation
- filesystem-identity evaluation
- runtime applicability evaluation
- runtime probing and processor resolution
- runtime policy evaluation
- runtime mutation planning and execution
Machine-readable diagnostics and runtime behavior expose a flattened compatibility view derived from these internal runtime stages while preserving deterministic stable 1.x runtime behavior.
Filesystem-identity evaluation occurs before runtime processing begins and includes:
- filesystem-identity normalization (for example processing-path selection for equivalent path spellings such as symlinks); and
- filesystem-identity eligibility checks (for example hard-link policy enforcement).
Configuration-source identity is evaluated independently during configuration loading and layered configuration resolution.
Workspace-root discovery and configuration-discovery evaluation are distinct from both configuration-source identity and filesystem-identity evaluation. Configuration discovery may use resolved filesystem locations to determine configuration search anchors, while compatibility contracts continue to expose a flattened runtime view rather than these internal discovery stages.
Troubleshooting¶
"uses deprecated stage names (commit, push)"¶
Use modern names in the manifest: pre-commit and pre-push.
FileNotFoundError when loading topmark-example.toml¶
Ensure the bundled example TOML resource is loaded through package resources (TopMark already does)
and that the file is included as package data. In pyproject.toml:
Symlink path differs from reported path¶
TopMark reports selected processing paths during runtime processing.
If a hook receives a symlink path from pre-commit, machine-readable output, runtime probing, and generated filesystem-related header metadata may report the resolved processing target rather than the original symlink spelling.
See:
Hard-linked files are reported as unsupported¶
TopMark blocks processing when multiple selected paths refer to the same filesystem object through hard links.
Each affected path is reported independently. TopMark does not select a preferred source, target, winner, or loser path from the hard-link group.
See:
Test your hook locally¶
# Uses the committed manifest from the current repo
topmark version
pre-commit clean
pre-commit try-repo . topmark-check --all-files --verbose