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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 9 additions & 17 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,35 +15,27 @@ No changes yet.

---

## [0.9.0] - 2026-05-31
## [0.9.1] - 2026-06-02

### Added

- `zenzic score --stamp`: deterministic, in-file badge stamping for score telemetry.
- `zenzic score --check-stamp`: config-aware freshness gate for stamped score badges.
- `badge_stamp_files` project metadata key to declare stamp targets.
- Domain-aware discovery exemptions for source-code assets in unused-asset analysis.
- `zenzic lab` command: empirical sandbox gallery covering 100% of Z-codes (20 scenarios).
- 15 new sandbox directories under `examples/` (z102 through z505), each with `.zenzic.toml`, `README.md`, and a minimal `docs/` tree that reliably triggers the target rule.
- `zenzic lab all` validation gate: all 20 scenarios emit the expected exit code.
- Native engine, fixtures, lab, and test validation coverage for `Z107 CIRCULAR_ANCHOR` (self-referential anchor link) and `Z104 FILE_NOT_FOUND`.

### Changed

- Suppression debt model migrated to flat-cost scoring (one point per suppression).
- `suppression_cap` behavior clarified as an independent hard-fail governance gate.
- Local overlay parsing hardened with strict unknown-key rejection.
- `just verify` standardized to a five-step operational sequence (hooks, tests, strict check, stamp, freshness check).
- **Performance — Z204 (FORBIDDEN_TERM):** `scan_line_for_forbidden_terms` now accepts a pre-compiled RE2 union regex. `ZenzicConfig` builds the union once via `_recompile_forbidden_patterns()` (called in `model_post_init` and after every `_apply_local_toml` merge). Scan complexity reduced from O(N_lines × N_patterns) to O(N_lines).
- **Performance — Z601 (BRAND_OBSOLESCENCE):** `BrandObsolescenceRule` replaced per-pattern `list[RegexPattern]` with a single RE2 union pattern compiled once at `__init__`. Same O(N_lines) reduction.
- **Unified Score Exclusions Pipeline:** Refactored `zenzic score` calculations (`_run_all_checks` in `_standalone.py`) to run the exact same `_collect_all_results` -> `_to_findings` pipeline as `check all`. Suppression exclusions (`per_file_ignores` and `directory_policies`) are now applied identically to ensure DQS aligns perfectly with linter findings.
- **Repository-Relative Path Resolution:** Refactored path mapping across the core engine scanner (`scanner.py`), CLI check commands (`_check.py`), findings reporter (`reporter.py`), and governance filter (`_governance.py`) to strictly resolve all finding relative paths against `repo_root` instead of `docs_root`, eliminating path inconsistencies.
- **Badge Stamping Path Resolution:** Fixed `score --stamp` and `score --check-stamp` path resolution so that configured `badge_stamp_files` paths are resolved relative to the target project's `repo_root` instead of the process's working directory.

### Removed
### Fixed

- Legacy adapter methods `map_url()` and `classify_route()` from the public adapter contract.
- Legacy score export path `--export-shields` in favor of native stamp/check-stamp telemetry.
- Core scanner integration fix for `Z403 MISSING_ALT_TEXT` to align fixture coverage with production scan paths.
- Fixture line-number correction in scanner test cases to keep finding locations deterministic and stable.

---

## Historical Releases

- v0.9.x archive: [changelogs/v0.9.md](./changelogs/v0.9.md)
- v0.8.x archive: [changelogs/v0.8.md](./changelogs/v0.8.md)
- v0.1.x–v0.7.x archive index: [changelogs/README.md](./changelogs/README.md)
22 changes: 22 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,28 @@ unlikely — the matrix covers the language boundary conditions, not every minor
- All PRs should target `main`; avoid direct commits.
- Update `CHANGELOG.md` in the same commit as the code change.

## REUSE Compliance & Copyright

This project is REUSE-compliant and enforced as a merge gate.

