Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
719 changes: 0 additions & 719 deletions CHANGELOG.it.md

This file was deleted.

142 changes: 142 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,148 @@ Versions follow [Semantic Versioning](https://semver.org/).

## [Unreleased]

## [0.5.0a1] — 2026-04-02 — The Sentinel: Hybrid Adaptive Engine & Plugin System

> **Sprint 11.** Zenzic enters the v0.5 cycle with a unified execution model and a
> first-class plugin system. `scan_docs_references` replaces the two separate serial
> and parallel functions. The engine selects sequential or `ProcessPoolExecutor` mode
> automatically based on repository size (threshold: 50 files). All rules are validated
> for pickle-serializability at construction time. Core rules are now registered as
> entry-points under `zenzic.rules`, establishing the public plugin contract.

### BREAKING CHANGES

- **`scan_docs_references` signature changed.** The function now returns
`tuple[list[IntegrityReport], list[str]]` instead of `list[IntegrityReport]`.
Callers that ignored link errors must unpack the tuple:

```python
# Before (0.4.x)
reports = scan_docs_references(repo_root)

# After (0.5.x)
reports, _ = scan_docs_references(repo_root)
```

- **`scan_docs_references_parallel` and `scan_docs_references_with_links` are
removed.** Use `scan_docs_references(..., workers=N)` and
`scan_docs_references(..., validate_links=True)` respectively.

- **`RuleEngine` is removed.** The class is now `AdaptiveRuleEngine` with no
alias. The constructor runs eager pickle validation on every rule and raises
`PluginContractError` if any rule is not serialisable.

### Added

- **`AdaptiveRuleEngine`** (`zenzic.core.rules`) — unified rule engine with
Hybrid Adaptive Mode. Replaces and removes `RuleEngine` (no alias).
Validates all rules for pickle-serializability at construction time via
`_assert_pickleable()`.

- **`_assert_pickleable(rule)`** (`zenzic.core.rules`) — module-level helper
called by `AdaptiveRuleEngine.__init__`. Raises `PluginContractError` on
failure with a diagnostic message including the rule ID, class name, and the
pickle error.

- **`ADAPTIVE_PARALLEL_THRESHOLD`** (`zenzic.core.scanner`) — module-level
constant (default: `50`). The file count above which parallel mode activates.
Exposed for test overrides without patching private internals.

- **`PluginContractError`** (`zenzic.core.exceptions`) — new exception for rule
plugin violations. Added to the exception hierarchy docstring.

- **`zenzic.rules` entry-point group** (`pyproject.toml`) — core rules
registered as first-class plugins:

```toml
[project.entry-points."zenzic.rules"]
broken-links = "zenzic.core.rules:VSMBrokenLinkRule"
```

- **`docs/developers/plugins.md`** (EN + IT) — new page documenting the rule
plugin contract: module-level requirement, pickle safety, purity, packaging
via `entry_points`, `plugins` key in `zenzic.toml`, error isolation, and a
pre-publication checklist.

- **`docs/developers/index.md`** (EN + IT) — added link to `plugins.md`.

- **`zenzic plugins list`** — new CLI sub-command. Lists every rule registered
in the `zenzic.rules` entry-point group with its `rule_id`, origin
distribution, and fully-qualified class name. Core rules are labelled
`(core)`; third-party rules show the installing package name.

- **`pyproject.toml` configuration support (ISSUE #5)** — `ZenzicConfig.load()`
now follows a three-level Agnostic Citizen priority chain:
`zenzic.toml` (Priority 1) → `[tool.zenzic]` in `pyproject.toml`
(Priority 2) → built-in defaults (Priority 3). If both files exist,
`zenzic.toml` wins unconditionally.

- **`plugins` config key** (`zenzic.toml` / `[tool.zenzic]`) —
`ZenzicConfig.plugins` now exposes an explicit allow-list of external
rule plugin entry-point names to activate during scanning. Core rules
remain always enabled.

- **`scan_docs_references` `verbose` flag** — new keyword-only parameter
`verbose: bool = False`. When `True`, prints a one-line performance
telemetry summary to stderr after the scan: engine mode (Sequential or
Parallel), worker count, file count, elapsed time, and estimated speedup
(parallel mode only).

- **`PluginRuleInfo` dataclass** (`zenzic.core.rules`) — lightweight struct
returned by the new `list_plugin_rules()` discovery function. Fields:
`rule_id`, `class_name`, `source`, `origin`.

- **`docs/configuration/index.md`** (EN + IT) — "Configuration loading" section
expanded with the three-level priority table and a `[tool.zenzic]` example.

### Changed

- **`scan_docs_references`** (`zenzic.core.scanner`) — unified function
replacing `scan_docs_references` + `scan_docs_references_parallel`. New
signature:

```python
scan_docs_references(
repo_root, config=None,
*, validate_links=False, workers=1
) -> tuple[list[IntegrityReport], list[str]]
```

Hybrid Adaptive Mode: sequential when `workers=1` or `< 50 files`; parallel
(`ProcessPoolExecutor`) otherwise. Results always sorted by `file_path`.

- **`docs/architecture.md`** and **`docs/it/architecture.md`** — "Parallel scan
(v0.4.0-rc5)" section replaced by "Hybrid Adaptive Engine (v0.5.0a1)" with
a Fan-out/Fan-in Mermaid diagram showing the threshold decision node.
IT section was previously absent; added from scratch.

- **`docs/usage/advanced.md`** and **`docs/it/usage/advanced.md`** — parallel
scan section rewritten to document the unified `scan_docs_references` API and
the Hybrid Adaptive Engine threshold table.

- **`docs/usage/commands.md`** (EN + IT) — added `zenzic plugins list` command
documentation and `--workers` flag reference for the Hybrid Adaptive Engine.

- **`README.md`** — "RC5 Highlights" replaced by "v0.5.0a1 Highlights —
The Sentinel".

- **`pyproject.toml`** — version bumped to `0.5.0a1`.

- **`src/zenzic/__init__.py`** — `__version__` bumped to `"0.5.0a1"`.

### Removed

- `scan_docs_references_parallel` — deleted; use `scan_docs_references(..., workers=N)`.
- `scan_docs_references_with_links` — deleted; use `scan_docs_references(..., validate_links=True)`.
- `RuleEngine` — deleted; use `AdaptiveRuleEngine` directly.

---

## 0.4.x (abandoned)

This release cycle was exploratory and included multiple breaking changes.
It has been superseded by the 0.5.x stabilization cycle.

## [0.4.0-rc4] — 2026-04-01 — Ghost Route Support, VSM Rule Engine & Content-Addressable Cache

## [0.4.0-rc5] — 2026-04-01 — The Sync Sprint: Zensical v0.0.31+ & Parallel API
Expand Down
40 changes: 27 additions & 13 deletions README.it.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,24 @@ fallback, nessuna supposizione.

---

## Novita RC5 (v0.4.0-rc5)

- **Sync Zensical v0.0.31+**: `ZensicalAdapter` legge ora la nav da `[project].nav`
(schema TOML ufficiale), incluse sezioni annidate.
- **Routing nav-aware**: con nav esplicita, i file presenti su disco ma assenti dalla nav
vengono classificati `ORPHAN_BUT_EXISTING`.
- **Parita URL**: `map_url()` rispetta `[project].use_directory_urls = false`
(`/pagina.html`) oltre al default directory-style (`/pagina/`).
- **Parallelismo API documentato**: modello shared-nothing con `ProcessPoolExecutor`,
note oneste sull'overhead e requisiti di picklability per regole custom.
- **Nuovo esempio canonico**: `examples/zensical-basic/` allineato agli snippet documentati.
## v0.5.0a1 — La Sentinella

- **Hybrid Adaptive Engine**: `scan_docs_references` è l'unico entry point unificato per
tutte le modalità di scansione. Il motore seleziona l'esecuzione sequenziale o parallela
automaticamente in base alla dimensione del repository (soglia: 50 file).
- **`AdaptiveRuleEngine` con validazione pickle anticipata**: tutte le regole vengono
validate per la serializzabilità pickle al momento della costruzione. Una regola non
serializzabile solleva `PluginContractError` immediatamente.
- **`zenzic plugins list`**: nuovo comando che mostra ogni regola registrata nel gruppo
entry-point `zenzic.rules` — regole Core e plugin di terze parti.
- **Supporto `pyproject.toml` (ISSUE #5)**: incorpora la configurazione Zenzic in
`[tool.zenzic]` quando `zenzic.toml` è assente. `zenzic.toml` vince sempre se entrambi
i file esistono.
- **Telemetria delle prestazioni**: `scan_docs_references(verbose=True)` stampa modalità
motore, numero di worker, tempo di esecuzione e speedup stimato su stderr.
- **`PluginContractError`**: nuova eccezione per le violazioni del contratto delle regole.
- **Documentazione plugin**: `docs/developers/plugins.md` (EN + IT) — contratto completo,
istruzioni di packaging ed esempi di registrazione `pyproject.toml`.

---

Expand Down Expand Up @@ -248,11 +255,18 @@ non segnalare mai i file tradotti come orfani.

## Changelog & Note di Rilascio

- 📋 [CHANGELOG.md](CHANGELOG.md) — storico completo delle modifiche (inglese)
- 📋 [CHANGELOG.it.md](CHANGELOG.it.md) — storico delle modifiche in italiano
- 📋 [CHANGELOG.md](CHANGELOG.md) — storico completo delle modifiche (unico, in inglese)
- 🚀 [RELEASE.md](RELEASE.md) — manifesto di rilascio v0.4.0 (inglese)
- 🚀 [RELEASE.it.md](RELEASE.it.md) — manifesto di rilascio v0.4.0 (italiano)

> Il changelog è ora mantenuto in un unico file inglese (`CHANGELOG.md`).
> Questa scelta segue gli standard dell'ecosistema Python open source:
> la cronologia delle versioni è documentazione tecnica, non interfaccia utente.
>
> Nota sul ciclo release: la linea `0.4.x` è stata abbandonata (fase
> esplorativa con breaking changes multipli); la linea attiva di
> stabilizzazione è `0.5.x`.

---

## Contribuire
Expand Down
47 changes: 33 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,18 +42,30 @@ absolute links are a hard error, and if you declare `engine = "zensical"` you mu

---

## RC5 Highlights (v0.4.0-rc5)

- **Zensical v0.0.31+ sync**: `ZensicalAdapter` now reads navigation from
`[project].nav` (official TOML schema), including nested sections.
- **Nav-aware routing**: with explicit nav, files present on disk but absent from nav are
classified as `ORPHAN_BUT_EXISTING`.
- **URL mode parity**: `map_url()` now respects `[project].use_directory_urls = false`
(`/page.html`) and default directory URLs (`/page/`).
- **Parallel scan API documented**: shared-nothing `ProcessPoolExecutor` model,
honest overhead notes, and picklability requirements for custom rules.
- **New canonical example**: `examples/zensical-basic/` mirrors the documented TOML
schema and migration flow.
## v0.5.0a1 Highlights — The Sentinel

- **Hybrid Adaptive Engine**: `scan_docs_references` is the single unified
entry point for all scan modes. The engine selects sequential or parallel
execution automatically based on repository size (threshold: 50 files). No
flags required — Zenzic is fast by default.
- **`AdaptiveRuleEngine` with eager pickle validation**: all rules are validated
for pickle-serializability at construction time. A non-serialisable rule raises
`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`.
- **`zenzic plugins list`**: new command that displays every rule registered in
the `zenzic.rules` entry-point group — Core rules and third-party plugins.
- **`pyproject.toml` support (ISSUE #5)**: embed Zenzic config in `[tool.zenzic]`
when `zenzic.toml` is absent. `zenzic.toml` always wins if both exist.
- **Performance telemetry**: `scan_docs_references(verbose=True)` prints engine
mode, worker count, elapsed time, and estimated speedup to stderr.
- **`PluginContractError`**: new exception for rule contract violations.
- **Plugin documentation**: `docs/developers/plugins.md` (EN + IT) — full
contract, packaging instructions, and `pyproject.toml` registration examples.
- **Release-track clarification**: the 0.4.x cycle is considered abandoned
(exploratory with repeated breaking changes); 0.5.x is the active
stabilization line.

---

Expand Down Expand Up @@ -492,11 +504,18 @@ For dynamic badge automation and regression detection, see the [CI/CD Integratio

---

## Configuration (`zenzic.toml`)
## Configuration

All fields are optional. Zenzic works with no configuration file.
All fields are optional. Zenzic works with no configuration file at all.

Zenzic follows a three-level **Agnostic Citizen** priority chain:

1. `zenzic.toml` at the repository root — sovereign; always wins.
2. `[tool.zenzic]` in `pyproject.toml` — used when `zenzic.toml` is absent.
3. Built-in defaults.

```toml
# zenzic.toml (or [tool.zenzic] in pyproject.toml)
docs_dir = "docs"
excluded_dirs = ["includes", "assets", "stylesheets", "overrides", "hooks"]
snippet_min_lines = 1
Expand Down
9 changes: 9 additions & 0 deletions docs/about/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,13 @@ Built by [PythonWoods](https://github.com/PythonWoods), it is designed to run in

[:lucide-arrow-right: Open](https://github.com/PythonWoods/zenzic)

- :lucide-history: &nbsp; __Changelog__

---

Full release history and current release track policy.
The 0.4.x line is abandoned; 0.5.x is the active stabilization cycle.

[:lucide-arrow-right: Read](https://github.com/PythonWoods/zenzic/blob/main/CHANGELOG.md)

</div>
78 changes: 53 additions & 25 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,43 +139,71 @@ I/O operations, called at process start and end respectively by the CLI layer.

---

## Parallel scan (v0.4.0-rc5)
## Hybrid Adaptive Engine (v0.5.0a1)

The Three-Phase Pipeline is pure by design: `_scan_single_file` takes a file path and
returns an `IntegrityReport` with zero shared state. This makes it trivially
parallelisable.
`scan_docs_references` is the single unified entry point for all scan modes.
There is no longer a separate "parallel" function — the engine **adapts
automatically** based on repository size.

```mermaid
flowchart LR
classDef node fill:#0f172a,stroke:#38bdf8,stroke-width:2px,color:#e2e8f0
flowchart TD
classDef node fill:#0f172a,stroke:#38bdf8,stroke-width:2px,color:#e2e8f0
classDef decide fill:#0f172a,stroke:#f59e0b,stroke-width:2px,color:#e2e8f0
classDef worker fill:#0f172a,stroke:#10b981,stroke-width:2px,color:#e2e8f0
classDef io fill:#0f172a,stroke:#4f46e5,stroke-width:2px,color:#e2e8f0
classDef seq fill:#0f172a,stroke:#6366f1,stroke-width:2px,color:#e2e8f0
classDef io fill:#0f172a,stroke:#4f46e5,stroke-width:2px,color:#e2e8f0

ENTRY["scan_docs_references()\nrepo_root, config\nworkers, validate_links"]:::node
THRESHOLD{"files ≥ 50\nAND workers ≠ 1?"}:::decide

MAIN["Main process\nbuilds RuleEngine\nlists .md files"]:::node
SEQ["Sequential path\n_scan_single_file × N\nO(N) reads"]:::seq
SORT_SEQ["Sorted list[IntegrityReport]"]:::io

FAN["Main process\npickle(config, engine)\n→ work_items"]:::node
W1["Worker 1\n_scan_single_file\n(page_A.md)"]:::worker
W2["Worker 2\n_scan_single_file\n(page_B.md)"]:::worker
WN["Worker N\n_scan_single_file\n(page_Z.md)"]:::worker
SORT["Sorted merge\nby file_path"]:::node
OUT["list[IntegrityReport]"]:::io

MAIN -->|"pickle(config, engine)"| W1 & W2 & WN
W1 & W2 & WN --> SORT
SORT --> OUT
MERGE["Sorted merge\nby file_path"]:::node
SORT_PAR["Sorted list[IntegrityReport]"]:::io

ENTRY --> THRESHOLD
THRESHOLD -->|"No (< 50 files\nor workers=1)"| SEQ
SEQ --> SORT_SEQ
THRESHOLD -->|"Yes"| FAN
FAN -->|"pickle(config, engine)"| W1 & W2 & WN
W1 & W2 & WN --> MERGE
MERGE --> SORT_PAR
```

**Shared-nothing architecture:** `config` and `rule_engine` are serialised by `pickle`
when dispatched to each worker. Every worker operates on an independent copy — there is
no shared memory, no lock, no race condition.
### Sequential path

Used when `workers=1` (the default) or when the repository has fewer than 50
files. Zero process-spawn overhead. Supports external URL validation in a
single O(N) pass.

### Parallel path

Activated when `workers != 1` and the file count is at or above
`ADAPTIVE_PARALLEL_THRESHOLD` (50). Each file is dispatched to an independent
worker process via `ProcessPoolExecutor`.

**Shared-nothing architecture:** `config` and the `AdaptiveRuleEngine`
(including all registered rules) are serialised by `pickle` before being sent
to each worker. Every worker operates on an independent copy — no shared
memory, no locks, no race conditions.

**Immutability contract:** workers must not mutate `config`. Rules that write
to mutable global state (e.g. a class-level counter) violate the Pure Functions
Pillar. In parallel mode, each worker holds an independent copy of that state
— mutations are local and discarded, producing results that diverge silently
from sequential mode.

**Immutability contract:** workers must not mutate `config`. All scan functions honour
this contract. Rules that write to shared state (e.g. a counter in a class variable)
violate the Pure Functions Pillar and will produce non-deterministic results in parallel
mode.
**Eager pickle validation:** `AdaptiveRuleEngine` calls `pickle.dumps()` on
every rule at construction time. A non-serialisable rule raises
`PluginContractError` immediately, before any file is scanned.

**Threshold for parallelism benefit:** process-spawn overhead is ~200–400 ms on a cold
Python interpreter. The crossover point where parallelism beats sequential scanning is
approximately 200 files on an 8-core machine. For smaller repos, use
`scan_docs_references` (sequential).
**Determinism guarantee:** results are sorted by `file_path` after collection
regardless of worker scheduling order.

---

Expand Down
Loading
Loading