Rule handbook metadata

Rule
DET.PY.KS_HASH_ATTRS · lane deterministic
Page status
current
page_version
2714cbe739f8cd59a730c408f5fd8b07d826fbff93b3ad3707bc7ff0aab86d03
generated_at
2026-05-25T16:20:00.000Z
registry_fingerprint
2ce40848effce579d3e4879f6ca85535183a14db201870a6c007da424624550c

How this rule is fixed

Pilot deterministic fixer: repo_overlay · harness mode repo_overlay · verify expect_rule_clean.

  • Harness: fixture includes a repo overlay tree; remediate_repo_overlay() writes catalog/repo files, then applies After HTML when present.
  • Production: repo_overlay Node adapter patches the website repository (contracts, registry JSON, CSS, Python stubs).

Detection module: docs/design/ux-audit/deterministic-design-rules.md#det-py-ks_hash_attrs. Scroll down for Before / After examples and Evidence and remediation steps.

Purpose

Kitchen Sink Python generators (components/layouts.py, components/components.py, generator/build-showcase.py, forge-autodoc/forge_autodoc/page.py, and registry-listed emitter modules) stamp governed visual roots with catalog markers: hash="XYZ", data-ks-hash="XYZ", data-ks-type, and data-ks-name. Those attributes must come from ks_hash_attrs() (components/ks_hash_attrs.py) or registry-backed wrappers in components/ks_catalog_hashes.pylayout_shell_attrs(), page_main_attrs(), and chrome_region_attrs() — not from hand-typed attribute strings in f-strings or concatenation.

This deterministic rule runs in the metrics phase on the kitchensink repo root (det-py-ks-hash-attrs.check.js). It scans Python under components/, generator/, and forge-autodoc/ for forbidden inline marker literals and verifies that every marker emitter module calls a canonical helper. DOM rules (DET.HASH.MARKERS, DET.HASH.REGISTRY_ROW) judge built HTML; DET.PY.KS_HASH_ATTRS catches emitters before a bad build ships incomplete or drift-prone markup.

Plan: When adding or editing a layout, chrome region, or showcase shell, resolve the hash in visual-registry.yaml first. Do: Import layout_shell_attrs / chrome_region_attrs from ks_catalog_hashes (or call ks_hash_attrs directly for one-off roots). Check: Run analyze-website-ux.mjs with --repo pointing at forgesdlc-kitchensink so pyKsHashAttrsReport is populated. Adjust: Replace manual literals; rebuild with python3 generator/build-showcase.py, then confirm DET.HASH.MARKERS on showcase/ or consumer website/.

Passing signals

  • Marker emitter modules call ks_hash_attrs(, layout_shell_attrs(, page_main_attrs(, or chrome_region_attrs(, or import ks_catalog_hashes — for example components/layouts.py sets _la_hbk = layout_shell_attrs("handbook_page") and splices the fragment onto class="container-fluid px-0".
  • components/components.py uses chrome_region_attrs("doc-breadcrumb") and chrome_region_attrs("doc-toc-sidebar") when rendering Kbc / Ktx chrome.
  • components/ks_catalog_hashes.py resolves registry rows and delegates to ks_hash_attrs for layout, chrome-region, page, and layout-preview types.
  • Built HTML shows paired markers on governed roots — handbook shell hash="Hbk" / data-ks-hash="Hbk", sidebar Ksr, breadcrumb Kbc, footer Ksf — with matching data-ks-type and data-ks-name slugs from the registry.
  • pyKsHashAttrsReport.issues is empty after scanning the repo root; emitterPathCount includes base emitters plus active registry source_paths ending in .py for layout, chrome-region, and layout-preview rows.
  • Only components/ks_hash_attrs.py may contain inline data-ks-hash= / hash="XYZ" string literals (canonical helper implementation).

Failing signals

  • manual-literal (minor): A .py file under components/, generator/, or forge-autodoc/ contains inline data-ks-hash=, data-ks-type=, data-ks-name=, or hash="XYZ" literals outside the exempt helper module — common in bootstrap scripts, defect fixtures, or copy-pasted HTML fragments embedded in Python.
  • missing-helper (minor): A registry-listed emitter module exists but never calls ks_hash_attrs or ks_catalog_hashes wrappers — emitters lose catalog alignment even if HTML looks correct today.
  • missing-emitter (warn): A registry source_paths entry points at a .py file that is absent on disk.
  • Emitted HTML may still fail DET.HASH.MARKERS when Python used partial manual strings — for example layout root with only data-ks-hash="Hbk" and no paired hash="Hbk", or breadcrumb with class ks-doc-breadcrumb but no Kbc markers.
  • Findings cap at MAX_PY_KS_HASH_ATTRS_FINDINGS (12) per pass with a trailing “additional issues omitted” message when many files violate at once.
  • Passing this rule does not prove registry membership (DET.HASH.REGISTRY_ROW) or contract presence (DET.CONTRACT.PATH).

Before example

Before (failing example)

Markers pasted manually in Python — not from chrome_region_attrs().

Failing emitted fragment (typical of manual Python attribute strings in a layout f-string): layout shell carries only data-ks-hash; doc breadcrumb omits catalog markers. A crawl reports DET.HASH.MARKERS incompleteMarkers even before DET.PY.KS_HASH_ATTRS flags the generator source.

After example

After (passing example)

Passing handbook shell (aligned with handbook_page in components/layouts.py): layout_shell_attrs("handbook_page"), chrome_region_attrs("doc-sidebar"), and chrome_region_attrs("doc-breadcrumb") emit the full helper fragment on each governed root.

Evidence and remediation

Capture: Save pyKsHashAttrsReport from analyze output (issues[] with kind, path, message) and the finding evidence string (python_source=<rel-path> kind=<kind>). For HTML validation, attach a built fragment from showcase/ or website/ and the matching ksVisualHashReport slice when DET.HASH.MARKERS also fires.

Kind Severity Remediation
manual-literal minor Remove inline hash= / data-ks-* strings; call ks_hash_attrs() or layout_shell_attrs / page_main_attrs / chrome_region_attrs.
missing-helper minor Add from ks_catalog_hashes import chrome_region_attrs, layout_shell_attrs (or equivalent) and splice _{var} = layout_shell_attrs("…") onto visual root class= / tag openers.
missing-emitter warn Restore the missing .py module or fix source_paths in visual-registry.yaml.

Remediation (PDCA):

  1. Plan — Locate the visual root in docs/design/catalog/visual-registry.yaml (hash, type, slug, source_paths). Prefer registry wrappers over hard-coded three-letter tokens.
  2. Do — In Python emitters, use layout_shell_attrs("handbook_page"), chrome_region_attrs("doc-breadcrumb"), or ks_hash_attrs(hash_id, visual_type, name); rebuild with python3 generator/build-showcase.py (and consumer site generators after submodule bump).
  3. Checknode tools/website-ux-auditor/analyze-website-ux.mjs --repo /path/to/forgesdlc-kitchensink with metrics including pyKsHashAttrsReport; grep built HTML for paired hash="XYZ" and data-ks-hash="XYZ".
  4. Adjust — For harness-only scripts that intentionally embed marker literals for defect fixtures, relocate strings outside scanned trees or refactor fixtures to call helpers; do not weaken the check for production emitters.

Module: design-rules/deterministic/generated/det-py-ks-hash-attrs.check.js (auditor package). Default severity minor; missing emitter modules use warn.