12 — No-Hardcode Enforcement Pack (scan patterns + SQL evidence checks)
title: 12 — No-Hardcode Enforcement Pack
date: 2026-05-31
gate: P10 (CI; runs from P2 onward)
scan_target: the WEB SOURCE REPO — NOT the built Nuxt bundle (the running image
nuxt-ssr-local:* is compiled; source is not server-readable, so CI must run in the repo)
12 — No-Hardcode Enforcement Pack
Enforces Hiến pháp no-hardcode-absolute + Đ26 §0-AU (add-row = INSERT) + Đ28 NT-D1/D3 (no business logic / config-in-PG). Two layers: static scan (web repo) + SQL evidence (live truth checks).
A. Static scan patterns (fail CI on match in the web source repo)
| # | pattern (regex, ripgrep) | catches |
|---|---|---|
| T-COUNT-1 | `\b(reduce | Math.abs |
| T-COUNT-2 | noi_chua|noi_sinh|totalGap |
the legacy gap-math vocabulary (health.get.ts) |
| T-CAT | ['"\]CAT-\d{3}['"`]` (outside test/fixtures) |
hardcoded registry codes |
| T-PIV | ['"\](PIV |
MTX)-[\w-]+['"`]` in render logic |
| T-SPECIES | `[\s*['"](catalog | collection |
| T-LABEL | `(labels? | groups?)\s*[:=]\s*[` with string literals |
| T-THRESH | `\b(>=? | <=? |
| T-DRILL | level\s*===?\s*\d / depth\s*===?\s*\d |
hierarchy depth branching in frontend |
| T-PIN | localStorage near pin/ghim |
client-side pin arrays |
| T-PHANTOM | literal phantom/orphan row objects in .vue/.ts |
injected fake rows |
| Allowlist: test fixtures, this KB, the mock-data.json preview (doc 10). A hit → write | ||
hardcode_violation / hc_finding_* to system_issues (existing pattern) and fail the build. |
B. SQL evidence checks (live, read-only — the Truth Check, Đ28 Test-4 / Đ31)
| # | check | query gist | pass condition |
|---|---|---|---|
| S-LEAF | leaf rule keys on data only | leaf count from composition_level/entity_type |
= 160, no CAT list used |
| S-NOFANOUT | integrity view is fan-out free | count(*) v_count_integrity == count(*) v_registry_leaf_set |
equal (160==160) |
| S-CLOSURE | net_gap fully explained | leaf net_gap == Σ v_count_drift.gap |
equal (132==132) |
| S-COVERAGE | every count pivot-backed or flagged | v_living_lists: pivot_backed vs PIVOT_MISSING counts |
sum = leaf_rows; missing explicit |
| S-PIVOTONLY | displayed counts trace to a pivot | each surfaced count has pivot_code or PIVOT_MISSING |
no count without provenance |
| S-LABELPG | grouping labels from PG | labels resolve via taxonomy/label_rules |
0 labels from a client array |
| S-THRESHPG | ceiling from PG | every ceiling resolves from display_policy |
0 literal 50 in resolution |
| S-PINPG | pins from PG | pinned set = registry_pin rows |
0 pins from localStorage |
| S-NOGAPMATH | no API gap-math | grep handlers for gap/noi_sinh reduce |
0 hits in converged surface |
C. Truth-Check (Đ28 Test-4): Nuxt == PG, 100%
For every count rendered on /knowledge/registries-pivot, assert
rendered_value == pivot_count(code) (or == view value). Coverage = 100% of surfaced counts
have a PG source; 0 counts computed in Nuxt. Run as an automated diff (rendered DOM vs PG) at P4.
D. Acceptance gates (per macro)
- P2 onward: static scan (A) green on the web repo;
hardcode_violationcount = 0. - P4 (production): S-checks (B) green + Truth-Check (C) 100% coverage + 0
hc_finding_*. - continuous: the scan re-runs per PR; regressions block merge.
E. Why scan the repo, not the image
The running container is nuxt-ssr-local:d28gmr-* (compiled bundle; minified, source not
allowlisted for read_file). A scan of the bundle is unreliable. CI must run in the web source
repository where .vue/.ts are readable — that is where hardcode is introduced and must be
caught. (This is logged so a future run does not waste effort scanning the served bundle.)