- Significant changes (new logic, new content, or substantial refactors) **MUST** add an author copyright line via SPDX.
- Trivial edits (for example, typo-only fixes) do not require adding a new copyright line.
- Any PR that adds new files or significantly modifies existing files without required SPDX attribution is rejected (Exit Code 1 in CI gate policy).

Example header pattern:

```text
SPDX-FileCopyrightText: 2026 PythonWoods <dev@pythonwoods.dev>
SPDX-FileCopyrightText: 2026 Contributor Name <contributor@example.com>
SPDX-License-Identifier: Apache-2.0
```

Legal model:

- No CLA is required.
- Contributions are governed by DCO (Developer Certificate of Origin) plus REUSE/SPDX attribution.
- Contributors retain copyright over significant modifications.

## Security & Compliance

- **Security First:** Any new path resolution MUST be tested against Path Traversal. Use `PathTraversal` logic from `core`.
Expand Down
2 changes: 1 addition & 1 deletion README.it.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ SPDX-License-Identifier: Apache-2.0
<!-- zenzic:audit-badge -->
<img src="https://img.shields.io/badge/%F0%9F%9B%A1%EF%B8%8F_zenzic--audit-passing-22c55e?style=flat-square" alt="zenzic-audit">
<!-- zenzic:score-badge -->
<img src="https://img.shields.io/badge/%F0%9F%9B%A1%EF%B8%8F_zenzic--score-100_%2F_100-4f46e5?style=flat-square" alt="zenzic-score">
<img src="https://img.shields.io/badge/%F0%9F%9B%A1%EF%B8%8F_zenzic--score-98_%2F_100-f59e0b?style=flat-square" alt="zenzic-score">

<a href="https://reuse.software/">
<img src="https://img.shields.io/badge/REUSE-3.x%20compliant-0d9488?style=flat-square" alt="REUSE 3.x compliant">
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ SPDX-License-Identifier: Apache-2.0
<!-- zenzic:audit-badge -->
<img src="https://img.shields.io/badge/%F0%9F%9B%A1%EF%B8%8F_zenzic--audit-passing-22c55e?style=flat-square" alt="zenzic-audit">
<!-- zenzic:score-badge -->
<img src="https://img.shields.io/badge/%F0%9F%9B%A1%EF%B8%8F_zenzic--score-100_%2F_100-4f46e5?style=flat-square" alt="zenzic-score">
<img src="https://img.shields.io/badge/%F0%9F%9B%A1%EF%B8%8F_zenzic--score-98_%2F_100-f59e0b?style=flat-square" alt="zenzic-score">

<a href="https://reuse.software/">
<img src="https://img.shields.io/badge/REUSE-3.x%20compliant-0d9488?style=flat-square" alt="REUSE 3.x compliant">
Expand Down
14 changes: 11 additions & 3 deletions RELEASE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
<!-- SPDX-License-Identifier: Apache-2.0 -->
# Release Procedure — Zenzic Core

> **[MAINTAINER SOP]** *This document contains the Standard Operating Procedure for Core Maintainers to cut and publish a new release. If you are an end-user looking for new features, please see the [CHANGELOG](./CHANGELOG.md).*

## Release Metadata

| Field | Value |
| :------- | :--------- |
| Version | v0.9.1 |
| Codename | Basalt |
| Codename | Graphite |
| Date | 2026-06-02 |
| Status | Stable |

Expand All @@ -32,7 +34,7 @@ Before tagging, every item must be green:

```bash
# Bump version
uv run bump-my-version bump minor
uv run bump-my-version bump patch

# Build wheel + sdist
python -m build
Expand All @@ -46,8 +48,14 @@ Distribution target: **PyPI** — `pip install zenzic` / `uvx zenzic`.
## Tag & Push

```bash
# 1. Merge the release branch into main via PR first!
# 2. Switch to main and pull latest
git checkout main
git pull origin main

