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.

TOML Section Nesting

TOML syntax dictates that any key-value pair defined after a section header (e.g., [build_context]) belongs to that section. Top-level keys like docs_dir, placeholder_patterns, or excluded_dirs MUST be placed at the very top of .zenzic.toml, before any [section] is declared. Otherwise, they will be silently ignored by the parser.

Config File Priority

Zenzic resolves configuration using a 4-level hierarchy — the most specific source wins:

PrioritySourceDescription
1 (highest)CLI flags--engine, --exclude-dir, --strict, etc. Override every other source for the current run.
2.zenzic.tomlStandalone file at the repository root — the authoritative sovereign config
3pyproject.toml[tool.zenzic] table inside pyproject.toml
4 (lowest)Built-in defaultsHardcoded defaults when no config file is found

CLI flags always win. A flag like --engine mkdocs overrides the engine value in .zenzic.toml for that single run without modifying any file.

Exclusions and inclusions are cumulative, not replacing:

  • --exclude-dir adds to the list already defined in the config file.
  • --include-dir is a force override: a directory excluded in .zenzic.toml but included via --include-dir will be scanned. The only exception is Level 1 System Guardrails (node_modules, .git, etc.) — these cannot be force-included.

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
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.

zenzic init also scaffolds .zenzic.local.toml as a machine-local overlay. This file is designed for Local Sovereignty: local values override shared config, but must remain private on your workstation.

.zenzic.local.toml Local Sanctuary

.zenzic.local.toml is the private maneuvering space for engineers.

  • It is loaded after shared config (.zenzic.toml or [tool.zenzic]) and therefore wins locally.
  • It is intended for machine-specific paths, temporary cleanup knobs, diagnostics, and private secrets.
  • It is never a team policy file.

When zenzic init runs in a Git repository, it enforces .zenzic.local.toml inside .gitignore (creating or updating .gitignore safely, without destructive edits).

.zenzic.local.toml
# --- ZENZIC LOCAL OVERRIDES ---
# This file is auto-generated and must stay in .gitignore.
# Everything declared here overrides shared .zenzic.toml only on your machine.

[core]
# docs_dir = "my/custom/path/to/docs"
forbidden_patterns = []

[governance]
# suppression_cap = 100
# suppression_cap_fail_hard = false

[secrets]
# github_pat = "YOUR_GITHUB_PAT"

[debug]
# log_level = "DEBUG"

[env]
# ZENZIC_FORCE_COLOR = "true"

Use .zenzic.toml for shared constitutional governance. Use .zenzic.local.toml for local experiments and private data only.

Source-of-Truth Introspection (zenzic config explain)

Use zenzic config explain to verify both active value and origin for each config field.

zenzic config explain

Expected provenance semantics:

  • local -> .zenzic.local.toml (Override)
  • global -> .zenzic.toml
  • default -> built-in fallback

Example (governance override):

suppression_cap = 45 Source: .zenzic.local.toml (Override)

Governance Suppression Contract (Suppression CAP)

[governance]
suppression_cap = 30
suppression_cap_fail_hard = true
per_file_ignores = { "docs/legacy/*.md" = ["Z601"] }
  • suppression_cap and suppression_cap_fail_hard enforce CAP governance.
  • per_file_ignores defines scoped suppressions in normal runs.
  • zenzic check all --audit ignores both inline suppressions and per_file_ignores to expose full debt truth.
# Create a .zenzic.toml file at the project root
zenzic init

# Or embed config in pyproject.toml
zenzic init --pyproject

Core Settings

docs_dir

TypePath
Default"docs"

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

When omitted, Zenzic defaults to "docs". Set to "." to scan the entire repository root (L1 system exclusions still apply). Set to any other relative path when your project stores documentation in a non-standard location such as website/ or content/.

# docs_dir = "docs" # default — omit if your docs live in docs/
docs_dir = "." # scan the entire repository (e.g. README-only projects)

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
Defaulttrue

When true, same-page anchor links (#section) are validated against headings present in the source file. Enabled by default for stronger source-level integrity checks. Disable it only when anchor IDs are 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"]

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", "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 credential scanner). 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",
]
Rule R19 — No Domain-Level Exclusions

Never add an entire domain as an exclusion (e.g. "https://zenzic.dev/"). A blanket domain exclusion creates a permanent blindspot that survives content restructures and silently masks broken links. Entries must target specific URLs or prefixes, not root domains. Use --exclude-url <url> at the CLI for temporary, one-off skips.


VCS-Aware Exclusion

See Exclusion Design for the rationale behind conscious exclusion vs. blind VCS automation.


respect_vcs_ignore

Typebool
Defaulttrue

When true, Zenzic reads .gitignore files from the repository root and docs directory and excludes matching files from all checks. Enabled by default — see Exclusion Design for operational guidance.

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"auto"

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

