Skip to main content

Scoring Algorithm

The Zenzic Documentation Quality Score (DQS) is a deterministic, 0–100 integer computed from the findings of every active check. Given the same repository state, the algorithm always produces the same score.

For design rationale, a worked example, and CLI output interpretation, see Scoring Design.


Architecture Overview

The scoring pipeline has five sequential stages:

1. Security Gate → Z2xx finding? score = 0, early return.
2. Penalty Table → per-code deductions, per-tier caps.
3. Governance Esc. → exponential amplification if Z6xx > 10.
4. Gravity Cap → brand score = 0 ⟹ total ≤ 70.
5. Suppression Debt → subtract ω_debt from capped total.

Each stage is described below with its full formula.


Stage 1 — Security Override

Before any score computation, the engine checks for Z2xx findings:

Sfinal=0if cSnc>0S_{\text{final}} = 0 \quad \text{if } \sum_{c \in \mathcal{S}} n_c > 0

where S={Z201,Z202,Z203,Z204}\mathcal{S} = \{Z201, Z202, Z203, Z204\}.

This is an unconditional early return — no flags, no config options, and no suppressions can bypass it. The four codes in S\mathcal{S} represent binary failure conditions:

CodeNameCondition
Z201CREDENTIALCredential pattern detected in document
Z202PATH_TRAVERSALLink target escapes docs/ to a non-system path
Z203PATH_TRAVERSAL_FATALLink target resolves to an OS system path
Z204FORBIDDEN_TERMPrivacy Gate — confidential term exposure

When the Security Override fires, ScoreReport returns security_override=True and security_findings=N (total Z2xx count). The --strict flag and fail-on-error configuration are irrelevant — the gate operates before all of them.

:::danger Security Codes Are Non-Suppressible No inline <!-- zenzic:ignore -->, no per_file_ignores, and no excluded_dirs can suppress a Z2xx finding. The finding still fires. The score is still 0. :::


Stage 2 — Penalty Table and Tier Caps

If no Z2xx finding is present, the engine computes a per-tier score.

Zenzic Weight Matrix (5-Tier)

TierCategoryCodesWeightCap
Security GateZ2xxscore = 0
StructuralstructuralZ1xx30%30 pts
NavigationnavigationZ3xx, Z4xx25%25 pts
ContentcontentZ5xx20%20 pts
GovernancebrandZ404, Z405, Z406, Z6xx25%25 pts

Per-Category Formula

For each tier ii:

cat_ptsi=max ⁣(0,  wi×100ctieripenaltyc×nc)\text{cat\_pts}_i = \max\!\left(0,\; w_i \times 100 - \sum_{c \in \text{tier}_i} \text{penalty}_c \times n_c\right)

The Category Cap Invariant guarantees that a single tier cannot drag the score below its floor. For example, 1 000 occurrences of Z505 (1 pt each) exhaust the content bucket at −20 pts. The remaining 80 pts from other tiers are unaffected.

Base Score

Sbase=i{structural, navigation, content, brand}cat_ptsiS_{\text{base}} = \sum_{i \in \{\text{structural, navigation, content, brand}\}} \text{cat\_pts}_i

Penalty Reference Table

CodeNamePenalty / occurrenceTier
Z101LINK_BROKEN8.0 ptsStructural
Z102ANCHOR_MISSING5.0 ptsStructural
Z103ORPHAN_LINK2.0 ptsStructural
Z104FILE_NOT_FOUND8.0 ptsStructural
Z105ABSOLUTE_PATH2.0 ptsStructural
Z107CIRCULAR_ANCHOR1.0 ptsStructural
Z106CIRCULAR_LINK0.0 ptsInformational (no DQS impact)
Z108EMPTY_LINK_TEXT1.0 ptsStructural
Z111VIRTUAL_ROUTE_BROKEN8.0 ptsStructural
Z113AUTHOR_KEY_COLLISION2.0 ptsStructural
Z301DANGLING_REF4.0 ptsNavigation
Z302DEAD_DEF1.0 ptsNavigation
Z303DUPLICATE_DEF3.0 ptsNavigation
Z402ORPHAN_PAGE4.0 ptsNavigation
Z401MISSING_DIRECTORY_INDEX2.0 ptsNavigation
Z501PLACEHOLDER2.0 ptsContent
Z502SHORT_CONTENT1.0 ptsContent
Z503SNIPPET_ERROR10.0 ptsContent
Z505UNTAGGED_CODE_BLOCK1.0 ptsContent
Z403MISSING_ALT1.0 ptsContent
Z405UNUSED_ASSET3.0 ptsGovernance
Z404CONFIG_ASSET_MISSING3.0 ptsGovernance
Z406NAV_CONTRACT2.0 ptsGovernance
Z601BRAND_OBSOLESCENCE2.0 ptsGovernance

