From 940bcc4214b9ab2047c76e4c2e9b95966077b98a Mon Sep 17 00:00:00 2001 From: PythonWoods-Dev Date: Sat, 4 Apr 2026 14:58:37 +0200 Subject: [PATCH 1/3] feat(assets): hero screenshot from readme-hero + social card light MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add examples/readme-hero/ as compact screenshot source (1 err + 3 warn) - Rewrite generate_hero_crop() → standalone SVG from readme-hero example - Add social-card-light.svg + PNG (white bg, dark text) - Regenerate all screenshot SVGs with v0.5.0a3 Sentinel UI - Update excluded_assets for new screenshot and social card files --- docs/assets/screenshots/screenshot-hero.svg | 173 ++++ .../screenshots/screenshot-hero.svg.license | 3 + .../screenshots/screenshot-score.license | 3 + docs/assets/screenshots/screenshot-score.svg | 118 +++ docs/assets/screenshots/screenshot.svg | 898 ++++++++++++++++-- docs/assets/social/social-card-light.png | Bin 0 -> 19968 bytes .../social/social-card-light.png.license | 3 + docs/assets/social/social-card-light.svg | 34 + .../social/social-card-light.svg.license | 3 + examples/readme-hero/docs/index.md | 15 + examples/readme-hero/docs/setup.md | 23 + examples/readme-hero/mkdocs.yml | 9 + examples/readme-hero/zenzic.toml | 11 + scripts/generate_docs_assets.py | 46 + zenzic.toml | 5 + 15 files changed, 1245 insertions(+), 99 deletions(-) create mode 100644 docs/assets/screenshots/screenshot-hero.svg create mode 100644 docs/assets/screenshots/screenshot-hero.svg.license create mode 100644 docs/assets/screenshots/screenshot-score.license create mode 100644 docs/assets/screenshots/screenshot-score.svg create mode 100644 docs/assets/social/social-card-light.png create mode 100644 docs/assets/social/social-card-light.png.license create mode 100644 docs/assets/social/social-card-light.svg create mode 100644 docs/assets/social/social-card-light.svg.license create mode 100644 examples/readme-hero/docs/index.md create mode 100644 examples/readme-hero/docs/setup.md create mode 100644 examples/readme-hero/mkdocs.yml create mode 100644 examples/readme-hero/zenzic.toml diff --git a/docs/assets/screenshots/screenshot-hero.svg b/docs/assets/screenshots/screenshot-hero.svg new file mode 100644 index 0000000..0c1f28a --- /dev/null +++ b/docs/assets/screenshots/screenshot-hero.svg @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + zenzic check all + + + + + + + + + + zenzic check all + + +╭─────────────────────────── 🛡  ZENZIC SENTINEL  v0.5.0a3 ───────────────────────────╮ + +mkdocs • 3 files (3 docs, 0 assets) • 0.0s + +───────────────────────────────── docs/index.md ────────────────────────────────── + +[FILE_NOT_FOUND]  'api-reference.md' not found in docs + +    13  │  ## API reference +    14  │   +    15  ❱  Browse the [API docs](api-reference.md) for the full public interface. +│             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +───────────────────────────────── docs/setup.md ────────────────────────────────── + +[SNIPPET]  SyntaxError in Python snippet — expected ':' + +────────────────────────────────────────────────────────────────────────────────── + +✘ 2 errors• 2 files with findings + +FAILED: One or more checks failed. + +╰──────────────────────────────────────────────────────────────────────────────────────╯ +Try 'zenzic check --help' for options. + + + + diff --git a/docs/assets/screenshots/screenshot-hero.svg.license b/docs/assets/screenshots/screenshot-hero.svg.license new file mode 100644 index 0000000..73c93a8 --- /dev/null +++ b/docs/assets/screenshots/screenshot-hero.svg.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 PythonWoods + +SPDX-License-Identifier: Apache-2.0 diff --git a/docs/assets/screenshots/screenshot-score.license b/docs/assets/screenshots/screenshot-score.license new file mode 100644 index 0000000..73c93a8 --- /dev/null +++ b/docs/assets/screenshots/screenshot-score.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 PythonWoods + +SPDX-License-Identifier: Apache-2.0 diff --git a/docs/assets/screenshots/screenshot-score.svg b/docs/assets/screenshots/screenshot-score.svg new file mode 100644 index 0000000..35d42a1 --- /dev/null +++ b/docs/assets/screenshots/screenshot-score.svg @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + zenzic score + + + + + + + + + + ╭─Zenzic Quality Score─╮ + ✘  32/100  +╰────────────────────────╯ + +Quality Breakdown +╭──────┬────────────────┬────────┬────────┬───────╮ + •  Category      IssuesWeightScore +├──────┼────────────────┼────────┼────────┼───────┤ +links         6   35% 0.00 +orphans       3   20% 0.08 +snippets      1   20% 0.16 +placeholders  7   15% 0.00 +assets        1   10% 0.08 +╰──────┴────────────────┴────────┴────────┴───────╯ + + + + diff --git a/docs/assets/screenshots/screenshot.svg b/docs/assets/screenshots/screenshot.svg index e28a481..7474a19 100644 --- a/docs/assets/screenshots/screenshot.svg +++ b/docs/assets/screenshots/screenshot.svg @@ -1,4 +1,4 @@ - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - zenzic check all --strict + zenzic check all --strict - - - - $zenzic check all --strict - -Running link check... -BROKEN LINKS (2): -index.md:15: 'non-existent.md' not found in docs -index.md:17: '../../../../etc/passwd' resolves outside the docs directory - -Running orphan check... -ORPHANS (1): -api.md - -Running snippet check... -INVALID SNIPPETS (1): -tutorial.md:30 - SyntaxError in Python snippet — expected ':' - -Running placeholder check... -PLACEHOLDERS (5): -api.md:1 - Page has only 18 words (minimum 50). -api.md:6 - Found placeholder text matching pattern: 'TODO' -index.md:20 - Found placeholder text matching pattern: 'TODO' -index.md:20 - Found placeholder text matching pattern: 'stub' -tutorial.md:7 - Found placeholder text matching pattern: 'stub' - -Running unused assets check... -UNUSED ASSETS (2): -assets/unused.png -assets/used.png - -FAILED: One or more checks failed. + + + + zenzic check all --strict + + +╭─────────────────────────── 🛡  ZENZIC SENTINEL  v0.5.0a3 ───────────────────────────╮ + +mkdocs • 12 files (6 docs, 6 assets) • 0.0s + +─────────────────────────── docs/_drafts/unreleased.md ─────────────────────────── + +[ORPHAN]  Physical file not listed in navigation. + +[placeholder-text]  Found placeholder text matching pattern: 'draft' + +    4  │  # Unreleased Feature +    5  │   +    6  ❱  This page is in `_drafts/` — a private directory that Zensical never  +serves. +│                    ^^^^^ +    7  │   +    8  │  Any link from a public page to this file will trigger  +**UNREACHABLE_LINK** + +────────────────────────────────── docs/api.md ─────────────────────────────────── + +[ORPHAN]  Physical file not listed in navigation. + +[short-content]  Page has only 18 words (minimum 50). + +    1  ❱  <!-- SPDX-FileCopyrightText: 2026 PythonWoods <dev@pythonwoods.dev> --> +    2  │  <!-- SPDX-License-Identifier: Apache-2.0 --> +    3  │   + +[placeholder-text]  Found placeholder text matching pattern: 'todo' + +    4  │  # API Reference +    5  │   +    6  ❱  TODO: write the API documentation. +│  ^^^^ + +───────────────────────────── docs/assets/unused.png ───────────────────────────── + +[ASSET]  File not referenced in any documentation page. + +───────────────────────────────── docs/index.md ────────────────────────────────── + +[FILE_NOT_FOUND]  'non-existent.md' not found in docs + +    14  │  | Check | Trigger | +    15  │  | --- | --- | +    16  ❱  | Links — missing file | [broken link to a page that does not  +exist](non-existent.md) | +    17  │  | Links — dead anchor | [jump to a section that does not  +exist](#non-existent-section) | +    18  │  | Links — path traversal | [escape from docs/](../../../../etc/passwd)  +| + +[PATH_TRAVERSAL]  '../../../../etc/passwd' resolves outside the docs directory + +    16  │  | Links — missing file | [broken link to a page that does not  +exist](non-existent.md) | +    17  │  | Links — dead anchor | [jump to a section that does not  +exist](#non-existent-section) | +    18  ❱  | Links — path traversal | [escape from docs/](../../../../etc/passwd)  +| +    19  │  | Links — **absolute path** | [absolute link to logo](/assets/logo.png) +| +    20  │  | Links — **broken i18n** | [Italian page that does not  +exist](missing.it.md) | + +[ABSOLUTE_PATH]  '/assets/logo.png' uses an absolute path — use a relative  +path (e.g. '../' or './') instead; absolute paths break portability when the site  +is hosted in a subdirectory + +    17  │  | Links — dead anchor | [jump to a section that does not  +exist](#non-existent-section) | +    18  │  | Links — path traversal | [escape from docs/](../../../../etc/passwd)  +| +    19  ❱  | Links — **absolute path** | [absolute link to logo](/assets/logo.png) +| +    20  │  | Links — **broken i18n** | [Italian page that does not  +exist](missing.it.md) | +    21  │  | Links — **UNREACHABLE_LINK** (nav orphan) | [Nav orphan — exists on  +disk, missing from nav](orphan-nav.md) | + +[FILE_NOT_FOUND]  'missing.it.md' not found in docs + +    18  │  | Links — path traversal | [escape from docs/](../../../../etc/passwd)  +| +    19  │  | Links — **absolute path** | [absolute link to logo](/assets/logo.png) +| +    20  ❱  | Links — **broken i18n** | [Italian page that does not  +exist](missing.it.md) | +    21  │  | Links — **UNREACHABLE_LINK** (nav orphan) | [Nav orphan — exists on  +disk, missing from nav](orphan-nav.md) | +    22  │  | Links — **UNREACHABLE_LINK** (private dir) | [Private draft — inside  +`_drafts/`](_drafts/unreleased.md) | + +[UNREACHABLE_LINK]  'orphan-nav.md' resolves to '/orphan-nav/' which exists on +disk but is not listed in the site navigation (UNREACHABLE_LINK) — add it to nav  +in mkdocs.yml or remove the link + +    19  │  | Links — **absolute path** | [absolute link to logo](/assets/logo.png) +| +    20  │  | Links — **broken i18n** | [Italian page that does not  +exist](missing.it.md) | +    21  ❱  | Links — **UNREACHABLE_LINK** (nav orphan) | [Nav orphan — exists on  +disk, missing from nav](orphan-nav.md) | +    22  │  | Links — **UNREACHABLE_LINK** (private dir) | [Private draft — inside  +`_drafts/`](_drafts/unreleased.md) | +    23  │  | Orphans | `api.md` exists on disk but is absent from `nav` | + +[UNREACHABLE_LINK]  '_drafts/unreleased.md' resolves to '/_drafts/unreleased/' +which exists on disk but is not listed in the site navigation (UNREACHABLE_LINK) — +add it to nav in mkdocs.yml or remove the link + +    20  │  | Links — **broken i18n** | [Italian page that does not  +exist](missing.it.md) | +    21  │  | Links — **UNREACHABLE_LINK** (nav orphan) | [Nav orphan — exists on  +disk, missing from nav](orphan-nav.md) | +    22  ❱  | Links — **UNREACHABLE_LINK** (private dir) | [Private draft — inside  +`_drafts/`](_drafts/unreleased.md) | +    23  │  | Orphans | `api.md` exists on disk but is absent from `nav` | +    24  │  | Snippets | `tutorial.md` contains a Python block with a `SyntaxError` +| + +[placeholder-text]  Found placeholder text matching pattern: 'draft' + +    20  │  | Links — **broken i18n** | [Italian page that does not  +exist](missing.it.md) | +    21  │  | Links — **UNREACHABLE_LINK** (nav orphan) | [Nav orphan — exists on  +disk, missing from nav](orphan-nav.md) | +    22  ❱  | Links — **UNREACHABLE_LINK** (private dir) | [Private draft — inside  +`_drafts/`](_drafts/unreleased.md) | +    23  │  | Orphans | `api.md` exists on disk but is absent from `nav` | +    24  │  | Snippets | `tutorial.md` contains a Python block with a `SyntaxError` +| + +[placeholder-text]  Found placeholder text matching pattern: 'placeholder' + +    23  │  | Orphans | `api.md` exists on disk but is absent from `nav` | +    24  │  | Snippets | `tutorial.md` contains a Python block with a `SyntaxError` +| +    25  ❱  | Placeholders | `api.md` has only 18 words and a bare task marker —  +see the file | +│    ^^^^^^^^^^^ +    26  │  | Assets | `assets/unused.png` is on disk but never referenced | +    27  │   + +[placeholder-text]  Found placeholder text matching pattern: 'draft' + +    41  │  Virtual Site Map and detects pages that exist on disk but cannot be  +reached through navigation. +    42  │  `orphan-nav.md` is absent from `mkdocs.yml`'s `nav:` — MkDocs copies it +to `site/` but no +    43  ❱  user can discover it.  `_drafts/unreleased.md` is in a private  +directory — Zensical never +│                           ^^^^^ +    44  │  serves `_`-prefixed paths at all.  Both produce `UNREACHABLE_LINK` with +a Visual Snippet +    45  │  showing the exact source line. + +─────────────────────────────── docs/orphan-nav.md ─────────────────────────────── + +[ORPHAN]  Physical file not listed in navigation. + +──────────────────────────────── docs/tutorial.md ──────────────────────────────── + +[placeholder-text]  Found placeholder text matching pattern: 'placeholder' + +    4  │  # Tutorial +    5  │   +    6  ❱  This tutorial page is intentionally well-formed: it has enough content  +to pass the placeholder +    7  │  check and does not contain any forbidden marker patterns. The only issue +here is the invalid +    8  │  Python code block below. + +[SNIPPET]  SyntaxError in Python snippet — expected ':' + +────────────────────────────────────────────────────────────────────────────────── + +✘ 7 errors⚠ 11 warnings• 6 files with findings + +FAILED: One or more checks failed. + +╰──────────────────────────────────────────────────────────────────────────────────────╯ +Try 'zenzic check --help' for options. + +╭─Zenzic Quality Score─╮ + ✘  32/100  +╰────────────────────────╯ + +Quality Breakdown +╭──────┬────────────────┬────────┬────────┬───────╮ + •  Category      IssuesWeightScore +├──────┼────────────────┼────────┼────────┼───────┤ +links         6   35% 0.00 +orphans       3   20% 0.08 +snippets      1   20% 0.16 +placeholders  7   15% 0.00 +assets        1   10% 0.08 +╰──────┴────────────────┴────────┴────────┴───────╯ diff --git a/docs/assets/social/social-card-light.png b/docs/assets/social/social-card-light.png new file mode 100644 index 0000000000000000000000000000000000000000..c78a14d687b6b9d1f5dc85f1fc34a6bdfd0e1b23 GIT binary patch literal 19968 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 a|Jk=lE))!%bMPl + +SPDX-License-Identifier: Apache-2.0 diff --git a/docs/assets/social/social-card-light.svg b/docs/assets/social/social-card-light.svg new file mode 100644 index 0000000..a7f9820 --- /dev/null +++ b/docs/assets/social/social-card-light.svg @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Zenzic + + ENGINEERING-GRADE DOCUMENTATION LINTER + diff --git a/docs/assets/social/social-card-light.svg.license b/docs/assets/social/social-card-light.svg.license new file mode 100644 index 0000000..73c93a8 --- /dev/null +++ b/docs/assets/social/social-card-light.svg.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 PythonWoods + +SPDX-License-Identifier: Apache-2.0 diff --git a/examples/readme-hero/docs/index.md b/examples/readme-hero/docs/index.md new file mode 100644 index 0000000..bda7cca --- /dev/null +++ b/examples/readme-hero/docs/index.md @@ -0,0 +1,15 @@ + + + +# Welcome to My Project + +This is the landing page for the project documentation. +It covers installation, configuration, and everyday usage. + +## Quick start + +Follow the [setup guide](setup.md) to get started in under five minutes. + +## API reference + +Browse the [API docs](api-reference.md) for the full public interface. diff --git a/examples/readme-hero/docs/setup.md b/examples/readme-hero/docs/setup.md new file mode 100644 index 0000000..d676f25 --- /dev/null +++ b/examples/readme-hero/docs/setup.md @@ -0,0 +1,23 @@ + + + +# Setup + +Install the library and verify the installation with the snippet below. + +## Installation + +```bash +pip install my-project +``` + +## Verify + +```python +from my_project import main + +if __name__ == "__main__" + main() +``` + +The command above should print the current version to stdout. diff --git a/examples/readme-hero/mkdocs.yml b/examples/readme-hero/mkdocs.yml new file mode 100644 index 0000000..4a71101 --- /dev/null +++ b/examples/readme-hero/mkdocs.yml @@ -0,0 +1,9 @@ +# SPDX-FileCopyrightText: 2026 PythonWoods +# SPDX-License-Identifier: Apache-2.0 +# mkdocs.yml — minimal nav listing both pages. + +site_name: README Hero Example + +nav: + - Home: index.md + - Setup: setup.md diff --git a/examples/readme-hero/zenzic.toml b/examples/readme-hero/zenzic.toml new file mode 100644 index 0000000..93064a6 --- /dev/null +++ b/examples/readme-hero/zenzic.toml @@ -0,0 +1,11 @@ +# SPDX-FileCopyrightText: 2026 PythonWoods +# SPDX-License-Identifier: Apache-2.0 +# zenzic.toml — minimal example used for the README hero screenshot. +# +# Two intentional errors: one broken link and one snippet syntax error. +# Run `zenzic check all` from this directory to see the compact report. + +docs_dir = "docs" + +[build_context] +engine = "mkdocs" diff --git a/scripts/generate_docs_assets.py b/scripts/generate_docs_assets.py index 62644f8..a20157d 100644 --- a/scripts/generate_docs_assets.py +++ b/scripts/generate_docs_assets.py @@ -180,6 +180,49 @@ def generate_hero() -> Path: return out +# ── Asset 1b: Hero (compact, from readme-hero example) ──────────────────────── + +README_HERO = Path(__file__).parent.parent / "examples" / "readme-hero" + + +def generate_hero_crop() -> Path: + """Compact ``zenzic check all`` on readme-hero → screenshot-hero.svg. + + Uses the lightweight *examples/readme-hero* project (1 error + 3 warnings) + to produce a naturally compact SVG that fits the README hero area without + any viewBox hacking. + """ + out = OUT_DIR / "screenshot-hero.svg" + + console = Console(highlight=False, record=True, width=88) + + config, _ = ZenzicConfig.load(README_HERO) + docs_root = (README_HERO / config.docs_dir).resolve() + + console.print(f"[dim]{emoji('arrow')}[/] [bold]zenzic check all[/bold]") + console.print() + + t0 = time.monotonic() + results = _collect_all_results(README_HERO, config, strict=True) + elapsed = time.monotonic() - t0 + + all_findings = _to_findings(results, docs_root) + reporter = SentinelReporter(console, docs_root) + docs_count, assets_count = _docs_assets_count(docs_root, README_HERO) + reporter.render( + all_findings, + version=__version__, + elapsed=elapsed, + docs_count=docs_count, + assets_count=assets_count, + engine=config.build_context.engine if hasattr(config, "build_context") else "auto", + ) + + console.save_svg(str(out), title="zenzic check all") + _cleanup_build_artefact(README_HERO) + return out + + # ── Asset 2: Quality score standalone ───────────────────────────────────────── @@ -282,5 +325,8 @@ def _cleanup_build_artefact(project_root: Path) -> None: hero = generate_hero() print(f"Saved → {hero.relative_to(root)}") + hero_crop = generate_hero_crop() + print(f"Saved → {hero_crop.relative_to(root)}") + score = generate_score() print(f"Saved → {score.relative_to(root)}") diff --git a/zenzic.toml b/zenzic.toml index 1531890..102d198 100644 --- a/zenzic.toml +++ b/zenzic.toml @@ -25,7 +25,12 @@ excluded_assets = [ "assets/brand/png/zenzic-wordmark.png", # social card — SVG source + PNG referenced by main.html (og:image, twitter:image) "assets/social/social-card.svg", + "assets/social/social-card-light.svg", "assets/social/social-card.png", + "assets/social/social-card-light.png", + # screenshots referenced by README.md (outside docs/) — not by any docs page + "assets/screenshots/screenshot-hero.svg", + "assets/screenshots/screenshot-score.svg", # brand-kit.zip — build artifact linked via plain [text](url) in brand-kit.md; # the asset scanner only tracks image references so this would always appear unused. "assets/brand-kit.zip", From 525453ad2041b89544fa041ba721b73fe29a15c0 Mon Sep 17 00:00:00 2001 From: PythonWoods-Dev Date: Sat, 4 Apr 2026 14:58:58 +0200 Subject: [PATCH 2/3] build: brand-kit includes social/ directory - _build_brand_kit_zip() bundles docs/assets/social/ alongside brand/ --- noxfile.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/noxfile.py b/noxfile.py index 7dbbcf1..b78d91d 100644 --- a/noxfile.py +++ b/noxfile.py @@ -159,14 +159,16 @@ def docs_serve(session: nox.Session) -> None: def _build_brand_kit_zip() -> None: - """Generate docs/assets/brand-kit.zip from docs/assets/brand/ (build artifact).""" - src = Path("docs/assets/brand") - out = Path("docs/assets/brand-kit.zip") + """Generate docs/assets/brand-kit.zip from docs/assets/brand/ + social/.""" + base = Path("docs/assets") + out = base / "brand-kit.zip" out.parent.mkdir(parents=True, exist_ok=True) with zipfile.ZipFile(out, "w", zipfile.ZIP_DEFLATED) as zf: - for file in sorted(src.rglob("*")): - if file.is_file(): - zf.write(file, file.relative_to(src.parent)) + for src_dir in ("brand", "social"): + src = base / src_dir + for file in sorted(src.rglob("*")): + if file.is_file() and not file.name.endswith(".license"): + zf.write(file, file.relative_to(base)) @nox.session(python="3.11") From aac5494f72bca2266f689d6939d915d358a9c226 Mon Sep 17 00:00:00 2001 From: PythonWoods-Dev Date: Sat, 4 Apr 2026 14:59:24 +0200 Subject: [PATCH 3/3] =?UTF-8?q?docs:=20v0.5.0a3=20sync=20=E2=80=94=20--pre?= =?UTF-8?q?=20flag,=20hero=20fix,=20Highlights=20update?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add --pre to all install commands (14 files, EN + IT) - Remove duplicate hero screenshot from READMEs - Update Highlights from v0.5.0a1 to v0.5.0a3 - Hero image now from compact readme-hero example --- README.it.md | 63 ++++++++++++++++---------- README.md | 80 ++++++++++++++++++---------------- docs/ci-cd.md | 14 +++--- docs/configuration/index.md | 2 +- docs/guide/index.md | 2 +- docs/index.md | 2 +- docs/it/ci-cd.md | 14 +++--- docs/it/configuration/index.md | 2 +- docs/it/guide/index.md | 2 +- docs/it/index.md | 2 +- docs/it/usage/index.md | 16 +++---- docs/usage/index.md | 16 +++---- 12 files changed, 119 insertions(+), 96 deletions(-) diff --git a/README.it.md b/README.it.md index 46ff71e..6f1a204 100644 --- a/README.it.md +++ b/README.it.md @@ -40,7 +40,7 @@ SPDX-License-Identifier: Apache-2.0

