Skip to content

Commit 043488a

Browse files
Merge pull request #69 from PythonWoods/release/v0.6.1
feat(core): release v0.6.1 'Obsidian Glass' stable
2 parents 0151201 + 8e443c6 commit 043488a

78 files changed

Lines changed: 3693 additions & 1506 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/copilot-instructions.md

Lines changed: 152 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,173 @@
1-
# Zenzic Agent Guidelines
1+
# Zenzic Agent Guidelines — v0.6.1 "Obsidian Glass" Stable
22

3-
Zenzic is an engine-agnostic linter and security shield for Markdown documentation (Docusaurus v3, MkDocs, Zensical, bare Markdown). It ships as a CLI (`zenzic`), a Python library, and a native MkDocs plugin. Python ≥ 3.11 required.
3+
Zenzic is the high-performance, engine-agnostic Safe Harbor for Markdown documentation.
4+
It is a STABLE product. Agents must prioritize precision, security, and "Value-First" communication.
45

5-
## Build & Test
6+
---
67

7-
```bash
8-
uv sync --all-groups # install all dependency groups (once after clone)
9-
just test # run tests — Hypothesis dev profile (50 examples)
10-
just test-full # CI-grade run (500 examples)
11-
just preflight # full local CI: lint + format + typecheck + pytest + reuse
12-
just verify # preflight + self-lint (zenzic check all --strict)
13-
```
8+
## 🎯 Mission: The Safe Harbor
149

15-
Common individual sessions:
10+
- **Target:** Engineers, Technical Writers, and curious users.
11+
- **Philosophy:** "If the engine builds the site, Zenzic guarantees the source."
12+
- **Communication:** README is a Landing Page, not a manual. Move technical deep-dives to the documentation portal (zenzic.dev).
1613

17-
```bash
18-
nox -s lint -- --fix # ruff autofix
19-
nox -s fmt # ruff format in-place
20-
nox -s typecheck # mypy --strict on src/
21-
nox -s reuse # SPDX/REUSE compliance check
22-
```
14+
---
15+
16+
## 🚀 Key Features (v0.6.1 — Obsidian Glass Stable)
17+
18+
- **Instant Entry:** `uvx zenzic check all ./path` is the primary curiosity path.
19+
- **Zenzic Lab:** 9 interactive Acts for onboarding (zero-config showroom). Run `zenzic lab` to see the menu; `zenzic lab <N>` to run a specific act.
20+
- **Standalone Mode:** Default engine for pure Markdown projects with no recognised build system. Replaces the old "Vanilla" identity entirely.
21+
- **Zensical Bridge:** Transparent Proxy for `mkdocs.yml` compatibility under `engine = "zensical"`.
22+
- **Enterprise Docusaurus:** Full versioning, `@site/` alias, and slug logic alignment.
23+
- **Offline Mode:** `--offline` flag for flat `.html` URL structure.
24+
- **SEO Guardrail:** `Z401 MISSING_DIRECTORY_INDEX` detection for directories without a landing page.
25+
- **Finding Codes (Zxxx):** Every diagnostic carries a unique `Zxxx` identifier for enterprise-grade auditing and future filtering.
26+
27+
---
28+
29+
## 🧱 The 3 Pillars (Non-Negotiable)
30+
31+
1. **Lint the Source:** Never depend on HTML output. Analyze raw Markdown and configs.
32+
2. **No Subprocesses:** 100% pure Python. No `subprocess.run`, no Node.js execution.
33+
3. **Pure Functions First:** Deterministic logic. No I/O in hot-path loops.
34+
35+
---
36+
37+
## 🛡️ Core Laws for Code
2338

24-
See [justfile](../justfile) for the full recipe list and [noxfile.py](../noxfile.py) for session definitions.
39+
- **Zero I/O in hot paths:** No `Path.exists()` or `open()` inside link/file loops.
40+
- **Mandatory ExclusionManager:** No discovery without an explicit exclusion manager.
41+
- **Exit Codes:** 0 (Success), 1 (Quality), 2 (Shield/Secrets), 3 (Blood Sentinel/Fatal).
42+
- **Finding Codes:** Every `Finding` object must carry a `Zxxx` code from `src/zenzic/core/codes.py`. Never hardcode a raw string code; always use `codes.normalize()`.
2543

26-
## Architecture
44+
---
45+
46+
## 📐 Architecture Map
2747