# 3. Tag the main branch and push
git tag v0.9.1
git push origin release/v0.9.1 --tags
git push origin main --tags
```

- [ ] Create GitHub Release from the tag, using the `## v0.9.1` CHANGELOG section as the release body.
Expand Down
2 changes: 2 additions & 0 deletions changelogs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ For the current release history, see the [main Changelog](../CHANGELOG.md).
| v0.6.x | Obsidian | 2026-04-12 | [v0.6.md](./v0.6.md) |
| v0.7.x | Quartz | 2026-05-07 | [v0.7.md](./v0.7.md) |
| v0.8.x | Basalt | 2026-05-15 to 2026-05-30 | [v0.8.md](./v0.8.md) |
| v0.9.x | Graphite | 2026-05-31 to 2026-06-02 | [main CHANGELOG](../CHANGELOG.md) |
| v0.10.x | TBD | Planned | — |

Archives follow [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) format.
All dates are git-tag verified for v0.4.0-rc2 and later.
2 changes: 1 addition & 1 deletion changelogs/v0.8.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Archive of release notes extracted from the main changelog.
- **Integrity Regression Check (`zenzic diff`):** Command to compare documentation state between branches; exits with code 4 on quality regression.
- **Config Genealogy (`zenzic explain`):** Introspection command to trace rule origin (Default vs Global vs Local TOML).
- **Rule Z108 (EMPTY_LINK_TEXT):** New validator to detect links with empty or whitespace-only labels.
- **MDX-Native Suppressions:** Support for JSX comment syntax `{/* zenzic:ignore */}` alongside standard HTML comments.
- **MDX-Native Suppressions:** Support for JSX comment syntax `{/* zenzic:ignore: Zxxx */}` alongside standard HTML comments.
- **Sovereign Audit Mode (`--audit`):** Global flag to bypass all suppressions for unfiltered repository inspection.
- **Privacy Gate (Z204):** Support for `.zenzic.local.toml` to enforce local-only forbidden patterns without repository leakage.
- **Core Hardening:** Native exclusion of system-critical files (`.zenzic.local.toml.example`, `*.sh`, `LICENSE`) from unused asset detection (Z405).
Expand Down
32 changes: 32 additions & 0 deletions changelogs/v0.9.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<!-- SPDX-FileCopyrightText: 2026 PythonWoods <dev@pythonwoods.dev> -->
<!-- SPDX-License-Identifier: Apache-2.0 -->
<!-- markdownlint-disable MD024 -->
# Changelog Archive — v0.9.x

Archive of release notes extracted from the main changelog.

## [0.9.0] — 2026-05-31

### Added

- `zenzic score --stamp`: deterministic, in-file badge stamping for score telemetry.
- `zenzic score --check-stamp`: config-aware freshness gate for stamped score badges.
- `badge_stamp_files` project metadata key to declare stamp targets.
- Domain-aware discovery exemptions for source-code assets in unused-asset analysis.
- `zenzic lab` command: empirical sandbox gallery covering 100% of Z-codes (20 scenarios).
- 15 new sandbox directories under `examples/` (z102 through z505), each with `.zenzic.toml`, `README.md`, and a minimal `docs/` tree that reliably triggers the target rule.
- `zenzic lab all` validation gate: all 20 scenarios emit the expected exit code.

### Changed

- Suppression debt model migrated to flat-cost scoring (one point per suppression).
- `suppression_cap` behavior clarified as an independent hard-fail governance gate.
- Local overlay parsing hardened with strict unknown-key rejection.
- `just verify` standardized to a five-step operational sequence (hooks, tests, strict check, stamp, freshness check).
- **Performance — Z204 (FORBIDDEN_TERM):** `scan_line_for_forbidden_terms` now accepts a pre-compiled RE2 union regex. `ZenzicConfig` builds the union once via `_recompile_forbidden_patterns()` (called in `model_post_init` and after every `_apply_local_toml` merge). Scan complexity reduced from O(N_lines × N_patterns) to O(N_lines).
- **Performance — Z601 (BRAND_OBSOLESCENCE):** `BrandObsolescenceRule` replaced per-pattern `list[RegexPattern]` with a single RE2 union pattern compiled once at `__init__`. Same O(N_lines) reduction.