- Zenzic Sentinel — linter di documentazione + Zenzic Sentinel — linter di documentazione

--- @@ -64,24 +64,31 @@ gli altri strumenti citati sono progetti di terze parti. --- -## 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`. +## v0.5.0a3 — La Sentinella + +- **Inizializzazione Intelligente**: `zenzic init` rileva `pyproject.toml` e offre di + incorporare la configurazione come `[tool.zenzic]` invece di creare un `zenzic.toml` + separato. Usa `--pyproject` per saltare il prompt. Auto-detection dell'engine in + entrambe le modalità. +- **Sentinel UI**: banner Indigo monolitico, gutter traceback con spaziatura a 2 spazi + (`│ 16 ❱`), sottolineature caret chirurgiche e respiro verticale tra i finding. +- **Target Agnostico**: `zenzic check all README.md` o `zenzic check all content/` + limita l'audit a un singolo file o directory. `VanillaAdapter` selezionato + automaticamente per target fuori da docs. +- **Plugin SDK**: `zenzic init --plugin ` scaffolda un pacchetto regole + pronto all'uso. Namespace pubblico `zenzic.rules` stabile — `BaseRule`, + `RuleFinding`, `CustomRule`, `Violation`, `Severity`. +- **Hybrid Adaptive Engine**: `scan_docs_references` seleziona esecuzione sequenziale + o parallela automaticamente in base alla dimensione del repository (soglia: 50 file). + Validazione ancore deterministica a due fasi elimina falsi positivi da race condition. +- **Z001/Z002 Split**: link rotti (Z001 errore) vs link a pagine orfane (Z002 warning). + Senza `--strict`, i warning orfani non bloccano la build. +- **Mutation-tested**: 86.7% mutation score (242/279 killed su `rules.py`). + 706 test, Hypothesis property-based testing con profili tiered. +- **Config `pyproject.toml`**: incorpora la configurazione Zenzic in `[tool.zenzic]` + quando `zenzic.toml` è assente. `zenzic.toml` vince sempre se entrambi esistono. +- **Zenzic Shield**: rilevamento credenziali (7 famiglie) + protezione path traversal. + Codice di uscita 2 riservato per eventi di sicurezza. --- @@ -146,19 +153,19 @@ zenzic check all --engine hugo ```bash # Esecuzione una-tantum senza installazione -uvx zenzic check all +uvx --pre zenzic check all # Strumento globale disponibile in qualsiasi progetto -uv tool install zenzic +uv tool install --pre zenzic # Dipendenza dev del progetto — versione fissata in uv.lock -uv add --dev zenzic +uv add --dev --pre zenzic ``` ### Con `pip` ```bash -pip install zenzic +pip install --pre zenzic ``` ### Lean e Agnostico per Design @@ -291,6 +298,16 @@ non segnalare mai i file tradotti come orfani. --- +## Visual Tour + +L'audit completo della Sentinella: banner, contesto gutter, sottolineature caret e punteggio qualità. + +