2848
```text
2949
src/zenzic/
30-
├── main.py / cli.py # Typer CLI (commands: check, score, diff)
31-
├── core/ # Hot-path analysis engine (zero I/O — see Core Laws)
32-
│ ├── adapter.py # BaseAdapter Protocol + RouteMetadata
33-
│ ├── adapters/ # docusaurus_v3, mkdocs, zensical, vanilla
34-
│ ├── discovery.py # Universal file discovery (os.walk + LayeredExclusionManager)
35-
│ ├── exclusion.py # LayeredExclusionManager (4-level: System → VCS → Config → CLI)
36-
│ ├── rules.py # AdaptiveRuleEngine + O(V+E) circular detection
37-
│ ├── shield.py # Credential scanner (9 families, 8-step normalization, lookback buffer)
38-
│ ├── resolver.py # InMemoryPathResolver (link resolution)
39-
│ ├── scanner.py # Two-Pass Reference Pipeline (Harvest → Cross-Check → Report)
40-
│ └── validator.py # validate_links_async orchestrator
41-
├── models/ # Pydantic models (config, references, vsm)
42-
└── integrations/mkdocs.py # Native MkDocs plugin
50+
cli.py — Typer CLI entry-points; builds Finding objects via _to_findings()
51+
lab.py — Interactive showcase (9 Acts); menu-driven with positional arg
52+
core/
53+
adapter.py — Public re-exports (StandaloneAdapter, MkDocsAdapter, …)
54+
adapters/
55+
_standalone.py — StandaloneAdapter: no-op engine for pure Markdown projects
56+
_mkdocs.py — MkDocs engine adapter
57+
_docusaurus.py — Docusaurus v3 engine adapter
58+
_zensical.py — Zensical engine adapter (+ Transparent Proxy)
59+
_factory.py — get_adapter() factory; contains vanilla→standalone migration guard
60+
__init__.py — Public adapter registry
61+
codes.py — Zxxx finding code registry (SINGLE SOURCE OF TRUTH)
62+
reporter.py — SentinelReporter; renders Finding objects to Rich output
63+
scanner.py — File discovery, orphan detection, shield bridge
64+
validator.py — Link / anchor / path-traversal validation
65+
rules.py — VSM-based rule engine (Z001, Z002)
66+
shield.py — Credential scanner (exits 2/3)
67+
scorer.py — Quality score engine
68+
models/
69+
config.py — ZenzicConfig / BuildContext (Pydantic)
70+
vsm.py — Virtual Site Map (Route, build_vsm, detect_collisions)
71+
references.py — Reference integrity (IntegrityReport, ReferenceFinding)
72+
ui.py — Shared Rich colour constants and emoji helpers
73+
tests/
74+
test_standalone_mode.py — StandaloneAdapter unit tests + factory routing
75+
test_vsm.py — Virtual Site Map tests
76+
test_blue_vsm_edge.py — VSM edge-case stress tests
77+
test_protocol_evolution.py — Adapter protocol compliance + Hypothesis stress tests
78+
test_cli.py — CLI integration tests (Typer runner)
79+
test_scanner.py — Scanner / orphan / i18n tests
80+
test_rules.py — Rule engine tests
81+
test_shield.py — Shield / credential detection tests
4382
```
4483

