{
  "tool": "acceptance-item-gate-recheck",
  "pin": "/Users/nancy/.ai-ds-lab/pins/tvu-ds-7161df26",
  "subjectSha": "7161df2600ab48ce8e7340e07e3931451e4ddd6d",
  "caliper": "scripts/audit-*.mjs",
  "gateCount": 81,
  "selfClaimCount": 50,
  "selfClaimGateCount": 22,
  "selfClaims": [
    {
      "gate": "audit-acceptance-gate-coverage.mjs",
      "line": 19,
      "text": "//   gated                 有闸在头注释里自陈覆盖它（`covers-acceptance:` 行）"
    },
    {
      "gate": "audit-acceptance-gate-coverage.mjs",
      "line": 61,
      "text": "//   // covers-acceptance: docs/internal/domain-tvu.md#M17 [7]"
    },
    {
      "gate": "audit-acceptance-gate-coverage.mjs",
      "line": 374,
      "text": "const COVERS_RE = /^\\s*\\/\\/\\s*covers-acceptance:\\s*(\\S+)\\s*(?:\\[(\\d+)\\])?\\s*$/"
    },
    {
      "gate": "audit-acceptance-gate-coverage.mjs",
      "line": 518,
      "text": "'       新增的 Acceptance 段必须当场分类：给它上闸并在闸头注释写 `// covers-acceptance: <id>`，\\n' +"
    },
    {
      "gate": "audit-commit-affordance-trace.mjs",
      "line": 19,
      "text": "// covers-acceptance: docs/internal/code-conventions.md#R13 [4]"
    },
    {
      "gate": "audit-commit-affordance-trace.mjs",
      "line": 20,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M35 [0]"
    },
    {
      "gate": "audit-deliverable-open-questions.mjs",
      "line": 30,
      "text": "// covers-acceptance: docs/internal/design-process.md#Delivery [5]"
    },
    {
      "gate": "audit-handoff-deliverable-sections.mjs",
      "line": 46,
      "text": "// covers-acceptance: docs/internal/code-conventions.md#R13 [4]"
    },
    {
      "gate": "audit-handoff-deliverable-sections.mjs",
      "line": 47,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M35 [0]"
    },
    {
      "gate": "audit-handoff-deliverable-sections.mjs",
      "line": 48,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M37 [5]"
    },
    {
      "gate": "audit-handoff-deliverable-sections.mjs",
      "line": 49,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M42 [4]"
    },
    {
      "gate": "audit-handoff-deliverable-sections.mjs",
      "line": 50,
      "text": "// covers-acceptance: docs/internal/design-process.md#M-LIFECYCLE-CRUD [3]"
    },
    {
      "gate": "audit-handoff-deliverable-sections.mjs",
      "line": 51,
      "text": "// covers-acceptance: docs/internal/design-process.md#M-PRD-EG [2]"
    },
    {
      "gate": "audit-handoff-deliverable-sections.mjs",
      "line": 52,
      "text": "// covers-acceptance: docs/internal/design-process.md#M-CARDINALITY [2]"
    },
    {
      "gate": "audit-handoff-deliverable-sections.mjs",
      "line": 53,
      "text": "// covers-acceptance: docs/internal/design-process.md#M-IA-FIRST [2]"
    },
    {
      "gate": "audit-handoff-deliverable-sections.mjs",
      "line": 54,
      "text": "// covers-acceptance: docs/internal/design-process.md#M21.1 [3]"
    },
    {
      "gate": "audit-handoff-deliverable-sections.mjs",
      "line": 55,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#I2 [4]"
    },
    {
      "gate": "audit-handoff-deliverable-sections.mjs",
      "line": 56,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M32 [3]"
    },
    {
      "gate": "audit-handoff-deliverable-sections.mjs",
      "line": 57,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M36 [3]"
    },
    {
      "gate": "audit-handoff-deliverable-sections.mjs",
      "line": 58,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M36.1 [4]"
    },
    {
      "gate": "audit-mockup-bilingual-spacing.mjs",
      "line": 8,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M23.14 [5]"
    },
    {
      "gate": "audit-mockup-bilingual-spacing.mjs",
      "line": 9,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M47.2 [4]"
    },
    {
      "gate": "audit-mockup-binding-fidelity.mjs",
      "line": 8,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#C5 [0]"
    },
    {
      "gate": "audit-mockup-binding-fidelity.mjs",
      "line": 9,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M52 [2]"
    },
    {
      "gate": "audit-mockup-binding-fidelity.mjs",
      "line": 10,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#C7 [3]"
    },
    {
      "gate": "audit-mockup-colors.mjs",
      "line": 44,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M-COLOR [3]"
    },
    {
      "gate": "audit-mockup-colors.mjs",
      "line": 45,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M23.18.1 [7]"
    },
    {
      "gate": "audit-mockup-connector.mjs",
      "line": 12,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#C4 [3]"
    },
    {
      "gate": "audit-mockup-default-white-bg.mjs",
      "line": 38,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M23.18.2 [1]"
    },
    {
      "gate": "audit-mockup-feedback-not-selfdrawn.mjs",
      "line": 9,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M41"
    },
    {
      "gate": "audit-mockup-feedback-not-selfdrawn.mjs",
      "line": 10,
      "text": "// covers-acceptance: docs/internal/domain-tvu.md#M12"
    },
    {
      "gate": "audit-mockup-geometry-consistency.mjs",
      "line": 9,
      "text": "// covers-acceptance: docs/internal/domain-tvu.md#M17 [7]"
    },
    {
      "gate": "audit-mockup-handoff-evidence.mjs",
      "line": 3,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M-DISCIPLINE.SYNC [0]"
    },
    {
      "gate": "audit-mockup-hash-n-semantic.mjs",
      "line": 9,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M40 [3]"
    },
    {
      "gate": "audit-mockup-integrity.mjs",
      "line": 1,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M-INTEGRITY~3 [4]"
    },
    {
      "gate": "audit-mockup-integrity.mjs",
      "line": 8,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M-INTEGRITY [2]"
    },
    {
      "gate": "audit-mockup-jira-hyperlink.mjs",
      "line": 9,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#`Jira"
    },
    {
      "gate": "audit-mockup-library-origin.mjs",
      "line": 41,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M32.1 [3]"
    },
    {
      "gate": "audit-mockup-library-origin.mjs",
      "line": 42,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M32.2 [3]"
    },
    {
      "gate": "audit-mockup-overlap.mjs",
      "line": 7,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M-INTEGRITY~2 [6]"
    },
    {
      "gate": "audit-mockup-source-link-integrity.mjs",
      "line": 10,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M23.8.1"
    },
    {
      "gate": "audit-mockup-typography-icon.mjs",
      "line": 46,
      "text": "// covers-acceptance: docs/internal/design-process.md#M48 [8]"
    },
    {
      "gate": "audit-mockup-typography-icon.mjs",
      "line": 57,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M-TXT-ICON-AUDIT [2]"
    },
    {
      "gate": "audit-mockup-variant-axis.mjs",
      "line": 13,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#§M-VARIANT-AXIS [3]"
    },
    {
      "gate": "audit-product-code.mjs",
      "line": 3,
      "text": "// covers-acceptance: docs/internal/code-conventions.md#R14 [3]"
    },
    {
      "gate": "audit-product-code.mjs",
      "line": 4,
      "text": "// covers-acceptance: docs/internal/code-conventions.md#R16 [3]"
    },
    {
      "gate": "audit-product-code.mjs",
      "line": 5,
      "text": "// covers-acceptance: docs/internal/code-conventions.md#R19 [5]"
    },
    {
      "gate": "audit-product-code.mjs",
      "line": 6,
      "text": "// covers-acceptance: docs/internal/code-conventions.md#R20 [4]"
    },
    {
      "gate": "audit-product-code.mjs",
      "line": 7,
      "text": "// covers-acceptance: docs/internal/code-conventions.md#R21 [5]"
    },
    {
      "gate": "audit-source-discipline-identifiers.mjs",
      "line": 6,
      "text": "// covers-acceptance: docs/internal/mockup-conventions.md#M-DISCIPLINE.SOURCE"
    }
  ],
  "items": [
    {
      "n": "1",
      "gist": "几何一致性 ① 同类控件是否同宽",
      "anchor": {
        "file": "docs/internal/domain-tvu.md",
        "line": 231,
        "text": "- [ ] 几何一致性至少核到这几类：**同类控件是否同宽** · **同一控件在各状态帧之间是否同宽** · **动作按钮右缘是否对齐控件右缘** · 行高与分隔线间距"
      },
      "heading": {
        "line": 201,
        "level": 2,
        "text": "M17 — 新页面一律照该 surface 既有 canonical 范式（布局 + 控件几何 + 图标 + 按键行为）"
      },
      "narrowTerms": [
        "sameWidth",
        "same width",
        "widthUniform",
        "uniform-width",
        "G1"
      ],
      "narrowGateCount": 1,
      "narrowGates": [
        "audit-mockup-geometry-consistency.mjs"
      ],
      "narrowHits": [
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 15,
          "term": "G1",
          "text": "//   由 G1/G2/G3 + R1/R2/R3 全部覆盖；另 3 条是 handoff / 实测数字 / 按钮 hug 纪律（本闸不管，",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 45,
          "term": "G1",
          "text": "// --family controls (G1/G2/G3) — width & right edge:",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 46,
          "term": "G1",
          "text": "//   G1  same-component-width      — every instance of one component (grouped by",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 51,
          "term": "same width",
          "text": "//                                   control the same width.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 57,
          "term": "G1",
          "text": "// the ONE of that Acceptance line's four check items that had no probe: G1-G3",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 74,
          "term": "G1",
          "text": "// (§M-DISCIPLINE.SOURCE): pass --config <json> or set BASELINE_G1/G2/G3/R1/R2/R3.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 128,
          "term": "G1",
          "text": "baseline: { G1: 0, G2: 0, G3: 0, R1: 0, R2: 0, R3: 0 },",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 131,
          "term": "G1",
          "text": "//   { probe: 'G1', label: '<regex>', widths: [148, 480], reason: '...' }",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 331,
          "term": "same width",
          "text": "// Sibling state frames of one feature: same parent, same width AND height.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 386,
          "term": "G1",
          "text": "// G1 — every instance of one component must share one width.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 387,
          "term": "G1",
          "text": "export function probeG1(controls, cfg) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 399,
          "term": "G1",
          "text": "probe: 'G1',",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 471,
          "term": "G1",
          "text": "// G1/G2/G3 all measure WIDTH and RIGHT EDGE (horizontal). The vertical",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 478,
          "term": "G1",
          "text": "// (`1 s` / `2 s` / `Continuous`), not rows. Feeding them to G1/G2 produced 2/2",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 729,
          "term": "G1",
          "text": "export const CONTROL_PROBES = ['G1', 'G2', 'G3']",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 762,
          "term": "G1",
          "text": "raw.G1 = probeG1(controls, cfg)",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 984,
          "term": "G1",
          "text": "for (const f of findings.G1 || []) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 985,
          "term": "G1",
          "text": "console.log(`G1 same-component-width — \"${f.label}\" has ${f.widths.length} widths: ${f.widths.join(' / ')} across ${f.count} instances`)",
          "comment": false
        }
      ],
      "excludedFalsePositives": [],
      "wideGateCount": 48,
      "wideGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "wideOnlyGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "narrowOnlyGates": []
    },
    {
      "n": "2",
      "gist": "几何一致性 ② 同一控件跨状态帧同宽",
      "anchor": {
        "file": "docs/internal/domain-tvu.md",
        "line": 231,
        "text": "- [ ] 几何一致性至少核到这几类：**同类控件是否同宽** · **同一控件在各状态帧之间是否同宽** · **动作按钮右缘是否对齐控件右缘** · 行高与分隔线间距"
      },
      "heading": {
        "line": 201,
        "level": 2,
        "text": "M17 — 新页面一律照该 surface 既有 canonical 范式（布局 + 控件几何 + 图标 + 按键行为）"
      },
      "narrowTerms": [
        "acrossStates",
        "across-states",
        "stateFrame",
        "G2"
      ],
      "narrowGateCount": 1,
      "narrowGates": [
        "audit-mockup-geometry-consistency.mjs"
      ],
      "narrowHits": [
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 15,
          "term": "G2",
          "text": "//   由 G1/G2/G3 + R1/R2/R3 全部覆盖；另 3 条是 handoff / 实测数字 / 按钮 hug 纪律（本闸不管，",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 45,
          "term": "G2",
          "text": "// --family controls (G1/G2/G3) — width & right edge:",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 49,
          "term": "G2",
          "text": "//   G2  cross-frame-same-name     — sibling state frames of one feature (same",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 74,
          "term": "G2",
          "text": "// (§M-DISCIPLINE.SOURCE): pass --config <json> or set BASELINE_G1/G2/G3/R1/R2/R3.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 128,
          "term": "G2",
          "text": "baseline: { G1: 0, G2: 0, G3: 0, R1: 0, R2: 0, R3: 0 },",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 337,
          "term": "G2",
          "text": "// grouping test used to just VANISH, so \"G2 never looked at it\" was output as",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 338,
          "term": "G2",
          "text": "// \"G2 checked and found nothing\". That is the exact ambiguity this gate exists",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 409,
          "term": "G2",
          "text": "// G2 — a same-named control must keep one width across sibling state frames.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 412,
          "term": "G2",
          "text": "export function probeG2(root, cfg, componentIndex = new Map(), stateScan = collectStateFrameGroups(root)) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 421,
          "term": "stateFrame",
          "text": "byName.get(key).push({ ...c, stateFrame: frame.name || frame.id })",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 425,
          "term": "stateFrame",
          "text": "const seenFrames = new Set(items.map(i => i.stateFrame))",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 430,
          "term": "G2",
          "text": "probe: 'G2',",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 432,
          "term": "stateFrame",
          "text": "stateFrameSize: group.size,",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 435,
          "term": "stateFrame",
          "text": "nodes: items.map(i => ({ id: i.id, frame: i.stateFrame, width: +i.width.toFixed(1) })),",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 471,
          "term": "G2",
          "text": "// G1/G2/G3 all measure WIDTH and RIGHT EDGE (horizontal). The vertical",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 478,
          "term": "G2",
          "text": "// (`1 s` / `2 s` / `Continuous`), not rows. Feeding them to G1/G2 produced 2/2",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 729,
          "term": "G2",
          "text": "export const CONTROL_PROBES = ['G1', 'G2', 'G3']",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 763,
          "term": "G2",
          "text": "raw.G2 = probeG2(root, cfg, componentIndex, stateScan)",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 812,
          "term": "stateFrame",
          "text": "...(wantControls ? { stateFramesRejected: stateScan.rejected } : {}),",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 819,
          "term": "G2",
          "text": "// ⚠️ State frame ids belong here: G2's judged object IS the frame, not the",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 834,
          "term": "stateFrame",
          "text": "stateFrameRejectedIds: new Set(stateScan.rejectedIds),",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 906,
          "term": "stateFrame",
          "text": "stateFramesRejected, stateFrameRejectedIds,",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 917,
          "term": "G2",
          "text": "// scope (the classifier saw it) but no G2 finding could ever come from it, so",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 920,
          "term": "stateFrame",
          "text": "if (PROBE_ID && stateFrameRejectedIds?.has(PROBE_ID)) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 923,
          "term": "G2",
          "text": "console.error('   ⛔ Do NOT read a G2 pass as \"this frame was checked\".')",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 932,
          "term": "stateFrame",
          "text": "...(probeKeys.some(k => !ROW_PROBES.includes(k)) ? { stateFramesRejected } : {}),",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 954,
          "term": "stateFrame",
          "text": "const sfRej = Object.entries(stateFramesRejected).filter(([, n]) => n > 0)",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 988,
          "term": "G2",
          "text": "for (const f of findings.G2 || []) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 989,
          "term": "stateFrame",
          "text": "console.log(`G2 cross-frame-same-name — \"${f.control}\" differs across ${f.stateFrameSize} state frames under \"${f.parent}\": ${f.widths.join(' / ')}`)",
          "comment": false
        }
      ],
      "excludedFalsePositives": [],
      "wideGateCount": 48,
      "wideGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "wideOnlyGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "narrowOnlyGates": []
    },
    {
      "n": "3",
      "gist": "几何一致性 ③ 动作按钮右缘对齐控件右缘",
      "anchor": {
        "file": "docs/internal/domain-tvu.md",
        "line": 231,
        "text": "- [ ] 几何一致性至少核到这几类：**同类控件是否同宽** · **同一控件在各状态帧之间是否同宽** · **动作按钮右缘是否对齐控件右缘** · 行高与分隔线间距"
      },
      "heading": {
        "line": 201,
        "level": 2,
        "text": "M17 — 新页面一律照该 surface 既有 canonical 范式（布局 + 控件几何 + 图标 + 按键行为）"
      },
      "narrowTerms": [
        "rightEdge",
        "right edge",
        "right-edge",
        "G3"
      ],
      "narrowGateCount": 1,
      "narrowGates": [
        "audit-mockup-geometry-consistency.mjs"
      ],
      "narrowHits": [
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 15,
          "term": "G3",
          "text": "//   由 G1/G2/G3 + R1/R2/R3 全部覆盖；另 3 条是 handoff / 实测数字 / 按钮 hug 纪律（本闸不管，",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 37,
          "term": "right edge",
          "text": "//   the action button had five different x positions, and the button right edge",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 38,
          "term": "right edge",
          "text": "//   sat ~191px away from the control right edge. None of I1 (section aspect",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 45,
          "term": "right edge",
          "text": "// --family controls (G1/G2/G3) — width & right edge:",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 52,
          "term": "right-edge",
          "text": "//   G3  action-right-edge-align   — per frame, the action-button group's right",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 53,
          "term": "right edge",
          "text": "//                                   edge must equal the input-control right edge.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 57,
          "term": "G3",
          "text": "// the ONE of that Acceptance line's four check items that had no probe: G1-G3",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 74,
          "term": "G3",
          "text": "// (§M-DISCIPLINE.SOURCE): pass --config <json> or set BASELINE_G1/G2/G3/R1/R2/R3.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 128,
          "term": "G3",
          "text": "baseline: { G1: 0, G2: 0, G3: 0, R1: 0, R2: 0, R3: 0 },",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 442,
          "term": "right edge",
          "text": "// G3 — per frame, action-button right edge must equal control right edge.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 443,
          "term": "G3",
          "text": "export function probeG3(controls, cfg) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 458,
          "term": "G3",
          "text": "probe: 'G3',",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 471,
          "term": "G3",
          "text": "// G1/G2/G3 all measure WIDTH and RIGHT EDGE (horizontal). The vertical",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 729,
          "term": "G3",
          "text": "export const CONTROL_PROBES = ['G1', 'G2', 'G3']",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 764,
          "term": "G3",
          "text": "raw.G3 = probeG3(controls, cfg)",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 992,
          "term": "G3",
          "text": "for (const f of findings.G3 || []) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 993,
          "term": "right-edge",
          "text": "console.log(`G3 action-right-edge — \"${f.frame}\": controls right ${f.controlRight} vs buttons right ${f.buttonRight}  (Δ ${f.delta})`)",
          "comment": false
        }
      ],
      "excludedFalsePositives": [],
      "wideGateCount": 48,
      "wideGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "wideOnlyGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "narrowOnlyGates": []
    },
    {
      "n": "4",
      "gist": "几何一致性 ④ 行高与分隔线间距",
      "anchor": {
        "file": "docs/internal/domain-tvu.md",
        "line": 231,
        "text": "- [ ] 几何一致性至少核到这几类：**同类控件是否同宽** · **同一控件在各状态帧之间是否同宽** · **动作按钮右缘是否对齐控件右缘** · 行高与分隔线间距"
      },
      "heading": {
        "line": 201,
        "level": 2,
        "text": "M17 — 新页面一律照该 surface 既有 canonical 范式（布局 + 控件几何 + 图标 + 按键行为）"
      },
      "narrowTerms": [
        "rowHeight",
        "row-height",
        "rowGap",
        "R1",
        "R2",
        "R3",
        "family rows"
      ],
      "narrowGateCount": 8,
      "narrowGates": [
        "audit-canonical-compliance.mjs",
        "audit-claude-design-reference-numbers.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-product-code.mjs"
      ],
      "narrowHits": [
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 7,
          "term": "R1",
          "text": "//   CC-R1  inline visual affordance class (__check / __radio / __dot / __indicator",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 9,
          "term": "R2",
          "text": "//   CC-R2  native <button> without aria-expanded disclosure semantics — should use Button",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 10,
          "term": "R3",
          "text": "//   CC-R3  native <input> outside the Input canonical wrappers",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 15,
          "term": "R1",
          "text": "//    这四条**与 `docs/internal/code-conventions.md` 的 R1/R2/R3/R4 语义毫无关系** —— 那边的",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 16,
          "term": "R1",
          "text": "//    R1/R3 讲的是完全不同的东西。此前本文件的规则叫裸 `R1`–`R4`，于是仓库里同时存在至少三套",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 18,
          "term": "R1",
          "text": "//    `RN-R1`–`RN-R9` · doc 侧的 R-rule），而 `audit:rule-number-collision` **只守 doc 侧编号，",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 81,
          "term": "R1",
          "text": "'CC-R1': {",
          "comment": false
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 86,
          "term": "R2",
          "text": "'CC-R2': {",
          "comment": false
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 101,
          "term": "R3",
          "text": "'CC-R3': {",
          "comment": false
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 133,
          "term": "R2",
          "text": "// 过渡正则：`(?:CC-)?` 让新形态 `allow CC-R2` 与旧形态 `allow R2` **都**被接受，",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 151,
          "term": "R2",
          "text": "// `rule` 形如 `CC-R2`；取数字部分再拼过渡正则，于是同一份判据同时认 `allow R2` 与 `allow CC-R2`。",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 187,
          "term": "R1",
          "text": "// line-based rules: CC-R1, CC-R3",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 190,
          "term": "R1",
          "text": "for (const id of ['CC-R1', 'CC-R3']) {",
          "comment": false
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 194,
          "term": "R3",
          "text": "if (id === 'CC-R3' && rule.fileWhitelistBasenames.has(base)) continue",
          "comment": false
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 201,
          "term": "R2",
          "text": "// window-based rules: CC-R2, CC-R4",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 202,
          "term": "R2",
          "text": "for (const id of ['CC-R2', 'CC-R4']) {",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 19,
          "term": "R1",
          "text": "* 下方检查项的编号一律带 **`RN-`** 前缀（`RN-R1` … `RN-R9`）。它们是**本文件私有的**检查项",
          "comment": true
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 20,
          "term": "R1",
          "text": "* 编号，与 `docs/internal/code-conventions.md` 的 `R1` / `R3` / `R9` **语义完全无关**",
          "comment": true
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 27,
          "term": "R1",
          "text": "* ⚠️ 同类未清的还有一处（`audit-canonical-compliance.mjs` 的 `R1`–`R4`）—— 它的爆炸半径大得多",
          "comment": true
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 88,
          "term": "R1",
          "text": "// ---------- RN-R1 registry 自身形状 ----------",
          "comment": true
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 92,
          "term": "R1",
          "text": "fail('RN-R1', `registry.numbers 的键 \"${num}\" 不是两位数字编号`)",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 97,
          "term": "R1",
          "text": "fail('RN-R1', `编号 ${num} 的 owner=\"${owner}\" 非法（只能是 producer / curated-remote / retired）`)",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 101,
          "term": "R1",
          "text": "if (entry.file) fail('RN-R1', `编号 ${num} 标了 retired 却还带 file=\"${entry.file}\"`)",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 102,
          "term": "R1",
          "text": "if (!entry.note) fail('RN-R1', `编号 ${num} 标了 retired 但没写 note（为何退役 / 为何不许复用）`)",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 107,
          "term": "R1",
          "text": "fail('RN-R1', `编号 ${num} 的 file=\"${entry.file}\" 不合 \\`NN-<kebab>.md\\` 形状`)",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 109,
          "term": "R1",
          "text": "fail('RN-R1', `编号 ${num} 的 file=\"${entry.file}\" 前缀号与键不一致`)",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 113,
          "term": "R1",
          "text": "fail('RN-R1', `编号 ${num} owner=producer 但 producers[] 为空 —— 谁出这个号？`)",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 116,
          "term": "R1",
          "text": "if (!producersCfg[p]) fail('RN-R1', `编号 ${num} 引用了未登记的 producer \"${p}\"`)",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 121,
          "term": "R1",
          "text": "if (!entry.project) fail('RN-R1', `编号 ${num} owner=curated-remote 但没写 project（哪个远端项目占的）`)",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 123,
          "term": "R1",
          "text": "fail('RN-R1', `编号 ${num} 的 project=\"${entry.project}\" 不在 registry.projects[] 里`)",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 141,
          "term": "R1",
          "text": "fail('RN-R1', `项目 ${p.id} 缺 observedNumbered[]（那次 list_files 看到的编号文件实况）`)",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 144,
          "term": "R1",
          "text": "if (!projectById.has(t)) fail('RN-R1', `producer 的 targets 里有未登记的项目 \"${t}\"`)",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 148,
          "term": "R2",
          "text": "// ---------- RN-R2 载入两个 producer 的编号表 ----------",
          "comment": true
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 154,
          "term": "R2",
          "text": "fail('RN-R2', `producer \"${id}\" 的 script 不存在：${cfg.script}`)",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 162,
          "term": "R2",
          "text": "fail('RN-R2', `producer \"${id}\" 载入失败（${cfg.script}）：${e.message}`)",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 167,
          "term": "R2",
          "text": "'RN-R2',",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 177,
          "term": "R2",
          "text": "fail('RN-R2', `producer \"${id}\" 的表里有不合 \\`NN-<kebab>.md\\` 形状的条目：\"${bundleName}\"`)",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 182,
          "term": "R3",
          "text": "'RN-R3',",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 192,
          "term": "R3",
          "text": "// ---------- RN-R3 跨 producer：同号必同名 ----------",
          "comment": true
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 199,
          "term": "R3",
          "text": "'RN-R3',",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 242,
          "term": "R1",
          "text": "if (!byNum) continue // RN-R1/RN-R2 已报",
          "comment": false
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 280,
          "term": "R1",
          "text": "if (!p) continue // RN-R1 已报",
          "comment": false
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 1,
          "term": "R1",
          "text": "// audit-commit-affordance-trace.mjs — R13 §Acceptance 的 commit-message 半边：",
          "comment": true
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 13,
          "term": "R1",
          "text": "//   · 3 行短语 / N/A 档格式 = `docs/internal/code-conventions.md` §R13 §Acceptance",
          "comment": true
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 16,
          "term": "R1",
          "text": "//   · 同义词矩阵 = 同文件 §R13 §Step 2 那张表，**每次运行现读**",
          "comment": true
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 19,
          "term": "R1",
          "text": "// covers-acceptance: docs/internal/code-conventions.md#R13 [4]",
          "comment": true
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 22,
          "term": "R1",
          "text": "//     R13 与已上闸的 handoff 半边（M35 行）**逐字同一套格式** ⇒ 同一条判据覆盖两个 id。",
          "comment": true
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 66,
          "term": "R1",
          "text": "//      自画 SVG / Unicode 字形 / emoji 当图标由**后果面**的闸守：`audit-product-code.mjs` R1",
          "comment": true
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 85,
          "term": "R1",
          "text": "/** R13 Step 2 表的行：`| \\`<category>\\` | \\`kw\\`, \\`kw\\` | … |` */",
          "comment": true
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 89,
          "term": "R1",
          "text": "* 从 `code-conventions.md` 里切出 §R13 → §Step 2 那一段。",
          "comment": true
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 94,
          "term": "R1",
          "text": "export function sliceR13Step2(md) {",
          "comment": false
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 96,
          "term": "R1",
          "text": "const start = lines.findIndex((l) => /^##\\s+R13\\b/.test(l))",
          "comment": false
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 109,
          "term": "R1",
          "text": "* 现读 R13 的 affordance 同义词矩阵。",
          "comment": true
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 113,
          "term": "R1",
          "text": "const seg = sliceR13Step2(md)",
          "comment": false
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 123,
          "term": "R1",
          "text": "'R13 §Step 2 的 affordance 关键词矩阵解析为 0 行 —— 判定面塌了（不是「今天没关键词」）。' +",
          "comment": false
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 124,
          "term": "R1",
          "text": "'去看 docs/internal/code-conventions.md 的 §R13 / §Step 2 标题是不是被改了。',",
          "comment": false
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 160,
          "term": "R1",
          "text": "'affordance 图标闭集为空 —— src/icons/index.ts 的导出形态或 R13 关键词矩阵塌了，' +",
          "comment": false
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 193,
          "term": "R1",
          "text": "'affordance 图标名闭集为空 —— src/icons/generated/*.ts 的 `name:` 形态或 R13 关键词矩阵塌了，' +",
          "comment": false
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 381,
          "term": "R1",
          "text": "console.error('   规则真源 = docs/internal/code-conventions.md §R13 §Acceptance。commit message 里补 3 行：')",
          "comment": false
        },
        {
          "gate": "audit-gate-regression-ratchet.mjs",
          "line": 277,
          "term": "R1",
          "text": "*     ① **一条闸守四条 L1 规则** —— M37 / M42 / M-LIFECYCLE-CRUD / M35(=R13) 各自那份可落盘",
          "comment": true
        },
        {
          "gate": "audit-gate-regression-ratchet.mjs",
          "line": 280,
          "term": "R1",
          "text": "*     正是无人会人眼核的那一类；② 它是 R13 三个落点里 handoff 那个的唯一守卫（PR description",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 2,
          "term": "R1",
          "text": "// （INFRA-F123 「一次落地关三条」，2026-08-14；**第 4 条 M35/R13 affordance trace 于 2026-08-21 并入**）",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 4,
          "term": "R1",
          "text": "// 治的病：M37 / M42 / M-LIFECYCLE-CRUD / M35(=R13) 四条规则**各自的正文里已经指定了一份可落盘产物**",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 31,
          "term": "R1",
          "text": "// 🔑 **M35/R13 那条为什么 2026-08-21 才进来 —— backlog 那句封路理由是假的（活源实测）**：",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 36,
          "term": "R1",
          "text": "//      R13（code-conventions.md §R13 Acceptance）：「commit message / PR description / handoff doc 必含 3 行 trace」，",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 38,
          "term": "R1",
          "text": "//    ⇒ 「够不着」只对 R0 / M48 成立；M35/R13 的 **handoff 那一半本闸就够得着**，零新格式、零新挂载点。",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 39,
          "term": "R1",
          "text": "//    ⚠️ ~~仍然没关的是 R13 的 commit-message 那一半~~ → **✅ 2026-08-24 已关**：",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 41,
          "term": "R1",
          "text": "//    ⛔ 仍别把本闸读成「M35/R13 已整条上闸」——**PR description 那一落点仍是 L1、无闸**",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 43,
          "term": "R1",
          "text": "//    `code-conventions.md` §R13 §Acceptance 那张表。（本行 2026-08-27 订正：原文写「没关」，",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 46,
          "term": "R1",
          "text": "// covers-acceptance: docs/internal/code-conventions.md#R13 [4]",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 99,
          "term": "R1",
          "text": "//   ⛔ **`code-conventions.md#R18`（R18 = M37 的 code 端镜像；2026-09-02 前记作 `#Acceptance~11`，",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 101,
          "term": "R1",
          "text": "//      RULES 表的 `rule:` 字段对 M37 只点了 **mockup 侧**（唯有 M35/R13 那条双侧都点，所以它才有",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 107,
          "term": "R1",
          "text": "//     M35 与 R13 是**逐字同一套格式**（见下方「⛔ 别在这里发明第二套格式」），",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 127,
          "term": "R1",
          "text": "//      代码侧 `scripts/audit-product-code.mjs` 的 R1 `no-inline-svg-or-arrow-icons`（npm 别名 =",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 182,
          "term": "R1",
          "text": "rule: 'mockup-conventions.md M35 / code-conventions.md R13（affordance 元素必含 3 行 trace：Affordance: / Vocabulary scan: / Chosen:）',",
          "comment": false
        },
        {
          "gate": "audit-mockup-feedback-not-selfdrawn.mjs",
          "line": 17,
          "term": "R2",
          "text": "//       与 R21 的落法逐字一致：只落两个真源都同意的那一半）· [4] variants 区标注。",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 15,
          "term": "R1",
          "text": "//   由 G1/G2/G3 + R1/R2/R3 全部覆盖；另 3 条是 handoff / 实测数字 / 按钮 hug 纪律（本闸不管，",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 55,
          "term": "R1",
          "text": "// --family rows (R1/R2/R3) — row rhythm & dividers (backlog CANONICAL-F104,",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 62,
          "term": "row-height",
          "text": "//   R1  row-height-uniform        — inside one row grid, all rows share a height.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 63,
          "term": "R2",
          "text": "//   R2  row-gap-uniform           — inside one row grid, all adjacent gaps are equal.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 64,
          "term": "R3",
          "text": "//   R3  divider-consistency       — dividers share one thickness AND one colour.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 65,
          "term": "R1",
          "text": "//   ⚠️ R1 and R2 are deliberately separate: equal rows with unequal gaps and",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 67,
          "term": "R2",
          "text": "//      symptom that prompted F104's follow-up (\"表格的行之间还放了间距\") is the R2",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 68,
          "term": "R1",
          "text": "//      half — R1 structurally cannot see it.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 69,
          "term": "family rows",
          "text": "//   ⛔ `--family rows` FAILS CLOSED on an empty denominator: zero classified row",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 74,
          "term": "R1",
          "text": "// (§M-DISCIPLINE.SOURCE): pass --config <json> or set BASELINE_G1/G2/G3/R1/R2/R3.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 80,
          "term": "R1",
          "text": "// (an unbudgeted, un-filed investment). R1/R2/R3 inherit that gap: their",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 86,
          "term": "family rows",
          "text": "//   node scripts/audit-mockup-geometry-consistency.mjs --file <k> --node <n> --family rows",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 128,
          "term": "R1",
          "text": "baseline: { G1: 0, G2: 0, G3: 0, R1: 0, R2: 0, R3: 0 },",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 135,
          "term": "R1",
          "text": "// ---- `--family rows` classifier (R1/R2/R3) ----",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 141,
          "term": "R1",
          "text": "//    1. **判据本体早就做完了、也没被否**：`--family rows` 的 R1/R2/R3 三条判据 2026-08-27",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 150,
          "term": "R1",
          "text": "//    4. ⚠️ **`baseline` 里的 R1/R2/R3 仍是 0，⛔ 别因为「不常开」就把它们调高** ——",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 152,
          "term": "family rows",
          "text": "//       自己跑 `--family rows --stats` 定自己的数，那是**它的**决定，不是本仓的默认。",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 157,
          "term": "R1",
          "text": "//       R1 复用 `widthTolerance` 不成立 · R3 全树扫扁平矩形得 785 个候选），逐条实证见下方",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 182,
          "term": "R1",
          "text": "// 1–4px drift R1 exists to catch is not. The excluded count is always",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 197,
          "term": "R1",
          "text": "// R1 exists to find. 0.5 absorbs sub-pixel float noise (12.3 vs 12.4) and",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 201,
          "term": "R1",
          "text": "// ⛔⛔ **别加「把 `layoutSizingVertical='HUG'` 的行从 R1 排除」这个开关。**",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 202,
          "term": "R1",
          "text": "// owner 2026-08-27 拍定：**R1 判据不动**。这不是「还没想到」，是量完分布后否掉的。",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 215,
          "term": "R1",
          "text": "//   已把 28 条降到实际要看的 ~11 条，**改 R1 只移动边界、不省人力** ⇒ 收益恒为零。",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 221,
          "term": "R3",
          "text": "// population (they sit BETWEEN rows) and fed to R3 instead.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 223,
          "term": "R3",
          "text": "// Where R3 looks for dividers. 'row-subtree' (default) = only inside the",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 229,
          "term": "R3",
          "text": "// a polluted denominator turns R3 into noise, and noise gets muted.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 469,
          "term": "R1",
          "text": "// ---------- `--family rows` (R1/R2/R3) — the VERTICAL half ----------",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 473,
          "term": "R1",
          "text": "// probe at all — backlog CANONICAL-F104. R1/R2/R3 are that half.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 505,
          "term": "R3",
          "text": "// the LCD row grids, and counting them split R3 into phantom shapes.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 525,
          "term": "R3",
          "text": "// Dividers sit BETWEEN rows — they are R3's subject, not rows.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 553,
          "term": "R3",
          "text": "// Non-enumerable so JSON output stays clean while R3 can still walk",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 577,
          "term": "R1",
          "text": "// R1 — inside one row grid, every row must share one height.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 578,
          "term": "R1",
          "text": "export function probeR1(groups, cfg) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 584,
          "term": "R1",
          "text": "probe: 'R1',",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 597,
          "term": "R2",
          "text": "// R2 — inside one row grid, every gap between adjacent rows must be equal.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 598,
          "term": "R1",
          "text": "// ⚠️ NOT redundant with R1 (§处方 3.2): equal rows with unequal gaps and equal",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 600,
          "term": "R1",
          "text": "// symptom (\"表格的行之间还放了间距\") is the R2 half — R1 cannot see it.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 605,
          "term": "R2",
          "text": "export function probeR2(groups, cfg) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 611,
          "term": "R2",
          "text": "probe: 'R2',",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 624,
          "term": "R3",
          "text": "// R3 — dividers must share one thickness AND one colour (F104 判据 ④).",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 677,
          "term": "R3",
          "text": "// R3 compares dividers WITHIN one row grid, not across the whole file.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 680,
          "term": "R3",
          "text": "// design variety as a defect. Grouping per row grid is what makes R3 actionable.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 681,
          "term": "R3",
          "text": "export function probeR3(dividersByGroup, cfg) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 700,
          "term": "R3",
          "text": "probe: 'R3',",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 730,
          "term": "R1",
          "text": "export const ROW_PROBES = ['R1', 'R2', 'R3']",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 734,
          "term": "R1",
          "text": "// exact behaviour it had before R1/R2/R3 existed.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 751,
          "term": "R3",
          "text": "// Dividers are collected PER row grid so R3 can compare within a screen",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 767,
          "term": "R1",
          "text": "raw.R1 = probeR1(rowScan.groups, cfg)",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 768,
          "term": "R2",
          "text": "raw.R2 = probeR2(rowScan.groups, cfg)",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 769,
          "term": "R3",
          "text": "raw.R3 = probeR3(dividersByGroup, cfg)",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 789,
          "term": "family rows",
          "text": "// used to be executed on the rows side only, so a `--family rows` run emitted",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 970,
          "term": "R1",
          "text": "for (const f of findings.R1 || []) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 971,
          "term": "row-height",
          "text": "console.log(`R1 row-height-uniform — \"${f.label}\" rows are not equal height (mode ${f.modeHeight}): ${f.heights.join(' / ')}`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 974,
          "term": "R2",
          "text": "for (const f of findings.R2 || []) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 976,
          "term": "R2",
          "text": "console.log(`R2 row-gap-uniform — \"${f.label}\" (${al}) gaps differ: ${f.gaps.join(' / ')}`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 979,
          "term": "R3",
          "text": "for (const f of findings.R3 || []) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 980,
          "term": "R3",
          "text": "console.log(`R3 divider-consistency — ${f.shapes.length} divider shapes (thickness spread ${f.thicknessSpread}, colours ${f.distinctColours.length})`)",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 3,
          "term": "R1",
          "text": "// covers-acceptance: docs/internal/code-conventions.md#R14 [3]",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 4,
          "term": "R1",
          "text": "// covers-acceptance: docs/internal/code-conventions.md#R16 [3]",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 5,
          "term": "R1",
          "text": "// covers-acceptance: docs/internal/code-conventions.md#R19 [5]",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 6,
          "term": "R2",
          "text": "// covers-acceptance: docs/internal/code-conventions.md#R20 [4]",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 7,
          "term": "R2",
          "text": "// covers-acceptance: docs/internal/code-conventions.md#R21 [5]",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 10,
          "term": "R1",
          "text": "//     probe R14 / R19」并点名本脚本；§Acceptance~8 逐字给了 probe",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 11,
          "term": "R1",
          "text": "//     `grep -rEn '\"\\s*[a-zA-Z]+\\s*\\p{Han}+\\s*\"'` → 0 命中，与下方 R16 判据同构。",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 17,
          "term": "R2",
          "text": "//   R20 / R21 是 2026-09-07 Q5 `build-with-gate` 新增（M40 / M41 的 code 端镜像）。",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 22,
          "term": "R1",
          "text": "//   R1  — Inline SVG / Unicode arrow / emoji icons forbidden.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 27,
          "term": "R1",
          "text": "//                 `// AUDIT-IGNORE-R1: <reason>` line marker.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 29,
          "term": "R2",
          "text": "//   R2  — Hex literal colors forbidden — use TVU color tokens.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 33,
          "term": "R2",
          "text": "//         Escape: `// AUDIT-IGNORE-R2: <reason>`.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 35,
          "term": "R2",
          "text": "//   R2S — Hardcoded spacing/radius px forbidden when a TVU token exists — use var(--sp-*) / var(--r-*).",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 36,
          "term": "R2",
          "text": "//         The spacing half of §R2 token discipline (\"颜色 / 间距值\"). Flags px in a",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 40,
          "term": "R2",
          "text": "//         Escape: `// AUDIT-IGNORE-R2S: <reason>`.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 42,
          "term": "R1",
          "text": "//   R15 — Hand-rolled native control forbidden — use TVU DS component.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 45,
          "term": "R1",
          "text": "//         (R15 Library-First). <input type=\"hidden|file\"> is skipped (no DS",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 47,
          "term": "R1",
          "text": "//                 `// AUDIT-IGNORE-R15: <reason>` line marker.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 49,
          "term": "R1",
          "text": "//   R16 — Mixed CN+EN string literal forbidden — use i18n key.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 54,
          "term": "R1",
          "text": "//               console.* calls. Escape: `// AUDIT-IGNORE-R16: <reason>`.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 56,
          "term": "R1",
          "text": "//   R14 — Inline-style `position: absolute|fixed` without an adjacent comment.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 57,
          "term": "R1",
          "text": "//         Mirrors §R14 (Layout Primitive; M31 code-side). INLINE styles only",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 61,
          "term": "R1",
          "text": "//         line). Escape: adjacent comment / `<!-- LIBRARY GAP -->` / `// AUDIT-IGNORE-R14`.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 63,
          "term": "R1",
          "text": "//   R19 — Selected-indicator anti-patterns. Mirrors §R19 (M38 code-side). Scoped to a",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 67,
          "term": "R1",
          "text": "//         px (must be a Spacing token `var(--sp-xxs)`+). Escape: `// AUDIT-IGNORE-R19`.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 76,
          "term": "R2",
          "text": "//   R20 — `#N` label derived from an index without a `#N semantic:` declaration.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 77,
          "term": "R2",
          "text": "//         Mirrors §R20 (M40 code-side). Closed set `{sequence, name}` 逐字取自 M40.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 79,
          "term": "R2",
          "text": "//         Escape: `// AUDIT-IGNORE-R20: <reason>`.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 81,
          "term": "R2",
          "text": "//   R21 — overlay/scrim background or backdrop-filter not going through the mask tokens.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 82,
          "term": "R2",
          "text": "//         Mirrors §R21 (M41 code-side). ⛔ 刻意不判遮罩**数值**（0.2 vs 0.6 是 C3，见 Q11）。",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 83,
          "term": "R2",
          "text": "//         Escape: `// AUDIT-IGNORE-R21: <reason>`.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 183,
          "term": "R1",
          "text": "// R15 — native control opening tags (lowercase only; PascalCase = DS component, OK).",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 213,
          "term": "R2",
          "text": "// Require a non-alphanumeric boundary after the rule token so AUDIT-IGNORE-R2",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 214,
          "term": "R1",
          "text": "// does not also match AUDIT-IGNORE-R2S, and AUDIT-IGNORE-R1 does not match R15/R16.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 238,
          "term": "R1",
          "text": "// ---------- R14 / R19 / R9 — Class-A rule detectors (pure, exported for planted-drift tests) ----------",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 240,
          "term": "R1",
          "text": "// These mirror code-conventions §R14 / §R19 / §R9 (themselves code-side mirrors of",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 247,
          "term": "R1",
          "text": "const R14_COMMENT_RE = /\\/\\/|\\/\\*|\\*\\/|<!--|-->/",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 249,
          "term": "R1",
          "text": "return R14_COMMENT_RE.test(line) || (!!prev && R14_COMMENT_RE.test(prev))",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 252,
          "term": "R1",
          "text": "// R14 — inline-style `position: absolute|fixed` WITHOUT an adjacent affordance comment.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 255,
          "term": "R1",
          "text": "// self-documenting → out of scope (code-conventions §R14 acceptance, \"ESLint rule 候选\").",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 256,
          "term": "R1",
          "text": "const R14_INLINE_POS_RE = /style\\s*=\\s*[\"'{][^\"']*?position\\s*:\\s*['\"]?(absolute|fixed)\\b/i",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 257,
          "term": "R1",
          "text": "const R14_POS_LOCATE_RE = /position\\s*:\\s*['\"]?(absolute|fixed)\\b/i",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 258,
          "term": "R1",
          "text": "export function findR14Violations(content) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 263,
          "term": "R1",
          "text": "if (!R14_INLINE_POS_RE.test(line)) continue",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 264,
          "term": "R1",
          "text": "if (lineHasIgnore(line, 'R14')) continue",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 267,
          "term": "R1",
          "text": "const m = line.match(R14_POS_LOCATE_RE)",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 273,
          "term": "R1",
          "text": "hint: `inline style position:${kw} without an adjacent affordance comment — prefer flex/grid, or annotate the overlay/anchor reason (R14)`,",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 279,
          "term": "R1",
          "text": "// R19 — selected-indicator anti-patterns. Scoped to a selected-context selector block",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 284,
          "term": "R1",
          "text": "const R19_SELECTED_RE = /selected|aria-selected|\\bis-active\\b|(?:^|[.&\\s>~+])active\\b/i",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 285,
          "term": "R1",
          "text": "const R19_BOXSHADOW_RE = /box-shadow\\s*:[^;{}]*var\\(\\s*--brand/i",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 286,
          "term": "R1",
          "text": "const R19_RAIL_PX_RE = /border(?:-(?:left|right|top|bottom))?\\s*:\\s*\\d+px\\b[^;{}]*var\\(\\s*--brand/i",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 299,
          "term": "R1",
          "text": "const isSel = stack.some(Boolean) || R19_SELECTED_RE.test(selectorBuf)",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 315,
          "term": "R1",
          "text": "export function findR19Violations(content) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 322,
          "term": "R1",
          "text": "if (lineHasIgnore(line, 'R19')) continue",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 323,
          "term": "R1",
          "text": "if (R19_BOXSHADOW_RE.test(line)) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 328,
          "term": "R1",
          "text": "hint: 'selected indicator via box-shadow outline — use left-accent border spec (border-left: var(--sp-xxs) solid var(--brand)) (R19)',",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 331,
          "term": "R1",
          "text": "if (R19_RAIL_PX_RE.test(line)) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 336,
          "term": "R1",
          "text": "hint: 'selected rail width hardcoded in px — use a Spacing token var(--sp-xxs)+ so it tracks the design system (R19)',",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 402,
          "term": "R2",
          "text": "// ---------- R20 / R21 — code-side mirrors of mockup M40 / M41 (2026-09-07, Q5 `build-with-gate`) ----------",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 405,
          "term": "R2",
          "text": "//   R20 的闭集 `{sequence, name}` 与声明行 `#N semantic: <值>` 取自",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 407,
          "term": "R2",
          "text": "//   R21 的 token 名取自 `src/tokens/variables.css` 的 `--mask-overlay` / `--mask-overlay-blur`。",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 412,
          "term": "R2",
          "text": "// R20 — `#N` list-item label rendered from an index MUST carry a `#N semantic:` declaration.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 429,
          "term": "R2",
          "text": "//   R20 的真实射程是消费仓（`--dir <消费仓>`）+ 未来新写的 DS 代码；它是**前向 fail-closed** 闸，",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 431,
          "term": "R2",
          "text": "// Escape: `// AUDIT-IGNORE-R20: <reason>`.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 432,
          "term": "R2",
          "text": "export const R20_SEMANTIC_VALUES = ['sequence', 'name']",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 433,
          "term": "R2",
          "text": "const R20_DECL_RE = /#N\\s+semantic\\s*:\\s*([A-Za-z][\\w-]*)/",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 435,
          "term": "R2",
          "text": "const R20_INDEX_IDENT = String.raw`(?:[ijn]|idx|index|ordinal|position|pos|rank|seq|[A-Za-z_$][\\w$]*(?:Index|Idx|Ordinal|Position|Rank))`",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 437,
          "term": "R2",
          "text": "const R20_TRIGGER_RES = [",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 439,
          "term": "R2",
          "text": "new RegExp(String.raw`#\\$\\{\\s*[^}]*\\b${R20_INDEX_IDENT}\\b[^}]*\\}`),",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 441,
          "term": "R2",
          "text": "new RegExp(String.raw`#\\{\\{\\s*[^}]*\\b${R20_INDEX_IDENT}\\b[^}]*\\}\\}`),",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 443,
          "term": "R2",
          "text": "new RegExp(String.raw`['\"]#['\"]\\s*\\+\\s*[^;,)]*\\b${R20_INDEX_IDENT}\\b`),",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 445,
          "term": "R2",
          "text": "export function findR20Violations(content) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 450,
          "term": "R2",
          "text": "const hit = R20_TRIGGER_RES.find((re) => re.test(line))",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 452,
          "term": "R2",
          "text": "if (lineHasIgnore(line, 'R20')) continue",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 455,
          "term": "R2",
          "text": "const decl = near.match(R20_DECL_RE)",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 465,
          "term": "R2",
          "text": "`declare one of ${R20_SEMANTIC_VALUES.join(' / ')} on an adjacent line ` +",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 466,
          "term": "R2",
          "text": "'(sequence = 自动生成 · 删除后重排; name = 手动输入 · 删除后不改后续序号) (R20; mirrors M40)',",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 470,
          "term": "R2",
          "text": "if (!R20_SEMANTIC_VALUES.includes(decl[1])) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 477,
          "term": "R2",
          "text": "`legal values are exactly ${R20_SEMANTIC_VALUES.join(' / ')} (R20; closed set 逐字取自 M40 语义类型表)`,",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 484,
          "term": "R2",
          "text": "// R21 — overlay / scrim / mask backdrop MUST go through the mask tokens.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 499,
          "term": "R2",
          "text": "//       + 行内 `// AUDIT-IGNORE-R21: <reason>`。",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 500,
          "term": "R2",
          "text": "// Escape: `// AUDIT-IGNORE-R21: <reason>`.",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 501,
          "term": "R2",
          "text": "const R21_TOKEN_DEF_FILE_RE = /(?:^|[\\\\/])tokens[\\\\/]variables\\.css$/",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 502,
          "term": "R2",
          "text": "const R21_CONTEXT_RE = /overlay|scrim|\\bmask\\b|backdrop/i",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 503,
          "term": "R2",
          "text": "const R21_RAW_RGBA_RE = /background(?:-color)?\\s*:\\s*[^;{}]*\\brgba?\\(/i",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 504,
          "term": "R2",
          "text": "const R21_RAW_BLUR_RE = /backdrop-filter\\s*:\\s*[^;{}]*\\bblur\\(\\s*(?!var\\()/i",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 505,
          "term": "R2",
          "text": "const R21_HAS_VAR_RE = /var\\(\\s*--mask-overlay/",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 514,
          "term": "R1",
          "text": "// ⇒ 机制照抄本文件 R19 已验证过的 `selectedContextByLine`（同一份花括号深度追踪范式）。",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 527,
          "term": "R2",
          "text": "const isCtx = stack.some(Boolean) || R21_CONTEXT_RE.test(selectorBuf)",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 545,
          "term": "R2",
          "text": "const R21_MASK_DECL_RE = /(?:background(?:-color)?|backdrop-filter)\\s*:/i",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 546,
          "term": "R2",
          "text": "export function findR21ContextUnits(content, filename = '') {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 547,
          "term": "R2",
          "text": "if (R21_TOKEN_DEF_FILE_RE.test(String(filename).replace(/\\\\/g, '/'))) return []",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 554,
          "term": "R2",
          "text": "if (!R21_MASK_DECL_RE.test(lines[i])) continue",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 559,
          "term": "R2",
          "text": "export function findR21Violations(content, filename = '') {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 560,
          "term": "R2",
          "text": "if (R21_TOKEN_DEF_FILE_RE.test(String(filename).replace(/\\\\/g, '/'))) return []",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 567,
          "term": "R2",
          "text": "if (lineHasIgnore(line, 'R21')) continue",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 569,
          "term": "R2",
          "text": "if (!inCtx[i] && !R21_CONTEXT_RE.test(line)) continue",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 570,
          "term": "R2",
          "text": "if (R21_HAS_VAR_RE.test(line)) continue",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 571,
          "term": "R2",
          "text": "if (R21_RAW_RGBA_RE.test(line)) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 578,
          "term": "R2",
          "text": "'(the single sanctioned mask in the system) (R21; mirrors M41)',",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 580,
          "term": "R2",
          "text": "} else if (R21_RAW_BLUR_RE.test(line)) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 587,
          "term": "R2",
          "text": "'blur(var(--mask-overlay-blur)) (R21; mirrors M41)',",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 596,
          "term": "R1",
          "text": "const violations = { R1: [], R2: [], R2S: [], R14: [], R19: [], R15: [], R16: [], R20: [], R21: [] }",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 617,
          "term": "R1",
          "text": "if (!lineHasIgnore(lines[i], 'R1') && !libraryGap(lines[i], prev)) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 618,
          "term": "R1",
          "text": "violations.R1.push({",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 622,
          "term": "R1",
          "text": "rule: 'R1',",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 635,
          "term": "R1",
          "text": "// ---- R1: unicode arrows / emoji in template/JSX text ----",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 636,
          "term": "R1",
          "text": "if (!lineHasIgnore(line, 'R1') && !libraryGap(line, prev)) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 643,
          "term": "R1",
          "text": "violations.R1.push({",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 647,
          "term": "R1",
          "text": "rule: 'R1',",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 652,
          "term": "R1",
          "text": "violations.R1.push({",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 656,
          "term": "R1",
          "text": "rule: 'R1',",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 663,
          "term": "R2",
          "text": "// ---- R2: hex literal colors ----",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 664,
          "term": "R2",
          "text": "if (!lineHasIgnore(line, 'R2')) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 683,
          "term": "R2",
          "text": "violations.R2.push({",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 687,
          "term": "R2",
          "text": "rule: 'R2',",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 693,
          "term": "R2",
          "text": "// ---- R2S: hardcoded spacing/radius px with a token equivalent ----",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 694,
          "term": "R2",
          "text": "if (!lineHasIgnore(line, 'R2S')) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 696,
          "term": "R2",
          "text": "violations.R2S.push({",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 700,
          "term": "R2",
          "text": "rule: 'R2S',",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 706,
          "term": "R1",
          "text": "// ---- R15: hand-rolled native control ----",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 707,
          "term": "R1",
          "text": "if (!lineHasIgnore(line, 'R15') && !libraryGap(line, prev)) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 713,
          "term": "R1",
          "text": "violations.R15.push({",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 717,
          "term": "R1",
          "text": "rule: 'R15',",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 723,
          "term": "R1",
          "text": "// ---- R16: mixed CN+EN string ----",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 724,
          "term": "R1",
          "text": "if (!isI18n && !lineHasIgnore(line, 'R16') && !isCommentOnlyLine(line)) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 732,
          "term": "R1",
          "text": "violations.R16.push({",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 736,
          "term": "R1",
          "text": "rule: 'R16',",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 746,
          "term": "R1",
          "text": "// ---- R14 / R19 (blocking) + R9 (advisory) — content-level detectors ----",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 747,
          "term": "R1",
          "text": "for (const v of findR14Violations(buf)) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 748,
          "term": "R1",
          "text": "violations.R14.push({ file: rel, line: v.line, col: v.col, rule: 'R14', hint: v.hint })",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 750,
          "term": "R1",
          "text": "for (const v of findR19Violations(buf)) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 751,
          "term": "R1",
          "text": "violations.R19.push({ file: rel, line: v.line, col: v.col, rule: 'R19', hint: v.hint })",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 756,
          "term": "R2",
          "text": "// ---- R20 / R21 (blocking) — code-side mirrors of M40 / M41 (Q5, 2026-09-07) ----",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 757,
          "term": "R2",
          "text": "for (const v of findR20Violations(buf)) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 758,
          "term": "R2",
          "text": "violations.R20.push({ file: rel, line: v.line, col: v.col, rule: 'R20', hint: v.hint })",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 760,
          "term": "R2",
          "text": "for (const v of findR21Violations(buf, rel)) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 761,
          "term": "R2",
          "text": "violations.R21.push({ file: rel, line: v.line, col: v.col, rule: 'R21', hint: v.hint })",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 800,
          "term": "R1",
          "text": "R1: violations.R1.length,",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 801,
          "term": "R2",
          "text": "R2: violations.R2.length,",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 802,
          "term": "R2",
          "text": "R2S: violations.R2S.length,",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 803,
          "term": "R1",
          "text": "R14: violations.R14.length,",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 804,
          "term": "R1",
          "text": "R19: violations.R19.length,",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 805,
          "term": "R1",
          "text": "R15: violations.R15.length,",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 806,
          "term": "R1",
          "text": "R16: violations.R16.length,",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 807,
          "term": "R2",
          "text": "R20: violations.R20.length,",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 808,
          "term": "R2",
          "text": "R21: violations.R21.length,",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 812,
          "term": "R1",
          "text": "counts.R1 + counts.R2 + counts.R2S + counts.R14 + counts.R19 + counts.R15 + counts.R16 +",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 813,
          "term": "R2",
          "text": "counts.R20 + counts.R21 > 0",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 821,
          "term": "R1",
          "text": "R1: { name: 'no-inline-svg-or-arrow-icons', pass: counts.R1 === 0, count: counts.R1, violations: violations.R1 },",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 822,
          "term": "R2",
          "text": "R2: { name: 'no-hex-literal-colors', pass: counts.R2 === 0, count: counts.R2, violations: violations.R2 },",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 823,
          "term": "R2",
          "text": "R2S: { name: 'no-hardcoded-spacing', pass: counts.R2S === 0, count: counts.R2S, violations: violations.R2S },",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 824,
          "term": "R1",
          "text": "R14: { name: 'no-inline-absolute-without-comment', pass: counts.R14 === 0, count: counts.R14, violations: violations.R14 },",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 825,
          "term": "R1",
          "text": "R19: { name: 'no-selected-indicator-antipattern', pass: counts.R19 === 0, count: counts.R19, violations: violations.R19 },",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 826,
          "term": "R1",
          "text": "R15: { name: 'no-native-element', pass: counts.R15 === 0, count: counts.R15, violations: violations.R15 },",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 827,
          "term": "R1",
          "text": "R16: { name: 'no-mixed-cn-en-string', pass: counts.R16 === 0, count: counts.R16, violations: violations.R16 },",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 828,
          "term": "R2",
          "text": "R20: { name: 'hash-n-semantic-declared', pass: counts.R20 === 0, count: counts.R20, violations: violations.R20 },",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 829,
          "term": "R2",
          "text": "R21: { name: 'overlay-uses-mask-token', pass: counts.R21 === 0, count: counts.R21, violations: violations.R21 },",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 853,
          "term": "R1",
          "text": "console.log(`   R1  no-inline-svg-or-arrow-icons        0 violations`)",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 854,
          "term": "R2",
          "text": "console.log(`   R2  no-hex-literal-colors               0 violations`)",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 855,
          "term": "R2",
          "text": "console.log(`   R2S no-hardcoded-spacing                0 violations`)",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 856,
          "term": "R1",
          "text": "console.log(`   R14 no-inline-absolute-without-comment  0 violations`)",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 857,
          "term": "R1",
          "text": "console.log(`   R19 no-selected-indicator-antipattern   0 violations`)",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 858,
          "term": "R1",
          "text": "console.log(`   R15 no-native-element                   0 violations`)",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 859,
          "term": "R1",
          "text": "console.log(`   R16 no-mixed-cn-en-string               0 violations`)",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 860,
          "term": "R2",
          "text": "console.log(`   R20 hash-n-semantic-declared            0 violations`)",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 861,
          "term": "R2",
          "text": "console.log(`   R21 overlay-uses-mask-token             0 violations`)",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 871,
          "term": "R1",
          "text": "for (const rule of ['R1', 'R2', 'R2S', 'R14', 'R19', 'R15', 'R16', 'R20', 'R21']) {",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 875,
          "term": "R2",
          "text": "const ref = rule === 'R2S' ? 'R2' : rule",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 877,
          "term": "R1",
          "text": "if (rule === 'R1') console.log('  Hint: consume icons from @ux-team/tvu-design-system; escape via <!-- LIBRARY GAP: ... -->')",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 878,
          "term": "R2",
          "text": "if (rule === 'R2') console.log('  Hint: use TVU color tokens (CSS var / Tailwind theme); #fff / #000 / transparent allowed')",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 879,
          "term": "R2",
          "text": "if (rule === 'R2S') console.log('  Hint: use TVU spacing/radius tokens var(--sp-*) / var(--r-*); escape via // AUDIT-IGNORE-R2S')",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 880,
          "term": "R1",
          "text": "if (rule === 'R14') console.log('  Hint: prefer flex/grid; overlay/anchor absolute needs an adjacent comment; escape via // AUDIT-IGNORE-R14')",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 881,
          "term": "R1",
          "text": "if (rule === 'R19') console.log('  Hint: selected indicator = left-accent border var(--sp-xxs) solid var(--brand); no box-shadow outline / no px rail; escape via // AUDIT-IGNORE-R19')",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 882,
          "term": "R1",
          "text": "if (rule === 'R15') console.log('  Hint: use canonical Button / InputBox* / SelectBox*; escape via <!-- LIBRARY GAP: ... --> or // AUDIT-IGNORE-R15')",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 883,
          "term": "R1",
          "text": "if (rule === 'R16') console.log('  Hint: move UI string to i18n key; escape via // AUDIT-IGNORE-R16')",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 884,
          "term": "R2",
          "text": "if (rule === 'R20') console.log('  Hint: 这个号是系统按位置生成的（sequence · 删除后重排），还是这条数据自带的（name · 删除后不改后续序号）？相邻行写 `#N semantic: <sequence|name>`；escape via // AUDIT-IGNORE-R20')",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 885,
          "term": "R2",
          "text": "if (rule === 'R21') console.log('  Hint: overlay/scrim 走 var(--mask-overlay) + blur(var(--mask-overlay-blur))，⛔ 别另写裸 rgba/blur；escape via // AUDIT-IGNORE-R21')",
          "comment": false
        }
      ],
      "excludedFalsePositives": [],
      "wideGateCount": 48,
      "wideGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "wideOnlyGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "narrowOnlyGates": [
        "audit-claude-design-reference-numbers.mjs"
      ]
    },
    {
      "n": "5",
      "gist": "按钮宽度按文字 hug + 统一 padding（⛔ 不写死宽度/改字体）",
      "anchor": {
        "file": "docs/internal/domain-tvu.md",
        "line": 233,
        "text": "- [ ] 按钮宽度按文字 hug + 统一 padding，**不为「看起来一样宽」写死宽度或改字体** —— 跨字体族字宽差异不构成偏差（见本节字体注记）"
      },
      "heading": {
        "line": 201,
        "level": 2,
        "text": "M17 — 新页面一律照该 surface 既有 canonical 范式（布局 + 控件几何 + 图标 + 按键行为）"
      },
      "narrowTerms": [
        "hug",
        "hugs",
        "textAutoResize",
        "WIDTH_AND_HEIGHT"
      ],
      "narrowGateCount": 1,
      "narrowGates": [
        "audit-mockup-geometry-consistency.mjs"
      ],
      "narrowHits": [
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 15,
          "term": "hug",
          "text": "//   由 G1/G2/G3 + R1/R2/R3 全部覆盖；另 3 条是 handoff / 实测数字 / 按钮 hug 纪律（本闸不管，",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 480,
          "term": "hug",
          "text": "// \"fixing\" (\"按钮宽度按文字 hug…不为「看起来一样宽」写死宽度\"). Rows are found",
          "comment": true
        }
      ],
      "excludedFalsePositives": [
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 30,
          "term": "hug",
          "text": "//     gap) vs ZH line lineHeight ≈ ZHsize×1.15 (ZH hugs its EN) → ratio ≈1.8.",
          "comment": true,
          "falsePositive": "hugs its EN"
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 483,
          "term": "hug",
          "text": "console.log(`        ≈ ZHsize×1.15 (ZH hugs its EN). EN/ZH lineHeight ratio must be ≥ ${GROUPING_RATIO}.`)",
          "comment": false,
          "falsePositive": "hugs its EN"
        },
        {
          "gate": "audit-mockup-html-conformance.mjs",
          "line": 15,
          "term": "hug",
          "text": "//                                   inline style size → renders at intrinsic/huge size",
          "comment": true,
          "falsePositive": "huge size"
        },
        {
          "gate": "audit-mockup-html-conformance.mjs",
          "line": 160,
          "term": "hug",
          "text": "push('unconstrained-svg', lineOf(html, m.index), 'Inline <svg> has no width/height attribute, no inline size, and no CSS size (class or element rule). It will render at its intrinsic/huge size. Constrain it (width/height attr or a sized CSS class), or use the DS Icon component.')",
          "comment": false,
          "falsePositive": "huge size"
        }
      ],
      "wideGateCount": 48,
      "wideGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "wideOnlyGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "narrowOnlyGates": []
    },
    {
      "n": "6",
      "gist": "三张卡等宽（同一数值 = PRD 宽度）",
      "anchor": {
        "file": "docs/internal/mockup-conventions.md",
        "line": 892,
        "text": "- [ ] PRD 卡宽 == UX Delivery 卡宽 == User Journey 卡宽（**同一数值 = PRD 宽度**）"
      },
      "heading": {
        "line": 865,
        "level": 4,
        "text": "M23.12 — UX/PRD 卡 Placement & Sizing（紧邻其描述的 mockup + 竖向 Auto Layout hug + 标准宽）— 2026-06-01/06-05 合并（原 M23.12 placement + M23.13 sizing）"
      },
      "narrowTerms": [
        "cardWidth",
        "card width",
        "PRD",
        "UX Delivery",
        "User Journey"
      ],
      "narrowGateCount": 11,
      "narrowGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-deliverable-open-questions.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-hash-n-semantic.mjs",
        "audit-mockup-jira-hyperlink.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-rule-inventory.mjs"
      ],
      "narrowHits": [
        {
          "gate": "audit-acceptance-gate-coverage.mjs",
          "line": 167,
          "term": "PRD",
          "text": "//     `design-process#M-PRD-EG` / `#M-CARDINALITY` / `#M-IA-FIRST` 上闸，自陈落",
          "comment": true
        },
        {
          "gate": "audit-deliverable-open-questions.mjs",
          "line": 3,
          "term": "PRD",
          "text": "// meta-rules.md §触发器 P：产品设计 / PRD / UX 交付物里**禁止**残留 open questions /",
          "comment": true
        },
        {
          "gate": "audit-deliverable-open-questions.mjs",
          "line": 195,
          "term": "PRD",
          "text": "`\\n   交付物（PRD/UX 卡/handoff）禁止残留未决项。处理方式：\\n` +",
          "comment": false
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 51,
          "term": "PRD",
          "text": "// covers-acceptance: docs/internal/design-process.md#M-PRD-EG [2]",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 64,
          "term": "PRD",
          "text": "//     ⇒ 见下方 SECTIONS 表的 `PRD-EG` / `CARDINALITY` / `IA-FIRST`。⛔ 同样只覆盖各段**首条**。",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 164,
          "term": "PRD",
          "text": "//     grep -n 'PRD examples scoped'      docs/internal/design-process.md",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 165,
          "term": "PRD",
          "text": "//   ⛔ `**PRD examples scoped**` 的星号**不进判据** —— 规则只圈了短语、没规定强调形态；",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 167,
          "term": "PRD",
          "text": "{ id: 'PRD-EG', phrase: 'PRD examples scoped', rule: 'design-process.md M-PRD-EG（Phase 0 mapping table 上方加 \"PRD examples scoped\" 段，逐条列出 PRD 里所有 e.g. 集合 + 对齐后的 in-scope 子集）' },",
          "comment": false
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 190,
          "term": "PRD",
          "text": "//   ⛔ 反引号与 `**` 不进判据 —— 规则只圈了短语、没规定强调形态（同上方 PRD-EG 那条的理由）。",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 59,
          "term": "PRD",
          "text": "// UX delivery / PRD / journey / spec / changelog artifact. Mirrors",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 82,
          "term": "PRD",
          "text": "export const ANNOTATION_RE = /(UX[\\s·•]|\\bPRD\\b|_flow-state|journey|jira\\s*requirement|\\bannot|\\blabel\\s*[·•]|注释|说明|\\bspec\\b|\\brules?\\b|changelog|change\\s*summary|\\bbaseline\\b)/i",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 57,
          "term": "PRD",
          "text": "//             * Annotation layer (M23 delivery cards — PRD / UX delivery /",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 58,
          "term": "User Journey",
          "text": "//               User Journey Map / annot chips): fill + stroke can NEVER bind.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 300,
          "term": "PRD",
          "text": "//     PRD / UX cards contain no control instances, so they drop out naturally",
          "comment": true
        },
        {
          "gate": "audit-mockup-hash-n-semantic.mjs",
          "line": 12,
          "term": "PRD",
          "text": "//   与第 3 条（spec / PRD / 实现三处语义一致）⛔ **本闸不管**：前者要判「哪一帧是 after-remove」，",
          "comment": true
        },
        {
          "gate": "audit-mockup-hash-n-semantic.mjs",
          "line": 13,
          "term": "PRD",
          "text": "//   后者的另两处判定面在消费产品仓与 PRD（不落 DS 仓）。⛔ 别把本闸的绿读成 §M40 已全覆盖。",
          "comment": true
        },
        {
          "gate": "audit-mockup-jira-hyperlink.mjs",
          "line": 22,
          "term": "PRD",
          "text": "//   `Jira requirement` 组件时其 issue-key 必带 hyperlink』」。§M23.8.1 把射程扩到 PRD",
          "comment": true
        },
        {
          "gate": "audit-mockup-source-link-integrity.mjs",
          "line": 51,
          "term": "PRD",
          "text": "//   **缘起是一次 `checkedUnits=0` 的假绿**：那天的 PRD 卡 Source 段里，段标题",
          "comment": true
        },
        {
          "gate": "audit-mockup-typography-icon.mjs",
          "line": 14,
          "term": "PRD",
          "text": "//     Annotation / PRD / journey cards are EXEMPT — descriptive",
          "comment": true
        },
        {
          "gate": "audit-mockup-typography-icon.mjs",
          "line": 18,
          "term": "PRD",
          "text": "//     Annotation-area TEXT (UX delivery cards / PRD / journey / state-note /",
          "comment": true
        },
        {
          "gate": "audit-mockup-typography-icon.mjs",
          "line": 149,
          "term": "PRD",
          "text": "// UX delivery / PRD / journey / spec / changelog artifact. Once inside an",
          "comment": true
        },
        {
          "gate": "audit-mockup-typography-icon.mjs",
          "line": 160,
          "term": "PRD",
          "text": "const ANNOTATION_RE = /(UX[\\s·•]|\\bPRD\\b|_flow-state|journey|jira\\s*requirement|\\bannot|\\blabel\\s*[·•]|注释|说明|\\bspec\\b|\\brules?\\b|changelog|change\\s*summary|\\bbaseline\\b)/i",
          "comment": false
        },
        {
          "gate": "audit-mockup-typography-icon.mjs",
          "line": 277,
          "term": "PRD",
          "text": "console.log('        Annotation/PRD/journey descriptive arrows are exempt (this probe already skips them).')",
          "comment": false
        },
        {
          "gate": "audit-mockup-typography-icon.mjs",
          "line": 287,
          "term": "PRD",
          "text": "console.log('  Hint: annotation/PRD/journey text must be EN=Roboto / ZH=Noto Sans SC.')",
          "comment": false
        },
        {
          "gate": "audit-rule-inventory.mjs",
          "line": 203,
          "term": "PRD",
          "text": "* ⚠️ 刻意不按普通连字符切 —— 否则 `M-PRD-EG` / `Library-First + Evidence Discipline` 会被截断。",
          "comment": true
        }
      ],
      "excludedFalsePositives": [],
      "wideGateCount": 48,
      "wideGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "wideOnlyGates": [
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "narrowOnlyGates": [
        "audit-deliverable-open-questions.mjs",
        "audit-mockup-hash-n-semantic.mjs",
        "audit-mockup-jira-hyperlink.mjs",
        "audit-rule-inventory.mjs"
      ]
    },
    {
      "n": "7",
      "gist": "双语三档间距可视递增（EN→其 ZH < 对→对 < 段→段）",
      "anchor": {
        "file": "docs/internal/mockup-conventions.md",
        "line": 939,
        "text": "- [ ] `EN→其 ZH` 间距 < `对→对` 间距 < `段→段` 间距（三档**可视递增**）"
      },
      "heading": {
        "line": 904,
        "level": 4,
        "text": "M23.14 — 卡内排版：双语堆叠行距 + 段名层级（M23 §双语 extension）— 2026-06-18/06-23 合并（原 M23.14 双语行距 + M23.15 段名层级）"
      },
      "narrowTerms": [
        "bilingual",
        "bilingual-spacing",
        "tier",
        "三档",
        "递增"
      ],
      "narrowGateCount": 7,
      "narrowGates": [
        "audit-claim-vs-livesource.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-rule-number-collision.mjs",
        "audit-stale-anchors.mjs",
        "audit-token-contract.mjs"
      ],
      "narrowHits": [
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 325,
          "term": "三档",
          "text": "* ⛔ review finding：结构化三档（table / deflist / field）都归 S2，一档都不能漏——",
          "comment": true
        },
        {
          "gate": "audit-gate-mount-declaration.mjs",
          "line": 115,
          "term": "bilingual",
          "text": "//    被删的 10 行：bilingual-spacing / binding-fidelity / colors / conformance /",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 1,
          "term": "bilingual",
          "text": "// audit-mockup-bilingual-spacing.mjs",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 4,
          "term": "bilingual",
          "text": "//   `audit:mockup-bilingual-spacing` 但没有任何自动执行点（pre-commit / prepublishOnly / CI",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 6,
          "term": "bilingual",
          "text": "//   `bilingual-spacing` 规则模块挂载。",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 11,
          "term": "bilingual",
          "text": "//     「**机器闸**：`pnpm audit:mockup-bilingual-spacing`」—— 前者还注明「已挂进",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 12,
          "term": "bilingual",
          "text": "//     `audit:mockup-conformance` G4」，后者点名本闸的 `bilingual-zh-style-tier` probe。",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 18,
          "term": "bilingual",
          "text": "// Audits annotation-region TEXT nodes for the M23.14 bilingual grouping rule:",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 19,
          "term": "tier",
          "text": "// when a ZH translation is stacked under each EN line, the spacing tiers",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 25,
          "term": "bilingual",
          "text": "//     For a SINGLE TEXT node that stacks MULTIPLE bilingual pairs",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 44,
          "term": "bilingual",
          "text": "//   node scripts/audit-mockup-bilingual-spacing.mjs --file <fileKey>",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 45,
          "term": "bilingual",
          "text": "//   node scripts/audit-mockup-bilingual-spacing.mjs --file <fileKey> --node <nodeId>",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 46,
          "term": "bilingual",
          "text": "//   node scripts/audit-mockup-bilingual-spacing.mjs --file <fileKey> --json",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 92,
          "term": "tier",
          "text": "// ---------- ZH style tier (M23.14 (B) + §双语) ----------",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 94,
          "term": "tier",
          "text": "// M23.14 (B) fixes the ZH tier for every annotation-card role: card title / section",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 108,
          "term": "tier",
          "text": "// chars left in the ZH tier) — with a Latin-only test that reads as ONE bad char",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 127,
          "term": "tier",
          "text": "// tier, so a line is only reported for \"EN in ZH font\" when it holds NO Han at all.",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 170,
          "term": "tier",
          "text": "// missing field means fully opaque (that is exactly the ZH-tier defect we hunt:",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 190,
          "term": "tier",
          "text": "// Also records, per line, the data the ZH-style-tier probe needs:",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 242,
          "term": "tier",
          "text": "// ZH tier bookkeeping — classified by SCRIPT, deliberately not by font.",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 294,
          "term": "bilingual",
          "text": "// single-pair (or near) stacked bilingual → itemSpacing mechanism, out of scope",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 306,
          "term": "tier",
          "text": "// Walk a Figma-REST-shaped tree and collect M23.14 (B) / §双语 ZH-tier violations.",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 309,
          "term": "tier",
          "text": "// and opacity tiers apply regardless of whether the node stacks multiple pairs.",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 365,
          "term": "tier",
          "text": "// a 7-char run of ZH-tier leftovers (`V4-2376`) look like a 1-char blip.",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 390,
          "term": "tier",
          "text": "// --allow-node: node ids exempted from the ZH-tier probe only (owner-deferred",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 399,
          "term": "bilingual",
          "text": "console.error('Usage: node scripts/audit-mockup-bilingual-spacing.mjs --file <fileKey> [--node <nodeId>] [--allow-node <ids>] [--json]')",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 434,
          "term": "bilingual",
          "text": "name: 'bilingual-stacked-line-grouping',",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 442,
          "term": "bilingual",
          "text": "name: 'bilingual-zh-style-tier',",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 462,
          "term": "三档",
          "text": "console.log('      Verify those via M23.14 自查 (parent itemSpacing 三档递增).')",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 463,
          "term": "tier",
          "text": "console.log('      (The ZH-style-tier probe below DOES cover them — font/opacity apply to every node.)')",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 469,
          "term": "tier",
          "text": "console.log(`Note: ${zh.allowedSkipped} node(s) exempted from the ZH-tier probe via --allow-node.`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 475,
          "term": "bilingual",
          "text": "console.log('✅ M23.14 bilingual stacked-line grouping audit pass')",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 476,
          "term": "bilingual",
          "text": "console.log(`   bilingual-stacked-line-grouping   scanned, 0 violations (ratio floor ${GROUPING_RATIO})`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 478,
          "term": "bilingual",
          "text": "console.log('❌ M23.14 bilingual stacked-line grouping audit FAIL')",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 480,
          "term": "bilingual",
          "text": "console.log(`SPACING — bilingual-stacked-line-grouping (${violations.length}) — §M23.14 (A)`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 491,
          "term": "tier",
          "text": "// ---- probe 2: ZH style tier (M23.14 (B) + §双语) ----",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 493,
          "term": "tier",
          "text": "console.log('✅ M23.14 (B) ZH style tier audit pass')",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 494,
          "term": "bilingual",
          "text": "console.log(`   bilingual-zh-style-tier   ${zh.scannedNodes} annotation TEXT node(s) scanned, 0 violations`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 497,
          "term": "tier",
          "text": "console.log('❌ M23.14 (B) ZH style tier audit FAIL')",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 499,
          "term": "bilingual",
          "text": "console.log(`STYLE — bilingual-zh-style-tier (${zh.violations.length}) — §M23.14 (B) + §双语`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 503,
          "term": "tier",
          "text": "console.log('        Latin fragments inside a Chinese line share the ZH tier and are NOT flagged;')",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 520,
          "term": "bilingual",
          "text": "console.error('audit-mockup-bilingual-spacing: fatal error')",
          "comment": false
        },
        {
          "gate": "audit-mockup-typography-icon.mjs",
          "line": 75,
          "term": "bilingual",
          "text": "//       正则 / bilingual spacing / typography-icon）全部 PASS……只有渲染图能发现」",
          "comment": true
        },
        {
          "gate": "audit-mockup-typography-icon.mjs",
          "line": 158,
          "term": "bilingual",
          "text": "// with audit-mockup-bilingual-spacing.mjs ANNOTATION_RE (deliberate duplication —",
          "comment": true
        },
        {
          "gate": "audit-rule-number-collision.mjs",
          "line": 121,
          "term": "递增",
          "text": "* 受管文件 = 用「全局递增编号」当条目标识的真源文档。",
          "comment": true
        },
        {
          "gate": "audit-stale-anchors.mjs",
          "line": 534,
          "term": "bilingual",
          "text": "//    ⇒ 零新报红；那 7 处正是此前不可见的带参数命令（conformance / bilingual-spacing /",
          "comment": true
        },
        {
          "gate": "audit-token-contract.mjs",
          "line": 32,
          "term": "tier",
          "text": "// \"variables.css in sync with **Figma** primitive tier\" —— 而本闸根本不读 Figma，",
          "comment": true
        },
        {
          "gate": "audit-token-contract.mjs",
          "line": 52,
          "term": "tier",
          "text": "console.error('❌ token-contract FAIL: variables.css is OUT OF SYNC with the Figma primitive tier.')",
          "comment": false
        }
      ],
      "excludedFalsePositives": [],
      "wideGateCount": 48,
      "wideGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "wideOnlyGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "narrowOnlyGates": []
    },
    {
      "n": "8",
      "gist": "不靠分隔线/换色/加粗制造分组（仅靠间距）",
      "anchor": {
        "file": "docs/internal/mockup-conventions.md",
        "line": 943,
        "text": "- [ ] 不靠分隔线 / 换色 / 加粗 制造分组（仅靠间距）"
      },
      "heading": {
        "line": 904,
        "level": 4,
        "text": "M23.14 — 卡内排版：双语堆叠行距 + 段名层级（M23 §双语 extension）— 2026-06-18/06-23 合并（原 M23.14 双语行距 + M23.15 段名层级）"
      },
      "narrowTerms": [
        "divider",
        "separator",
        "stroke",
        "分隔线",
        "grouping"
      ],
      "narrowGateCount": 14,
      "narrowGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-verification-coverage.mjs"
      ],
      "narrowHits": [
        {
          "gate": "audit-acceptance-gate-coverage.mjs",
          "line": 11,
          "term": "分隔线",
          "text": "//   「行高与分隔线间距」零覆盖 —— 而**没有任何东西会发现这个差额**。它不是「还没想到」，",
          "comment": true
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 53,
          "term": "separator",
          "text": "//   `href`/`state`/`separatorType`/`showSeparator` 属于 `BreadcrumbItem`，活源",
          "comment": true
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 133,
          "term": "分隔线",
          "text": "// 字段块终止：空行 / markdown 标题（# 任意级别）/ 水平分隔线（---、***、___）。",
          "comment": true
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 342,
          "term": "分隔线",
          "text": "// （空行 / 标题 / 分隔线）。⛔ 别改这段逻辑本身——只在 extractClaims 改了",
          "comment": true
        },
        {
          "gate": "audit-demo-css-page-scope.mjs",
          "line": 17,
          "term": "divider",
          "text": "//   · **只是命名分叉**：`.try-button` 的 border 在 badge 写 `--line-divider-light`、",
          "comment": true
        },
        {
          "gate": "audit-demo-css-page-scope.mjs",
          "line": 19,
          "term": "divider",
          "text": "//     （`--line-divider-light: var(--line-light)`），解析值逐字相同、**无视觉差**。",
          "comment": true
        },
        {
          "gate": "audit-file-local-fidelity.mjs",
          "line": 6,
          "term": "分隔线",
          "text": "// 面板曾有 4 条分隔线而 Figma 只有 2 条，从 2026-07-02 `e8bb4e2b` 起近 4 周无人发现。",
          "comment": true
        },
        {
          "gate": "audit-file-local-fidelity.mjs",
          "line": 15,
          "term": "分隔线",
          "text": "//                  （分隔线多长出来一条就红 —— 正是历史那个 bug 的形态）",
          "comment": true
        },
        {
          "gate": "audit-hard-rule-compliance.mjs",
          "line": 97,
          "term": "stroke",
          "text": "const H2_SUGGEST = 'fills[]/strokes[] are multi-layer stacks — never read [0] directly. Use a compositeFills(node.fills) helper that walks bottom→top honouring opacity/blendMode. See backlog EXTRACT-008 + memory feedback_figma-fills-must-composite.'",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 18,
          "term": "grouping",
          "text": "// Audits annotation-region TEXT nodes for the M23.14 bilingual grouping rule:",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 27,
          "term": "grouping",
          "text": "//     line grouping can ONLY come from a per-range lineHeight differential",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 33,
          "term": "grouping",
          "text": "//     grouping was applied.",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 36,
          "term": "grouping",
          "text": "//     • The per-bullet mechanism (1 EN + 1 ZH per node, grouping via parent",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 38,
          "term": "grouping",
          "text": "//       their grouping lives in itemSpacing, not lineHeight — flagging them",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 73,
          "term": "separator",
          "text": "// `label · 分支 B（参数设置）`, which M23.14 governs too. The `·` / `•` separator is",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 88,
          "term": "grouping",
          "text": "// to count as deliberate M23.14 grouping. Genuine application ≈1.8; uniform",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 114,
          "term": "separator",
          "text": "// dry-run caught on 2026-08-06, where a ZH run starting at one of those separators",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 131,
          "term": "grouping",
          "text": "// node is a multi-pair single-node layout where lineHeight is the only grouping",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 434,
          "term": "grouping",
          "text": "name: 'bilingual-stacked-line-grouping',",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 436,
          "term": "grouping",
          "text": "groupingRatioFloor: GROUPING_RATIO,",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 461,
          "term": "grouping",
          "text": "console.log('      (1 EN + 1 ZH per node → grouping via Auto-Layout itemSpacing, not lineHeight).')",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 473,
          "term": "grouping",
          "text": "// ---- probe 1: line-spacing grouping (M23.14 (A)) ----",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 475,
          "term": "grouping",
          "text": "console.log('✅ M23.14 bilingual stacked-line grouping audit pass')",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 476,
          "term": "grouping",
          "text": "console.log(`   bilingual-stacked-line-grouping   scanned, 0 violations (ratio floor ${GROUPING_RATIO})`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 478,
          "term": "grouping",
          "text": "console.log('❌ M23.14 bilingual stacked-line grouping audit FAIL')",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 480,
          "term": "grouping",
          "text": "console.log(`SPACING — bilingual-stacked-line-grouping (${violations.length}) — §M23.14 (A)`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 481,
          "term": "grouping",
          "text": "console.log('  Hint: stacked EN/ZH uses uniform line spacing → no grouping → \"拥挤\".')",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 54,
          "term": "stroke",
          "text": "//   B-COVERAGE — coverage census of unbound spacing / fill / stroke. This is a",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 58,
          "term": "stroke",
          "text": "//               User Journey Map / annot chips): fill + stroke can NEVER bind.",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 65,
          "term": "stroke",
          "text": "//               => fill/stroke-unbound on annotation cards is EXPECTED. Do not",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 75,
          "term": "stroke",
          "text": "//             * Product layer: unbound fill/stroke/spacing there is a real",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 208,
          "term": "stroke",
          "text": "// 未绑即报：autolayout itemSpacing/padding + 容器/文字 fill·stroke 没绑任何变量/style 就报，",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 230,
          "term": "stroke",
          "text": "const solidStroke = (node.strokes || []).some(s => s.visible !== false && s.type === 'SOLID')",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 231,
          "term": "stroke",
          "text": "if (solidStroke && !bv.strokes) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 232,
          "term": "stroke",
          "text": "out.push({ nodeId: node.id, name, prop: 'strokes', value: null, kind: 'stroke-unbound' })",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 241,
          "term": "stroke",
          "text": "// 只去掉「未绑」条件 —— 用作摘要 X% 的分母。返回 {spacing-unbound, fill-unbound, stroke-unbound}。",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 243,
          "term": "stroke",
          "text": "const totals = { 'spacing-unbound': 0, 'fill-unbound': 0, 'stroke-unbound': 0 }",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 256,
          "term": "stroke",
          "text": "const solidStroke = (node.strokes || []).some(s => s.visible !== false && s.type === 'SOLID')",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 257,
          "term": "stroke",
          "text": "if (solidStroke) totals['stroke-unbound']++",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 362,
          "term": "stroke",
          "text": "console.log(`B-COVERAGE — unbound spacing/fill/stroke (${bCoverage.length}) — §M52.1 未绑即报（摘要）`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 365,
          "term": "stroke",
          "text": "for (const kind of ['spacing-unbound', 'fill-unbound', 'stroke-unbound']) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-connector.mjs",
          "line": 95,
          "term": "stroke",
          "text": "const solidStrokes = n => (n.strokes || []).filter(s => s.visible !== false && s.type === 'SOLID')",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 14,
          "term": "分隔线",
          "text": "//   ——①同类控件同宽 ②同一控件跨状态帧同宽 ③动作按钮右缘对齐 ④行高与分隔线间距——",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 55,
          "term": "divider",
          "text": "// --family rows (R1/R2/R3) — row rhythm & dividers (backlog CANONICAL-F104,",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 56,
          "term": "分隔线",
          "text": "// added 2026-08-27). These close domain-tvu.md §M17 Acceptance ④「行高与分隔线间距」,",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 60,
          "term": "grouping",
          "text": "// aspect-ratio / a grouping key / a report field — NOT ONE compared two sibling",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 64,
          "term": "divider",
          "text": "//   R3  divider-consistency       — dividers share one thickness AND one colour.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 220,
          "term": "divider",
          "text": "// A node this flat is a divider, not a row: excluded from the row",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 222,
          "term": "divider",
          "text": "dividerMaxThickness: 4,",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 223,
          "term": "divider",
          "text": "// Where R3 looks for dividers. 'row-subtree' (default) = only inside the",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 224,
          "term": "divider",
          "text": "// subtrees of classified row grids — F104 判据 ④ is about the dividers OF a",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 230,
          "term": "divider",
          "text": "dividerScope: 'row-subtree',",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 232,
          "term": "divider",
          "text": "// tiny 2×2 dot as a divider.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 233,
          "term": "divider",
          "text": "dividerMinAspect: 8,",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 234,
          "term": "divider",
          "text": "// Divider candidates whose name matches are NOT design dividers. Default",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 237,
          "term": "divider",
          "text": "// file: 15 real dividers (`RECTANGLE h=0.9 #252525`) vs 2 `_connector`",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 239,
          "term": "divider",
          "text": "dividerIgnoreNames: ['^_'],",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 257,
          "term": "divider",
          "text": "_dividerIgnore: cfg.rows.dividerIgnoreNames.map(p => new RegExp(p, 'i')),",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 261,
          "term": "grouping",
          "text": "// componentId → grouping label. Prefers the component SET name so that two",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 337,
          "term": "grouping",
          "text": "// grouping test used to just VANISH, so \"G2 never looked at it\" was output as",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 342,
          "term": "grouping",
          "text": "// ⛔ The grouping key is DELIBERATELY unchanged (still `WxH`). Whether",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 353,
          "term": "grouping",
          "text": "// grouping test has exactly one way to drop a frame; a new key goes here when",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 472,
          "term": "分隔线",
          "text": "// dimension named in domain-tvu.md §M17 Acceptance ④ (\"行高与分隔线间距\") had no",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 500,
          "term": "divider",
          "text": "const { dividerMaxThickness, dividerMinAspect } = cfg.rows",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 501,
          "term": "divider",
          "text": "if (b.height > dividerMaxThickness) return false",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 502,
          "term": "divider",
          "text": "if (b.width / b.height < dividerMinAspect) return false",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 503,
          "term": "divider",
          "text": "// A divider you cannot see is not a divider. Flat nodes with no visible solid",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 526,
          "term": "divider",
          "text": "c.absoluteBoundingBox.height > cfg.rows.dividerMaxThickness,",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 554,
          "term": "divider",
          "text": "// the group's subtree for its divider scope.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 624,
          "term": "divider",
          "text": "// R3 — dividers must share one thickness AND one colour (F104 判据 ④).",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 627,
          "term": "divider",
          "text": "// grid (cfg.rows.dividerScope === 'row-subtree', the default). Scanning the whole",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 628,
          "term": "divider",
          "text": "// file instead conflates real row dividers with annotation underlines, status",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 639,
          "term": "divider",
          "text": "!cfg._dividerIgnore.some(r => r.test(n.name || ''))",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 652,
          "term": "divider",
          "text": "const roots = cfg.rows.dividerScope === 'file' || !scopeRoots ? [root] : scopeRoots",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 658,
          "term": "stroke",
          "text": "// ⛔ Never fills[0] alone — Figma fills/strokes are multi-layer stacks",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 671,
          "term": "stroke",
          "text": "const strokes = take(node.strokes)",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 672,
          "term": "stroke",
          "text": "return [fills.length ? `fill:${fills.join('+')}` : null, strokes.length ? `stroke:${strokes.join('+')}` : null]",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 677,
          "term": "divider",
          "text": "// R3 compares dividers WITHIN one row grid, not across the whole file.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 681,
          "term": "divider",
          "text": "export function probeR3(dividersByGroup, cfg) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 683,
          "term": "divider",
          "text": "for (const { group, dividers } of dividersByGroup) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 684,
          "term": "divider",
          "text": "if (dividers.length < 2) continue",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 686,
          "term": "divider",
          "text": "for (const d of dividers) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 692,
          "term": "divider",
          "text": "const distinctColours = new Set(dividers.map(d => d.colors))",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 693,
          "term": "divider",
          "text": "const thicknessSpread = spread(dividers.map(d => d.thickness))",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 753,
          "term": "divider",
          "text": "const dividersByGroup = wantRows",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 754,
          "term": "divider",
          "text": "? cfg.rows.dividerScope === 'file'",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 755,
          "term": "divider",
          "text": "? [{ group: { id: root?.id ?? null, name: '(whole file)' }, dividers: collectDividers(root, cfg) }]",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 756,
          "term": "divider",
          "text": ": rowScan.groups.map(g => ({ group: g, dividers: collectDividers(g.node, cfg, [g.node]) }))",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 758,
          "term": "divider",
          "text": "const dividers = dividersByGroup.flatMap(d => d.dividers)",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 769,
          "term": "divider",
          "text": "raw.R3 = probeR3(dividersByGroup, cfg)",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 799,
          "term": "divider",
          "text": "dividers: dividers.length,",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 831,
          "term": "divider",
          "text": "...dividers.map(d => d.id),",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 916,
          "term": "grouping",
          "text": "// ⚠️ Reached ≠ judged. A state frame that the grouping test dropped IS in",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 922,
          "term": "grouping",
          "text": "console.error('   grouping test excluded it (singletonSize — no same-size sibling).')",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 956,
          "term": "grouping",
          "text": "`         state-frame grouping excluded: ${sfRej.length ? sfRej.map(([k, n]) => `${k}=${n}`).join(' · ') : 'none'}`,",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 960,
          "term": "divider",
          "text": "console.log(`         ${scanned.rowGroups} row grids / ${scanned.rows} rows / ${scanned.dividers} dividers`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 980,
          "term": "divider",
          "text": "console.log(`R3 divider-consistency — ${f.shapes.length} divider shapes (thickness spread ${f.thicknessSpread}, colours ${f.distinctColours.length})`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-html-conformance.mjs",
          "line": 190,
          "term": "stroke",
          "text": "const COLOR_DECL = /\\b(color|background|background-color|fill|stroke|border(?:-[a-z]+)?-?color)\\s*:\\s*([^;\"}']*#[0-9a-fA-F]{3,8})/gi",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 674,
          "term": "stroke",
          "text": "const isTailwindArbitrary = /\\[[^\\]]*$/.test(before) && /(text|bg|border|fill|stroke|from|to|via|ring|shadow|outline|caret|decoration|accent)-\\[$/.test(",
          "comment": false
        },
        {
          "gate": "audit-render-coverage-gaps.mjs",
          "line": 10,
          "term": "divider",
          "text": "// checkmarks, dots, dividers — carries color/background/border but is NEVER",
          "comment": true
        },
        {
          "gate": "audit-render-coverage-gaps.mjs",
          "line": 41,
          "term": "stroke",
          "text": "const COLOR_PROPS = /(?:^|[;{]\\s*)(color|background|background-color|border|border-color|border-top-color|border-bottom-color|border-left-color|border-right-color|fill|stroke)\\s*:/i",
          "comment": false
        },
        {
          "gate": "audit-render-coverage-gaps.mjs",
          "line": 50,
          "term": "stroke",
          "text": "const BG_BORDER_PROPS = /(?:^|[\\s;{])(background|background-color|border|border-color|border-top-color|border-bottom-color|border-left-color|border-right-color|fill|stroke)\\s*:/i",
          "comment": false
        },
        {
          "gate": "audit-render-drift-gate-react.mjs",
          "line": 88,
          "term": "分隔线",
          "text": "// 该行的成因句已就地订正、fixDirection 改判，连带的产品缺陷（两条链的表格行分隔线**实际渲成",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 87,
          "term": "separator",
          "text": "// figma name containing the separator cannot forge a different pair's key.",
          "comment": true
        }
      ],
      "excludedFalsePositives": [],
      "wideGateCount": 48,
      "wideGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "wideOnlyGates": [
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-render-drift-gate.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "narrowOnlyGates": []
    },
    {
      "n": "9",
      "gist": "容器尺寸变更后跑兄弟求交",
      "anchor": {
        "file": "docs/internal/mockup-conventions.md",
        "line": 1541,
        "text": "- [ ] 容器尺寸变更后跑一次兄弟求交（`audit:mockup-overlap` 或 in-file probe），含底栏 / 内容区边界这类固定元素"
      },
      "heading": {
        "line": 1499,
        "level": 4,
        "text": "M-INTEGRITY §I7 — canonical 尺寸容器加内容后必须复测尺寸 + 兄弟碰撞 + 依附几何（2026-07-30 新增）"
      },
      "narrowTerms": [
        "overlap",
        "intersect",
        "sibling",
        "求交"
      ],
      "narrowGateCount": 15,
      "narrowGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-hash-n-semantic.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-stale-anchors.mjs"
      ],
      "narrowHits": [
        {
          "gate": "audit-acceptance-gate-coverage.mjs",
          "line": 139,
          "term": "求交",
          "text": "//   63 → 62（2026-08-28 第四批）：**第四条缝 = 上一批闸侧扫描按「规则 id」与段求交集，而分母里",
          "comment": true
        },
        {
          "gate": "audit-acceptance-gate-coverage.mjs",
          "line": 141,
          "term": "求交",
          "text": "//       ⇒ 那批段任何 id 求交都匹配不上。补扫命中 1 段：`mockup-conventions.md#Acceptance`",
          "comment": true
        },
        {
          "gate": "audit-gate-mount-declaration.mjs",
          "line": 116,
          "term": "overlap",
          "text": "//    geometry-consistency / integrity / library-binding / library-origin / overlap /",
          "comment": true
        },
        {
          "gate": "audit-handoff-deliverable-sections.mjs",
          "line": 208,
          "term": "overlap",
          "text": "// 两个数，不是一个笼统的 overlap`）⇒ 复用 IA-FIRST 那条的「锚点 + 追加行」两层机制，",
          "comment": true
        },
        {
          "gate": "audit-mockup-conformance.mjs",
          "line": 52,
          "term": "overlap",
          "text": "//   node scripts/audit-mockup-conformance.mjs --file <fileKey> --rule binding-fidelity,overlap",
          "comment": true
        },
        {
          "gate": "audit-mockup-connector.mjs",
          "line": 244,
          "term": "overlap",
          "text": "//      包 GROUP 会让连线首次进入 overlap 闸的 BLOCK_TYPES 比较集，L 形折线的虚胖 bbox",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 39,
          "term": "overlap",
          "text": "//   ratio) / I2 (section overlap) / I3 (children wrap) / I4 (parent discipline)",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 49,
          "term": "sibling",
          "text": "//   G2  cross-frame-same-name     — sibling state frames of one feature (same",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 59,
          "term": "intersect",
          "text": "// 6 of the repo's 68 audit-*.mjs and every use was intersection / overflow /",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 60,
          "term": "sibling",
          "text": "// aspect-ratio / a grouping key / a report field — NOT ONE compared two sibling",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 180,
          "term": "sibling",
          "text": "// its siblings and the container stops being classified as a row grid. That",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 303,
          "term": "overlap",
          "text": "// (unlike audit-mockup-overlap), because a control override nested in an",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 343,
          "term": "sibling",
          "text": "// same-width / different-height siblings are still \"state variants of one",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 409,
          "term": "sibling",
          "text": "// G2 — a same-named control must keep one width across sibling state frames.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 531,
          "term": "overlap",
          "text": "// Must actually stack: no vertical overlap between neighbours. Rules out",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 922,
          "term": "sibling",
          "text": "console.error('   grouping test excluded it (singletonSize — no same-size sibling).')",
          "comment": false
        },
        {
          "gate": "audit-mockup-hash-n-semantic.mjs",
          "line": 82,
          "term": "求交",
          "text": "* 把 payload 拆成「交付单位」—— 优先 SECTION（design-process 要求交付物收进具名 Section），",
          "comment": true
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 34,
          "term": "overlap",
          "text": "*   I2 — Sibling SECTION overlap (error on any bbox overlap)",
          "comment": true
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 219,
          "term": "sibling",
          "text": "// ---------- I1: sibling plan/version layout consistency ----------",
          "comment": true
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 221,
          "term": "sibling",
          "text": "// BEFORE/AFTER 也是 version 对照 section（design-process ① 要求二者分属不同 Section）→ 纳入 sibling 探测，否则其布局 mirror 一致性静默漏检",
          "comment": true
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 225,
          "term": "sibling",
          "text": "const siblings = (page.children || [])",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 243,
          "term": "sibling",
          "text": "if (siblings.length >= 2) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 244,
          "term": "sibling",
          "text": "const ratios = siblings.map(s => s.aspectRatio).filter(r => r > 0)",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 259,
          "term": "sibling",
          "text": "return { pageId: page.id, pageName: page.name, siblings, divergent, hint }",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 262,
          "term": "overlap",
          "text": "// ---------- I2: sibling SECTION overlap ----------",
          "comment": true
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 266,
          "term": "sibling",
          "text": "// group by parent so we only compare actual siblings",
          "comment": true
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 279,
          "term": "overlap",
          "text": "// them WITH ITSELF (aId === bId, overlap = its own bbox) => I2 reported one",
          "comment": true
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 284,
          "term": "overlap",
          "text": "const overlaps = []",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 292,
          "term": "overlap",
          "text": "overlaps.push({",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 299,
          "term": "overlap",
          "text": "overlapWxH: `${ov.w.toFixed(1)}x${ov.h.toFixed(1)}`,",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 310,
          "term": "overlap",
          "text": "const overlapsDedup = overlaps.filter(o => {",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 321,
          "term": "overlap",
          "text": "overlaps: overlapsDedup,",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 322,
          "term": "overlap",
          "text": "pass: overlapsDedup.length === 0,",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 324,
          "term": "overlap",
          "text": "// 它存在的理由是 §I2 实证段逐字记的那个病根 —— handoff 里写个孤零零的 `overlap 0`，",
          "comment": true
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 511,
          "term": "sibling",
          "text": "console.log('— I1 sibling plan/version layout consistency —')",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 513,
          "term": "sibling",
          "text": "if (r.siblings.length < 2) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 514,
          "term": "sibling",
          "text": "console.log(`  [page ${r.pageName}] <2 plan/version siblings, skipped`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 517,
          "term": "sibling",
          "text": "console.log(`  [page ${r.pageName}] ${r.siblings.length} siblings:`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 518,
          "term": "sibling",
          "text": "for (const s of r.siblings) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 532,
          "term": "overlap",
          "text": "console.log('— I2 sibling SECTION overlap —')",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 537,
          "term": "overlap",
          "text": "console.log(`  [page ${r.pageName}] FAIL: ${r.overlaps.length} overlap(s) / ${r.sectionCount} SECTION`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 538,
          "term": "overlap",
          "text": "for (const o of r.overlaps) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 539,
          "term": "overlap",
          "text": "console.log(`    - \"${o.aName}\" <-> \"${o.bName}\" overlap ${o.overlapWxH}`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 546,
          "term": "overlap",
          "text": "// 另一个（sectionInternalOverlap）在 audit-mockup-overlap.mjs —— 两个量分在两个闸里，",
          "comment": true
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 547,
          "term": "overlap",
          "text": "// 此前都只印笼统措辞，于是 handoff 作者只能自己填一个 `overlap 0`，正是 §I2 要治的病。",
          "comment": true
        },
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 549,
          "term": "overlap",
          "text": "const sectionToSectionOverlap = report.i2.reduce((n, r) => n + r.overlaps.length, 0)",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 1,
          "term": "overlap",
          "text": "// audit-mockup-overlap.mjs",
          "comment": true
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 4,
          "term": "overlap",
          "text": "//   `audit:mockup-overlap` 但没有任何自动执行点（pre-commit / prepublishOnly / CI 都给不出",
          "comment": true
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 5,
          "term": "overlap",
          "text": "//   fileKey + Figma token）。⇒ 经 `audit-mockup-conformance.mjs` 引擎的 `overlap` 规则模块挂载。",
          "comment": true
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 9,
          "term": "overlap",
          "text": "//     「容器尺寸变更后跑一次兄弟求交（`audit:mockup-overlap` 或 in-file probe）」——",
          "comment": true
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 16,
          "term": "sibling",
          "text": "// Audits a Figma mockup subtree for unintended OVERLAP between sibling layout",
          "comment": true
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 21,
          "term": "overlap",
          "text": "// audit-mockup-integrity.mjs §I2 already catches SECTION↔SECTION overlap. This",
          "comment": true
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 24,
          "term": "overlap",
          "text": "// should tile, never overlap.",
          "comment": true
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 27,
          "term": "overlap",
          "text": "//   their internal children: product UI legitimately layers/overlaps children",
          "comment": true
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 37,
          "term": "overlap",
          "text": "//   node scripts/audit-mockup-overlap.mjs --file <fileKey>",
          "comment": true
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 38,
          "term": "overlap",
          "text": "//   node scripts/audit-mockup-overlap.mjs --file <fileKey> --node <nodeId>",
          "comment": true
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 39,
          "term": "overlap",
          "text": "//   node scripts/audit-mockup-overlap.mjs --file <fileKey> --json",
          "comment": true
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 52,
          "term": "overlap",
          "text": "// rounding from absoluteBoundingBox; deliberate overlaps are always >> 1px).",
          "comment": true
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 87,
          "term": "overlap",
          "text": "// Find overlapping sibling block pairs within each container's DIRECT children.",
          "comment": true
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 89,
          "term": "overlap",
          "text": "const overlaps = []",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 104,
          "term": "overlap",
          "text": "overlaps.push({",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 109,
          "term": "overlap",
          "text": "overlapWxH: `${ov.w.toFixed(1)}x${ov.h.toFixed(1)}`,",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 114,
          "term": "overlap",
          "text": "return overlaps",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 131,
          "term": "overlap",
          "text": "console.error('Usage: node scripts/audit-mockup-overlap.mjs --file <fileKey> [--node <nodeId>] [--json]')",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 158,
          "term": "overlap",
          "text": "const overlaps = findOverlaps(root)",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 162,
          "term": "overlap",
          "text": "// ⛔ `name` 保持 `sibling-block-overlap` 不动 —— 别处在读这个值。",
          "comment": true
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 167,
          "term": "overlap",
          "text": "probe: { name: 'sibling-block-overlap', acceptanceName: 'sectionInternalOverlap', pass: overlaps.length === 0, overlaps },",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 172,
          "term": "overlap",
          "text": "process.exit(overlaps.length ? 1 : 0)",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 175,
          "term": "overlap",
          "text": "if (overlaps.length === 0) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 176,
          "term": "overlap",
          "text": "console.log('✅ M-INTEGRITY sibling-block-overlap audit pass')",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 177,
          "term": "overlap",
          "text": "console.log('   sibling-block-overlap   scanned, 0 overlaps')",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 178,
          "term": "overlap",
          "text": "console.log(`   sectionInternalOverlap = ${overlaps.length}   (§I2 ③ 具名量)`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 185,
          "term": "overlap",
          "text": "console.log('❌ M-INTEGRITY sibling-block-overlap audit FAIL')",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 187,
          "term": "overlap",
          "text": "console.log(`   sectionInternalOverlap = ${overlaps.length}   (§I2 ③ 具名量)`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 188,
          "term": "overlap",
          "text": "console.log(`OVERLAP — sibling-block-overlap (${overlaps.length}) — §M-INTEGRITY (place-前 probe 目标区)`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 189,
          "term": "overlap",
          "text": "console.log('  Hint: laid-out cards/frames overlap — likely placed without probing the target region first.')",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 190,
          "term": "overlap",
          "text": "for (const o of overlaps) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 191,
          "term": "overlap",
          "text": "console.log(`  • in \"${o.containerName}\": \"${o.aName}\" ⨯ \"${o.bName}\"  overlap ${o.overlapWxH}`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 201,
          "term": "overlap",
          "text": "console.error('audit-mockup-overlap: fatal error')",
          "comment": false
        },
        {
          "gate": "audit-mockup-source-link-integrity.mjs",
          "line": 67,
          "term": "sibling",
          "text": "//   摘掉 `|| siblingMarked` 实测**这 3 条转红、其余 23 条仍绿**（F-T1 与「只放宽一层」",
          "comment": true
        },
        {
          "gate": "audit-mockup-source-link-integrity.mjs",
          "line": 165,
          "term": "sibling",
          "text": "const visit = (node, inJira, siblingMarked) => {",
          "comment": false
        },
        {
          "gate": "audit-mockup-source-link-integrity.mjs",
          "line": 175,
          "term": "sibling",
          "text": "const marked = selfMarked || siblingMarked",
          "comment": false
        },
        {
          "gate": "audit-render-coverage-gaps.mjs",
          "line": 99,
          "term": "sibling",
          "text": "// last combinator token (descendant / child / sibling)",
          "comment": true
        },
        {
          "gate": "audit-render-drift-gate-react.mjs",
          "line": 106,
          "term": "sibling",
          "text": "siblingReportPath: path.resolve('figma-data/normalized/render-verification.report.json'),",
          "comment": false
        },
        {
          "gate": "audit-render-drift-gate-react.mjs",
          "line": 107,
          "term": "sibling",
          "text": "siblingRefreshCommand: 'pnpm test:render-verification',",
          "comment": false
        },
        {
          "gate": "audit-render-drift-gate.mjs",
          "line": 102,
          "term": "sibling",
          "text": "siblingReportPath: path.resolve('figma-data/normalized/react-render-verification.report.json'),",
          "comment": false
        },
        {
          "gate": "audit-render-drift-gate.mjs",
          "line": 103,
          "term": "sibling",
          "text": "siblingRefreshCommand: 'pnpm build:wc && pnpm test:render-verification-react',",
          "comment": false
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 20,
          "term": "sibling",
          "text": "// which cannot be satisfied by a sibling component's entries. Tab got real entries (Figma",
          "comment": true
        },
        {
          "gate": "audit-stale-anchors.mjs",
          "line": 535,
          "term": "overlap",
          "text": "//    overlap / library-origin），从此任何一次 npm key 改名都会被抓。",
          "comment": true
        }
      ],
      "excludedFalsePositives": [],
      "wideGateCount": 48,
      "wideGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "wideOnlyGates": [
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "narrowOnlyGates": [
        "audit-mockup-hash-n-semantic.mjs"
      ]
    },
    {
      "n": "10",
      "gist": "handoff 贴实测尺寸并与 canonical 比对",
      "anchor": {
        "file": "docs/internal/mockup-conventions.md",
        "line": 1540,
        "text": "- [ ] 任何按 canonical 尺寸建的容器，加内容后 handoff 能贴出实测尺寸并与 canonical 值比对（机器输出，非目测）"
      },
      "heading": {
        "line": 1499,
        "level": 4,
        "text": "M-INTEGRITY §I7 — canonical 尺寸容器加内容后必须复测尺寸 + 兄弟碰撞 + 依附几何（2026-07-30 新增）"
      },
      "narrowTerms": [
        "canonical",
        "measuredSize",
        "实测尺寸"
      ],
      "narrowGateCount": 30,
      "narrowGates": [
        "audit-artifact-routing.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-canonical-slot-guard.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-claude-design-reference-numbers.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-export-coverage.mjs",
        "audit-figma-library-vs-canonical.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-icon-canonical-names.mjs",
        "audit-icon-naming-rules.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-library-binding.mjs",
        "audit-mockup-overlap.mjs",
        "audit-no-hardcoded-ui-strings.mjs",
        "audit-on-fill-content-color.mjs",
        "audit-page-recipes.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-stale-anchors.mjs",
        "audit-style-contract.mjs"
      ],
      "narrowHits": [
        {
          "gate": "audit-artifact-routing.mjs",
          "line": 1,
          "term": "canonical",
          "text": "// audit-artifact-routing.mjs — canonical 制品路由 audit（跨文件、防\"死唤醒词\"漂移）。",
          "comment": true
        },
        {
          "gate": "audit-binding-config-parity.mjs",
          "line": 5,
          "term": "canonical",
          "text": "// canonical SFC defineProps they describe. Two checks per component:",
          "comment": true
        },
        {
          "gate": "audit-binding-config-parity.mjs",
          "line": 148,
          "term": "canonical",
          "text": "* supported (both legitimate and both used by canonical SFCs):",
          "comment": true
        },
        {
          "gate": "audit-binding-config-parity.mjs",
          "line": 183,
          "term": "canonical",
          "text": "//    imported from a deeper module — e.g. canonical/Chart.vue imports ChartType from",
          "comment": true
        },
        {
          "gate": "audit-binding-config-parity.mjs",
          "line": 236,
          "term": "canonical",
          "text": "const sfcRelPath = config.canonicalPath // e.g. '../canonical/ButtonBridge.vue'",
          "comment": false
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 2,
          "term": "canonical",
          "text": "// audit-canonical-compliance.mjs — INFRA-F37 Phase 1",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 4,
          "term": "canonical",
          "text": "// Scans canonical-layer components (src/canonical/*.vue + the src/components/**/*.vue",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 8,
          "term": "canonical",
          "text": "//          / __circle / __tick / __bullet) — should compose CheckBox / Radio canonical",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 10,
          "term": "canonical",
          "text": "//   CC-R3  native <input> outside the Input canonical wrappers",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 12,
          "term": "canonical",
          "text": "//          affordance) — should be a real <button> or canonical Button",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 20,
          "term": "canonical",
          "text": "//    现取三套编号空间：`grep -nE '\\bR[0-9]+\\b' scripts/audit-canonical-compliance.mjs`",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 24,
          "term": "canonical",
          "text": "// `grep -n 'audit-canonical-compliance' .husky/pre-commit .gitea/workflows/pr-checks.yml`）：",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 32,
          "term": "canonical",
          "text": "// Whitelist mechanism: place `<!-- audit-canonical-compliance: allow CC-R<n> -->`",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 40,
          "term": "canonical",
          "text": "//       `grep -rn 'audit-canonical-compliance:\\s*allow\\s\\+R[0-9]' src`（EXIT=1 才算迁干净），",
          "comment": true
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 55,
          "term": "canonical",
          "text": "'src/canonical',          // *.vue (1 level)",
          "comment": false
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 83,
          "term": "canonical",
          "text": "suggest: 'Compose <CheckBox> / <Radio> canonical instead of hand-rolled __check/__radio/__dot/__indicator/__circle/__tick/__bullet span/div.',",
          "comment": false
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 97,
          "term": "canonical",
          "text": "suggest: 'Use canonical <Button> (or canonical wrapper). Native <button> only allowed for disclosure triggers carrying aria-expanded.',",
          "comment": false
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 103,
          "term": "canonical",
          "text": "suggest: 'Use canonical <InputBoxLine|InputBoxFilled|InputNumber>. Native <input> permitted only inside the Input canonical implementation itself.',",
          "comment": false
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 117,
          "term": "canonical",
          "text": "suggest: 'Replace bare <span|<div> @click + <Icon> with a real <button type=\"button\"> (or canonical Button rimless+iconOnly). Required for keyboard a11y.',",
          "comment": false
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 138,
          "term": "canonical",
          "text": "for (const m of text.matchAll(/audit-canonical-compliance:\\s*allow\\s+(?:CC-)?R(\\d+)\\s+file/g)) {",
          "comment": false
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 154,
          "term": "canonical",
          "text": "if (!n) throw new Error(`[audit-canonical-compliance] 规则 id 不是 CC-R<n> 形态: ${rule}`)",
          "comment": false
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 155,
          "term": "canonical",
          "text": "const re = new RegExp(`audit-canonical-compliance:\\\\s*allow\\\\s+(?:CC-)?R${n}\\\\b`)",
          "comment": false
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 221,
          "term": "canonical",
          "text": "console.log('audit-canonical-compliance: OK — 0 findings across', files.length, 'files.')",
          "comment": false
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 225,
          "term": "canonical",
          "text": "console.log(`audit-canonical-compliance: ${findings.length} finding(s) across ${files.length} files\\n`)",
          "comment": false
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 236,
          "term": "canonical",
          "text": "console.log('\\nWhitelist: prefix offending tag with `<!-- audit-canonical-compliance: allow CC-R<n> -->`')",
          "comment": false
        },
        {
          "gate": "audit-canonical-compliance.mjs",
          "line": 237,
          "term": "canonical",
          "text": "console.log('           or place `<!-- audit-canonical-compliance: allow CC-R<n> file -->` anywhere in the file.')",
          "comment": false
        },
        {
          "gate": "audit-canonical-slot-guard.mjs",
          "line": 1,
          "term": "canonical",
          "text": "// audit-canonical-slot-guard.mjs",
          "comment": true
        },
        {
          "gate": "audit-canonical-slot-guard.mjs",
          "line": 3,
          "term": "canonical",
          "text": "// Read-only: flag CE-unsafe slot-presence detection in canonical components.",
          "comment": true
        },
        {
          "gate": "audit-canonical-slot-guard.mjs",
          "line": 5,
          "term": "canonical",
          "text": "// WHY (INFRA-F54): a canonical component becomes a Vue custom element via",
          "comment": true
        },
        {
          "gate": "audit-canonical-slot-guard.mjs",
          "line": 7,
          "term": "canonical",
          "text": "// the CE root vnode with props only — NO slot children — so the canonical root's",
          "comment": true
        },
        {
          "gate": "audit-canonical-slot-guard.mjs",
          "line": 15,
          "term": "canonical",
          "text": "// THE RULE: canonical components must detect slot presence via the CE-safe",
          "comment": true
        },
        {
          "gate": "audit-canonical-slot-guard.mjs",
          "line": 16,
          "term": "canonical",
          "text": "// `useHasSlot()` composable (src/canonical/composables/useHasSlot.ts), never via",
          "comment": true
        },
        {
          "gate": "audit-canonical-slot-guard.mjs",
          "line": 21,
          "term": "canonical",
          "text": "// SCOPE: src/canonical/**/*.vue ONLY — these are the CE roots. Base components in",
          "comment": true
        },
        {
          "gate": "audit-canonical-slot-guard.mjs",
          "line": 31,
          "term": "canonical",
          "text": "// Exit 1 if any live `$slots.` / `useSlots(` remains in canonical code; 0 clean.",
          "comment": true
        },
        {
          "gate": "audit-canonical-slot-guard.mjs",
          "line": 39,
          "term": "canonical",
          "text": "const CANONICAL_DIR = resolve(REPO_ROOT, 'src/canonical')",
          "comment": false
        },
        {
          "gate": "audit-canonical-slot-guard.mjs",
          "line": 63,
          "term": "canonical",
          "text": "findings.push({ file: `src/canonical/${file}`, line: i + 1, text: line.trim(), hint })",
          "comment": false
        },
        {
          "gate": "audit-canonical-slot-guard.mjs",
          "line": 70,
          "term": "canonical",
          "text": "console.error('\\n❌ CE-unsafe slot detection in canonical component(s) [INFRA-F54]:\\n')",
          "comment": false
        },
        {
          "gate": "audit-canonical-slot-guard.mjs",
          "line": 77,
          "term": "canonical",
          "text": "console.error('   (src/canonical/composables/useHasSlot.ts) instead of $slots / useSlots().')",
          "comment": false
        },
        {
          "gate": "audit-canonical-slot-guard.mjs",
          "line": 83,
          "term": "canonical",
          "text": "console.log('✓ audit-canonical-slot-guard: no CE-unsafe $slots/useSlots in src/canonical/*.vue')",
          "comment": false
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 15,
          "term": "canonical",
          "text": "//       · S1-no-canonical-props —— src/canonical 解析不出任何组件 props → 红",
          "comment": true
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 68,
          "term": "canonical",
          "text": "import { scanCanonicalProps, collectBarrelExports, BARREL_FILES } from './lib/canonical-props.mjs'",
          "comment": false
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 83,
          "term": "canonical",
          "text": "// unparsableProps = 有 defineProps 但读不出 props 的组件（canonical-props.mjs 头注释",
          "comment": true
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 163,
          "term": "canonical",
          "text": "//       都被归到 `Foo` 名下，`Foo` 不是 canonical 组件 ⇒ 两条一起丢，**连真阳性",
          "comment": true
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 171,
          "term": "canonical",
          "text": "//    （只认真实 canonical 组件当宿主），这边不要求。这是刻意的 —— 这边选错宿主后",
          "comment": true
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 192,
          "term": "canonical",
          "text": "// spec §6:189 的 S1 逐字是「barrel 导出解析为 0 / `src/canonical` 空 → 红」，",
          "comment": true
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 196,
          "term": "canonical",
          "text": "// 即把 src/canonical 删空 / 改名并不判红 —— 而 props 侧一空，S2 的每条 claim 都会在",
          "comment": true
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 198,
          "term": "canonical",
          "text": "// 覆盖面那行印「canonical 组件 : 0 个」。这正是 S1 存在的理由（防把 canonical",
          "comment": true
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 204,
          "term": "canonical",
          "text": "code: 'S1-no-canonical-props',",
          "comment": false
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 206,
          "term": "canonical",
          "text": "'src/canonical 解析不出任何组件 props —— 活源分母的 canonical 侧为 0，' +",
          "comment": false
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 208,
          "term": "canonical",
          "text": "'若真要退役 src/canonical，请连这条闸一起删。',",
          "comment": false
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 224,
          "term": "canonical",
          "text": "//  编的活源断言）。见 scripts/lib/canonical-props.mjs 头注释 ③ 的三态。",
          "comment": true
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 230,
          "term": "canonical",
          "text": "`${unparsable.length} 个 canonical 组件的 props 读不出来（有 defineProps 但取不到 ` +",
          "comment": false
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 233,
          "term": "canonical",
          "text": "'读不出来不许当成零 props 放行 —— 请给 scripts/lib/canonical-props.mjs 补上这种形态，' +",
          "comment": false
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 246,
          "term": "canonical",
          "text": "if (compProps === undefined) continue // 不是 canonical 组件，本闸不管",
          "comment": false
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 252,
          "term": "canonical",
          "text": "// 「零 props」这句话只许对**真的零 props**（canonical-props.mjs 三态里的 a）印。",
          "comment": true
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 386,
          "term": "canonical",
          "text": "//   1 —— failures 非空（S1-no-canonical-props / S1-no-barrel-exports / S1-unparsable-props /",
          "comment": true
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 416,
          "term": "canonical",
          "text": "console.log(`  canonical 组件 : ${live.props.size} 个`)",
          "comment": false
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 461,
          "term": "canonical",
          "text": "// src/canonical/*.vue、barrel 或本闸自身被 staged 的那一刻——恰好在那一刻，",
          "comment": true
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 464,
          "term": "canonical",
          "text": "// src/canonical」的那次 commit 上打印\"✓ 没有需要判定的文件（skipped）\"然后",
          "comment": true
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 465,
          "term": "canonical",
          "text": "// 假绿放行——而这恰恰是 S1 存在的理由（见 S1 注释：「防把 canonical 删空",
          "comment": true
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 21,
          "term": "canonical",
          "text": "* （那三个分别是「图标必须从 TVU library 取」/「复合组件优先 mirror canonical」/",
          "comment": true
        },
        {
          "gate": "audit-claude-design-reference-numbers.mjs",
          "line": 27,
          "term": "canonical",
          "text": "* ⚠️ 同类未清的还有一处（`audit-canonical-compliance.mjs` 的 `R1`–`R4`）—— 它的爆炸半径大得多",
          "comment": true
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 59,
          "term": "canonical",
          "text": "//   · **不认裸 `icon=`**：`<Button canonical-icon=\"loading\">` 是 Button 的 **variant 轴值**",
          "comment": true
        },
        {
          "gate": "audit-commit-affordance-trace.mjs",
          "line": 247,
          "term": "canonical",
          "text": "*   ② 不认裸 `icon=` —— `<Button canonical-icon=\"loading\">` 是 Button 的 **variant 轴值**",
          "comment": true
        },
        {
          "gate": "audit-component-affordances.mjs",
          "line": 9,
          "term": "canonical",
          "text": "//   B  Coverage both ways: every entry.code_name has src/canonical/<name>.vue,",
          "comment": true
        },
        {
          "gate": "audit-component-affordances.mjs",
          "line": 10,
          "term": "canonical",
          "text": "//      and every src/canonical/*.vue has an entry (no silent gaps)",
          "comment": true
        },
        {
          "gate": "audit-component-affordances.mjs",
          "line": 13,
          "term": "canonical",
          "text": "//      NOT the internal src/canonical/index.ts barrel — that one hid three wrong answers",
          "comment": true
        },
        {
          "gate": "audit-component-affordances.mjs",
          "line": 31,
          "term": "canonical",
          "text": "const CANONICAL_DIR = resolve(REPO_ROOT, 'src/canonical')",
          "comment": false
        },
        {
          "gate": "audit-component-affordances.mjs",
          "line": 64,
          "term": "canonical",
          "text": "const canonicalFiles = readdirSync(CANONICAL_DIR).filter((f) => f.endsWith('.vue'))",
          "comment": false
        },
        {
          "gate": "audit-component-affordances.mjs",
          "line": 65,
          "term": "canonical",
          "text": "const canonicalNames = new Set(canonicalFiles.map((f) => f.slice(0, -4)))",
          "comment": false
        },
        {
          "gate": "audit-component-affordances.mjs",
          "line": 70,
          "term": "canonical",
          "text": "fail(`[B] entry \"${c.code_name}\" has no src/canonical/${c.code_name}.vue`)",
          "comment": false
        },
        {
          "gate": "audit-component-affordances.mjs",
          "line": 73,
          "term": "canonical",
          "text": "for (const name of canonicalNames) {",
          "comment": false
        },
        {
          "gate": "audit-component-affordances.mjs",
          "line": 75,
          "term": "canonical",
          "text": "fail(`[B] src/canonical/${name}.vue has no entry in component-affordances.json (silent gap)`)",
          "comment": false
        },
        {
          "gate": "audit-component-affordances.mjs",
          "line": 80,
          "term": "canonical",
          "text": "// 对账真源 = src/index.ts（`exports['.']` 背后的那个文件），**不是** src/canonical/index.ts。",
          "comment": true
        },
        {
          "gate": "audit-component-affordances.mjs",
          "line": 81,
          "term": "canonical",
          "text": "// 2026-08-11 改：canonical/index.ts 只是内部 barrel（33 个），而 consumer 真正能",
          "comment": true
        },
        {
          "gate": "audit-component-affordances.mjs",
          "line": 130,
          "term": "canonical",
          "text": "// report-only：公开出口理应来自 src/canonical/*（AGENTS 硬规则 #6）",
          "comment": true
        },
        {
          "gate": "audit-component-affordances.mjs",
          "line": 133,
          "term": "canonical",
          "text": "if (!hit.dir.includes('canonical')) nonCanonical.push(`${exp} ← ${hit.dir}/${hit.file}.vue`)",
          "comment": false
        },
        {
          "gate": "audit-component-affordances.mjs",
          "line": 211,
          "term": "canonical",
          "text": "`[audit:component-affordances] PASS — ${comps.length} entries; coverage ${canonicalNames.size}/${canonicalNames.size}; ` +",
          "comment": false
        },
        {
          "gate": "audit-component-affordances.mjs",
          "line": 216,
          "term": "canonical",
          "text": "// report-only：AGENTS 硬规则 #6 说公开出口应来自 src/canonical/*，这里只点名不拦",
          "comment": true
        },
        {
          "gate": "audit-component-affordances.mjs",
          "line": 218,
          "term": "canonical",
          "text": "`[audit:component-affordances] note — ${nonCanonical.length} public export(s) not sourced from src/canonical/: ` +",
          "comment": false
        },
        {
          "gate": "audit-demo-disabled-style.mjs",
          "line": 41,
          "term": "canonical",
          "text": "//     - **19 条逐条判完，真违例 0 条**：站点外壳控件 12（meta-link / search / canonical-toggle /",
          "comment": true
        },
        {
          "gate": "audit-demo-slot-boolean-coverage.mjs",
          "line": 133,
          "term": "canonical",
          "text": "return { name: c.name, canonicalImport: c.canonicalImport, hasDefaultSlot, namedSlots, hasSlot, boolProps }",
          "comment": false
        },
        {
          "gate": "audit-demo-slot-boolean-coverage.mjs",
          "line": 212,
          "term": "canonical",
          "text": "// Vue pages import the canonical component, sometimes under a LOCAL ALIAS — e.g.",
          "comment": true
        },
        {
          "gate": "audit-demo-slot-boolean-coverage.mjs",
          "line": 215,
          "term": "canonical",
          "text": "// matches the tag actually rendered; fall back to the canonicalImport name.",
          "comment": true
        },
        {
          "gate": "audit-demo-slot-boolean-coverage.mjs",
          "line": 216,
          "term": "canonical",
          "text": "// (Previously the raw canonicalImport tag was only ever demonstrated in the",
          "comment": true
        },
        {
          "gate": "audit-demo-slot-boolean-coverage.mjs",
          "line": 218,
          "term": "canonical",
          "text": "const importRe = new RegExp(`import\\\\s+([A-Za-z0-9_]+)\\\\s+from\\\\s+['\"][^'\"]*\\\\b${comp.canonicalImport}\\\\.vue['\"]`)",
          "comment": false
        },
        {
          "gate": "audit-demo-slot-boolean-coverage.mjs",
          "line": 220,
          "term": "canonical",
          "text": "const tag = aliasMatch ? aliasMatch[1] : comp.canonicalImport",
          "comment": false
        },
        {
          "gate": "audit-export-coverage.mjs",
          "line": 5,
          "term": "canonical",
          "text": "* Flags Vue components that EXIST under src/components/ or src/canonical/ but are",
          "comment": true
        },
        {
          "gate": "audit-export-coverage.mjs",
          "line": 19,
          "term": "canonical",
          "text": "* canonical wrappers import them, so they need no allowlist. Only genuinely",
          "comment": true
        },
        {
          "gate": "audit-export-coverage.mjs",
          "line": 47,
          "term": "canonical",
          "text": "['Badge', 'base superseded by self-contained canonical/Badge.vue; orphan only used by legacy playground/App.vue (base type-split API). Repoint + remove per 原则 8 — see backlog CANONICAL-030'],",
          "comment": false
        },
        {
          "gate": "audit-export-coverage.mjs",
          "line": 48,
          "term": "canonical",
          "text": "['Select', 'legacy native-<select> base superseded by canonical SelectBox family (SelectBoxLine/Filled); orphaned from the public graph after INFRA-F50 repointed Pagination onto SelectBox. Only legacy playground/App.vue still imports it. Repoint + remove per 原则 8 — same disposition as Badge'],",
          "comment": false
        },
        {
          "gate": "audit-export-coverage.mjs",
          "line": 50,
          "term": "canonical",
          "text": "// What this entry exempts: src/canonical/Logo.vue, which IS orphaned (nothing on a public entry",
          "comment": true
        },
        {
          "gate": "audit-export-coverage.mjs",
          "line": 55,
          "term": "canonical",
          "text": "// ONLY public component in that file imported from src/components/ instead of src/canonical/.",
          "comment": true
        },
        {
          "gate": "audit-export-coverage.mjs",
          "line": 56,
          "term": "canonical",
          "text": "// ⇒ npm Vue consumers get the base, so they miss what the canonical wrapper adds:",
          "comment": true
        },
        {
          "gate": "audit-export-coverage.mjs",
          "line": 60,
          "term": "canonical",
          "text": "// untouched\", which is true of the canonical file but false as a statement about the package.",
          "comment": true
        },
        {
          "gate": "audit-export-coverage.mjs",
          "line": 61,
          "term": "canonical",
          "text": "['Logo', 'META-02 dual-framework code-first component: src/canonical/Logo.vue is the orphan being exempted (ships to claude.ai/design via components.config.ts, WC + React wrapper). NOTE: the BASE src/components/Logo/Logo.vue IS exported from src/index.ts — see the comment above before concluding anything about the published package. No standalone Figma source. See backlog META-02 + divergence logo-menulist-usermenu-code-first-2026-07-02'],",
          "comment": false
        },
        {
          "gate": "audit-export-coverage.mjs",
          "line": 132,
          "term": "canonical",
          "text": "...listFiles(resolve(SRC, 'canonical'), ['.vue']),",
          "comment": false
        },
        {
          "gate": "audit-export-coverage.mjs",
          "line": 137,
          "term": "canonical",
          "text": "// reachable because their canonical wrapper imports them; orphans (canonical",
          "comment": true
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 2,
          "term": "canonical",
          "text": "// audit-figma-library-vs-canonical.mjs — INFRA-F37 Phase 4",
          "comment": true
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 4,
          "term": "canonical",
          "text": "// Bidirectional diff between the Figma published library and src/canonical/.",
          "comment": true
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 6,
          "term": "canonical",
          "text": "//   A. Figma has X, no canonical → flag as missing canonical wrapper (decide:",
          "comment": true
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 8,
          "term": "canonical",
          "text": "//   B. canonical/X.vue exists, no figma-to-code-mapping.json entry → either the",
          "comment": true
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 9,
          "term": "canonical",
          "text": "//      canonical is genuinely Figma-divorced (divergence) OR the mapping is stale.",
          "comment": true
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 14,
          "term": "canonical",
          "text": "//   src/canonical/*.vue                                (canonical wrappers)",
          "comment": true
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 41,
          "term": "canonical",
          "text": "// canonical Vue wrappers — they have a different audit path",
          "comment": true
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 42,
          "term": "canonical",
          "text": "// (audit:icon-fill-currentcolor) and should not pollute this canonical diff.",
          "comment": true
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 60,
          "term": "canonical",
          "text": "// canonical mapping → which figma names code already binds",
          "comment": true
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 69,
          "term": "canonical",
          "text": "const canonicals = readdirSync(resolve(REPO_ROOT, 'src/canonical'))",
          "comment": false
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 77,
          "term": "canonical",
          "text": "'Chart',                // covered by Chart canonical via mapping (verify)",
          "comment": false
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 81,
          "term": "canonical",
          "text": "// Aliases — canonical filename → \"effective\" figma codeName for mapping check",
          "comment": true
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 86,
          "term": "canonical",
          "text": "// Known intentional B-side skips — canonical/*.vue that legitimately have no",
          "comment": true
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 88,
          "term": "canonical",
          "text": "// (figma-data/audit-allowlist/canonical-exempt.json), single SoT across",
          "comment": true
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 90,
          "term": "canonical",
          "text": "const EXEMPT_SOT = JSON.parse(readFileSync(resolve(REPO_ROOT, 'figma-data/audit-allowlist/canonical-exempt.json'), 'utf8'))",
          "comment": false
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 93,
          "term": "canonical",
          "text": "// ---- diff A: Figma has X, no canonical mapped ----------------------------",
          "comment": true
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 102,
          "term": "canonical",
          "text": "// ---- diff B: canonical exists, not in mapping ----------------------------",
          "comment": true
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 104,
          "term": "canonical",
          "text": "const canonicalNoMapping = []",
          "comment": false
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 105,
          "term": "canonical",
          "text": "for (const name of canonicals) {",
          "comment": false
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 109,
          "term": "canonical",
          "text": "canonicalNoMapping.push(name)",
          "comment": false
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 114,
          "term": "canonical",
          "text": "const total = unmapped.length + canonicalNoMapping.length",
          "comment": false
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 116,
          "term": "canonical",
          "text": "console.log(`audit-figma-library-vs-canonical: ${figmaPublished.size} figma published, ${canonicals.length} canonicals, ${mappedFigmaNames.size} mapped`)",
          "comment": false
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 117,
          "term": "canonical",
          "text": "console.log(`Findings: ${unmapped.length} figma-without-canonical, ${canonicalNoMapping.length} canonical-without-mapping\\n`)",
          "comment": false
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 120,
          "term": "canonical",
          "text": "console.log('A. Figma published but no canonical wrapper / mapping (decide: build canonical OR add to KNOWN_FIGMA_NO_CANONICAL):')",
          "comment": false
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 125,
          "term": "canonical",
          "text": "if (canonicalNoMapping.length) {",
          "comment": false
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 126,
          "term": "canonical",
          "text": "console.log('B. canonical/*.vue without figma-to-code-mapping.json entry (divergence OR stale mapping):')",
          "comment": false
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 127,
          "term": "canonical",
          "text": "for (const n of canonicalNoMapping.sort()) console.log(`   ${n}`)",
          "comment": false
        },
        {
          "gate": "audit-figma-library-vs-canonical.mjs",
          "line": 137,
          "term": "canonical",
          "text": "console.log('  - For A: confirm each name is a real component (not extract orphan). Build canonical OR add to KNOWN_FIGMA_NO_CANONICAL with reason.')",
          "comment": false
        },
        {
          "gate": "audit-gate-mount-declaration.mjs",
          "line": 120,
          "term": "canonical",
          "text": "'scripts/audit-canonical-slot-guard.mjs',",
          "comment": false
        },
        {
          "gate": "audit-gate-mount-declaration.mjs",
          "line": 128,
          "term": "canonical",
          "text": "'scripts/audit-figma-library-vs-canonical.mjs',",
          "comment": false
        },
        {
          "gate": "audit-hard-rule-compliance.mjs",
          "line": 11,
          "term": "canonical",
          "text": "//   - audit:component-no-inline-svg (inline <svg> in canonical/component .vue)",
          "comment": true
        },
        {
          "gate": "audit-hard-rule-compliance.mjs",
          "line": 122,
          "term": "canonical",
          "text": "const H3_SUGGEST = 'Consumer-facing template/demo code must use the TVU icon registry (Icon component or dist/icons/svg). Inline <svg> in these locations leaks non-canonical assets to consumers. See memory feedback_tvu-icons-mandatory.'",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 1,
          "term": "canonical",
          "text": "// audit-icon-canonical-names.mjs",
          "comment": true
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 4,
          "term": "canonical",
          "text": "// instead of the canonical registry name (e.g. name=\"close\" → canonical",
          "comment": true
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 7,
          "term": "canonical",
          "text": "// WHY: the icon registry assigns every icon a canonical `category/name` plus",
          "comment": true
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 9,
          "term": "canonical",
          "text": "// \"work\" — but mixing alias + canonical references is inconsistent and hides the",
          "comment": true
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 10,
          "term": "canonical",
          "text": "// icon's category/affordance. This audit lists each non-canonical reference with",
          "comment": true
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 11,
          "term": "canonical",
          "text": "// its correct canonical name so they can be normalized (and stay normalized).",
          "comment": true
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 14,
          "term": "canonical",
          "text": "// `name: '<canonical>'` and `aliases: ['<short>', ...]`. (dist/icons/manifest.json",
          "comment": true
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 18,
          "term": "canonical",
          "text": "// SCOPE: <Icon name=\"STATIC\"> in src/canonical/**/*.vue + src/components/**/*.vue.",
          "comment": true
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 23,
          "term": "canonical",
          "text": "// `grep -n 'audit-icon-canonical-names' .husky/pre-commit .gitea/workflows/pr-checks.yml`）：",
          "comment": true
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 24,
          "term": "canonical",
          "text": "//   L4 = .husky/pre-commit 的 `▶ pre-commit: icon-canonical-names` 条件块",
          "comment": true
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 29,
          "term": "canonical",
          "text": "// Exit 1 if any non-canonical reference is found (actionable); 0 when clean.",
          "comment": true
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 39,
          "term": "canonical",
          "text": "const SCAN_DIRS = ['src/canonical', 'src/components'].map((d) => resolve(REPO_ROOT, d))",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 41,
          "term": "canonical",
          "text": "// ---- build canonical-name set + alias→canonical map from generated manifests --",
          "comment": true
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 43,
          "term": "canonical",
          "text": "const canonicalNames = new Set()",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 65,
          "term": "canonical",
          "text": "const canonical = m[1]",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 66,
          "term": "canonical",
          "text": "canonicalNames.add(canonical)",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 72,
          "term": "canonical",
          "text": "if (!aliasToCanonical.has(a)) aliasToCanonical.set(a, canonical)",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 75,
          "term": "canonical",
          "text": "// Also catch entries that have a name but no aliases array (still canonical names).",
          "comment": true
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 78,
          "term": "canonical",
          "text": "while ((n = nameOnlyRe.exec(src))) canonicalNames.add(n[1])",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 126,
          "term": "canonical",
          "text": "if (canonicalNames.has(used)) continue // already canonical ✓",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 128,
          "term": "canonical",
          "text": "const canonical = aliasToCanonical.get(used)",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 136,
          "term": "canonical",
          "text": "canonical: canonical ?? null, // null = unknown (not a registered alias/canonical)",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 145,
          "term": "canonical",
          "text": "`audit-icon-canonical-names: ${canonicalNames.size} canonical names, ${aliasToCanonical.size} aliases; scanned ${SCAN_DIRS.length} dirs`,",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 149,
          "term": "canonical",
          "text": "console.log('OK — every static <Icon name> uses its canonical registry name.')",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 153,
          "term": "canonical",
          "text": "console.log(`\\n${findings.length} non-canonical <Icon name> reference(s):\\n`)",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 154,
          "term": "canonical",
          "text": "const known = findings.filter((f) => f.canonical)",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 155,
          "term": "canonical",
          "text": "const unknown = findings.filter((f) => !f.canonical)",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 158,
          "term": "canonical",
          "text": "console.log('A. Alias used — replace with canonical name:')",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 160,
          "term": "canonical",
          "text": "console.log(`   ${f.file}:${f.line}  name=\"${f.used}\"  →  name=\"${f.canonical}\"`)",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 165,
          "term": "canonical",
          "text": "console.log('B. Unknown name (not a registered canonical or alias — verify it resolves):')",
          "comment": false
        },
        {
          "gate": "audit-icon-canonical-names.mjs",
          "line": 171,
          "term": "canonical",
          "text": "console.log('Fix: replace each alias with its canonical name (col A), then re-run to confirm 0.')",
          "comment": false
        },
        {
          "gate": "audit-icon-naming-rules.mjs",
          "line": 3,
          "term": "canonical",
          "text": "// Read-only guards on the icon registry canonical names (Q4 / icon-naming §7).",
          "comment": true
        },
        {
          "gate": "audit-icon-naming-rules.mjs",
          "line": 14,
          "term": "canonical",
          "text": "// (machine-generated, stable shape — same source audit-icon-canonical-names parses).",
          "comment": true
        },
        {
          "gate": "audit-icon-naming-rules.mjs",
          "line": 16,
          "term": "canonical",
          "text": "// runtime-resolved catalog names, not registry canonical names — out of scope).",
          "comment": true
        },
        {
          "gate": "audit-icon-naming-rules.mjs",
          "line": 46,
          "term": "canonical",
          "text": "// ---- collect canonical names (must contain a '/') from generated manifests ----",
          "comment": true
        },
        {
          "gate": "audit-layout-tokens.mjs",
          "line": 46,
          "term": "canonical",
          "text": "// .canonical-shell 内边距归 ≤--bp-lg(1100) 的抽屉块、.docs-pager 三条归 --bp-sm(640)，",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 162,
          "term": "canonical",
          "text": "// the consuming project's spec (LCD: `TVU Pack/docs/specs/lcd-canonical-spec.md`",
          "comment": true
        },
        {
          "gate": "audit-mockup-library-binding.mjs",
          "line": 66,
          "term": "canonical",
          "text": "// canonical 组件名集（小写、去标点后匹配整词）——手搓 frame 伪装组件 warn（补 H 盲区）",
          "comment": true
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 12,
          "term": "canonical",
          "text": "//     「列一遍谁按它的坐标画的」「贴出 `间距 baseline → now` 两个实测值」「超出 canonical",
          "comment": true
        },
        {
          "gate": "audit-no-hardcoded-ui-strings.mjs",
          "line": 19,
          "term": "canonical",
          "text": "const SCAN_DIRS = ['src/canonical', 'src/components']",
          "comment": false
        },
        {
          "gate": "audit-on-fill-content-color.mjs",
          "line": 52,
          "term": "canonical",
          "text": "'src/canonical',",
          "comment": false
        },
        {
          "gate": "audit-page-recipes.mjs",
          "line": 13,
          "term": "canonical",
          "text": "//                （src/index.ts ∪ src/canonical/index.ts；不是文件名 —— Button 的文件叫",
          "comment": true
        },
        {
          "gate": "audit-page-recipes.mjs",
          "line": 27,
          "term": "canonical",
          "text": "// S2/S3 的分母都从**活源目录/文件**现算，不维护第二份清单：删掉一个 canonical 组件",
          "comment": true
        },
        {
          "gate": "audit-page-recipes.mjs",
          "line": 63,
          "term": "canonical",
          "text": "* S2 — 每个 slot 的 component 要么是 null（纯布局占位），要么必须在 canonicalNames 里",
          "comment": true
        },
        {
          "gate": "audit-page-recipes.mjs",
          "line": 67,
          "term": "canonical",
          "text": "export function validateComponentRefs(recipes, canonicalNames) {",
          "comment": false
        },
        {
          "gate": "audit-page-recipes.mjs",
          "line": 68,
          "term": "canonical",
          "text": "const known = new Set(canonicalNames)",
          "comment": false
        },
        {
          "gate": "audit-page-recipes.mjs",
          "line": 75,
          "term": "canonical",
          "text": "`recipe \"${recipe.id}\" slot \"${slot.slot}\": component \"${slot.component}\" 不是可消费的组件名（src/index.ts 与 src/canonical/index.ts 两个 barrel 的并集）` +",
          "comment": false
        },
        {
          "gate": "audit-page-recipes.mjs",
          "line": 199,
          "term": "canonical",
          "text": "*   它独有 `PillCounter` / `Logo`（这两个不在 canonical barrel 里）。",
          "comment": true
        },
        {
          "gate": "audit-page-recipes.mjs",
          "line": 200,
          "term": "canonical",
          "text": "* · `src/canonical/index.ts` = canonical barrel。它独有 `Chart`（经 `./chart` 子路径分发，",
          "comment": true
        },
        {
          "gate": "audit-page-recipes.mjs",
          "line": 223,
          "term": "canonical",
          "text": "// ① canonical barrel：`export { default as X } from './Y.vue'`",
          "comment": true
        },
        {
          "gate": "audit-page-recipes.mjs",
          "line": 224,
          "term": "canonical",
          "text": "const canonicalBarrel = readFileSync(resolve(root, 'src/canonical/index.ts'), 'utf8')",
          "comment": false
        },
        {
          "gate": "audit-page-recipes.mjs",
          "line": 225,
          "term": "canonical",
          "text": "for (const m of canonicalBarrel.matchAll(",
          "comment": false
        },
        {
          "gate": "audit-page-recipes.mjs",
          "line": 244,
          "term": "canonical",
          "text": "* 仅供交叉核对：src/canonical/*.vue 的文件名。**不是 S2 的分母**（见上）。",
          "comment": true
        },
        {
          "gate": "audit-page-recipes.mjs",
          "line": 248,
          "term": "canonical",
          "text": "return readdirSync(resolve(root, 'src/canonical'))",
          "comment": false
        },
        {
          "gate": "audit-page-recipes.mjs",
          "line": 333,
          "term": "canonical",
          "text": "['S2 slots[].component → 包入口/canonical barrel 公开导出', validateComponentRefs(data.recipes, publicNames)],",
          "comment": false
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 44,
          "term": "canonical",
          "text": "//         markup; consumers must use canonical Button / InputBox* / SelectBox*",
          "comment": true
        },
        {
          "gate": "audit-product-code.mjs",
          "line": 882,
          "term": "canonical",
          "text": "if (rule === 'R15') console.log('  Hint: use canonical Button / InputBox* / SelectBox*; escape via <!-- LIBRARY GAP: ... --> or // AUDIT-IGNORE-R15')",
          "comment": false
        },
        {
          "gate": "audit-render-coverage-gaps.mjs",
          "line": 14,
          "term": "canonical",
          "text": "// WHAT: statically enumerate, per canonical component (+ the base/component .vue",
          "comment": true
        },
        {
          "gate": "audit-render-coverage-gaps.mjs",
          "line": 37,
          "term": "canonical",
          "text": "const CANONICAL_DIR = resolve(REPO_ROOT, 'src/canonical')",
          "comment": false
        },
        {
          "gate": "audit-render-coverage-gaps.mjs",
          "line": 165,
          "term": "canonical",
          "text": "// .vue files this canonical pulls in (1-level import follow into src/components & src/canonical).",
          "comment": true
        },
        {
          "gate": "audit-render-coverage-gaps.mjs",
          "line": 166,
          "term": "canonical",
          "text": "function wrappedFiles(canonicalPath) {",
          "comment": false
        },
        {
          "gate": "audit-render-coverage-gaps.mjs",
          "line": 167,
          "term": "canonical",
          "text": "const src = readFileSync(canonicalPath, 'utf8')",
          "comment": false
        },
        {
          "gate": "audit-render-coverage-gaps.mjs",
          "line": 171,
          "term": "canonical",
          "text": "const abs = resolve(dirname(canonicalPath), rel)",
          "comment": false
        },
        {
          "gate": "audit-render-coverage-gaps.mjs",
          "line": 172,
          "term": "canonical",
          "text": "if (/\\/src\\/(components|canonical)\\//.test(abs.replace(REPO_ROOT, '/src').replace('//', '/')) || abs.includes('/components/') || abs.includes('/canonical/')) {",
          "comment": false
        },
        {
          "gate": "audit-render-coverage-gaps.mjs",
          "line": 179,
          "term": "canonical",
          "text": "// ---- collect styled selectors per canonical component ----------------------",
          "comment": true
        },
        {
          "gate": "audit-render-coverage-gaps.mjs",
          "line": 184,
          "term": "canonical",
          "text": "const canonicalPath = resolve(CANONICAL_DIR, file)",
          "comment": false
        },
        {
          "gate": "audit-render-coverage-gaps.mjs",
          "line": 186,
          "term": "canonical",
          "text": "const files = [canonicalPath, ...wrappedFiles(canonicalPath)]",
          "comment": false
        },
        {
          "gate": "audit-render-coverage-gaps.mjs",
          "line": 241,
          "term": "canonical",
          "text": "md += `**${totalGaps} styled selectors uncovered** out of ${totalStyled} across ${report.length} canonical components.\\n\\n`",
          "comment": false
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 4,
          "term": "canonical",
          "text": "// For every src/canonical/*.vue, report whether the render-verification MANIFEST has",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 5,
          "term": "canonical",
          "text": "// >=1 entry whose `codeComponent` IS that canonical.",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 7,
          "term": "canonical",
          "text": "// \"0 entries\" means the canonical claims a Figma binding (via data-figma-*",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 19,
          "term": "canonical",
          "text": "// caught it, in the light theme. Now the tally is `entry.codeComponent === canonical`,",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 24,
          "term": "canonical",
          "text": "// ⚠️ 2026-09-11 second root-fix — S1 (\">=1 entry per canonical\") is not enough.",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 30,
          "term": "canonical",
          "text": "// The per-canonical line even PRINTED \"via 9 figma name(s)\", but that 9 was never a",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 31,
          "term": "canonical",
          "text": "// criterion, only decoration. Measured baseline the day this landed: of 29 canonicals",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 35,
          "term": "canonical",
          "text": "//   S1 (unchanged): every non-exempt canonical has >=1 manifest entry.",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 53,
          "term": "canonical",
          "text": "//   - \"covered\" canonicals: name + entry count (+ silent-name breakdown when partial)",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 54,
          "term": "canonical",
          "text": "//   - \"uncovered\" canonicals: name + suggested next action (register mapping +",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 56,
          "term": "canonical",
          "text": "// Exit 1 if any canonical is uncovered (S1), any approved name is silently",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 69,
          "term": "canonical",
          "text": "// canonical name → set of approved figma names",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 70,
          "term": "canonical",
          "text": "const canonicalToFigma = new Map()",
          "comment": false
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 74,
          "term": "canonical",
          "text": "if (!canonicalToFigma.has(code)) canonicalToFigma.set(code, new Set())",
          "comment": false
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 75,
          "term": "canonical",
          "text": "canonicalToFigma.get(code).add(m.figmaName)",
          "comment": false
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 79,
          "term": "canonical",
          "text": "// codeComponent → entry count. The MANIFEST is authoritative for \"is this canonical",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 123,
          "term": "canonical",
          "text": "const canonicals = readdirSync(resolve(REPO_ROOT, 'src/canonical'))",
          "comment": false
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 134,
          "term": "canonical",
          "text": "// (figma-data/audit-allowlist/canonical-exempt.json), which is the single SoT",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 135,
          "term": "canonical",
          "text": "// for this set across F (this audit) and G (figma-library-vs-canonical).",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 136,
          "term": "canonical",
          "text": "const EXEMPT_SOT = JSON.parse(readFileSync(resolve(REPO_ROOT, 'figma-data/audit-allowlist/canonical-exempt.json'), 'utf8'))",
          "comment": false
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 140,
          "term": "canonical",
          "text": "for (const name of canonicals) {",
          "comment": false
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 142,
          "term": "canonical",
          "text": "const figmaNames = canonicalToFigma.get(effective) ?? new Set()",
          "comment": false
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 158,
          "term": "canonical",
          "text": "console.log(`audit-render-verification-coverage: ${canonicals.length} canonicals, ${covered.length} covered, ${exempt.length} exempt, ${uncovered.length} uncovered\\n`)",
          "comment": false
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 187,
          "term": "canonical",
          "text": "console.log('             For canonicals with mapping but 0 entries, inspect generate-render-verification-manifest.mjs')",
          "comment": false
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 193,
          "term": "canonical",
          "text": "// ⛔ 判定面排除 COVERAGE_EXEMPT 的 canonical：它们「按设计 0 条 entry」已由",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 194,
          "term": "canonical",
          "text": "// canonical-exempt.json 具名解释过，再要求把每个名字在第二张表里登记一遍 = 重复记账，",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 196,
          "term": "canonical",
          "text": "// （这一条是既有回归用例「0 entry 但在 canonical-exempt.json 里 → 绿」当场抓出来的，⛔ 不是事后想到的。）",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 197,
          "term": "canonical",
          "text": "// ⚠️ 如实登记的逃逸方向：把某个 canonical 塞进 canonical-exempt.json 同样能让 S2 闭嘴。",
          "comment": true
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 250,
          "term": "canonical",
          "text": "console.log(`OK — all ${covered.length} non-exempt canonicals have ≥1 verifier entry (${exempt.length} exempt by design).`)",
          "comment": false
        },
        {
          "gate": "audit-rule-number-collision.mjs",
          "line": 147,
          "term": "canonical",
          "text": "'docs/internal/retrospection/design-spec-canonical-alignment-tracker.md',",
          "comment": false
        },
        {
          "gate": "audit-share-frontmatter.mjs",
          "line": 101,
          "term": "canonical",
          "text": "const RETRO_EXCLUDE = new Set(['design-spec-canonical-alignment-tracker.md'])",
          "comment": false
        },
        {
          "gate": "audit-share-frontmatter.mjs",
          "line": 368,
          "term": "canonical",
          "text": "[`${RETRO_DIR}/design-spec-canonical-alignment-tracker.md`, false],",
          "comment": false
        },
        {
          "gate": "audit-stale-anchors.mjs",
          "line": 242,
          "term": "canonical",
          "text": "//       `src/canonical/generated/*.vue` —— 它不是一个路径，是一个模式。",
          "comment": true
        },
        {
          "gate": "audit-style-contract.mjs",
          "line": 11,
          "term": "canonical",
          "text": "// 无启发式：figma-styles 每个 style 的 `canonicalToken` 是**显式标注**，直接查表比对。",
          "comment": true
        },
        {
          "gate": "audit-style-contract.mjs",
          "line": 12,
          "term": "canonical",
          "text": "//   - Effect：只处理有 `cssBoxShadow`（drop-shadow）且 canonicalToken 是单一 `--xxx`",
          "comment": true
        },
        {
          "gate": "audit-style-contract.mjs",
          "line": 14,
          "term": "canonical",
          "text": "//   - Text：只处理有干净 canonicalToken 的 style。PingFang SC 变体无 canonicalToken → 跳过。",
          "comment": true
        },
        {
          "gate": "audit-style-contract.mjs",
          "line": 80,
          "term": "canonical",
          "text": "const token = st.canonicalToken",
          "comment": false
        },
        {
          "gate": "audit-style-contract.mjs",
          "line": 95,
          "term": "canonical",
          "text": "const token = st.canonicalToken",
          "comment": false
        },
        {
          "gate": "audit-style-contract.mjs",
          "line": 96,
          "term": "canonical",
          "text": "if (!token || !CLEAN_TOKEN.test(token)) continue // PingFang SC 变体等无 canonicalToken",
          "comment": false
        },
        {
          "gate": "audit-style-contract.mjs",
          "line": 125,
          "term": "canonical",
          "text": "console.log(`✓ style-contract PASS: variables.css 与 Figma Styles 真源一致（checked ${checked} 个 canonicalToken：Effect + Text）。`)",
          "comment": false
        }
      ],
      "excludedFalsePositives": [],
      "wideGateCount": 48,
      "wideGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "wideOnlyGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-status-consistency.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "narrowOnlyGates": [
        "audit-artifact-routing.mjs",
        "audit-canonical-slot-guard.mjs",
        "audit-claude-design-reference-numbers.mjs",
        "audit-export-coverage.mjs",
        "audit-figma-library-vs-canonical.mjs",
        "audit-icon-canonical-names.mjs",
        "audit-icon-naming-rules.mjs",
        "audit-mockup-library-binding.mjs",
        "audit-no-hardcoded-ui-strings.mjs",
        "audit-on-fill-content-color.mjs"
      ]
    },
    {
      "n": "11",
      "gist": "列一遍「谁按它的坐标画的」（含 ABSOLUTE 浮层）",
      "anchor": {
        "file": "docs/internal/mockup-conventions.md",
        "line": 1542,
        "text": "- [ ] 容器尺寸变更后**列一遍\"谁按它的坐标画的\"**（含 `ABSOLUTE` 浮层），逐个复测是否需要同步改（无依附元素也要显式答\"无\"）"
      },
      "heading": {
        "line": 1499,
        "level": 4,
        "text": "M-INTEGRITY §I7 — canonical 尺寸容器加内容后必须复测尺寸 + 兄弟碰撞 + 依附几何（2026-07-30 新增）"
      },
      "narrowTerms": [
        "ABSOLUTE",
        "dependent",
        "dependant",
        "依附"
      ],
      "narrowGateCount": 2,
      "narrowGates": [
        "audit-mockup-integrity.mjs",
        "audit-prop-naming.mjs"
      ],
      "narrowHits": [
        {
          "gate": "audit-mockup-integrity.mjs",
          "line": 6,
          "term": "依附",
          "text": "//     §I5/§I6/§I7 是 L1 自律前置 gate、且「§I7 依附几何脱钩只有 L1 probe 能抓，无机器兜底」，",
          "comment": true
        },
        {
          "gate": "audit-prop-naming.mjs",
          "line": 39,
          "term": "dependent",
          "text": "// on Node's built-in type stripping (>=22). This gate is a node-version-independent",
          "comment": true
        }
      ],
      "excludedFalsePositives": [],
      "wideGateCount": 48,
      "wideGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "wideOnlyGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "narrowOnlyGates": [
        "audit-prop-naming.mjs"
      ]
    },
    {
      "n": "12",
      "gist": "handoff 贴 `间距 baseline → now` 两个实测值",
      "anchor": {
        "file": "docs/internal/mockup-conventions.md",
        "line": 1543,
        "text": "- [ ] 每个依附元素在 handoff 贴出 **`间距 baseline → now`** 两个实测值（如 `dropdown vs row 底缘 : baseline 4 → now 4`），而不是\"判断为容器整体位移故无需补\"这类分类结论"
      },
      "heading": {
        "line": 1499,
        "level": 4,
        "text": "M-INTEGRITY §I7 — canonical 尺寸容器加内容后必须复测尺寸 + 兄弟碰撞 + 依附几何（2026-07-30 新增）"
      },
      "narrowTerms": [
        "baseline → now",
        "baselineNow",
        "baseline"
      ],
      "narrowGateCount": 11,
      "narrowGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-default-white-bg.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs"
      ],
      "narrowHits": [
        {
          "gate": "audit-acceptance-gate-coverage.mjs",
          "line": 544,
          "term": "baseline",
          "text": "allow: allowEntries.length, counts, baseline: BASELINE_UNCLASSIFIED,",
          "comment": false
        },
        {
          "gate": "audit-acceptance-gate-coverage.mjs",
          "line": 573,
          "term": "baseline",
          "text": "baselineUnclassified: BASELINE_UNCLASSIFIED,",
          "comment": false
        },
        {
          "gate": "audit-demo-css-page-scope.mjs",
          "line": 47,
          "term": "baseline",
          "text": "* 已知碰撞豁免表（**只许缩不许长**，范式同 `tests/visual-baseline-coverage.test.ts`",
          "comment": true
        },
        {
          "gate": "audit-gate-regression-ratchet.mjs",
          "line": 490,
          "term": "baseline",
          "text": "baseline: BASELINE,",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 82,
          "term": "baseline",
          "text": "export const ANNOTATION_RE = /(UX[\\s·•]|\\bPRD\\b|_flow-state|journey|jira\\s*requirement|\\bannot|\\blabel\\s*[·•]|注释|说明|\\bspec\\b|\\brules?\\b|changelog|change\\s*summary|\\bbaseline\\b)/i",
          "comment": false
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 159,
          "term": "baseline",
          "text": "// baseline is the TEXT node's own top-level `fills` (NOT node.style.fills).",
          "comment": true
        },
        {
          "gate": "audit-mockup-default-white-bg.mjs",
          "line": 51,
          "term": "baseline",
          "text": "export const BASELINE_PATH = resolve(__dirname, '..', 'figma-data', 'audit-allowlist', 'default-white-bg-baseline.json')",
          "comment": false
        },
        {
          "gate": "audit-mockup-default-white-bg.mjs",
          "line": 163,
          "term": "baseline",
          "text": "* @returns {{added:object[], stale:string[], baselineCount:number, hasBaseline:boolean}}",
          "comment": true
        },
        {
          "gate": "audit-mockup-default-white-bg.mjs",
          "line": 167,
          "term": "baseline",
          "text": "export function diffAgainstBaseline(fileKey, violations, baseline) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-default-white-bg.mjs",
          "line": 168,
          "term": "baseline",
          "text": "const entry = baseline?.byFile?.[fileKey]",
          "comment": false
        },
        {
          "gate": "audit-mockup-default-white-bg.mjs",
          "line": 169,
          "term": "baseline",
          "text": "if (!entry) return { added: violations, stale: [], baselineCount: 0, hasBaseline: false }",
          "comment": false
        },
        {
          "gate": "audit-mockup-default-white-bg.mjs",
          "line": 175,
          "term": "baseline",
          "text": "baselineCount: frozen.size,",
          "comment": false
        },
        {
          "gate": "audit-mockup-default-white-bg.mjs",
          "line": 204,
          "term": "baseline",
          "text": "const baseline = readBaseline()",
          "comment": false
        },
        {
          "gate": "audit-mockup-default-white-bg.mjs",
          "line": 205,
          "term": "baseline",
          "text": "const diff = diffAgainstBaseline(fileKey, violations, baseline)",
          "comment": false
        },
        {
          "gate": "audit-mockup-default-white-bg.mjs",
          "line": 208,
          "term": "baseline",
          "text": "baseline.byFile = baseline.byFile ?? {}",
          "comment": false
        },
        {
          "gate": "audit-mockup-default-white-bg.mjs",
          "line": 209,
          "term": "baseline",
          "text": "baseline.byFile[fileKey] = {",
          "comment": false
        },
        {
          "gate": "audit-mockup-default-white-bg.mjs",
          "line": 218,
          "term": "baseline",
          "text": "fs.writeFileSync(BASELINE_PATH, `${JSON.stringify(baseline, null, 2)}\\n`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-default-white-bg.mjs",
          "line": 231,
          "term": "baseline",
          "text": "baselineCount: diff.baselineCount,",
          "comment": false
        },
        {
          "gate": "audit-mockup-default-white-bg.mjs",
          "line": 239,
          "term": "baseline",
          "text": "console.log(`  扫描产品 frame ${scanned} · 违反 ${violations.length} · 基线 ${diff.hasBaseline ? diff.baselineCount : '(未冻结)'}`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 72,
          "term": "baseline",
          "text": "// Thresholds, classifiers and per-probe baselines are ALL configurable — the",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 128,
          "term": "baseline",
          "text": "baseline: { G1: 0, G2: 0, G3: 0, R1: 0, R2: 0, R3: 0 },",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 129,
          "term": "baseline",
          "text": "// Named exceptions, preferred over raising `baseline`: a bare count lets a NEW",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 150,
          "term": "baseline",
          "text": "//    4. ⚠️ **`baseline` 里的 R1/R2/R3 仍是 0，⛔ 别因为「不常开」就把它们调高** ——",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 207,
          "term": "baseline",
          "text": "//   `card-baseline` 40 差 20px = 一行矮一半 · `Frame 2233` 41 差 16 · `card-requirements` 31 差 15）·",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 249,
          "term": "baseline",
          "text": "baseline: { ...DEFAULT_CONFIG.baseline, ...(overrides.baseline || {}) },",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 874,
          "term": "baseline",
          "text": "if (env !== undefined) overrides.baseline = { ...(overrides.baseline || {}), [k]: Number(env) }",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 927,
          "term": "baseline",
          "text": "const over = Object.entries(counts).filter(([k, n]) => n > cfg.baseline[k])",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 930,
          "term": "baseline",
          "text": "baseline: Object.fromEntries(probeKeys.map(k => [k, cfg.baseline[k]])),",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 965,
          "term": "baseline",
          "text": "console.log(`counts:  ${probeKeys.map(k => `${k}=${counts[k]}`).join('  ')}   (baseline ${probeKeys.map(k => `${k}=${cfg.baseline[k]}`).join(' ')})`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 1014,
          "term": "baseline",
          "text": "console.log('✅ geometry-consistency audit pass (within baseline)')",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 1018,
          "term": "baseline",
          "text": "console.log(`❌ geometry-consistency audit FAIL — over baseline: ${over.map(([k, n]) => `${k} ${n}>${cfg.baseline[k]}`).join(', ')}`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 12,
          "term": "baseline → now",
          "text": "//     「列一遍谁按它的坐标画的」「贴出 `间距 baseline → now` 两个实测值」「超出 canonical",
          "comment": true
        },
        {
          "gate": "audit-mockup-typography-icon.mjs",
          "line": 22,
          "term": "baseline",
          "text": "//     file-level baseline, see mockup-conventions.md §342 C4).",
          "comment": true
        },
        {
          "gate": "audit-mockup-typography-icon.mjs",
          "line": 160,
          "term": "baseline",
          "text": "const ANNOTATION_RE = /(UX[\\s·•]|\\bPRD\\b|_flow-state|journey|jira\\s*requirement|\\bannot|\\blabel\\s*[·•]|注释|说明|\\bspec\\b|\\brules?\\b|changelog|change\\s*summary|\\bbaseline\\b)/i",
          "comment": false
        },
        {
          "gate": "audit-mockup-typography-icon.mjs",
          "line": 205,
          "term": "baseline",
          "text": "// FONT — annotation area only (product body follows library/file baseline)",
          "comment": true
        },
        {
          "gate": "audit-render-drift-gate-react.mjs",
          "line": 103,
          "term": "baseline",
          "text": "baselineA: BASELINE_A,",
          "comment": false
        },
        {
          "gate": "audit-render-drift-gate-react.mjs",
          "line": 104,
          "term": "baseline",
          "text": "baselineNodeMismatch: BASELINE_NODE_MISMATCH,",
          "comment": false
        },
        {
          "gate": "audit-render-drift-gate.mjs",
          "line": 16,
          "term": "baseline",
          "text": "// summary.classifications.A_TRUE_DRIFT_CANDIDATE，超 baseline 就 exit 1。",
          "comment": true
        },
        {
          "gate": "audit-render-drift-gate.mjs",
          "line": 36,
          "term": "baseline",
          "text": "// · S4（measuredEntries === total）。⛔ 四条**没有一条写死 baseline**。",
          "comment": true
        },
        {
          "gate": "audit-render-drift-gate.mjs",
          "line": 75,
          "term": "baseline",
          "text": "// baseline = 允许的 A_TRUE_DRIFT_CANDIDATE 上限。",
          "comment": true
        },
        {
          "gate": "audit-render-drift-gate.mjs",
          "line": 96,
          "term": "baseline",
          "text": "baselineA: BASELINE_A,",
          "comment": false
        },
        {
          "gate": "audit-render-drift-gate.mjs",
          "line": 97,
          "term": "baseline",
          "text": "baselineNodeMismatch: BASELINE_NODE_MISMATCH,",
          "comment": false
        },
        {
          "gate": "audit-render-verification-coverage.mjs",
          "line": 31,
          "term": "baseline",
          "text": "// criterion, only decoration. Measured baseline the day this landed: of 29 canonicals",
          "comment": true
        }
      ],
      "excludedFalsePositives": [],
      "wideGateCount": 48,
      "wideGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "wideOnlyGates": [
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "narrowOnlyGates": [
        "audit-mockup-default-white-bg.mjs"
      ]
    },
    {
      "n": "13",
      "gist": "同一批次多次改动 ⇒ 每次改动后都量",
      "anchor": {
        "file": "docs/internal/mockup-conventions.md",
        "line": 1544,
        "text": "- [ ] **同一批次里多次改动同一容器的内容时，每次改动后都量一次，不能只在批次末量一次**（2026-08-05 新增）—— 条文里「容器**或其内容**发生任何变化后」字面已含此意，但执行上极易做成\"批次末统一复位\"。**失败形态：复位做完了，之后又有一次文本编辑把容器撑高，前面那次复位当场作废，而 handoff 里贴的是复位后的数**。实证：Config-T UX 卡刚把与 Journey 卡的间距复位到 60，紧接着一次给追加内容加日期标注的小编辑又让卡长高 14 → 间距变 46，得再复位一次（V4-2333 §1k-Z ④）。**判据：改容器内容的调用与量间距的调用要一一对应；文本编辑再小也算改内容。**"
      },
      "heading": {
        "line": 1499,
        "level": 4,
        "text": "M-INTEGRITY §I7 — canonical 尺寸容器加内容后必须复测尺寸 + 兄弟碰撞 + 依附几何（2026-07-30 新增）"
      },
      "narrowTerms": [
        "batch",
        "批次",
        "perEdit"
      ],
      "narrowGateCount": 7,
      "narrowGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-jira-hyperlink.mjs",
        "audit-plan-lifecycle.mjs"
      ],
      "narrowHits": [
        {
          "gate": "audit-acceptance-gate-coverage.mjs",
          "line": 211,
          "term": "batch",
          "text": "//     `ai-ds-lab/docs/2026-09-08-q1-b2-batch1-preregistration.md`）：",
          "comment": true
        },
        {
          "gate": "audit-claim-vs-livesource.mjs",
          "line": 4,
          "term": "batch",
          "text": "//   实证 2026-07-30 `docs/superpowers/plans/2026-07-30-v1x-next-batch-ai-consumable-page-layer.md`",
          "comment": true
        },
        {
          "gate": "audit-mockup-conformance.mjs",
          "line": 57,
          "term": "批次",
          "text": "// ⚠️ **`--node` 取单个 id**（`argVal` = flag 后的**下一个** argv token）。「覆盖本批次触碰的",
          "comment": true
        },
        {
          "gate": "audit-mockup-connector.mjs",
          "line": 193,
          "term": "批次",
          "text": "return []   // 纯快照/批次 map，无 connector 可验证",
          "comment": false
        },
        {
          "gate": "audit-mockup-html-conformance.mjs",
          "line": 10,
          "term": "batch",
          "text": "// Rules (first batch — all from real violations observed 2026-07-24):",
          "comment": true
        },
        {
          "gate": "audit-mockup-jira-hyperlink.mjs",
          "line": 26,
          "term": "batch",
          "text": "//   `ai-ds-lab/docs/2026-09-08-q1-b2-batch1-preregistration.md` §2.2。⛔ 别顺手扩本闸的射程。",
          "comment": true
        },
        {
          "gate": "audit-plan-lifecycle.mjs",
          "line": 4,
          "term": "batch",
          "text": "//   立闸实例：`2026-07-30-v1x-next-batch` Task 6 Step 2③ 教人把 `build:wc` 串在 `build` 尾巴上，",
          "comment": true
        }
      ],
      "excludedFalsePositives": [],
      "wideGateCount": 48,
      "wideGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "wideOnlyGates": [
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "narrowOnlyGates": [
        "audit-mockup-jira-hyperlink.mjs"
      ]
    },
    {
      "n": "14",
      "gist": "超 canonical ⇒ handoff 写授权来源 + 边界余量算式",
      "anchor": {
        "file": "docs/internal/mockup-conventions.md",
        "line": 1545,
        "text": "- [ ] 容器最终尺寸**超出 canonical 数值**时，handoff 写明**授权来源**（谁拍板 / 哪条决策编号）+ **物理边界余量算式**（容器底 vs 内容区底 vs 底栏 y），不靠\"看起来还放得下\""
      },
      "heading": {
        "line": 1499,
        "level": 4,
        "text": "M-INTEGRITY §I7 — canonical 尺寸容器加内容后必须复测尺寸 + 兄弟碰撞 + 依附几何（2026-07-30 新增）"
      },
      "narrowTerms": [
        "授权",
        "authoriz",
        "override",
        "waiver"
      ],
      "narrowGateCount": 8,
      "narrowGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-jira-hyperlink.mjs",
        "audit-mockup-overlap.mjs",
        "audit-on-fill-content-color.mjs"
      ],
      "narrowHits": [
        {
          "gate": "audit-acceptance-gate-coverage.mjs",
          "line": 214,
          "term": "override",
          "text": "//        节点的链全部走 `style`、override 表里 0 个 ⇒ **只读 override 表的判据会把 109 条",
          "comment": true
        },
        {
          "gate": "audit-file-local-fidelity.mjs",
          "line": 9,
          "term": "授权",
          "text": "// 授权），CI 里拿不到。它守的是**另一个方向**、也正是真实漏出来的那个方向：",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 139,
          "term": "override",
          "text": "// override pointed to by characterStyleOverrides[i] in styleOverrideTable.",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 143,
          "term": "override",
          "text": "// a surrogate pair = 2 code units) consumes exactly ONE override slot. Callers",
          "comment": true
        },
        {
          "gate": "audit-mockup-bilingual-spacing.mjs",
          "line": 158,
          "term": "override",
          "text": "// fills: per-range override lives in styleOverrideTable[ovId].fills; the node",
          "comment": true
        },
        {
          "gate": "audit-mockup-feedback-not-selfdrawn.mjs",
          "line": 107,
          "term": "override",
          "text": "'instance（component_set `1408:17154`，form=pop confirm），再按需切变体 / 改 text override。',",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 106,
          "term": "override",
          "text": "// figma-component-catalog.md). Every consumer can override via --config.",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 164,
          "term": "override",
          "text": "// `controlPatterns` already is: a default that every consumer can override",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 245,
          "term": "override",
          "text": "export function compileConfig(overrides = {}) {",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 248,
          "term": "override",
          "text": "...overrides,",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 249,
          "term": "override",
          "text": "baseline: { ...DEFAULT_CONFIG.baseline, ...(overrides.baseline || {}) },",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 250,
          "term": "override",
          "text": "rows: { ...DEFAULT_CONFIG.rows, ...(overrides.rows || {}) },",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 303,
          "term": "override",
          "text": "// (unlike audit-mockup-overlap), because a control override nested in an",
          "comment": true
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 867,
          "term": "override",
          "text": "let overrides = {}",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 870,
          "term": "override",
          "text": "overrides = JSON.parse(readFileSync(CONFIG_PATH, 'utf8'))",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 874,
          "term": "override",
          "text": "if (env !== undefined) overrides.baseline = { ...(overrides.baseline || {}), [k]: Number(env) }",
          "comment": false
        },
        {
          "gate": "audit-mockup-geometry-consistency.mjs",
          "line": 876,
          "term": "override",
          "text": "const cfg = compileConfig(overrides)",
          "comment": false
        },
        {
          "gate": "audit-mockup-jira-hyperlink.mjs",
          "line": 31,
          "term": "override",
          "text": "//      节点的链**全部**走 `style`、override 表里 0 个 ⇒ 只读 override 表的判据会把",
          "comment": true
        },
        {
          "gate": "audit-mockup-jira-hyperlink.mjs",
          "line": 42,
          "term": "override",
          "text": "//                      override hyperlink ⇒ 违反。逐字对应 Acceptance 的「返回 … 非 null」。",
          "comment": true
        },
        {
          "gate": "audit-mockup-jira-hyperlink.mjs",
          "line": 94,
          "term": "override",
          "text": "* @returns {{url:string, via:'style'|'override'}|null}",
          "comment": true
        },
        {
          "gate": "audit-mockup-jira-hyperlink.mjs",
          "line": 105,
          "term": "override",
          "text": "if (u) return { url: u, via: 'override' }",
          "comment": false
        },
        {
          "gate": "audit-mockup-overlap.mjs",
          "line": 13,
          "term": "授权",
          "text": "//     时写明授权来源」都是 handoff 文本要求，本闸够不着，照样计入 6。⛔ 别读成「这 6 项都有闸」。",
          "comment": true
        },
        {
          "gate": "audit-on-fill-content-color.mjs",
          "line": 87,
          "term": "override",
          "text": "if (v === undefined && theme === 'light') v = raw.dark.get(name) // light block only overrides",
          "comment": false
        }
      ],
      "excludedFalsePositives": [],
      "wideGateCount": 48,
      "wideGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "wideOnlyGates": [
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "narrowOnlyGates": [
        "audit-mockup-jira-hyperlink.mjs",
        "audit-on-fill-content-color.mjs"
      ]
    },
    {
      "n": "15",
      "gist": "交付前 probe 四清单（style 绑定率 / 间距绑定 + scale 合规 / …）",
      "anchor": {
        "file": "docs/internal/mockup-conventions.md",
        "line": 3852,
        "text": "- [ ] 交付前 probe 四清单：style 绑定率 / 间距绑定 + scale 合规 / 字段 parity 表（重构任务，存档 handoff）/ 反馈类 inventory + 每卡强调预算"
      },
      "heading": {
        "line": 3850,
        "level": 4,
        "text": "M52 共用 Acceptance"
      },
      "narrowTerms": [
        "B-SCALE",
        "B-COVERAGE",
        "bindingRate",
        "binding-fidelity",
        "绑定率"
      ],
      "narrowGateCount": 7,
      "narrowGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-library-origin.mjs",
        "audit-stale-anchors.mjs"
      ],
      "narrowHits": [
        {
          "gate": "audit-acceptance-gate-coverage.mjs",
          "line": 178,
          "term": "binding-fidelity",
          "text": "//       • `mockup-conventions#C7`（文字双绑）→ `scripts/audit-mockup-binding-fidelity.mjs`",
          "comment": true
        },
        {
          "gate": "audit-gate-mount-declaration.mjs",
          "line": 115,
          "term": "binding-fidelity",
          "text": "//    被删的 10 行：bilingual-spacing / binding-fidelity / colors / conformance /",
          "comment": true
        },
        {
          "gate": "audit-gate-regression-ratchet.mjs",
          "line": 365,
          "term": "binding-fidelity",
          "text": "// 承载物：**`tests/audit-mockup-binding-fidelity-cli.test.ts`**（新增，整脚本 spawn 形态）",
          "comment": true
        },
        {
          "gate": "audit-gate-regression-ratchet.mjs",
          "line": 366,
          "term": "binding-fidelity",
          "text": "// ⇒ `audit-mockup-binding-fidelity.mjs` 的 `face` 由非 E 变 **`E-fx`**（`blocking: true` 未变）。",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 1,
          "term": "binding-fidelity",
          "text": "// audit-mockup-binding-fidelity.mjs  (D16 verifier)",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 4,
          "term": "binding-fidelity",
          "text": "//   `audit:mockup-binding-fidelity` 但没有任何自动执行点（pre-commit / prepublishOnly / CI",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 6,
          "term": "binding-fidelity",
          "text": "//   `binding-fidelity` 规则模块挂载。整文件模式实测可达 1009 条 findings。",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 18,
          "term": "binding-fidelity",
          "text": "//     §C5 的 Acceptance 逐字「机检见 D16 verifier（`audit-mockup-binding-fidelity`）」；",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 19,
          "term": "绑定率",
          "text": "//     §M52 共用 Acceptance 首条「style 绑定率」= 本闸判据，而下面这行逐字写着本闸",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 50,
          "term": "B-SCALE",
          "text": "//   B-SCALE (§M52.1) — itemSpacing / padding* / cornerRadius set to an",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 54,
          "term": "B-COVERAGE",
          "text": "//   B-COVERAGE — coverage census of unbound spacing / fill / stroke. This is a",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 82,
          "term": "binding-fidelity",
          "text": "//   node scripts/audit-mockup-binding-fidelity.mjs --file <fileKey> [--node <nodeId>] [--json] [--coverage]",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 179,
          "term": "B-SCALE",
          "text": "// ---- B-SCALE: off-scale raw spacing/radius not bound to a variable ----",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 204,
          "term": "B-COVERAGE",
          "text": "// ---------- B-COVERAGE: 未绑即报（不管 on/off-scale）----------",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 209,
          "term": "B-SCALE",
          "text": "// 不管裸值是否恰好 = token（区别 B-SCALE 只报 off-scale）。带 ignore 逃生口。",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 276,
          "term": "binding-fidelity",
          "text": "console.error('Usage: node scripts/audit-mockup-binding-fidelity.mjs --file <fileKey> [--node <nodeId>] [--json] [--coverage]')",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 304,
          "term": "B-SCALE",
          "text": "console.warn('Warning: /variables/local 403 (non-Enterprise); B-SEM 将 under-report（无法解析 grey vs semantic）。B-TYPO/B-EFFECT/B-SCALE 不受影响。')",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 315,
          "term": "B-COVERAGE",
          "text": "// B-COVERAGE: 仅在 --coverage flag 传入时运行（向后兼容；不传时不报）",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 325,
          "term": "B-SCALE",
          "text": "'B-SCALE': { pass: bScale.length === 0, violations: bScale },",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 326,
          "term": "B-COVERAGE",
          "text": "...(COVERAGE ? { 'B-COVERAGE': { pass: bCoverage.length === 0, violations: bCoverage } } : {}),",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 335,
          "term": "binding-fidelity",
          "text": "console.log('✅ D16 binding-fidelity audit pass (§M52.1 / §C5)')",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 338,
          "term": "binding-fidelity",
          "text": "console.log('❌ D16 binding-fidelity audit FAIL (§M52.1 / §C5)\\n')",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 355,
          "term": "B-SCALE",
          "text": "console.log(`B-SCALE — off-scale raw spacing/radius (${bScale.length}) — §M52.1 off-scale`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 360,
          "term": "B-COVERAGE",
          "text": "// 呈现层折叠：真实文件 B-COVERAGE 可达 6 万条，逐条列 = 噪音墙。",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 361,
          "term": "绑定率",
          "text": "// 改为绑定率摘要（按 kind 分组报 未绑 N / 总 M (X%)）+ top-10 样本。--json 仍出全量数组。",
          "comment": true
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 362,
          "term": "B-COVERAGE",
          "text": "console.log(`B-COVERAGE — unbound spacing/fill/stroke (${bCoverage.length}) — §M52.1 未绑即报（摘要）`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 379,
          "term": "B-COVERAGE",
          "text": "console.log(`  （共 ${bCoverage.length} 条 B-COVERAGE，已折叠为摘要；--json 看全量）`)",
          "comment": false
        },
        {
          "gate": "audit-mockup-binding-fidelity.mjs",
          "line": 388,
          "term": "binding-fidelity",
          "text": "console.error('audit-mockup-binding-fidelity: fatal\\n' + (err.stack || err.message))",
          "comment": false
        },
        {
          "gate": "audit-mockup-conformance.mjs",
          "line": 52,
          "term": "binding-fidelity",
          "text": "//   node scripts/audit-mockup-conformance.mjs --file <fileKey> --rule binding-fidelity,overlap",
          "comment": true
        },
        {
          "gate": "audit-mockup-conformance.mjs",
          "line": 81,
          "term": "binding-fidelity",
          "text": "//   · 整文件模式的 stdout 实测 **19 MB / 190 962 行**（binding-fidelity 一条就 118 371 条 findings）",
          "comment": true
        },
        {
          "gate": "audit-mockup-conformance.mjs",
          "line": 128,
          "term": "binding-fidelity",
          "text": "// binding-fidelity 在整文件模式下实测可达 1009 条，故上限只截 findingLines 文本，",
          "comment": true
        },
        {
          "gate": "audit-mockup-conformance.mjs",
          "line": 134,
          "term": "binding-fidelity",
          "text": "// 覆盖子闸打印过的节点。**折叠发生在打印之前、在本引擎看不见的上游**：binding-fidelity",
          "comment": true
        },
        {
          "gate": "audit-mockup-conformance.mjs",
          "line": 135,
          "term": "B-COVERAGE",
          "text": "// 的 B-COVERAGE 把 2649 条违例折成「摘要 4 行 + top 10 样本」再打印，于是 report 里",
          "comment": true
        },
        {
          "gate": "audit-mockup-library-origin.mjs",
          "line": 49,
          "term": "binding-fidelity",
          "text": "//     由 `audit-mockup-binding-fidelity` / `audit-mockup-colors` 守、③ 复用措辞无闸，",
          "comment": true
        },
        {
          "gate": "audit-stale-anchors.mjs",
          "line": 303,
          "term": "binding-fidelity",
          "text": "//   tests/mockup-binding-fidelity.test.ts 的 describe 名里带着退役的 M49.1（还加了 §），闸报绿而缺陷仍在",
          "comment": true
        }
      ],
      "excludedFalsePositives": [],
      "wideGateCount": 48,
      "wideGates": [
        "audit-acceptance-gate-coverage.mjs",
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-gate-mount-declaration.mjs",
        "audit-gate-regression-ratchet.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-binding-fidelity.mjs",
        "audit-mockup-conformance.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-stale-anchors.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "wideOnlyGates": [
        "audit-binding-config-parity.mjs",
        "audit-canonical-compliance.mjs",
        "audit-claim-vs-livesource.mjs",
        "audit-commit-affordance-trace.mjs",
        "audit-component-affordances.mjs",
        "audit-copied-token-drift.mjs",
        "audit-demo-css-page-scope.mjs",
        "audit-demo-disabled-style.mjs",
        "audit-demo-framework-parity.mjs",
        "audit-demo-slot-boolean-coverage.mjs",
        "audit-figma-env-single-source.mjs",
        "audit-figma-variables-freshness.mjs",
        "audit-file-local-fidelity.mjs",
        "audit-gate-ci-parity.mjs",
        "audit-handoff-deliverable-sections.mjs",
        "audit-hard-rule-compliance.mjs",
        "audit-layout-tokens.mjs",
        "audit-mockup-bilingual-spacing.mjs",
        "audit-mockup-connector.mjs",
        "audit-mockup-feedback-not-selfdrawn.mjs",
        "audit-mockup-geometry-consistency.mjs",
        "audit-mockup-handoff-evidence.mjs",
        "audit-mockup-html-conformance.mjs",
        "audit-mockup-integrity.mjs",
        "audit-mockup-overlap.mjs",
        "audit-mockup-source-link-integrity.mjs",
        "audit-mockup-typography-icon.mjs",
        "audit-page-recipes.mjs",
        "audit-plan-lifecycle.mjs",
        "audit-product-code.mjs",
        "audit-render-coverage-gaps.mjs",
        "audit-render-drift-gate-react.mjs",
        "audit-render-drift-gate.mjs",
        "audit-render-verification-coverage.mjs",
        "audit-rule-number-collision.mjs",
        "audit-share-frontmatter.mjs",
        "audit-shipped-import-closure.mjs",
        "audit-source-discipline-identifiers.mjs",
        "audit-status-consistency.mjs",
        "audit-style-contract.mjs",
        "audit-token-contract.mjs",
        "audit-typecheck-scope.mjs"
      ],
      "narrowOnlyGates": [
        "audit-mockup-library-origin.mjs"
      ]
    }
  ],
  "controls": [
    {
      "id": "MH3-denominator-nonempty",
      "ok": true,
      "detail": "判定面 = 81 道 scripts/audit-*.mjs · 1380823 B"
    },
    {
      "id": "MH1-anchors-unique",
      "ok": true,
      "detail": "15 条锚点全部现取到唯一行号：#1:231 #2:231 #3:231 #4:231 #5:233 #6:892 #7:939 #8:943 #9:1541 #10:1540 #11:1542 #12:1543 #13:1544 #14:1545 #15:3852"
    },
    {
      "id": "MH4-heading-resolved",
      "ok": true,
      "detail": "15 条全部定位到所属 heading：5 个不同 heading"
    },
    {
      "id": "MH2-geometry-gate-visible",
      "ok": true,
      "detail": "条目 1/2/3 的窄尺候选均含 audit-mockup-geometry-consistency.mjs"
    },
    {
      "id": "MN1-known-false-positives-excluded",
      "ok": true,
      "detail": "排除 4 处（应为 4）：#5 audit-mockup-bilingual-spacing.mjs:30〔hugs its EN〕 · #5 audit-mockup-bilingual-spacing.mjs:483〔hugs its EN〕 · #5 audit-mockup-html-conformance.mjs:15〔huge size〕 · #5 audit-mockup-html-conformance.mjs:160〔huge size〕"
    },
    {
      "id": "MN3-narrow-has-discrimination",
      "ok": true,
      "detail": "每条窄尺候选 < 判定面 81 道（实测 1–30 道）"
    },
    {
      "id": "MN2-no-verdict-fields",
      "ok": true,
      "detail": "输出零判定字段 ⇒ 本器只交候选面，(a)/(b)/(e) 定案必须人读闸源码"
    }
  ]
}