+ Zenzic Sentinel — output completo con punteggio qualità +

+ +--- + ## Contribuire Bug report, miglioramenti alla documentazione e pull request sono benvenuti. Prima di iniziare: diff --git a/README.md b/README.md index 25bce42..5203f8f 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ SPDX-License-Identifier: Apache-2.0

- Zenzic Sentinel — engineering-grade documentation linter + Zenzic Sentinel — engineering-grade documentation linter

--- @@ -63,30 +63,31 @@ referenced ecosystem tools are third-party projects. --- -## 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. +## v0.5.0a3 Highlights — The Sentinel + +- **Smart Initialization**: `zenzic init` detects `pyproject.toml` and offers to + embed config as `[tool.zenzic]` instead of a standalone `zenzic.toml`. Use + `--pyproject` to skip the prompt. Engine auto-detection in both modes. +- **Sentinel UI**: monolithic Indigo banner, traceback gutter with 2-space + padding (`│ 16 ❱`), surgical caret underlines, and vertical breathing + between findings. +- **Agnostic Target**: `zenzic check all README.md` or `zenzic check all content/` + scopes audits to a single file or directory. `VanillaAdapter` auto-selected for + out-of-docs targets. +- **Plugin SDK**: `zenzic init --plugin ` scaffolds a ready-to-edit rule + package. `zenzic.rules` public namespace is stable — `BaseRule`, `RuleFinding`, + `CustomRule`, `Violation`, `Severity`. +- **Hybrid Adaptive Engine**: `scan_docs_references` selects sequential or parallel + execution automatically based on repository size (threshold: 50 files). + Deterministic two-phase anchor validation eliminates race-induced false positives. +- **Z001/Z002 Split**: broken links (Z001 error) vs orphan-page links (Z002 warning). + Without `--strict`, orphan warnings don't block the build. +- **Mutation-tested**: 86.7% mutation score (242/279 killed on `rules.py`). + 706 tests, Hypothesis property-based testing with tiered profiles. +- **`pyproject.toml` config**: embed Zenzic config in `[tool.zenzic]` when + `zenzic.toml` is absent. `zenzic.toml` always wins if both exist. +- **Zenzic Shield**: credential detection (7 secret families) + path traversal + protection. Exit code 2 reserved for security events. --- @@ -106,10 +107,6 @@ Zenzic provides an extensive, engineering-grade documentation portal: --- -![Zenzic CLI Output][screenshot] - ---- - ## What Zenzic checks | Check | CLI command | What it detects | @@ -331,25 +328,25 @@ fallback — for all documentation systems. ```bash # Zero-install, one-shot audit -uvx zenzic check all +uvx --pre zenzic check all # Global CLI tool — available in any project -uv tool install zenzic +uv tool install --pre zenzic # Project dev dependency — version-pinned in uv.lock -uv add --dev zenzic +uv add --dev --pre zenzic ``` ### With `pip` ```bash # Global install (consider a virtual environment) -pip install zenzic +pip install --pre zenzic # Inside a virtual environment (recommended) python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate -pip install zenzic +pip install --pre zenzic ``` ### Lean & Agnostic by Design @@ -509,10 +506,10 @@ silently collapsed into a generic "file not found". ```yaml - name: Lint documentation - run: uvx zenzic check all + run: uvx --pre zenzic check all - name: Check references and run Shield - run: uvx zenzic check references + run: uvx --pre zenzic check references ``` Full workflow: [`.github/workflows/zenzic.yml`][ci-workflow] @@ -569,6 +566,16 @@ nox -s preflight # zenzic check all (self-check) --- +## Visual Tour + +The full Sentinel audit: banner, gutter context, caret underlines, and quality score breakdown. + +