45-
Third-party adapters and rules are discoverable via `zenzic.adapters` / `zenzic.rules` entry-point groups.
84+
---
85+
86+
## 🔎 Finding Code Standard (Zxxx)
87+
88+
All diagnostics emitted by Zenzic carry a `Zxxx` code. The registry is in
89+
`src/zenzic/core/codes.py`. **Never add a new finding without registering its code there first.**
90+
91+
| Range | Category | Examples |
92+
|-------|----------|---------|
93+
| Z1xx | Link Integrity | Z101 LINK_BROKEN, Z102 ANCHOR_MISSING, Z104 FILE_NOT_FOUND |
94+
| Z2xx | Security | Z201 SHIELD_SECRET, Z202 PATH_TRAVERSAL |
95+
| Z3xx | Reference Integrity | Z301 DANGLING_REF, Z302 DEAD_DEF |
96+
| Z4xx | Structure | Z401 MISSING_DIRECTORY_INDEX, Z402 ORPHAN_PAGE |
97+
| Z5xx | Content Quality | Z501 PLACEHOLDER, Z503 SNIPPET_ERROR |
98+
| Z9xx | Engine / System | Z902 RULE_TIMEOUT |
99+
100+
When creating a `Finding`, always call `codes.normalize(raw_code)` to map legacy strings to canonical `Zxxx` codes. The `_to_findings()` function in `cli.py` is the authorised conversion point.
101+
102+
---
103+
104+
## 🏭 Adapter Identity Rules
105+
106+
- **"standalone"** is the canonical engine name for projects with no build config. Use `StandaloneAdapter`.
107+
- **"vanilla"** is a removed legacy name. Any usage raises `ConfigurationError` with code `Z000`.
108+
- `pyproject.toml` entry-point: `standalone = "zenzic.core.adapters:StandaloneAdapter"`.
109+
- In Standalone Mode, navigation-based checks (orphan detection) are disabled because there is no declared nav.
110+
- When `zenzic init` finds no engine config, it writes `engine = "standalone"` in the `[build_context]` block.
111+
112+
---
113+
114+
## 🧪 Quality Gate
115+
116+
- **Coverage:** ≥ 80% mandatory.
117+
- **Mutation:** Mutmut audit on `rules.py` and `shield.py`.
118+
- **Property Testing:** Hypothesis for `resolver.py` and adapter stress-testing.
119+
- **Test naming:** All test functions relating to the Standalone adapter use the prefix `test_standalone_*`.
120+
121+
---
122+
123+
## 📝 README Restyling Rules
124+
125+
When editing `README.md` or `README.it.md`:
126+
127+
1. **Above the Fold:** Hook + uvx command + Quick Start + Sentinel Report must be visible within 2 scrolls.
128+
2. **Problem/Solution:** Use tables to contrast "Without Zenzic" vs "With Zenzic".
129+
3. **Deduplicate:** Technical implementation details belong in `/docs/internals` on zenzic.dev.
130+
4. **Bilingual Parity:** EN and IT must be perfectly mirrored in structure and quality.
131+
5. **Branding:** Keep "Shield", "Blood Sentinel", "VSM", "Ghost Routes" as-is (Proper Nouns).
132+
6. **Standalone, not Vanilla:** Never write "Vanilla Mode" in user-facing copy. Use "Standalone Mode".
133+
134+
---
135+
136+
## 🧹 Maintenance & Sunset Clauses (Technical Debt Tracking)
137+
138+
### 1. Vanilla-to-Standalone Migration Guard
139+
140+
- **Status:** Deprecated.
141+
- **Location:** `src/zenzic/core/adapters/_factory.py` (check for `engine == "vanilla"`).
142+
- **Behavior:** Raises a `ConfigurationError` with code `Z000` to guide users to `engine = "standalone"`.
143+
- **Sunset Target:** **v0.7.0**.
144+
- **Instruction:** Do NOT remove this guard in 0.6.x patches. MUST be deleted entirely in the 0.7.0 release to achieve zero-legacy codebase.
145+
146+
### 2. Finding Codes (Zxxx)
147+
148+
- **Standard:** All diagnostics must use the `Zxxx` format.
149+
- **Rule:** Never introduce a new finding string without a corresponding code in `src/zenzic/core/codes.py`.
150+
151+
---
152+
153+
## 🗺️ Sprint History (for institutional memory)
154+
155+
### Direttiva 036 — Finding Code Mapping
46156

47-
## Core Laws
157+
Codified all diagnostic outputs into the `Zxxx` scheme. `codes.py` created as single source of truth.
48158

49-
1. **Zero I/O in the hot path**: nothing inside `src/zenzic/core/` may call `Path.exists()`, `open()`, or subprocesses inside per-link or per-file loops. Only two I/O phases are permitted: `discovery.py` file enumeration (via `os.walk` + `LayeredExclusionManager`) and `InMemoryPathResolver.__init__`.
50-
2. **Subprocess-free linting**: `zenzic check` never calls `mkdocs build` or any external process.
51-
3. **Mandatory ExclusionManager**: every file-discovery entry point requires a `LayeredExclusionManager` argument — no `Optional`, no `None` default. Omitting it is a `TypeError` at call time, not a silent full-tree scan at runtime.
159+
### Direttiva 037 — Standalone Renaissance
52160

53-
Violating any of these laws is a blocking defect — do not introduce exceptions.
161+
Full rename: `VanillaAdapter``StandaloneAdapter`, `_vanilla.py``_standalone.py`, entry-point `vanilla``standalone`. Breaking change: `engine = "vanilla"` raises `ConfigurationError [Z000]`. Test suite fully migrated to `test_standalone_mode.py`.
54162

55-
## Code Conventions
163+
### Direttiva 038 — Final Audit Record
56164

