Skip to main content

Configuration Reference

Zenzic is configured through a TOML file. Every field has a sensible default, so zero-config usage is fully supported -- but production projects benefit from explicit tuning.

Config File Priority

Zenzic resolves configuration using a first-match-wins priority chain:

PrioritySourceDescription
1zenzic.tomlStandalone file at the repository root -- the authoritative sovereign config
2pyproject.toml[tool.zenzic] table inside pyproject.toml
3Built-in defaultsHardcoded defaults when no config file is found

When a config file is present but contains a TOML syntax error, Zenzic raises a ConfigurationError with a Rich-formatted message. It will never silently fall back to defaults when a file exists but cannot be parsed.

Standalone zenzic.toml

# zenzic.toml — at the repository root
docs_dir = "docs"
snippet_min_lines = 3
strict = true

[build_context]
engine = "mkdocs"

Embedded in pyproject.toml

# pyproject.toml
[tool.zenzic]
docs_dir = "docs"
snippet_min_lines = 3
strict = true

[tool.zenzic.build_context]
engine = "mkdocs"

Use zenzic init to scaffold a config file. If pyproject.toml exists, the command will prompt whether to embed the config there. Use zenzic init --pyproject to skip the prompt.


Core Settings

docs_dir

TypePath
Default"docs"

Path to the documentation root directory, relative to the repository root.

docs_dir = "content"

snippet_min_lines

Typeint
Default1

Minimum number of lines for a fenced code block to be syntax-checked. Set to 3 or higher to skip trivial one-liner import stubs.

snippet_min_lines = 3

placeholder_max_words

Typeint
Default50

Pages with fewer words than this threshold are flagged as short-content placeholders.

placeholder_max_words = 100

placeholder_patterns

Typelist[str]
DefaultSee below

Case-insensitive strings that flag a page as containing placeholder text. The default list includes both English and Italian patterns:

# Default patterns (shown for reference — override to customise)
placeholder_patterns = [
"coming soon", "work in progress", "wip", "todo", "to do",
"stub", "placeholder", "fixme", "tbd", "to be written",
"to be completed", "to be added", "under construction",
"not yet written", "draft",
# Italian
"da completare", "in costruzione", "in lavorazione",
"da scrivere", "da aggiungere", "bozza", "prossimamente",
]

validate_same_page_anchors

Typebool
Defaultfalse

