feat: v0.5.0a1 "The Sentinel" — Integration Finale & Obsolescence-Free#20
feat: v0.5.0a1 "The Sentinel" — Integration Finale & Obsolescence-Free#20PythonWoods-Dev merged 1 commit intomainfrom
Conversation
- ISSUE #5: pyproject.toml support via [tool.zenzic] (Priority 2 in load chain) - Add zenzic plugins list CLI command (zenzic.rules entry-point discovery) - Add scan_docs_references(verbose=True) performance telemetry - Add PluginRuleInfo dataclass + list_plugin_rules() discovery function - Purge all legacy aliases: RuleEngine, scan_docs_references_parallel, scan_docs_references_with_links — no backward-compat shims - Drop CHANGELOG.it.md; unify version history in CHANGELOG.md (English) - Update commands.md (EN + IT): document plugins list, remove --jobs phantom flag - Update developers/plugins.md (EN + IT): danger admonition for global mutable state, zenzic plugins list verification example - Update README.md + README.it.md: v0.5.0a1 highlights, pyproject.toml config - Update CHANGELOG.md BREAKING CHANGES: aliases deleted (not deprecated) - Tests: +20 new tests (556 total, 0 failed)
There was a problem hiding this comment.
Pull request overview
This PR targets the v0.5.0a1 “The Sentinel” release, consolidating scanning APIs into a single adaptive entry point, adding [tool.zenzic] support in pyproject.toml, and introducing plugin rule discovery + a zenzic plugins list CLI surface, alongside accompanying docs/tests and version bumps.
Changes:
- Unify reference scanning into
scan_docs_references(..., workers=..., validate_links=...) -> (reports, link_errors)with adaptive parallelism and optional telemetry. - Add
pyproject.tomlconfig fallback ([tool.zenzic]) behindzenzic.tomlpriority; bump version to0.5.0a1. - Add plugin rule discovery (
PluginRuleInfo,list_plugin_rules) and azenzic plugins listCLI command; update docs/tests accordingly.
Reviewed changes
Copilot reviewed 31 out of 32 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| uv.lock | Bumps locked project version and constrains httpx to <1.0. |
| tests/test_rules.py | Updates engine tests for AdaptiveRuleEngine + pickle contract error coverage. |
| tests/test_references.py | Updates scanner calls to unpack (reports, link_errors) and removes legacy alias usage. |
| tests/test_parallel.py | Migrates “parallel” tests to unified scan_docs_references(..., workers=...) behavior. |
| tests/test_integration_finale.py | Adds integration coverage for plugin listing and telemetry output. |
| tests/test_config.py | Adds tests for [tool.zenzic] pyproject loading + priority over zenzic.toml. |
| tests/test_cli.py | Updates CLI mocks/patches to the unified scan entry point. |
| src/zenzic/models/references.py | Updates IntegrityReport docs to reference AdaptiveRuleEngine. |
| src/zenzic/models/config.py | Implements config loading priority chain incl. pyproject fallback. |
| src/zenzic/main.py | Registers new plugins Typer sub-app. |
| src/zenzic/core/scanner.py | Unifies scan entry point, adds adaptive parallelism + telemetry, removes legacy parallel/with-links entry points. |
| src/zenzic/core/rules.py | Introduces AdaptiveRuleEngine eager pickle validation + plugin discovery (list_plugin_rules). |
| src/zenzic/core/exceptions.py | Adds PluginContractError to the exception hierarchy. |
| src/zenzic/cli.py | Adds zenzic plugins list command and routes checks through unified scan API. |
| src/zenzic/init.py | Version bump to 0.5.0a1. |
| README.md | Updates release highlights and documents new config priority chain. |
| README.it.md | Updates Italian highlights and changelog references. |
| pyproject.toml | Version bump + adds zenzic.rules entry-point for core rule. |
| docs/usage/commands.md | Documents zenzic plugins list. |
| docs/usage/advanced.md | Updates programmatic API docs to unified scan function and adaptive engine. |
| docs/it/usage/commands.md | Italian docs for zenzic plugins list. |
| docs/it/usage/advanced.md | Italian docs for unified scan API and adaptive engine. |
| docs/it/developers/plugins.md | Adds Italian plugin-rule contract documentation. |
| docs/it/developers/index.md | Links to new Italian plugin docs. |
| docs/it/configuration/index.md | Documents config priority chain incl. pyproject fallback (IT). |
| docs/it/architecture.md | Documents adaptive engine architecture (IT). |
| docs/developers/plugins.md | Adds English plugin-rule contract documentation. |
| docs/developers/index.md | Links to new English plugin docs. |
| docs/configuration/index.md | Documents config priority chain incl. pyproject fallback (EN). |
| docs/architecture.md | Documents adaptive engine architecture (EN). |
| CHANGELOG.md | Adds v0.5.0a1 entry incl. breaking changes and new features. |
| CHANGELOG.it.md | Removes Italian changelog file per “single source” changelog policy. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| # Shield-as-firewall guarantee (no URLs from compromised files). | ||
| secure_scanners_b: list[ReferenceScanner] = [] | ||
| for md_file in md_files: | ||
| _report_b, secure_scanner_b = _scan_single_file(md_file, config, rule_engine) |
There was a problem hiding this comment.
In the parallel+validate_links path, Phase B calls _scan_single_file(md_file, config, rule_engine) just to collect ref_maps. Passing rule_engine re-runs rule checks and triggers an extra full-file read per doc (since _scan_single_file reads the file again for rules), which makes this pass heavier than intended. Consider calling _scan_single_file with rule_engine=None (or adding a dedicated “collect scanners only” helper) so link registration doesn’t re-run rules.
| _report_b, secure_scanner_b = _scan_single_file(md_file, config, rule_engine) | |
| _report_b, secure_scanner_b = _scan_single_file(md_file, config, None) |
| exceeds the parallelism benefit. Override with ``workers=N`` to force a | ||
| specific pool size regardless of file count. |
There was a problem hiding this comment.
The docstring says “Override with workers=N to force a specific pool size regardless of file count”, but the implementation still requires len(md_files) >= ADAPTIVE_PARALLEL_THRESHOLD to enter the parallel path. Either adjust the wording, or change the use_parallel condition so callers can explicitly force parallel mode for smaller repos when desired.
| exceeds the parallelism benefit. Override with ``workers=N`` to force a | |
| specific pool size regardless of file count. | |
| exceeds the parallelism benefit. Override with ``workers=N`` to select a | |
| specific pool size when parallel mode is active. |
| workers: Number of worker processes for parallel mode. | ||
| ``1`` (default) always uses sequential execution. | ||
| ``None`` lets ``ProcessPoolExecutor`` pick based on | ||
| ``os.cpu_count()``. Any value other than ``1`` | ||
| activates parallel mode when the file count is at or | ||
| above :data:`ADAPTIVE_PARALLEL_THRESHOLD`. |
There was a problem hiding this comment.
workers is documented as accepting any value other than 1 to activate parallel mode, but passing 0 or a negative number will raise a ValueError inside ProcessPoolExecutor with a fairly opaque message. It would be safer to validate workers up-front (e.g., allow None or >= 1, otherwise raise a clear ValueError).
| ## Enabling plugins | ||
|
|
||
| Core rules (registered under `zenzic.rules` by Zenzic itself) are always | ||
| active. External plugin rules must be explicitly enabled in `zenzic.toml` | ||
| under the `plugins` key: | ||
|
|
||
| ```toml | ||
| # zenzic.toml | ||
| [build_context] | ||
| engine = "mkdocs" | ||
|
|
||
| plugins = ["no-internal-hostname"] | ||
| ``` | ||
|
|
||
| Only plugins listed here will be loaded. Installing a package that registers | ||
| rules under `zenzic.rules` without listing it in `plugins` has no effect — | ||
| this is intentional **Safe Harbor** behaviour: you always know exactly which | ||
| rules are active in your project. |
There was a problem hiding this comment.
This section documents enabling third-party rule plugins via a plugins = [...] key in zenzic.toml, but the current ZenzicConfig / BuildContext models don’t define a plugins field and the scanner only builds the rule engine from config.custom_rules (it doesn’t load/enable entry-point rules for scanning). Either implement the config field + loading logic, or update the docs to reflect the current behavior (discovery/inspection only).
| ## Abilitare i plugin | ||
|
|
||
| Le regole core (registrate sotto `zenzic.rules` da Zenzic stesso) sono sempre | ||
| attive. Le regole plugin esterne devono essere esplicitamente abilitate in | ||
| `zenzic.toml` sotto la chiave `plugins`: | ||
|
|
||
| ```toml | ||
| # zenzic.toml | ||
| [build_context] | ||
| engine = "mkdocs" | ||
|
|
||
| plugins = ["no-internal-hostname"] | ||
| ``` | ||
|
|
||
| Solo i plugin elencati qui verranno caricati. L'installazione di un pacchetto | ||
| che registra regole sotto `zenzic.rules` senza elencarlo in `plugins` non ha | ||
| effetto — questo è il comportamento intenzionale del **Safe Harbor**: sai sempre | ||
| esattamente quali regole sono attive nel tuo progetto. |
There was a problem hiding this comment.
Qui si documenta l’abilitazione dei plugin tramite plugins = [...] in zenzic.toml, ma i modelli ZenzicConfig / BuildContext attuali non espongono alcun campo plugins e lo scanner costruisce il motore regole solo da config.custom_rules (non carica/abilita regole da entry-point per la scansione). Serve implementare il campo + la logica di caricamento, oppure allineare la documentazione al comportamento reale (solo discovery/ispezione).
| `PluginContractError` immediately — before any file is scanned. | ||
| - **`zenzic.rules` entry-point group**: core rules (`VSMBrokenLinkRule`) are | ||
| registered as first-class plugins. Third-party packages can extend Zenzic by | ||
| registering under the same group and enabling their plugin ID in `zenzic.toml`. |
There was a problem hiding this comment.
The highlights mention that third-party rules can be enabled via their plugin ID in zenzic.toml, but the current config models don’t define a plugins field and the scanner’s rule-engine construction only uses custom_rules. This makes the README claim misleading unless plugin enablement/loading is implemented elsewhere.
| registering under the same group and enabling their plugin ID in `zenzic.toml`. | |
| registering under the same group; Zenzic automatically discovers all installed rule plugins. |
| rule_findings: Issues raised by the AdaptiveRuleEngine (custom rules and | ||
| plugin-registered rules). Empty when no rules are configured. |
There was a problem hiding this comment.
IntegrityReport.rule_findings is described as including “plugin-registered rules”, but the scanner currently builds the rule engine only from config.custom_rules and there’s no visible path that loads entry-point rules into rule_findings. Either adjust this docstring, or wire plugin rules into the scan so the report can actually contain them.
| rule_findings: Issues raised by the AdaptiveRuleEngine (custom rules and | |
| plugin-registered rules). Empty when no rules are configured. | |
| rule_findings: Issues raised by the AdaptiveRuleEngine (configured/custom | |
| rules). Empty when no rules are configured. |
Description
Closes #
Type of change
The Zenzic Way — mandatory checklist
Zenzic's Core is built on three non-negotiable design pillars. Every PR that touches
src/must satisfy all that apply.
1. Source-first
mkdocs build,import a documentation framework, or depend on generated HTML or build artefacts.
2. No subprocesses
subprocess.run,os.system, or equivalent shell calls have been added to thelinting path (
src/zenzic/core/).tomllib,json,yaml.safe_load,compile()).3. Pure functions
network access, no global state mutations inside pure functions.
Quality gates
nox -s testspasses (all existing tests green, coverage ≥ 80%).nox -s lintandnox -s typecheckpass (ruff check+mypy --strict).nox -s preflightpasses end-to-end (includeszenzic check all --strictself-dogfood).nox -s reuse).