### Removed

- Legacy adapter methods `map_url()` and `classify_route()` from the public adapter contract.
- Legacy score export path `--export-shields` in favor of native stamp/check-stamp telemetry.
16 changes: 16 additions & 0 deletions examples/z104-file-not-found/.zenzic.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# SPDX-FileCopyrightText: 2026 PythonWoods <dev@pythonwoods.dev>
# SPDX-License-Identifier: Apache-2.0
# .zenzic.toml — Z104 FILE_NOT_FOUND gallery example
#
# docs/index.md references api/reference.md, which does not exist on disk.
# Zenzic fires Z104 FILE_NOT_FOUND (penalty 8.0) — exit 1.
#
# Expected results:
# zenzic check links → EXIT 1 (Z104 ×1)
# zenzic check all → EXIT 1

docs_dir = "docs"
fail_under = 0

[build_context]
engine = "standalone"
25 changes: 25 additions & 0 deletions examples/z104-file-not-found/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<!-- SPDX-FileCopyrightText: 2026 PythonWoods <dev@pythonwoods.dev> -->
<!-- SPDX-License-Identifier: Apache-2.0 -->

# Z104 FILE_NOT_FOUND — Gallery Example

**Category:** Z1xx Link Integrity
**Expected exit:** 1 (error)

## What this demonstrates

`docs/index.md` contains a link to `api/reference.md`, which does not exist on
disk. Zenzic fires Z104 FILE_NOT_FOUND — a hard error that mandates exit 1.

## Run it

```bash
cd examples/z104-file-not-found
uvx zenzic check all
```

## Expected output

```text
docs/index.md:10:44 x [Z104] 'api/reference.md' not found in docs
```
17 changes: 17 additions & 0 deletions examples/z104-file-not-found/docs/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<!-- SPDX-FileCopyrightText: 2026 PythonWoods <dev@pythonwoods.dev> -->
<!-- SPDX-License-Identifier: Apache-2.0 -->

# Documentation

Welcome to the project documentation. This guide covers all aspects of
installation, configuration, and everyday usage for new and experienced users.

## API Reference

For the complete API specification, see the [API Reference](api/reference.md).
The API reference contains all endpoints, request formats, and response schemas.

## Getting Started

Install the package with your preferred package manager and follow the setup
wizard. Detailed instructions are available in the installation section.
16 changes: 16 additions & 0 deletions examples/z107-circular-anchor/.zenzic.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# SPDX-FileCopyrightText: 2026 PythonWoods <dev@pythonwoods.dev>
# SPDX-License-Identifier: Apache-2.0
# .zenzic.toml — Z107 CIRCULAR_ANCHOR gallery example
#
# docs/guide.md contains a self-referential anchor link: [Setup](#setup)
# whose link text slugifies to the same fragment as the target.
# Zenzic fires Z107 CIRCULAR_ANCHOR (warning) — exit 0.
#
# Expected results:
# zenzic check all → EXIT 0 (Z107 ×1, warning)

docs_dir = "docs"
fail_under = 0

[build_context]
engine = "standalone"
27 changes: 27 additions & 0 deletions examples/z107-circular-anchor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!-- SPDX-FileCopyrightText: 2026 PythonWoods <dev@pythonwoods.dev> -->
<!-- SPDX-License-Identifier: Apache-2.0 -->

# Z107 CIRCULAR_ANCHOR — Gallery Example

**Category:** Z1xx Link Integrity
**Expected exit:** 0 (warning)

## What this demonstrates

`docs/guide.md` contains the link `[Setup](#setup)` inside the `## Setup`
section. The link text "Setup" slugifies to `#setup`, which is the same
fragment as the containing heading — a circular self-reference.

## Run it

```bash
cd examples/z107-circular-anchor
uvx zenzic check all
```

## Expected output