When true, same-page anchor links (#section) are validated against headings present in the source file. Disabled by default because anchor IDs can be generated by HTML attributes, custom plugins, or build-time macros invisible at source-scan time.

validate_same_page_anchors = true

Exclusion Settings

excluded_dirs

Typelist[str]
Default["includes", "stylesheets", "overrides", "hooks"]

Directories inside docs/ to exclude from orphan and snippet checks. User entries are merged with the immutable System Guardrails (SYSTEM_EXCLUDED_DIRS) -- they can never be removed.

excluded_dirs = ["includes", "stylesheets", "overrides", "hooks", "snippets"]
System Guardrails (always excluded)

The following directories are excluded unconditionally, regardless of configuration:

.git, .github, .venv, node_modules, .nox, .tox, .pytest_cache, .mypy_cache, .ruff_cache, __pycache__, .docusaurus, .cache, .hypothesis, .temp

These represent the L1 System Guardrails layer. No configuration can override them.

excluded_file_patterns

Typelist[str]
Default[]

Filename glob patterns excluded from all checks (orphan detection, placeholder scanning, reference pipeline, and Shield). Uses fnmatch syntax.

# Skip locale-suffixed files and changelogs
excluded_file_patterns = ["*.it.md", "*.fr.md", "CHANGELOG*.md"]

excluded_assets

Typelist[str]
Default[]

Asset paths (relative to docs_dir) excluded from the unused-assets check. Entries may be literal paths or glob patterns (fnmatch syntax). Use for files referenced by the build tool or theme templates rather than by Markdown pages.

excluded_assets = [
"img/favicon.ico",
"img/logo.svg",
"img/social/*.png",
"_category_.json",
]

excluded_asset_dirs

Typelist[str]
Default["overrides"]

Directories inside docs/ whose non-Markdown files are excluded from the unused-assets check. Use for theme override directories whose files are consumed by the build tool rather than referenced from Markdown pages.

excluded_asset_dirs = ["overrides", "theme"]

excluded_build_artifacts

Typelist[str]
Default[]

Glob patterns (relative to docs_dir) for assets generated at build time. Links to matching paths are not flagged as broken even when the file does not exist on disk at lint time.

excluded_build_artifacts = ["pdf/*.pdf", "assets/bundle.zip"]

excluded_external_urls

Typelist[str]
Default[]

External URLs (or URL prefixes) excluded from the broken-link check in --strict mode. A URL is skipped when it starts with any entry in this list.

excluded_external_urls = [
"https://internal.example.com",
"https://github.com/PythonWoods/unreleased-repo",
]

VCS-Aware Exclusion

The Zenzic Way: Conscious Control vs. Blind Automation

Design Philosophy

Zenzic deliberately defaults to Conscious Control rather than Blind Automation. Understanding this principle is the key to configuring the tool effectively in production projects.

respect_vcs_ignore is false by default. This is not an oversight — it is a deliberate architectural choice rooted in a concrete failure mode.

The Noisy .gitignore Problem

Consider a repository where docs_dir = "." (the repo root is also the docs root). This is common for projects that lint their README.md, CHANGELOG.md, and other root-level Markdown files. A typical Python project .gitignore contains entries like:

*.egg-info/
.coverage
dist/
htmlcov/
*.pyc
.venv/

If respect_vcs_ignore = true, Zenzic would silently exclude any documentation file whose path matches these patterns. A docs/coverage-report.md page, for instance, would vanish from orphan detection without any diagnostic message. The linter would appear healthy while silently skipping entire documentation subtrees.

The Explicit zenzic.toml is Superior

The excluded_dirs and excluded_file_patterns fields in your project config — zenzic.toml or [tool.zenzic] in pyproject.toml — (L3 in the Layered Exclusion hierarchy) are:

  • Visible — exclusions are declared in one authoritative file, not scattered across .gitignore, .dockerignore, and .npmignore
  • Reviewable — a new contributor running git diff sees exactly what Zenzic excludes and why
  • Stable — exclusions do not change when a developer updates .gitignore for unrelated tooling reasons
# zenzic.toml (or [tool.zenzic] in pyproject.toml)
# Explicit exclusions are maintainable and auditable
excluded_dirs = ["includes", "stylesheets", "overrides"]
excluded_file_patterns = ["*.it.md", "CHANGELOG*.md"]

# respect_vcs_ignore = false ← default; omit or set explicitly

When to enable respect_vcs_ignore

Enable it for projects with a clean, documentation-focused .gitignore where VCS-excluded paths genuinely map to documentation that should not be linted (e.g. auto-generated API reference in site/). Always audit the exclusion effect using --show-info after enabling.


respect_vcs_ignore

Typebool
Defaultfalse

When true, Zenzic reads .gitignore files from the repository root and docs directory and excludes matching files from all checks. Disabled by default — see The Zenzic Way above for the rationale.

Forced inclusions (included_dirs, included_file_patterns) override VCS exclusions, but System Guardrails are always enforced.

respect_vcs_ignore = true

included_dirs

Typelist[str]
Default[]

Directory names inside docs/ that are forcefully included even when excluded by VCS ignore patterns or excluded_dirs. Forced inclusions cannot override System Guardrails (.git, .venv, etc.).

included_dirs = ["generated-api"]

included_file_patterns

Typelist[str]
Default[]

Filename glob patterns (fnmatch syntax) forcefully included even when excluded by VCS ignore patterns or excluded_file_patterns. Use for build-generated documentation that should be linted despite being in .gitignore.

included_file_patterns = ["api.generated.md"]

Build Context

The [build_context] table tells Zenzic which documentation engine produced the site and how to resolve locale-specific paths.

engine

Typestr
Default"mkdocs"

Build engine identifier. Used by the adapter factory to select the correct path-resolution strategy. Built-in adapters: mkdocs, zensical, docusaurus, vanilla.

[build_context]
engine = "mkdocs"

default_locale

Typestr
Default"en"

ISO 639-1 code of the default locale. Used by adapters for i18n fallback logic.

[build_context]
default_locale = "en"

locales

Typelist[str]
Default[]

Non-default locale directory names. Pages in locale directories receive special handling during orphan detection and anchor resolution.

[build_context]
locales = ["it", "fr", "de"]

base_url

Typestr
Default""

Site base URL (e.g. "/" or "/docs/"). When set, the adapter uses this value instead of attempting static extraction from the build tool's config file. Recommended when the config file uses dynamic patterns that cannot be parsed statically.

[build_context]
base_url = "/docs/"

fallback_to_default

Typebool
Defaulttrue

When true, missing locale-tree assets and pages fall back to the default-locale tree. Mirrors the fallback_to_default option in mkdocs-i18n. Set to false to report every missing locale file as an error.

[build_context]
fallback_to_default = false

CI / Exit Behaviour

fail_under

Typeint
Default0

Minimum quality score (0--100). If the Sentinel Score falls below this value, zenzic score exits with code 1. A value of 0 disables the threshold (observational mode).

fail_under = 80

strict

Typebool
Defaultfalse

When true, treat warnings as errors and validate external URLs via network requests. Equivalent to passing --strict on every invocation of check all, score, or diff.

strict = true

exit_zero

Typebool
Defaultfalse

When true, zenzic check all always exits with code 0 even when issues are found. Issues are still printed and scored. Useful for observation-only pipelines. Shield violations (exit code 2) and Blood Sentinel events (exit code 3) are never suppressed.

exit_zero = true

Custom Rules

Project-specific lint rules can be declared inline without writing Python. Each entry applies a regex pattern line-by-line to every .md file.

[[custom_rules]]
id = "ZZ-NOINTERNAL"
pattern = "internal\\.corp\\.example\\.com"
message = "Internal hostname must not appear in public docs."
severity = "error"

[[custom_rules]]
id = "ZZ-NODRAFT"
pattern = "(?i)\\bDRAFT\\b"
message = "Remove DRAFT marker before publishing."
severity = "warning"
FieldTypeDefaultDescription
idstr(required)Stable unique identifier (e.g. "ZZ001")
patternstr(required)Regex applied to each content line
messagestr(required)Human-readable explanation shown in findings
severitystr"error""error", "warning", or "info"

Plugins

Typelist[str]
Default[]

Explicit allow-list of external rule plugins to activate from the zenzic.rules entry-point group. Core rules shipped by Zenzic are always enabled.

plugins = ["zenzic-no-draft", "zenzic-link-policy"]

Use zenzic plugins list to see all discovered rules and their origins.


CLI Flags

Several configuration values can be overridden per-run via CLI flags on zenzic check all:

FlagOverridesDescription
--strict / -sstrictTreat warnings as errors; validate external URLs
--exit-zeroexit_zeroAlways exit 0 (issues still reported)
--engine ENGINEbuild_context.engineOverride the build engine adapter
--exclude-dir DIR(additive)Additional directories to exclude (repeatable)
--include-dir DIR(additive)Force-include directories even if excluded by config (repeatable). Cannot override System Guardrails
--show-info(display)Show info-level findings (e.g. circular links)
--format json(display)Output in JSON format instead of the Sentinel report
--fail-under Nfail_underExit non-zero if score is below threshold (on zenzic score)
--quiet / -q(display)Minimal one-line output for pre-commit hooks

Override Priority

CLI flags always override both zenzic.toml and pyproject.toml values for a single run. The full priority chain is:

CLI flags > zenzic.toml > pyproject.toml [tool.zenzic] > built-in defaults

Complete Example

# zenzic.toml — full configuration example

docs_dir = "docs"
snippet_min_lines = 3
placeholder_max_words = 100
validate_same_page_anchors = true

# Exclusions
excluded_dirs = ["includes", "stylesheets", "overrides"]
excluded_file_patterns = ["*.it.md", "*.fr.md"]
excluded_assets = ["img/favicon.ico", "img/social/*.png"]
excluded_asset_dirs = ["overrides"]
excluded_build_artifacts = ["pdf/*.pdf"]
excluded_external_urls = ["https://internal.example.com"]

# VCS-aware discovery
respect_vcs_ignore = true
included_dirs = ["generated-api"]
included_file_patterns = ["api.generated.md"]

# Build engine
[build_context]
engine = "mkdocs"
default_locale = "en"
locales = ["it", "fr"]
base_url = "/"
fallback_to_default = true

# CI behaviour
strict = false
fail_under = 80
exit_zero = false

# Custom rules
[[custom_rules]]
id = "ZZ-NOINTERNAL"
pattern = "internal\\.corp\\.example\\.com"
message = "Internal hostname must not appear in public docs."
severity = "error"

# Plugins
plugins = []

TOML Pitfalls

Field Order is Law

In TOML, every key written after a [section] header belongs to that section, not to the root.
Zenzic loads the root with _build_from_data, which filters against ZenzicConfig.model_fields — any key nested inside an unknown section is silently discarded.

Wrong — all root fields after [project] are swallowed:

[project]
name = "My Project"

# ❌ These lines look like root settings but they are INSIDE [project].
# Zenzic ignores them — the section is unknown.
placeholder_patterns = []
docs_dir = "docs"

Correct — all root fields BEFORE the first section header:

# ✔ Root fields first
docs_dir = "docs"
placeholder_patterns = []
fail_under = 100

# ✔ Sub-table section last
[build_context]
engine = "docusaurus"
base_url = "/"

Unknown Sections Emit a Warning

Since v0.6.1, Zenzic emits a WARNING when it encounters an unrecognised TOML section (e.g. [project]) instead of discarding it silently.
If you see:

WARNING zenzic.toml: unknown section [project] will be ignored …

move all settings that follow that header to the top of the file, before any [section] tag.

Dogfooding Pattern with Docusaurus

Documenting a linter with its own linter creates intentional false positives: pages that explain placeholder patterns will trigger the placeholder checker.
Disable the checker in the zenzic.toml of the documentation repository:

# Doc repository — explains lint rules without triggering them
placeholder_patterns = [] # disabled: this doc describes patterns by example
placeholder_max_words = 0 # disabled: glossary entries are intentionally short

[build_context]
engine = "docusaurus"