::: note Z106 — Knowledge Graph telemetry, not a defect Z106 is excluded from the penalty table by design. Elevating it to a scored finding would deduct Quality Score points, pressuring engineers to remove cross-links to satisfy the linter — a perverse incentive that degrades real documentation quality. Circular links in a Knowledge Graph are structural data, not defects. Z106 is emitted as topological telemetry; inspect it with --show-info. :::

:::note Z602 is not scored Z602 (I18N_PARITY) is a Governance gate that fires as a standalone finding. It does not contribute to any DQS bucket and therefore has no penalty value in the table above. :::


Stage 3 — Governance Escalation

Beyond 10 Z6xx occurrences, the engine applies an exponential amplifier to the Governance bucket deductions:

deductionbrand=min ⁣(capbrand,  deductionbrand×2nexcess/5) \text{deduction}_{\text{brand}}' = \min\!\left(\text{cap}_{\text{brand}},\; \text{deduction}_{\text{brand}} \times 2^{n_{\text{excess}} / 5}\right)

where nexcess=nZ6xx10n_{\text{excess}} = n_{Z6xx} - 10.

The deduction is capped at the Governance tier maximum (25 pts). The practical effect: a repository with 20 Z601 violations (10 excess → multiplier = 22=42^2 = 4) takes four times the normal governance hit.


Stage 4 — Gravity Cap

If the Governance bucket is fully zeroed by its deductions:

Sbase=min ⁣(Sbase,  70)if cat_ptsbrand=0S_{\text{base}} = \min\!\left(S_{\text{base}},\; 70\right) \quad \text{if } \text{cat\_pts}_{\text{brand}} = 0

Stage 5 — Suppression Debt

Every active suppression is an acknowledged assumption of responsibility. The flat-cost model deducts exactly 1 point per suppression, regardless of how many suppressions are present. There is no free allowance.

suppression_cap (default: 30) is a hard-fail threshold, not an allowance boundary. In zenzic score, the fail_under gate is evaluated first and the suppression-cap gate is evaluated after it; both remain independent controls. The penalty formula is independent of the cap:

ωdebt=n\omega_{\text{debt}} = n

where:

  • nn = total active suppressions (inline zenzic:ignore + per_file_ignores entries)

The final score is:

Sfinal=max ⁣(0,  Sbasen)S_{\text{final}} = \max\!\left(0,\; S_{\text{base}} - n\right)

Suppression Cost Reference

Suppression countCost per suppressionNotes
ncapn \leq \text{cap}1 pt eachManaged posture — every suppression costs
n>capn > \text{cap}1 pt eachHard-fail: zenzic score exits with code 1

:::info Boundary Condition — Configuration Invariant Because every suppression deducts 1 point, the maximum achievable score for a repository is:

Max Achievable Score=100Fs\text{Max Achievable Score} = 100 - |F_s|

where Fs|F_s| is the total active suppression count. Configuring fail_under > 100 - suppression_cap creates a mathematical contradiction. Safe configuration rule: fail_under100 - suppression_cap. :::

See Scoring Design — Dual-Gate Architecture and Result Interpretation for worked examples, CLI output guide, and governance posture semantics.


Complete Formula

Assembling all five stages:

Sfinal={0if cSnc>0(Security Override)max ⁣(0,  Sgravityn)otherwiseS_{\text{final}} = \begin{cases} 0 & \text{if } \sum_{c \in \mathcal{S}} n_c > 0 \quad \text{(Security Override)} \\[6pt] \max\!\left(0,\; S_{\text{gravity}} - n\right) & \text{otherwise} \end{cases}

where nn is the total active suppression count and:

Sgravity={min ⁣(Sbase,  70)if cat_ptsbrand=0SbaseotherwiseS_{\text{gravity}} = \begin{cases} \min\!\left(S_{\text{base}},\; 70\right) & \text{if } \text{cat\_pts}_{\text{brand}} = 0 \\ S_{\text{base}} & \text{otherwise} \end{cases}

See Also