diff --git a/docs/about/brand-kit.md b/docs/about/brand-kit.md index ecb87df..dbbb72a 100644 --- a/docs/about/brand-kit.md +++ b/docs/about/brand-kit.md @@ -2,6 +2,9 @@ icon: lucide/palette --- + + + # Brand Ecosystem {#brand-ecosystem} Welcome to the Zenzic Brand Ecosystem. This is not just a style guide; it is the constitution of how Zenzic is presented across open-source communities, CI/CD integrations, and enterprise deployment landscapes. diff --git a/docs/adr/003-discovery-logic.md b/docs/adr/003-discovery-logic.md index 5a7fa09..847ffce 100644 --- a/docs/adr/003-discovery-logic.md +++ b/docs/adr/003-discovery-logic.md @@ -1,3 +1,6 @@ + + + # ADR 003: Root Discovery Protocol (RDP) {#adr-003-root-discovery-protocol} **Status:** Active (amended by ZRT-005, 2026-04-08) diff --git a/docs/assets/screenshots/screenshot-assets.svg b/docs/assets/screenshots/screenshot-assets.svg new file mode 100644 index 0000000..b514ff0 --- /dev/null +++ b/docs/assets/screenshots/screenshot-assets.svg @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + zenzic check assets + + + + + + + + + + zenzic check assets + + +╭─────────────────────────── 🛡  ZENZIC SENTINEL  v0.5.0a5 ───────────────────────────╮ + +mkdocs • 12 files (6 docs, 6 assets) • 0.0s + +───────────────────────────── docs/assets/unused.png ───────────────────────────── + +[ASSET]  File not referenced in any documentation page. + +────────────────────────────────────────────────────────────────────────────────── + +⚠ 1 warning• 1 file with findings + +FAILED: One or more checks failed. + +╰──────────────────────────────────────────────────────────────────────────────────────╯ +Try 'zenzic check --help' for options. + + + + diff --git a/docs/assets/screenshots/screenshot-assets.svg.license b/docs/assets/screenshots/screenshot-assets.svg.license new file mode 100644 index 0000000..73c93a8 --- /dev/null +++ b/docs/assets/screenshots/screenshot-assets.svg.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 PythonWoods + +SPDX-License-Identifier: Apache-2.0 diff --git a/docs/assets/screenshots/screenshot-hero.svg b/docs/assets/screenshots/screenshot-hero.svg index 04ecfed..0fe612e 100644 --- a/docs/assets/screenshots/screenshot-hero.svg +++ b/docs/assets/screenshots/screenshot-hero.svg @@ -1,4 +1,4 @@ - + - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - zenzic check all + zenzic check all --strict - - - - zenzic check all - - -╭─────────────────────────── 🛡  ZENZIC SENTINEL  v0.5.0a5 ───────────────────────────╮ - -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. + + + + zenzic check all --strict + + +╭────────────────────────────  SECURITY BREACH DETECTED  ────────────────────────────╮ + +  ✘ Finding:    Secret detected (aws-access-key) — rotate immediately. +  ✘ Location:docs/secrets.md:18 +  ✘ Credential: AKIA************MPLE  + +Action: Rotate this credential immediately and purge it from the repository  +history. + +╰──────────────────────────────────────────────────────────────────────────────────────╯ + +╭─────────────────────────── 🛡  ZENZIC SENTINEL  v0.5.0a5 ───────────────────────────╮ + +mkdocs • 4 files (4 docs, 0 assets) • 0.0s + +───────────────────────────────── docs/deploy.md ───────────────────────────────── + +[PATH_TRAVERSAL_SUSPICIOUS]  '../../../../etc/shadow' resolves outside the  +docs directory + +    21  │  Review the host configuration before deployment: +    22  │   +    23  ❱  [Host Config](../../../../etc/shadow) +│  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +    24  │   +    25  │  Verify the target environment against the security checklist before + +───────────────────────────────── docs/index.md ────────────────────────────────── + +[FILE_NOT_FOUND]  'api-reference.md' not found in docs + +    12  │  | :--- | :--- | +    13  │  | Deployment | [Deploy guide](deploy.md) | +    14  ❱  | API docs | [Full API reference](api-reference.md) | +│               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +────────────────────────────────────────────────────────────────────────────────── + +✘ 1 security incident✘ 1 error• 2 files with findings + +FAILED: One or more checks failed. + +╰──────────────────────────────────────────────────────────────────────────────────────╯ +Try 'zenzic check --help' for options. diff --git a/docs/assets/screenshots/screenshot-links.svg b/docs/assets/screenshots/screenshot-links.svg new file mode 100644 index 0000000..d680e02 --- /dev/null +++ b/docs/assets/screenshots/screenshot-links.svg @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + zenzic check links + + + + + + + + + + zenzic check links + + +╭─────────────────────────── 🛡  ZENZIC SENTINEL  v0.5.0a5 ───────────────────────────╮ + +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. +│             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +────────────────────────────────────────────────────────────────────────────────── + +✘ 1 error• 1 file with findings + +FAILED: One or more checks failed. + +╰──────────────────────────────────────────────────────────────────────────────────────╯ +Try 'zenzic check --help' for options. + + + + diff --git a/docs/assets/screenshots/screenshot-links.svg.license b/docs/assets/screenshots/screenshot-links.svg.license new file mode 100644 index 0000000..73c93a8 --- /dev/null +++ b/docs/assets/screenshots/screenshot-links.svg.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 PythonWoods + +SPDX-License-Identifier: Apache-2.0 diff --git a/docs/assets/screenshots/screenshot-orphans.svg b/docs/assets/screenshots/screenshot-orphans.svg new file mode 100644 index 0000000..4d843ef --- /dev/null +++ b/docs/assets/screenshots/screenshot-orphans.svg @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + zenzic check orphans + + + + + + + + + + zenzic check orphans + + +╭─────────────────────────── 🛡  ZENZIC SENTINEL  v0.5.0a5 ───────────────────────────╮ + +mkdocs • 12 files (6 docs, 6 assets) • 0.0s + +─────────────────────────── docs/_drafts/unreleased.md ─────────────────────────── + +[ORPHAN]  Physical file not listed in navigation. + +────────────────────────────────── docs/api.md ─────────────────────────────────── + +[ORPHAN]  Physical file not listed in navigation. + +─────────────────────────────── docs/orphan-nav.md ─────────────────────────────── + +[ORPHAN]  Physical file not listed in navigation. + +────────────────────────────────────────────────────────────────────────────────── + +⚠ 3 warnings• 3 files with findings + +FAILED: One or more checks failed. + +╰──────────────────────────────────────────────────────────────────────────────────────╯ +Try 'zenzic check --help' for options. + + + + diff --git a/docs/assets/screenshots/screenshot-orphans.svg.license b/docs/assets/screenshots/screenshot-orphans.svg.license new file mode 100644 index 0000000..73c93a8 --- /dev/null +++ b/docs/assets/screenshots/screenshot-orphans.svg.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 PythonWoods + +SPDX-License-Identifier: Apache-2.0 diff --git a/docs/assets/screenshots/screenshot-placeholders.svg b/docs/assets/screenshots/screenshot-placeholders.svg new file mode 100644 index 0000000..3f8eb1e --- /dev/null +++ b/docs/assets/screenshots/screenshot-placeholders.svg @@ -0,0 +1,434 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + zenzic check placeholders + + + + + + + + + + zenzic check placeholders + + +╭─────────────────────────── 🛡  ZENZIC SENTINEL  v0.5.0a5 ───────────────────────────╮ + +mkdocs • 12 files (6 docs, 6 assets) • 0.0s + +─────────────────────────── docs/_drafts/unreleased.md ─────────────────────────── + +[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 ─────────────────────────────────── + +[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/index.md ────────────────────────────────── + +[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/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. + +────────────────────────────────────────────────────────────────────────────────── + +⚠ 7 warnings• 4 files with findings + +FAILED: One or more checks failed. + +╰──────────────────────────────────────────────────────────────────────────────────────╯ +Try 'zenzic check --help' for options. + + + + diff --git a/docs/assets/screenshots/screenshot-placeholders.svg.license b/docs/assets/screenshots/screenshot-placeholders.svg.license new file mode 100644 index 0000000..73c93a8 --- /dev/null +++ b/docs/assets/screenshots/screenshot-placeholders.svg.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 PythonWoods + +SPDX-License-Identifier: Apache-2.0 diff --git a/docs/assets/screenshots/screenshot-snippets.svg b/docs/assets/screenshots/screenshot-snippets.svg new file mode 100644 index 0000000..69c8770 --- /dev/null +++ b/docs/assets/screenshots/screenshot-snippets.svg @@ -0,0 +1,137 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + zenzic check snippets + + + + + + + + + + zenzic check snippets + + +╭─────────────────────────── 🛡  ZENZIC SENTINEL  v0.5.0a5 ───────────────────────────╮ + +mkdocs • 12 files (6 docs, 6 assets) • 0.0s + +──────────────────────────────── docs/tutorial.md ──────────────────────────────── + +[SNIPPET]  SyntaxError in Python snippet — expected ':' + +────────────────────────────────────────────────────────────────────────────────── + +✘ 1 error• 1 file with findings + +FAILED: One or more checks failed. + +╰──────────────────────────────────────────────────────────────────────────────────────╯ +Try 'zenzic check --help' for options. + + + + diff --git a/docs/assets/screenshots/screenshot-snippets.svg.license b/docs/assets/screenshots/screenshot-snippets.svg.license new file mode 100644 index 0000000..73c93a8 --- /dev/null +++ b/docs/assets/screenshots/screenshot-snippets.svg.license @@ -0,0 +1,3 @@ +SPDX-FileCopyrightText: 2026 PythonWoods + +SPDX-License-Identifier: Apache-2.0 diff --git a/docs/checks.md b/docs/checks.md index eba233b..17242a0 100644 --- a/docs/checks.md +++ b/docs/checks.md @@ -98,18 +98,6 @@ extracted from the source file. A link to a non-existent heading slug is reporte __Why CLI:__ the native link extractor operates on raw Markdown source files. Source-level analysis gives reproducible output independent of the build driver. -!!! example "Sentinel Output — broken link" - - ```text - docs/index.md - ✘ 12: [FILE_NOT_FOUND] 'setup.md' not found in docs - │ - 12 │ Read the [setup guide](setup.md) for initial configuration. - │ - - ✘ 1 error • 1 file with findings - ``` - ### Violation codes The link check uses the Virtual Site Map (VSM) to distinguish between two categories of link failure: @@ -123,32 +111,23 @@ The link check uses the Virtual Site Map (VSM) to distinguish between two catego __Why orphan links matter:__ a link to an orphan page _works_ at the filesystem level — the file exists and the build engine may serve it. But the page is invisible in the nav tree, creating a fragmented user experience. Readers who follow the link land on a page with no sidebar context and no way to navigate back. `Z002` catches this anti-pattern. -!!! example "Sentinel Output — orphan link" - - ```text - docs/guide.md - ⚠ 18: [UNREACHABLE_LINK] 'drafts/experiment.md' not in site navigation - │ - 18 │ See [experiment](drafts/experiment.md) for details. - │ - - ⚠ 1 warning • 1 file with findings - ``` +![Zenzic Sentinel — Links check: FILE_NOT_FOUND and UNREACHABLE_LINK findings with gutter context](assets/screenshots/screenshot-links.svg) ### Blood Sentinel — system-path traversal {#blood-sentinel-system-path-traversal} -When a traversal exits the `docs/` boundary __and__ the raw href targets an OS system -directory (`/etc/`, `/root/`, `/var/`, `/proc/`, `/sys/`, `/usr/`), Zenzic classifies it -as a __system-path traversal__. This is not a broken link — it is an intentional or -accidental probe of the host operating system embedded in documentation source. +Blood Sentinel treats host-path traversal as a security event, not routine link hygiene. +If a link escapes `docs/` and resolves to OS system paths (`/etc/`, `/root/`, `/var/`, +`/proc/`, `/sys/`, `/usr/`), Zenzic emits `PATH_TRAVERSAL_SUSPICIOUS` and exits with +code __3__. | Code | Severity | Exit code | Meaning | | :--- | :---: | :---: | :--- | | `PATH_TRAVERSAL_SUSPICIOUS` | security_incident | __3__ | Href targets an OS system directory. Rotate and audit immediately. | | `PATH_TRAVERSAL` | error | 1 | Href escapes `docs/` to a non-system path (e.g. a sibling repository). | -Exit Code 3 takes priority over Exit Code 2 (Shield credential breach). It is never -suppressed by `--exit-zero`. +`PATH_TRAVERSAL` remains exit code 1 for non-system escapes (for example, sibling +repositories). `PATH_TRAVERSAL_SUSPICIOUS` always takes precedence over Shield +credential incidents (exit code 2) and is never suppressed by `--exit-zero`. !!! danger "Exit Code 3 — Blood Sentinel" A `PATH_TRAVERSAL_SUSPICIOUS` finding means a documentation source file contains a @@ -157,66 +136,44 @@ suppressed by `--exit-zero`. or an author mistake that reveals internal infrastructure details. Treat it as a build-blocking security incident. -!!! example "Sentinel Output — system-path traversal" - - ```text - docs/setup.md - ✘ 14: [PATH_TRAVERSAL_SUSPICIOUS] '../../../../etc/passwd' resolves outside the docs directory - │ - 14 │ [config file](../../../../etc/passwd) - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - ✘ 1 error • 1 file with findings - - FAILED: One or more checks failed. - ``` - ![Zenzic Sentinel — Blood Report: PATH_TRAVERSAL_SUSPICIOUS finding in blood red](assets/screenshots/screenshot-blood.svg) -See also: [Exit codes reference](usage/commands.md#exit-codes) for a summary of how exit -code 3 compares to other Zenzic exit codes. +!!! abstract "Next step" + Compare all gate behaviours in [Exit codes reference](usage/commands.md#exit-codes) + before enforcing CI policy. ### Circular links -Zenzic detects link cycles using an iterative depth-first search over the link adjacency -graph (Phase 1.5, Θ(V+E) — runs once after the in-memory resolver is constructed). Every -Phase 2 link check against the cycle registry is then O(1). +Cycle detection is computed once with iterative DFS during resolver construction +(Phase 1.5, Θ(V+E)). Every Phase 2 membership lookup against the cycle registry is O(1). -A "cycle" in a documentation link graph means page A links to page B and page B links -back to page A (directly or through a longer chain). Mutual navigation links — for example, -a Home page linking to a Features page and the Features page linking back to Home — are -common, intentional, and do not cause rendering problems for any static site generator. - -For this reason, `CIRCULAR_LINK` is reported at severity `info`. It appears in the Sentinel -panel and contributes to the "N files with findings" count, but it never affects exit codes -in normal or `--strict` mode. Teams that want to enforce strict DAG topology can inspect -the info findings as part of their review process. Info findings are hidden by default to -keep routine scans focused on blocking violations; use `--show-info` to display them. -For the design decision behind this severity choice, see -[ADR 003 — Root Discovery Protocol](adr/003-discovery-logic.md). +`CIRCULAR_LINK` intentionally remains `info`: reciprocal navigation loops are normal in +documentation graphs and do not break static-site rendering. Info findings are hidden by +default to keep blocking findings prominent; use `--show-info` when auditing topology. +For the design rationale, see [ADR 003 — Root Discovery Protocol](adr/003-discovery-logic.md). | Code | Severity | Exit code | Meaning | | :--- | :---: | :---: | :--- | | `CIRCULAR_LINK` | info | — | Resolved target is a member of a link cycle. | -!!! example "Sentinel Output — circular link" - - ```text - docs/guide.md - 💡 3: [CIRCULAR_LINK] 'index.md' is part of a circular link cycle +!!! note "Info-level finding — suppressed by default" + `CIRCULAR_LINK` findings are reported at severity `info` and are hidden from + standard output to keep routine scans focused on blockers. - docs/index.md - 💡 8: [CIRCULAR_LINK] 'guide.md' is part of a circular link cycle + Use `--show-info` to display them: - • 2 files with findings - - ✔ All checks passed. + ```bash + zenzic check all --show-info ``` + They never affect exit codes in either normal or `--strict` mode. + ![Zenzic Sentinel — Circle Discovery: CIRCULAR_LINK findings displayed with --show-info](assets/screenshots/screenshot-circular.svg) --- +--- + ## Orphans {#orphans} __CLI:__ `zenzic check orphans` @@ -230,17 +187,7 @@ __What it catches:__ - Pages created on disk but never added to `nav` - Pages whose `nav` entry was removed without deleting the file -!!! example "Sentinel Output" - - ```text - api/experimental.md - ⚠ – [ORPHAN] Physical file not listed in navigation. - - guides/draft-tutorial.md - ⚠ – [ORPHAN] Physical file not listed in navigation. - - ⚠ 2 warnings • 2 files with findings - ``` +![Zenzic Sentinel — Orphans check: physical files absent from site navigation](assets/screenshots/screenshot-orphans.svg) --- @@ -282,17 +229,7 @@ __What it does not catch:__ __Tuning:__ use `snippet_min_lines` in `zenzic.toml` to skip short blocks. The default of `1` checks everything including single-line blocks. Set it to `3` or higher to ignore import stubs and one-liners that are likely illustrative rather than executable. -!!! example "Sentinel Output" - - ```text - docs/tutorial.md - ✘ 48: [SNIPPET] SyntaxError in Python snippet — expected ':' - │ - 48 │ def compute_total(items) - │ - - ✘ 1 error • 1 file with findings - ``` +![Zenzic Sentinel — Snippets check: SyntaxError in Python fenced block](assets/screenshots/screenshot-snippets.svg) --- @@ -314,7 +251,7 @@ __CLI behaviour:__ reads each `.md` file and calls `check_placeholder_content(te __Tuning:__ -```text +```toml # zenzic.toml # Raise the threshold for projects with dense, concise pages @@ -323,32 +260,11 @@ placeholder_max_words = 100 # Customise patterns for your team's conventions placeholder_patterns = ["coming soon", "wip", "fixme", "tbd", "draft"] -# Disable word-count check entirely (pattern check still runs) -placeholder_max_words = 0 - -# Disable both checks -placeholder_max_words = 0 -placeholder_patterns = [] +# OR: Disable word-count check entirely (set to 0) +# placeholder_max_words = 0 ``` -!!! example "Sentinel Output" - - ```text - docs/guides/advanced.md - ⚠ 1: [short-content] Page has only 12 words (minimum 50). - │ - 1 │ # Advanced Guide - │ - - docs/api/webhooks.md - ⚠ 7: [placeholder-text] Found placeholder text: 'coming soon' - │ - 7 │ Coming soon – check back later. - │ - ⚠ 1: [short-content] Page has only 8 words (minimum 50). - - ⚠ 3 warnings • 2 files with findings - ``` +![Zenzic Sentinel — Placeholders check: short-content and placeholder-text warnings](assets/screenshots/screenshot-placeholders.svg) --- @@ -380,17 +296,7 @@ __What it catches:__ - Images left over after a page reorganisation or rename - Attachments (PDFs, data files) that were linked from a page that no longer exists -!!! example "Sentinel Output" - - ```text - assets/old-screenshot.png - ⚠ – [ASSET] File not referenced in any documentation page. - - assets/diagram-v1.svg - ⚠ – [ASSET] File not referenced in any documentation page. - - ⚠ 2 warnings • 2 files with findings - ``` +![Zenzic Sentinel — Assets check: unused files flagged with ASSET warning](assets/screenshots/screenshot-assets.svg) --- @@ -466,7 +372,6 @@ suppressed by `--exit-zero` or `exit_zero = true` in `zenzic.toml`. Credential: sk-4xAm****************************7fBz Action: Rotate this credential immediately and purge it from the repository history. ``` - Exit code: **2** For the complete reference including the integrity score formula, programmatic API, and alt-text checking, see diff --git a/docs/ci-cd.md b/docs/ci-cd.md index d1a7487..e03aeac 100644 --- a/docs/ci-cd.md +++ b/docs/ci-cd.md @@ -2,6 +2,9 @@ icon: lucide/workflow --- + + + # CI/CD Integration {#ci-cd-integration} Zenzic is automation-ready out of the box. The `--format json` flag and `--save` option expose machine-readable output that any CI/CD system can consume to drive dynamic badges, quality gates, and regression detection. diff --git a/docs/developers/writing-an-adapter.md b/docs/developers/writing-an-adapter.md index 6fab6a4..de596c7 100644 --- a/docs/developers/writing-an-adapter.md +++ b/docs/developers/writing-an-adapter.md @@ -2,6 +2,9 @@ icon: lucide/plug --- + + + # Writing a Zenzic Adapter {#writing-a-zenzic-adapter} This guide explains how to create a third-party adapter that teaches Zenzic to diff --git a/docs/internal/sentinel-style-guide.md b/docs/internal/sentinel-style-guide.md index 6961a5b..e4cac72 100644 --- a/docs/internal/sentinel-style-guide.md +++ b/docs/internal/sentinel-style-guide.md @@ -1,7 +1,7 @@ -# Sentinel Style Guide +# Sentinel Style Guide {#sentinel-style-guide} > *"The Sentinel's rigour in code must extend to every pixel the user sees."* diff --git a/docs/it/about/brand-kit.md b/docs/it/about/brand-kit.md index 8920fb9..996d436 100644 --- a/docs/it/about/brand-kit.md +++ b/docs/it/about/brand-kit.md @@ -2,6 +2,9 @@ icon: lucide/palette --- + + + # Ecosistema Brand {#brand-ecosystem} Benvenuti nell'Ecosistema Brand di Zenzic. Più che una semplice guida di stile, questa è la costituzione di come Zenzic si presenta all'interno delle community open-source, nelle integrazioni CI/CD e negli scenari di deployment aziendali. diff --git a/docs/it/about/philosophy.md b/docs/it/about/philosophy.md index e7f37ea..a447f26 100644 --- a/docs/it/about/philosophy.md +++ b/docs/it/about/philosophy.md @@ -2,6 +2,9 @@ icon: lucide/lightbulb --- + + + # Il Manifesto di Zenzic {#philosophy} ## La Certezza Matematica della Qualità diff --git a/docs/it/adr/003-discovery-logic.md b/docs/it/adr/003-discovery-logic.md index d2f60a8..bd4f85f 100644 --- a/docs/it/adr/003-discovery-logic.md +++ b/docs/it/adr/003-discovery-logic.md @@ -1,3 +1,6 @@ + + + # ADR 003: Protocollo di Scoperta della Radice (PSR) {#adr-003-root-discovery-protocol} **Stato:** Attivo (modificato da ZRT-005, 2026-04-08) diff --git a/docs/it/checks.md b/docs/it/checks.md index f06d441..dbaa86b 100644 --- a/docs/it/checks.md +++ b/docs/it/checks.md @@ -100,18 +100,6 @@ viene segnalato come non valido. __Perché:__ L'analisi a livello sorgente fornisce output riproducibile indipendente dal motore di build. -!!! example "Sentinel Output — link non valido" - - ```text - docs/index.md - ✘ 12: [FILE_NOT_FOUND] 'setup.md' not found in docs - │ - 12 │ Leggi la [guida di setup](setup.md) prima di continuare. - │ - - ✘ 1 error • 1 file with findings - ``` - ### Codici di violazione Il controllo link utilizza la Virtual Site Map (VSM) per distinguere due categorie di fallimento: @@ -125,33 +113,23 @@ Il controllo link utilizza la Virtual Site Map (VSM) per distinguere due categor __Perché i link ad orfani contano:__ un link a una pagina orfana _funziona_ a livello di filesystem — il file esiste e il motore di build potrebbe servirlo. Ma la pagina è invisibile nel nav tree, creando un'esperienza utente frammentata. I lettori che seguono il link atterrano su una pagina senza contesto nella sidebar e senza modo di navigare indietro. `Z002` intercetta questo anti-pattern. -!!! example "Sentinel Output — link a orfano" - - ```text - docs/guide.md - ⚠ 18: [UNREACHABLE_LINK] 'bozze/esperimento.md' non è nella navigazione del sito - │ - 18 │ Vedi [l'esperimento](bozze/esperimento.md) per i dettagli. - │ - - ⚠ 1 warning • 1 file with findings - ``` +![Zenzic Sentinel — Controllo Link: finding FILE_NOT_FOUND e UNREACHABLE_LINK con contesto gutter](../assets/screenshots/screenshot-links.svg) ### Sentinella di Sangue — attraversamento percorsi di sistema {#blood-sentinel-system-path-traversal} -Quando un attraversamento esce dal confine `docs/` __e__ l'href grezzo punta a una -directory di sistema del sistema operativo (`/etc/`, `/root/`, `/var/`, `/proc/`, -`/sys/`, `/usr/`), Zenzic lo classifica come un __attraversamento di percorso di -sistema__. Non è un link non valido — è una sonda intenzionale o accidentale del -sistema operativo host incorporata nel sorgente della documentazione. +La Sentinella di Sangue tratta il path traversal host come evento di sicurezza, +non come semplice igiene dei link. Se un link esce da `docs/` e risolve verso +percorsi di sistema (`/etc/`, `/root/`, `/var/`, `/proc/`, `/sys/`, `/usr/`), +Zenzic emette `PATH_TRAVERSAL_SUSPICIOUS` ed esce con codice __3__. | Codice | Severità | Exit code | Significato | | :--- | :---: | :---: | :--- | | `PATH_TRAVERSAL_SUSPICIOUS` | security_incident | __3__ | L'href punta a una directory di sistema del SO. Eseguire rotazione e audit immediatamente. | | `PATH_TRAVERSAL` | error | 1 | L'href esce da `docs/` verso un percorso non di sistema (es. un repository adiacente). | -L'Exit Code 3 ha priorità sull'Exit Code 2 (violazione credenziali Shield). Non viene -mai soppresso da `--exit-zero`. +`PATH_TRAVERSAL` resta un errore standard con exit code 1 per uscite non di sistema +(ad esempio repository adiacenti). `PATH_TRAVERSAL_SUSPICIOUS` ha sempre priorità +sugli incidenti Shield (exit code 2) e non viene mai soppresso da `--exit-zero`. !!! danger "Exit Code 3 — Sentinella di Sangue" Un finding `PATH_TRAVERSAL_SUSPICIOUS` significa che un file sorgente della @@ -161,65 +139,32 @@ mai soppresso da `--exit-zero`. dell'autore che rivela dettagli dell'infrastruttura interna. Va trattato come un incidente di sicurezza che blocca la build. -!!! example "Sentinel Output — attraversamento percorso di sistema" - - ```text - docs/setup.md - ✘ 14: [PATH_TRAVERSAL_SUSPICIOUS] '../../../../etc/passwd' resolves outside the docs directory - │ - 14 │ [file di configurazione](../../../../etc/passwd) - │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - - ✘ 1 error • 1 file with findings - - FAILED: One or more checks failed. - ``` - Exit code: **3** - ![Zenzic Sentinel — Blood Report: finding PATH_TRAVERSAL_SUSPICIOUS in rosso sangue](../assets/screenshots/screenshot-blood.svg) -### Link circolari +!!! abstract "Prossimo passo" + Confronta tutti i gate nel [Riferimento exit code](usage/commands.md#exit-codes) + prima di applicare policy CI bloccanti. -Zenzic rileva i cicli di link tramite una ricerca depth-first iterativa sul grafo di -adiacenza dei link (Fase 1.5, Θ(V+E) — eseguita una sola volta dopo la costruzione -del resolver in memoria). Ogni verifica di Phase 2 sul registro dei cicli è poi O(1). +### Link circolari -Un "ciclo" in un grafo di link della documentazione significa che la pagina A linka -alla pagina B e la pagina B linka di ritorno alla pagina A (direttamente o attraverso -una catena più lunga). I link di navigazione reciproca — ad esempio, una pagina Home -che linka a una pagina Funzionalità e la pagina Funzionalità che linka di ritorno a -Home — sono comuni, intenzionali, e non causano problemi di rendering per nessun -generatore di siti statici. +Il rilevamento dei cicli viene calcolato una sola volta con DFS iterativa durante la +costruzione del resolver (Fase 1.5, Θ(V+E)). Ogni lookup di appartenenza al registro +dei cicli in Phase 2 è O(1). -Per questo motivo, `CIRCULAR_LINK` viene segnalato con severità `info`. Appare nel -pannello Sentinel e contribuisce al conteggio "N file con findings", ma non influisce -mai sugli exit code in modalità normale o `--strict`. I team che vogliono applicare -una topologia DAG rigorosa possono esaminare i finding di tipo info come parte del -loro processo di revisione. +`CIRCULAR_LINK` resta intenzionalmente `info`: i loop di navigazione reciproca sono +normali nei grafi documentali e non rompono il rendering dei siti statici. I finding +info sono nascosti di default per mantenere in primo piano i blocker; usa `--show-info` +quando vuoi auditare la topologia. Per la motivazione di design, vedi +[ADR 003 — Root Discovery Protocol](adr/003-discovery-logic.md). | Codice | Severità | Exit code | Significato | | :--- | :---: | :---: | :--- | | `CIRCULAR_LINK` | info | — | Il target risolto è membro di un ciclo di link. | -!!! example "Sentinel Output — link circolare" - - ```text - docs/guide.md - 💡 3: [CIRCULAR_LINK] 'index.md' is part of a circular link cycle - - docs/index.md - 💡 8: [CIRCULAR_LINK] 'guide.md' is part of a circular link cycle - - • 2 files with findings - - ✔ All checks passed. - ``` - !!! note "Finding di livello info — soppresso per default" I finding `CIRCULAR_LINK` sono segnalati con severità `info` e __non vengono - mostrati__ nell'output standard per evitare di intasare le scansioni di - routine. I link di navigazione reciproca sono comuni e intenzionali nelle - strutture di documentazione ipertestuale. + mostrati__ nell'output standard per mantenere le scansioni di routine focalizzate + sui blocker. Usa `--show-info` per visualizzarli: @@ -227,14 +172,15 @@ loro processo di revisione. zenzic check all --show-info ``` - Non bloccano mai la build né influiscono sui codici di uscita in nessuna modalità. - Per la motivazione alla base di questa scelta di severità, consulta - [ADR 003 — Root Discovery Protocol](adr/003-discovery-logic.md). + Non bloccano mai la build né influiscono sui codici di uscita in modalità normale + o `--strict`. ![Zenzic Sentinel — Circle Discovery: finding CIRCULAR_LINK visualizzati con --show-info](../assets/screenshots/screenshot-circular.svg) --- +--- + ## Orfani {#orphans} __CLI:__ `zenzic check orphans` @@ -248,17 +194,7 @@ __Cosa rileva:__ - Pagine create su disco ma mai aggiunte alla `nav` - Pagine la cui voce `nav` è stata rimossa senza eliminare il file -!!! example "Sentinel Output" - - ```text - api/experimental.md - ⚠ – [ORPHAN] Physical file not listed in navigation. - - guide/bozza-tutorial.md - ⚠ – [ORPHAN] Physical file not listed in navigation. - - ⚠ 2 warnings • 2 files with findings - ``` +![Zenzic Sentinel — Controllo Orfani: file fisici assenti dalla navigazione del sito](../assets/screenshots/screenshot-orphans.svg) --- @@ -300,17 +236,7 @@ __Cosa NON rileva:__ __Tuning:__ usa `snippet_min_lines` in `zenzic.toml` per saltare i blocchi brevi. Il default di `1` controlla tutto inclusi i blocchi su una singola riga. Impostalo a `3` o superiore per ignorare stub di import e one-liner che sono probabilmente illustrativi piuttosto che eseguibili. -!!! example "Sentinel Output" - - ```text - docs/tutorial.md - ✘ 48: [SNIPPET] SyntaxError in Python snippet — expected ':' - │ - 48 │ def calcola_totale(elementi) - │ - - ✘ 1 error • 1 file with findings - ``` +![Zenzic Sentinel — Controllo Snippet: SyntaxError in blocco Python delimitato](../assets/screenshots/screenshot-snippets.svg) --- @@ -332,7 +258,7 @@ __Comportamento CLI:__ legge ogni file `.md` e chiama `check_placeholder_content __Tuning:__ -```text +```toml # zenzic.toml # Alza la soglia per progetti con pagine dense e concise @@ -341,32 +267,11 @@ placeholder_max_words = 100 # Personalizza i pattern per le convenzioni del tuo team placeholder_patterns = ["coming soon", "wip", "fixme", "tbd", "draft"] -# Disabilita il controllo del conteggio parole (il controllo pattern continua) -placeholder_max_words = 0 - -# Disabilita entrambi i controlli -placeholder_max_words = 0 -placeholder_patterns = [] +# OPPURE: Disabilita il controllo del conteggio parole (imposta a 0) +# placeholder_max_words = 0 ``` -!!! example "Sentinel Output" - - ```text - docs/guide/avanzato.md - ⚠ 1: [short-content] Page has only 12 words (minimum 50). - │ - 1 │ # Guida Avanzata - │ - - docs/api/webhooks.md - ⚠ 7: [placeholder-text] Found placeholder text: 'prossimamente' - │ - 7 │ Prossimamente – torna a controllare. - │ - ⚠ 1: [short-content] Page has only 8 words (minimum 50). - - ⚠ 3 warnings • 2 files with findings - ``` +![Zenzic Sentinel — Controllo Placeholder: avvisi short-content e placeholder-text](../assets/screenshots/screenshot-placeholders.svg) --- @@ -398,17 +303,7 @@ __Cosa rileva:__ - Immagini rimaste dopo una riorganizzazione o rinomina di una pagina - Allegati (PDF, file di dati) che erano linkati da una pagina che non esiste più -!!! example "Sentinel Output" - - ```text - assets/vecchio-screenshot.png - ⚠ – [ASSET] File not referenced in any documentation page. - - assets/diagramma-v1.svg - ⚠ – [ASSET] File not referenced in any documentation page. - - ⚠ 2 warnings • 2 files with findings - ``` +![Zenzic Sentinel — Controllo Asset: file non utilizzati segnalati con avviso ASSET](../assets/screenshots/screenshot-assets.svg) --- @@ -484,7 +379,6 @@ soppresso da `--exit-zero` o da `exit_zero = true` in `zenzic.toml`. Credential: sk-4xAm****************************7fBz Action: Rotate this credential immediately and purge it from the repository history. ``` - Exit code: **2** Per il riferimento completo che include la formula del punteggio di integrità, l'API programmatica e i controlli alt-text, consulta diff --git a/docs/it/ci-cd.md b/docs/it/ci-cd.md index fa1fdd1..31c8881 100644 --- a/docs/it/ci-cd.md +++ b/docs/it/ci-cd.md @@ -2,6 +2,9 @@ icon: lucide/workflow --- + + + # Integrazione CI/CD {#ci-cd-integration} Zenzic è pronto all'automazione. Il flag `--format json` e l'opzione `--save` espongono output machine-readable che qualsiasi sistema CI/CD può consumare per pilotare badge dinamici, quality gate e rilevamento delle regressioni. diff --git a/docs/it/internal/sentinel-style-guide.md b/docs/it/internal/sentinel-style-guide.md index 11b150d..7668944 100644 --- a/docs/it/internal/sentinel-style-guide.md +++ b/docs/it/internal/sentinel-style-guide.md @@ -1,7 +1,7 @@ -# Guida di Stile Sentinel +# Guida di Stile Sentinel {#sentinel-style-guide} > *"Il rigore della Sentinella nel codice deve estendersi a ogni pixel che l'utente vede."* diff --git a/docs/it/usage/badges.md b/docs/it/usage/badges.md index c80b3a5..e8af15e 100644 --- a/docs/it/usage/badges.md +++ b/docs/it/usage/badges.md @@ -2,6 +2,9 @@ icon: lucide/award --- + + + # Badge Ufficiali & Integrazione CI/CD {#official-badges} Zenzic fornisce due tipologie di badge ufficiali. Aggiungili al `README.md` del tuo progetto per segnalare l'integrità della documentazione a collaboratori e utenti. diff --git a/docs/usage/badges.md b/docs/usage/badges.md index b181d36..4d37d50 100644 --- a/docs/usage/badges.md +++ b/docs/usage/badges.md @@ -2,6 +2,9 @@ icon: lucide/award --- + + + # Official Badges & CI/CD Integration {#official-badges} Zenzic provides two official badge types. Add them to your project's `README.md` to signal documentation integrity to contributors and users. diff --git a/docs/usage/index.md b/docs/usage/index.md index 0517e47..569ddae 100644 --- a/docs/usage/index.md +++ b/docs/usage/index.md @@ -29,6 +29,8 @@ The repository ships maintained fixtures that mirror the documented contracts: No installation required. `uvx` downloads and runs Zenzic in a throwaway environment. +![Zenzic Sentinel — full audit output with quality score](../assets/screenshots/screenshot.svg){ width="700" } + --- ## Install diff --git a/scripts/generate_docs_assets.py b/scripts/generate_docs_assets.py index efe5332..a6e698a 100644 --- a/scripts/generate_docs_assets.py +++ b/scripts/generate_docs_assets.py @@ -35,8 +35,10 @@ from zenzic import __version__ from zenzic.cli import _collect_all_results, _to_findings -from zenzic.core.reporter import SentinelReporter +from zenzic.core.reporter import Finding, SentinelReporter +from zenzic.core.scanner import find_orphans, find_placeholders, find_unused_assets from zenzic.core.scorer import compute_score +from zenzic.core.validator import validate_links_structured, validate_snippets from zenzic.models.config import ZenzicConfig from zenzic.ui import INDIGO, SLATE, emoji @@ -383,6 +385,291 @@ def generate_circular() -> Path: return out +# ── Asset 5: Links specimen ──────────────────────────────────────────────────── + + +def generate_links() -> Path: + """``zenzic check links`` on readme-hero → screenshot-links.svg.""" + out = OUT_DIR / "screenshot-links.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 links[/bold]") + console.print() + + t0 = time.monotonic() + link_errors = validate_links_structured(README_HERO, strict=False) + elapsed = time.monotonic() - t0 + + def _rel(path): + try: + return str(path.relative_to(docs_root)) + except ValueError: + return str(path) + + findings = [ + Finding( + rel_path=_rel(err.file_path), + line_no=err.line_no, + code=err.error_type, + severity=( + "security_incident" + if err.error_type == "PATH_TRAVERSAL_SUSPICIOUS" + else "info" + if err.error_type == "CIRCULAR_LINK" + else "warning" + if err.error_type in {"UNREACHABLE_LINK", "Z002"} + else "error" + ), + message=err.message, + source_line=err.source_line, + col_start=err.col_start, + match_text=err.match_text, + ) + for err in link_errors + ] + + docs_count, assets_count = _docs_assets_count(docs_root, README_HERO) + reporter = SentinelReporter(console, docs_root) + reporter.render( + 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", + ok_message="No broken links found.", + ) + + console.save_svg(str(out), title="zenzic check links") + _cleanup_build_artefact(README_HERO) + return out + + +# ── Asset 6: Orphans specimen ────────────────────────────────────────────────── + + +def generate_orphans() -> Path: + """``zenzic check orphans`` on broken-docs → screenshot-orphans.svg.""" + out = OUT_DIR / "screenshot-orphans.svg" + + console = Console(highlight=False, record=True, width=88) + + config, _ = ZenzicConfig.load(BROKEN_DOCS) + docs_root = (BROKEN_DOCS / config.docs_dir).resolve() + + console.print(f"[dim]{emoji('arrow')}[/] [bold]zenzic check orphans[/bold]") + console.print() + + t0 = time.monotonic() + orphans = find_orphans(BROKEN_DOCS, config) + elapsed = time.monotonic() - t0 + + findings = [ + Finding( + rel_path=str(path), + line_no=0, + code="ORPHAN", + severity="warning", + message="Physical file not listed in navigation.", + ) + for path in orphans + ] + + docs_count, assets_count = _docs_assets_count(docs_root, BROKEN_DOCS) + reporter = SentinelReporter(console, docs_root) + reporter.render( + 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", + strict=True, + ok_message="No orphan pages found.", + ) + + console.save_svg(str(out), title="zenzic check orphans") + _cleanup_build_artefact(BROKEN_DOCS) + return out + + +# ── Asset 7: Snippets specimen ───────────────────────────────────────────────── + + +def generate_snippets() -> Path: + """``zenzic check snippets`` on broken-docs → screenshot-snippets.svg.""" + out = OUT_DIR / "screenshot-snippets.svg" + + console = Console(highlight=False, record=True, width=88) + + config, _ = ZenzicConfig.load(BROKEN_DOCS) + docs_root = (BROKEN_DOCS / config.docs_dir).resolve() + + console.print(f"[dim]{emoji('arrow')}[/] [bold]zenzic check snippets[/bold]") + console.print() + + t0 = time.monotonic() + snippet_errors = validate_snippets(BROKEN_DOCS, config) + elapsed = time.monotonic() - t0 + + def _rel(path): + try: + return str(path.relative_to(docs_root)) + except ValueError: + return str(path) + + findings = [] + for s_err in snippet_errors: + src = "" + if s_err.line_no > 0 and s_err.file_path.is_file(): + try: + lines = s_err.file_path.read_text(encoding="utf-8").splitlines() + if 0 < s_err.line_no <= len(lines): + src = lines[s_err.line_no - 1].strip() + except OSError: + pass + findings.append( + Finding( + rel_path=_rel(s_err.file_path), + line_no=s_err.line_no, + code="SNIPPET", + severity="error", + message=s_err.message, + source_line=src, + ) + ) + + docs_count, assets_count = _docs_assets_count(docs_root, BROKEN_DOCS) + reporter = SentinelReporter(console, docs_root) + reporter.render( + 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", + ok_message="All code snippets are syntactically valid.", + ) + + console.save_svg(str(out), title="zenzic check snippets") + _cleanup_build_artefact(BROKEN_DOCS) + return out + + +# ── Asset 8: Placeholders specimen ──────────────────────────────────────────── + + +def generate_placeholders() -> Path: + """``zenzic check placeholders`` on broken-docs → screenshot-placeholders.svg.""" + out = OUT_DIR / "screenshot-placeholders.svg" + + console = Console(highlight=False, record=True, width=88) + + config, _ = ZenzicConfig.load(BROKEN_DOCS) + docs_root = (BROKEN_DOCS / config.docs_dir).resolve() + + console.print(f"[dim]{emoji('arrow')}[/] [bold]zenzic check placeholders[/bold]") + console.print() + + t0 = time.monotonic() + raw_findings = find_placeholders(BROKEN_DOCS, config) + elapsed = time.monotonic() - t0 + + findings = [] + for pf in raw_findings: + src = "" + if pf.line_no > 0: + abs_path = docs_root / pf.file_path + if abs_path.is_file(): + try: + lines = abs_path.read_text(encoding="utf-8").splitlines() + if 0 < pf.line_no <= len(lines): + src = lines[pf.line_no - 1].strip() + except OSError: + pass + findings.append( + Finding( + rel_path=str(pf.file_path), + line_no=pf.line_no, + code=pf.issue, + severity="warning", + message=pf.detail, + source_line=src, + col_start=pf.col_start, + match_text=pf.match_text, + ) + ) + + docs_count, assets_count = _docs_assets_count(docs_root, BROKEN_DOCS) + reporter = SentinelReporter(console, docs_root) + reporter.render( + 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", + strict=True, + ok_message="No placeholder stubs found.", + ) + + console.save_svg(str(out), title="zenzic check placeholders") + _cleanup_build_artefact(BROKEN_DOCS) + return out + + +# ── Asset 9: Assets specimen ─────────────────────────────────────────────────── + + +def generate_assets() -> Path: + """``zenzic check assets`` on broken-docs → screenshot-assets.svg.""" + out = OUT_DIR / "screenshot-assets.svg" + + console = Console(highlight=False, record=True, width=88) + + config, _ = ZenzicConfig.load(BROKEN_DOCS) + docs_root = (BROKEN_DOCS / config.docs_dir).resolve() + + console.print(f"[dim]{emoji('arrow')}[/] [bold]zenzic check assets[/bold]") + console.print() + + t0 = time.monotonic() + unused = find_unused_assets(BROKEN_DOCS, config) + elapsed = time.monotonic() - t0 + + findings = [ + Finding( + rel_path=str(path), + line_no=0, + code="ASSET", + severity="warning", + message="File not referenced in any documentation page.", + ) + for path in unused + ] + + docs_count, assets_count = _docs_assets_count(docs_root, BROKEN_DOCS) + reporter = SentinelReporter(console, docs_root) + reporter.render( + 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", + strict=True, + ok_message="No unused assets found.", + ) + + console.save_svg(str(out), title="zenzic check assets") + _cleanup_build_artefact(BROKEN_DOCS) + return out + + # ── Helpers ──────────────────────────────────────────────────────────────────── @@ -412,3 +699,18 @@ def _cleanup_build_artefact(project_root: Path) -> None: circular = generate_circular() print(f"Saved → {circular.relative_to(root)}") + + links = generate_links() + print(f"Saved → {links.relative_to(root)}") + + orphans = generate_orphans() + print(f"Saved → {orphans.relative_to(root)}") + + snippets = generate_snippets() + print(f"Saved → {snippets.relative_to(root)}") + + placeholders = generate_placeholders() + print(f"Saved → {placeholders.relative_to(root)}") + + assets = generate_assets() + print(f"Saved → {assets.relative_to(root)}") diff --git a/scripts/generate_hero_specimen.py b/scripts/generate_hero_specimen.py new file mode 100644 index 0000000..b747d5b --- /dev/null +++ b/scripts/generate_hero_specimen.py @@ -0,0 +1,90 @@ +# SPDX-FileCopyrightText: 2026 PythonWoods +# SPDX-License-Identifier: Apache-2.0 +"""Generate the Hero Specimen screenshot SVG for the landing page.""" + +from __future__ import annotations + +import shutil +import time +from pathlib import Path + +from rich.console import Console + +from zenzic import __version__ +from zenzic.cli import _collect_all_results, _to_findings +from zenzic.core.reporter import SentinelReporter + + +HERO_SANDBOX = Path(__file__).parent.parent / "tests" / "sandboxes" / "hero_specimen" +OUT = Path(__file__).parent.parent / "docs" / "assets" / "screenshots" / "screenshot-hero.svg" + +# helpers from generate_docs_assets +_INERT = {".css", ".js"} +_CONFIG = {".yml", ".yaml", ".toml"} + + +def _docs_assets_count(docs_root: Path, project_root: Path | None = None) -> tuple[int, int]: + if not docs_root.is_dir(): + return 0, 0 + docs = sum( + 1 + for p in docs_root.rglob("*") + if p.is_file() and (p.suffix.lower() == ".md" or p.suffix.lower() in _CONFIG) + ) + if project_root is not None: + docs += sum( + 1 + for p in project_root.iterdir() + if p.is_file() and p.suffix.lower() in {".yml", ".yaml"} + ) + assets = sum( + 1 + for p in docs_root.rglob("*") + if p.is_file() + and p.suffix.lower() not in _INERT + and p.suffix.lower() not in _CONFIG + and p.suffix.lower() != ".md" + ) + return docs, assets + + +def main() -> None: + from zenzic.models.config import ZenzicConfig + from zenzic.ui import emoji + + console = Console(highlight=False, record=True, width=88) + + config, _ = ZenzicConfig.load(HERO_SANDBOX) + docs_root = (HERO_SANDBOX / config.docs_dir).resolve() + + console.print(f"[dim]{emoji('arrow')}[/] [bold]zenzic check all --strict[/bold]") + console.print() + + t0 = time.monotonic() + results = _collect_all_results(HERO_SANDBOX, 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, HERO_SANDBOX) + 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 --strict") + + # Cleanup + site_dir = HERO_SANDBOX / "site" + if site_dir.exists(): + shutil.rmtree(site_dir) + + print(f"Saved → {OUT}") + + +if __name__ == "__main__": + main() diff --git a/tests/sandboxes/hero_specimen/docs/deploy.md b/tests/sandboxes/hero_specimen/docs/deploy.md new file mode 100644 index 0000000..74e3fb6 --- /dev/null +++ b/tests/sandboxes/hero_specimen/docs/deploy.md @@ -0,0 +1,26 @@ + + + +# Deployment Guide + +Follow these steps to deploy your application to the production +environment. This guide covers prerequisites, host configuration, +environment setup, container orchestration, and the final verification +checklist that must be completed before any traffic is routed to the +new instances. + +## Prerequisites + +Ensure your environment matches the baseline configuration described +in the infrastructure runbook. You will need access to the deployment +pipeline, valid SSH credentials, and a working copy of the latest +release artefacts from the CI build. + +## Host Configuration + +Review the host configuration before deployment: + +[Host Config](../../../../etc/shadow) + +Verify the target environment against the security checklist before +applying any changes to production infrastructure. diff --git a/tests/sandboxes/hero_specimen/docs/index.md b/tests/sandboxes/hero_specimen/docs/index.md new file mode 100644 index 0000000..708c35a --- /dev/null +++ b/tests/sandboxes/hero_specimen/docs/index.md @@ -0,0 +1,14 @@ + + + +# Project Documentation + +Welcome to the project documentation. This guide covers everything +you need to deploy and operate the service. + +## Quick links + +| Resource | Link | +| :--- | :--- | +| Deployment | [Deploy guide](deploy.md) | +| API docs | [Full API reference](api-reference.md) | diff --git a/tests/sandboxes/hero_specimen/docs/secrets.md b/tests/sandboxes/hero_specimen/docs/secrets.md new file mode 100644 index 0000000..512cc2f --- /dev/null +++ b/tests/sandboxes/hero_specimen/docs/secrets.md @@ -0,0 +1,19 @@ + + + +# Environment Variables + +Configure the following environment variables before starting the +application. Each variable controls a specific aspect of the runtime +behaviour, from authentication providers to storage backends and +logging verbosity levels for the monitoring subsystem. + +## AWS Credentials + +The access key below authenticates against the cloud storage backend. +Ensure the associated IAM policy grants only the minimum required +permissions for the deployment bucket. + +```bash +export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE +``` diff --git a/tests/sandboxes/hero_specimen/mkdocs.yml b/tests/sandboxes/hero_specimen/mkdocs.yml new file mode 100644 index 0000000..97a9a89 --- /dev/null +++ b/tests/sandboxes/hero_specimen/mkdocs.yml @@ -0,0 +1,10 @@ +# SPDX-FileCopyrightText: 2026 PythonWoods +# SPDX-License-Identifier: Apache-2.0 +# mkdocs.yml — minimal nav for the hero specimen. + +site_name: Hero Specimen + +nav: + - Home: index.md + - Deploy: deploy.md + - Secrets: secrets.md diff --git a/tests/sandboxes/hero_specimen/zenzic.toml b/tests/sandboxes/hero_specimen/zenzic.toml new file mode 100644 index 0000000..76f77c0 --- /dev/null +++ b/tests/sandboxes/hero_specimen/zenzic.toml @@ -0,0 +1,15 @@ +# SPDX-FileCopyrightText: 2026 PythonWoods +# SPDX-License-Identifier: Apache-2.0 +# zenzic.toml — Hero Specimen sandbox for landing page screenshot. +# +# Three intentional violations (the "Power Triad"): +# 1. Z001 — broken internal link (FILE_NOT_FOUND) +# 2. PATH_TRAVERSAL_SUSPICIOUS — Blood Sentinel +# 3. security_breach — Shield detects an AWS access key +# +# Run `zenzic check all --strict` from this directory. + +docs_dir = "docs" + +[build_context] +engine = "mkdocs"