57-
- **Type checking**: `mypy --strict` must pass on all of `src/`. Never suppress with `# type: ignore` without a comment explaining why.
58-
- **Linting**: ruff rules `E, F, W, I, B, C4, UP, A`; line length 100; isort `known-first-party = ["zenzic"]`.
59-
- **SPDX headers**: every source file must start with `# SPDX-FileCopyrightText: ...` and `# SPDX-License-Identifier: Apache-2.0`. Run `nox -s reuse` to verify.
60-
- **No stubs**: no `TODO`, placeholder text, or stub implementations in committed code.
61-
- **Coverage**: ≥ 80% branch coverage enforced by pytest. Mutation goal ≥ 90% on `rules.py`, `shield.py`, `reporter.py`.
62-
- **Discovery**: never use `Path.rglob()` or `glob.glob()` directly. All file enumeration goes through `discovery.iter_markdown_sources()` or `discovery.walk_files()` with a `LayeredExclusionManager`.
165+
CHANGELOG.md, CHANGELOG.it.md, and RELEASE.md updated to reflect the Breaking Change (Vanilla → Standalone), the Zxxx code introduction, and the interactive Lab menu.
63166

64-
## Tests
167+
### Direttiva 039 — The Guardrail Lifecycle
65168

66-
- Tests live in `tests/`; helpers in `tests/_helpers.py`; fixtures in `tests/conftest.py`.
67-
- Hypothesis profiles: `dev` (50), `ci` (500), `purity` (1000) — set via `HYPOTHESIS_PROFILE`.
68-
- Markers: `slow`, `integration` — run with `-m "not slow"` to skip heavy tests locally.
69-
- The `_reset_zenzic_logger` autouse fixture resets the `RichHandler` after each test; do not remove it.
169+
Migration guard in `_factory.py` annotated with `# TODO: Remove this migration guard in v0.7.0.` and error message prefixed with `[Z000]`. Docstring clarified.
70170

71-
## Key Docs
171+
### Direttiva 040 — Institutional Memory
72172

73-
- [CONTRIBUTING.md](../CONTRIBUTING.md) — dev workflow, PR conventions, Core Laws reference
74-
- [SECURITY.md](../SECURITY.md) — vulnerability reporting and scope
75-
- [CHANGELOG.md](../CHANGELOG.md) — version history
76-
- [RELEASE.md](../RELEASE.md) — release checklist
173+
This file (`.github/copilot-instructions.md`) created / restored as the canonical agent briefing document, embedding all sprint directives and sunset clauses for permanent institutional memory.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ dist/
4141

4242
# Zenzic: Drafts
4343
drafts/
44+
Draft/
4445
*.egg
4546
MANIFEST
4647
.installed.cfg