```text
docs/guide.md:13:51 ! [Z107] Self-referential anchor link: '[Setup](#setup)'
slugifies to its own fragment. Replace with a meaningful target or remove the link.
```
18 changes: 18 additions & 0 deletions examples/z107-circular-anchor/docs/guide.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<!-- SPDX-FileCopyrightText: 2026 PythonWoods <dev@pythonwoods.dev> -->
<!-- SPDX-License-Identifier: Apache-2.0 -->

# Guide

This guide explains how to set up your Zenzic documentation project step by step.
It covers installation, configuration, and first run verification for all users.

## Setup

To begin, install the package and run the initial configuration command.
For advanced options, consult the reference documentation linked below.

This page contains a self-referential anchor link: [Setup](#setup)

## Next Steps

After completing setup, proceed to the tutorial section for hands-on examples.
17 changes: 17 additions & 0 deletions examples/z401-missing-directory-index/.zenzic.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# SPDX-FileCopyrightText: 2026 PythonWoods <dev@pythonwoods.dev>
# SPDX-License-Identifier: Apache-2.0
# .zenzic.toml — Z401 MISSING_DIRECTORY_INDEX gallery example
#
# docs/guide/ contains guide/page.md but has no index.md.
# Visiting /guide/ in the built site will return a 404.
# Zenzic fires Z401 (info) — exit 0. Run with --show-info to see it.
#
# Expected results:
# zenzic check all → EXIT 0 (Z401 ×1, suppressed as info)
# zenzic check all --show-info → EXIT 0 (Z401 ×1 visible)

docs_dir = "docs"
fail_under = 0

[build_context]
engine = "zensical"
27 changes: 27 additions & 0 deletions examples/z401-missing-directory-index/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<!-- SPDX-FileCopyrightText: 2026 PythonWoods <dev@pythonwoods.dev> -->
<!-- SPDX-License-Identifier: Apache-2.0 -->

# Z401 MISSING_DIRECTORY_INDEX — Gallery Example

**Category:** Z4xx Topology & Assets
**Expected exit:** 0 (info)

## What this demonstrates

`docs/guide/` contains `page.md` but has no `index.md`.
Navigating to `/guide/` in the built site will return a 404.
Zenzic fires Z401 MISSING_DIRECTORY_INDEX as an info finding.

## Run it

```bash
cd examples/z401-missing-directory-index
uvx zenzic check all --show-info
```

## Expected output

```text
docs/guide i [Z401] Directory contains Markdown files but has no index page
— the directory URL may return a 404.
```
13 changes: 13 additions & 0 deletions examples/z401-missing-directory-index/docs/guide/page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!-- SPDX-FileCopyrightText: 2026 PythonWoods <dev@pythonwoods.dev> -->
<!-- SPDX-License-Identifier: Apache-2.0 -->

# Installation Guide

This page covers installation of the package on Linux, macOS, and Windows.
Follow the steps below to get started with the package manager of your choice.
The installation takes approximately two minutes on a standard connection.

## Prerequisites

Ensure you have Python 3.10 or higher and pip installed on your system before
proceeding with the installation steps described in this document.
8 changes: 8 additions & 0 deletions examples/z401-missing-directory-index/zensical.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# SPDX-FileCopyrightText: 2026 PythonWoods <dev@pythonwoods.dev>
# SPDX-License-Identifier: Apache-2.0
[project]
site_name = "My Docs"
docs_dir = "docs"
nav = [
"guide/page.md",
]
16 changes: 16 additions & 0 deletions examples/z404-config-asset-missing/.zenzic.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# SPDX-FileCopyrightText: 2026 PythonWoods <dev@pythonwoods.dev>
# SPDX-License-Identifier: Apache-2.0
# .zenzic.toml — Z404 CONFIG_ASSET_MISSING gallery example
#
# mkdocs.yml declares theme.logo = assets/logo.svg, but
# docs/assets/logo.svg does not exist on disk.
# Zenzic fires Z404 CONFIG_ASSET_MISSING (warning) — exit 0.
#
# Expected results:
# zenzic check all → EXIT 0 (Z404 ×1, warning)

docs_dir = "docs"
fail_under = 0

[build_context]
engine = "mkdocs"
Loading