When set to "auto" (the default), Zenzic probes the project root at runtime using engine auto-discovery, scanning for engine config files in priority order:

  1. zensical.tomlzensical
  2. docusaurus.config.ts / docusaurus.config.jsdocusaurus
  3. mkdocs.ymlmkdocs
  4. (no match)standalone

For production CI, pin the engine explicitly to skip discovery overhead:

[build_context]
engine = "docusaurus"

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

I18N Parity Settings

The [i18n] section controls structural translation parity checks (Z907) and frontmatter key parity across language mirrors.

FieldTypeDefaultDescription
enabledboolfalseActivates the i18n parity scanner
base_langstr"en"Base language code
base_sourcePath"docs"Base-language source root
targetsdict[str, Path]{}Mapping of locale -> mirror root
strict_paritybooltrueMissing mirror is error when true, warning when false
require_frontmatter_paritylist[str]["title", "description"]Required frontmatter keys in translated pages
extra_sourceslist[I18nSource][]Additional base/targets pairs (e.g. developers plugin docs)
[i18n]
enabled = true
base_lang = "en"
base_source = "docs"
strict_parity = true
require_frontmatter_parity = ["title", "description"]

[i18n.targets]
it = "i18n/it/docusaurus-plugin-content-docs/current"

[[i18n.extra_sources]]
base_source = "developers"

[i18n.extra_sources.targets]
it = "i18n/it/docusaurus-plugin-content-docs-developers/current"

CI / Exit Behaviour

fail_under

Typeint
Default0

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

fail_under = 80

See Exclusion Design — Governance Score Math for the flat-cost model and hybrid governance policy design.

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. Credential scanner violations (exit code 2) and path traversal guard events (exit code 3) are never suppressed.

exit_zero = true

Project Metadata

release_name

Typestr
Default""
Section[project_metadata]

The current release codename. Used as the protected term in brand_obsolescence enforcement — Zenzic emits Z601 if this string appears as an obsolete term in documentation.

[project_metadata]
release_name = "Graphite"

badge_stamp_files

Typelist[str]
Default["README.md"]
Section[project_metadata]

Files updated by zenzic score --stamp. Each file must contain one or both HTML comment markers: <!-- zenzic:audit-badge --> and <!-- zenzic:score-badge -->. The Shields.io badge URL on the line immediately following each marker is replaced in place with deterministic audit and score telemetry.

The stamp runs before exit-code checks, so the badge always reflects the actual score — including a red badge in local development, which is immediate feedback that the commit will be rejected by CI.

[project_metadata]
badge_stamp_files = ["README.md", "README.it.md"]

Add one or both markers to each listed file, followed on the next line by any Shields.io badge as a placeholder. See Official Badges for the complete setup guide.


Governance Settings

brand_obsolescence

Typelist[str]
Default[]
Section[governance]
FindingZ601 BRAND_OBSOLESCENCE

A governance rule to enforce terminology standards across documentation. Ideal for corporate rebranding or deprecating internal project names. Zenzic ships with an empty default list — teams configure their own deprecated term lists here.

When a term in this list appears in any scanned file, Zenzic emits Z601 BRAND_OBSOLESCENCE with exit code 2 (same severity as a credential leak). Historical files (e.g. CHANGELOG*.md) are excluded via excluded_file_patterns. Use an inline [HISTORICAL] comment to suppress individual intentional references in other files.

[governance]
brand_obsolescence = [
"OldProductName",
"LegacyBrand",
"DeprecatedInternalTerm",
]

Pattern matching: case-sensitive whole-word scan. The term "Deprecated" does not match "DeprecatedFeature" or "deprecated".

Scope: applies to all files within the active docs_dir scan scope, subject to the standard exclusion hierarchy.

i18n_parity

Typebool
Defaultfalse
Section[governance]
FindingZ602 I18N_PARITY

Enables governance reporting for translation parity on configured locale trees.

per_file_ignores

Typedict[str, list[str]]
Default{}
Section[governance]

Scoped suppressions per glob pattern. Security findings remain non-suppressible.

directory_policies

Typedict[str, list[str]]
Default{}
Section[governance]

Strategic directory-level policy exemptions (zero debt). In --audit mode, these findings are surfaced with the [POLICY_EXEMPTION] label.

suppression_cap_scope

Type"all"
Default"all"
Section[governance]

Defines suppression counting scope. Current supported value is "all".

suppression_cap_fail_hard

Typebool
Defaulttrue
Section[governance]

When true, exceeding suppression_cap triggers immediate exit code 1.

[governance]
i18n_parity = true
suppression_cap = 30
suppression_cap_scope = "all"
suppression_cap_fail_hard = true

[governance.per_file_ignores]
"docs/legacy/**" = ["Z601"]

[governance.directory_policies]
"blog/**" = ["Z601"]

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 inspect capabilities 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 Zenzic 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

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

Zenzic, 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"