CHANGELOG.it.md

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,76 @@ Le versioni seguono il [Semantic Versioning](https://semver.org/).
1111

1212
## [Non rilasciato]
1313

14+
## [0.6.1] — 2026-04-19 — Obsidian Glass (Stable)
15+
16+
### Modifiche che rompono la compatibilità
17+
18+
- **Standalone Engine sostituisce Vanilla (Direttiva 037).** `VanillaAdapter` e la
19+
keyword `engine = "vanilla"` sono stati rimossi. Tutti i progetti devono migrare a
20+
`engine = "standalone"`. Qualsiasi `zenzic.toml` che usa ancora `engine = "vanilla"`
21+
genera una `ConfigurationError [Z000]` all'avvio con un messaggio di migrazione chiaro.
22+
*Migrazione:* sostituire `engine = "vanilla"` con `engine = "standalone"` nel proprio
23+
`zenzic.toml` o nel blocco `[tool.zenzic]`.
24+
25+
### Aggiunto
26+
27+
- **Codici Finding (Zxxx) (Direttiva 036).** Ogni diagnostica emessa da Zenzic ora
28+
porta un identificatore univoco leggibile dalla macchina (es. `Z101 LINK_BROKEN`,
29+
`Z201 SHIELD_SECRET`, `Z401 MISSING_DIRECTORY_INDEX`). Il registro completo si trova
30+
in `src/zenzic/core/codes.py` — unica fonte di verità per tutti i codici.
31+
- **Menu interattivo del Lab.** `zenzic lab` senza argomenti mostra ora l'indice degli
32+
atti per scegliere quale scenario esplorare. Eseguire `zenzic lab <N>` per avviare
33+
un atto specifico (0–8). L'opzione `--act` è stata sostituita da un argomento
34+
posizionale.
35+
- **Identità Standalone Mode.** `StandaloneAdapter` è il motore no-op canonico per
36+
progetti Markdown puri. `zenzic init` ora scrive `engine = "standalone"` quando non
37+
viene rilevata nessuna configurazione di framework.
38+
39+
- **Flag `--offline` per la risoluzione URL Flat.** Disponibile su `check all`,
40+
`check links` e `check orphans`. Forza tutti gli adapter a produrre URL `.html`
41+
(es. `guida/install.md``/guida/install.html`) invece di slug in stile directory.
42+
- **Supporto multi-versione Docusaurus v3.** `DocusaurusAdapter` ora identifica
43+
`versions.json`, `versioned_docs/` e le traduzioni versionate.
44+
- **Proxy Trasparente Zensical.** Se viene dichiarato `engine = "zensical"` ma
45+
`zensical.toml` è assente, l'adapter crea automaticamente un ponte con il tuo
46+
`mkdocs.yml` esistente.
47+
- **Ghost Routing consapevole delle versioni.** I percorsi della documentazione
48+
versionata sono automaticamente classificati come `REACHABLE`.
49+
- **Risoluzione Alias @site/.** Aggiunto il supporto per l'alias di percorso `@site/`
50+
in `DocusaurusAdapter`, permettendo la corretta risoluzione dei link relativi al progetto.
51+
- **Integrità dell'Indice di Directory.** Nuovo metodo `provides_index(path)` nel protocollo
52+
`BaseAdapter` per il rilevamento engine-aware delle directory prive di landing page.
53+
Il finding `MISSING_DIRECTORY_INDEX` (severità: `info`), emesso da `zenzic check all`,
54+
avvisa di ogni sottodirectory che contiene sorgenti Markdown ma nessun indice fornito
55+
dall'engine — prevenendo i 404 gerarchici prima del deploy.
56+
- **Notifiche nel Banner Sentinel.** Nuovi messaggi di stato per l'attivazione della
57+
**Modalità Offline** e della **Modalità Proxy**.
58+
59+
### Corretto
60+
61+
- **Audit dei Guardiani: Allineamento Specifiche Ufficiali.**
62+
- **Versioning Docusaurus:** Corretta la mappatura URL della versione "latest" (prima voce
63+
in `versions.json`) per escludere il prefisso dell'etichetta di versione, allineandosi
64+
al comportamento ufficiale di Docusaurus. In precedenza ogni file versionato riceveva
65+
un prefisso `/versione/`, generando falsi positivi per tutte le pagine della versione latest.
66+
- **Slug Docusaurus:** Gli slug frontmatter assoluti (es. `slug: /mio-percorso`) sono
67+
ora correttamente preceduti dalla `routeBasePath` (es. `/docs/mio-percorso/`),
68+
allineandosi alla specifica Docusaurus `normalizeUrl([versionMetadata.path, docSlug])`.
69+
- **Collasso Intelligente dei File:** La logica `isCategoryIndex` ora rispecchia
70+
esattamente Docusaurus: `README.md`, `INDEX.md` (case-insensitive) e
71+
`{NomeCartella}/{NomeCartella}.md` collassano nell'URL della directory genitore,
72+
prevenendo falsi positivi per le convenzioni valide di landing page di categoria.
73+
- **Risoluzione Alias `@site/`:** `InMemoryPathResolver` ora risolve i link `@site/`
74+
rispetto al corretto confine `repo_root` invece di sfuggire tramite `../`,
75+
eliminando errori `PathTraversal` spuri per tutti i link relativi al progetto Docusaurus.
76+
- **Integrità dei Metadati.** Corretto l'allineamento delle stringhe di versione in
77+
`CITATION.cff` e `pyproject.toml`.
78+
- **Default routeBasePath Docusaurus.** Ripristinato `docs` come prefisso URL predefinito
79+
per i progetti Docusaurus per corrispondere al comportamento ufficiale dell'engine.
80+
81+
- **Parità Documentale Bilingue.** Copertura completa della documentazione EN/IT per
82+
tutte le feature della v0.6.1 nelle guide Architettura, Motori e Comandi.
83+
1484
## [0.6.1rc2] — 2026-04-16 — Obsidian Bastion (Hardened)
1585

1686
### SICUREZZA: Risultati Operation Obsidian Stress
@@ -97,7 +167,7 @@ Le versioni seguono il [Semantic Versioning](https://semver.org/).
97167
`check_nav_contract`, e tutte le funzioni dello scanner. Nessun default
98168
`None` retrocompatibile.
99169

100-
## [0.6.0a2] — 2026-04-13 — Obsidian Glass
170+
## [0.6.0a2] — 2026-04-13 — Obsidian Glass (Alpha 2)
101171

102172
### Aggiunto
103173

0 commit comments

Comments
 (0)