+ Zenzic Sentinel — full audit output with quality score +

+ +--- + ## Contributing We welcome bug reports, documentation improvements, and pull requests. Before you start: @@ -606,7 +613,6 @@ Apache-2.0 — see [LICENSE][license]. [docs-cicd]: https://zenzic.pythonwoods.dev/ci-cd/ [docs-arch]: https://zenzic.pythonwoods.dev/architecture/ [docs-contributing]: https://zenzic.pythonwoods.dev/community/contribute/ -[screenshot]: docs/assets/screenshots/screenshot.svg [ci-workflow]: .github/workflows/zenzic.yml [contributing]: CONTRIBUTING.md [license]: LICENSE diff --git a/docs/ci-cd.md b/docs/ci-cd.md index 4648f36..9ba6e5f 100644 --- a/docs/ci-cd.md +++ b/docs/ci-cd.md @@ -85,10 +85,10 @@ The simplest integration — fails the build on any documentation error. - uses: actions/checkout@v6 - name: Lint documentation - run: uvx zenzic check all --strict + run: uvx --pre zenzic check all --strict - name: Check references and run Shield - run: uvx zenzic check references + run: uvx --pre zenzic check references ``` === "astral-sh/setup-uv (pinned version)" @@ -118,10 +118,10 @@ The simplest integration — fails the build on any documentation error. enable-cache: true - name: Lint documentation - run: uvx zenzic check all --strict + run: uvx --pre zenzic check all --strict - name: Check references and run Shield - run: uvx zenzic check references + run: uvx --pre zenzic check references ``` The `enable-cache: true` option caches the uv tool cache across runs, @@ -152,7 +152,7 @@ jobs: - name: 🛡️ Run Zenzic Score id: zenzic_step run: | - uvx zenzic score --save # threshold read from fail_under in zenzic.toml + uvx --pre zenzic score --save # threshold read from fail_under in zenzic.toml SCORE=$(jq '.score' .zenzic-score.json) echo "SCORE=$SCORE" >> "$GITHUB_OUTPUT" @@ -191,8 +191,8 @@ jobs: ```yaml - name: Detect score regression run: | - uvx zenzic score --save # update snapshot - uvx zenzic diff --threshold 5 # fail if score drops > 5 points + uvx --pre zenzic score --save # update snapshot + uvx --pre zenzic diff --threshold 5 # fail if score drops > 5 points ``` Combine with branch protection rules to block merges that degrade documentation quality. diff --git a/docs/configuration/index.md b/docs/configuration/index.md index 8db2243..2004a84 100644 --- a/docs/configuration/index.md +++ b/docs/configuration/index.md @@ -11,7 +11,7 @@ Zenzic reads a single `zenzic.toml` file at the repository root. All fields are !!! tip "Zero configuration" - Most projects need no `zenzic.toml` at all. Run `uvx zenzic check all` — if it passes, + Most projects need no `zenzic.toml` at all. Run `uvx --pre zenzic check all` — if it passes, you're done. Only add configuration when you need to customise specific behaviour. --- diff --git a/docs/guide/index.md b/docs/guide/index.md index da8171e..bfa3179 100644 --- a/docs/guide/index.md +++ b/docs/guide/index.md @@ -14,7 +14,7 @@ Zenzic is a **CI-first documentation linter** for any Markdown-based project. It !!! tip "Zero install — run it now" ```bash - uvx zenzic check all + uvx --pre zenzic check all ``` `uvx` downloads and runs Zenzic in a throwaway environment. Nothing is installed on your system. diff --git a/docs/index.md b/docs/index.md index 5274c78..a589413 100644 --- a/docs/index.md +++ b/docs/index.md @@ -27,7 +27,7 @@ Catch broken links, orphan pages, and leaked credentials — before your users d
-![Zenzic terminal output showing six checks passing](assets/screenshots/screenshot.svg){ .zz-hero__screenshot } +![Zenzic Sentinel — full audit output with quality score](assets/screenshots/screenshot.svg){ .zz-hero__screenshot }
diff --git a/docs/it/ci-cd.md b/docs/it/ci-cd.md index d901d45..09af2bd 100644 --- a/docs/it/ci-cd.md +++ b/docs/it/ci-cd.md @@ -82,10 +82,10 @@ L'integrazione più semplice — blocca la build a qualsiasi errore di documenta - uses: actions/checkout@v6 - name: Lint documentazione - run: uvx zenzic check all --strict + run: uvx --pre zenzic check all --strict - name: Controllo riferimenti e Shield - run: uvx zenzic check references + run: uvx --pre zenzic check references ``` === "astral-sh/setup-uv (versione fissata)" @@ -115,10 +115,10 @@ L'integrazione più semplice — blocca la build a qualsiasi errore di documenta enable-cache: true - name: Lint documentazione - run: uvx zenzic check all --strict + run: uvx --pre zenzic check all --strict - name: Controllo riferimenti e Shield - run: uvx zenzic check references + run: uvx --pre zenzic check references ``` L'opzione `enable-cache: true` mantiene la cache degli strumenti uv tra @@ -149,7 +149,7 @@ jobs: - name: 🛡️ Calcola Zenzic Score id: zenzic_step run: | - uvx zenzic score --save # soglia letta da fail_under in zenzic.toml + uvx --pre zenzic score --save # soglia letta da fail_under in zenzic.toml SCORE=$(jq '.score' .zenzic-score.json) echo "SCORE=$SCORE" >> "$GITHUB_OUTPUT" @@ -188,8 +188,8 @@ jobs: ```yaml - name: Rileva regressione del punteggio run: | - uvx zenzic score --save # aggiorna snapshot - uvx zenzic diff --threshold 5 # fallisce se il punteggio scende > 5 punti + uvx --pre zenzic score --save # aggiorna snapshot + uvx --pre zenzic diff --threshold 5 # fallisce se il punteggio scende > 5 punti ``` Combina con le branch protection rules per bloccare i merge che degradano la qualità della documentazione. diff --git a/docs/it/configuration/index.md b/docs/it/configuration/index.md index 0c6e075..1e71400 100644 --- a/docs/it/configuration/index.md +++ b/docs/it/configuration/index.md @@ -13,7 +13,7 @@ opzionali — Zenzic funziona senza alcun file di configurazione. !!! tip "Configurazione zero" La maggior parte dei progetti non ha bisogno di alcun `zenzic.toml`. Esegui - `uvx zenzic check all` — se passa, hai finito. Aggiungi configurazione solo quando + `uvx --pre zenzic check all` — se passa, hai finito. Aggiungi configurazione solo quando devi personalizzare un comportamento specifico. --- diff --git a/docs/it/guide/index.md b/docs/it/guide/index.md index 41c3016..f84a7aa 100644 --- a/docs/it/guide/index.md +++ b/docs/it/guide/index.md @@ -14,7 +14,7 @@ Zenzic è un **linter di documentazione CI-first** per qualsiasi progetto basato !!! tip "Zero installazione — eseguilo ora" ```bash - uvx zenzic check all + uvx --pre zenzic check all ``` `uvx` scarica ed esegue Zenzic in un ambiente temporaneo. Nessuna installazione richiesta. diff --git a/docs/it/index.md b/docs/it/index.md index aff9b67..7f3c9dd 100644 --- a/docs/it/index.md +++ b/docs/it/index.md @@ -27,7 +27,7 @@ Intercetta link non validi, pagine orfane e credenziali esposte — prima dei tu
-![Output terminale Zenzic con sei controlli superati](assets/screenshots/screenshot.svg){ .zz-hero__screenshot } +![Zenzic Sentinel — output completo con punteggio qualità](assets/screenshots/screenshot.svg){ .zz-hero__screenshot }
diff --git a/docs/it/usage/index.md b/docs/it/usage/index.md index cd88be1..1459569 100644 --- a/docs/it/usage/index.md +++ b/docs/it/usage/index.md @@ -24,7 +24,7 @@ Il repository include fixture mantenuti e allineati ai contratti documentati: !!! tip "Vuoi eseguirlo subito?" ```bash - uvx zenzic check all + uvx --pre zenzic check all ``` Nessuna installazione richiesta. `uvx` scarica ed esegue Zenzic in un ambiente temporaneo. @@ -38,7 +38,7 @@ Il repository include fixture mantenuti e allineati ai contratti documentati: === ":simple-astral: uv" ```bash - uvx zenzic check all + uvx --pre zenzic check all ``` `uvx` risolve ed esegue Zenzic da PyPI in un ambiente temporaneo. Nulla viene installato sul @@ -48,7 +48,7 @@ Il repository include fixture mantenuti e allineati ai contratti documentati: === ":simple-pypi: pip" ```bash - pip install zenzic + pip install --pre zenzic zenzic check all ``` @@ -60,7 +60,7 @@ Il repository include fixture mantenuti e allineati ai contratti documentati: === ":simple-astral: uv" ```bash - uv tool install zenzic + uv tool install --pre zenzic zenzic check all ``` @@ -72,7 +72,7 @@ Il repository include fixture mantenuti e allineati ai contratti documentati: ```bash python -m venv ~/.local/zenzic-env source ~/.local/zenzic-env/bin/activate # Windows: .venv\Scripts\activate - pip install zenzic + pip install --pre zenzic ``` Installa in un virtual environment dedicato, poi aggiungi la directory `bin/` al `PATH`. @@ -82,7 +82,7 @@ Il repository include fixture mantenuti e allineati ai contratti documentati: === ":simple-astral: uv" ```bash - uv add --dev zenzic + uv add --dev --pre zenzic uv run zenzic check all ``` @@ -95,7 +95,7 @@ Il repository include fixture mantenuti e allineati ai contratti documentati: ```bash python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate - pip install zenzic + pip install --pre zenzic zenzic check all ``` @@ -109,7 +109,7 @@ Questo significa che **non è necessario installare** MkDocs, Material for MkDoc ```bash # Fare il lint di qualsiasi progetto MkDocs — nessun extra necessario -uvx zenzic check all +uvx --pre zenzic check all ``` !!! note "Adapter di terze parti" diff --git a/docs/usage/index.md b/docs/usage/index.md index f4da4b2..9c613a9 100644 --- a/docs/usage/index.md +++ b/docs/usage/index.md @@ -24,7 +24,7 @@ The repository ships maintained fixtures that mirror the documented contracts: !!! tip "Just want to run it now?" ```bash - uvx zenzic check all + uvx --pre zenzic check all ``` No installation required. `uvx` downloads and runs Zenzic in a throwaway environment. @@ -38,7 +38,7 @@ The repository ships maintained fixtures that mirror the documented contracts: === ":simple-astral: uv" ```bash - uvx zenzic check all + uvx --pre zenzic check all ``` `uvx` resolves and runs Zenzic from PyPI in a throwaway environment. Nothing is installed on @@ -48,7 +48,7 @@ The repository ships maintained fixtures that mirror the documented contracts: === ":simple-pypi: pip" ```bash - pip install zenzic + pip install --pre zenzic zenzic check all ``` @@ -60,7 +60,7 @@ The repository ships maintained fixtures that mirror the documented contracts: === ":simple-astral: uv" ```bash - uv tool install zenzic + uv tool install --pre zenzic zenzic check all ``` @@ -72,7 +72,7 @@ The repository ships maintained fixtures that mirror the documented contracts: ```bash python -m venv ~/.local/zenzic-env source ~/.local/zenzic-env/bin/activate # Windows: .venv\Scripts\activate - pip install zenzic + pip install --pre zenzic ``` Install into a dedicated virtual environment, then add the `bin/` directory to your `PATH`. @@ -82,7 +82,7 @@ The repository ships maintained fixtures that mirror the documented contracts: === ":simple-astral: uv" ```bash - uv add --dev zenzic + uv add --dev --pre zenzic uv run zenzic check all ``` @@ -95,7 +95,7 @@ The repository ships maintained fixtures that mirror the documented contracts: ```bash python -m venv .venv source .venv/bin/activate # Windows: .venv\Scripts\activate - pip install zenzic + pip install --pre zenzic zenzic check all ``` @@ -109,7 +109,7 @@ This means you **do not need to install** MkDocs, Material for MkDocs, or any ot ```bash # Lint any MkDocs project — no extras needed -uvx zenzic check all +uvx --pre zenzic check all ``` !!! note "Third-party engine adapters"