{
  "_meta": {
    "purpose": "给『Figma 真源在产品文件、DS 库里没有对应组件』的 code 组件一条轻量保真闸（INFRA-F82 残余④）。这类组件在 render-verification-manifest.json（936 条，只收已发布库组件）里 0 命中，因此在本快照之前是零机械覆盖 —— 实证后果：UserMenu 面板曾有 4 条分隔线而 Figma 只有 2 条，自 2026-07-02 `e8bb4e2b` 起近 4 周无人发现。",
    "whatThisGateDoesNotClaim": "本快照**不**声称这些值等于 Figma 今天的值。Figma 侧取值走 MCP（需交互授权），CI 里拿不到。它冻结的是『owner 签核那一刻代码declare 的状态』，守的是**代码从签核状态悄悄漂走**这一个方向 —— 也正是真实漏出来的那个方向。Figma↔快照 的对应关系由下面 ack 的 evidence + reviewDueBy 到期即红来管，形态同 INFRA-F79 的变量层新鲜度闸。",
    "howToUpdate": "断言红了不要直接改这里的数字。先按各条 figmaNodeId 对 Figma 真源核一遍（node 树 / 渲染图取样，见 backlog INFRA-F82 §全量对账里记的手法与采样陷阱），确认新值才是设计意图后，同时更新对应条目与 _meta.ack.verifiedAt / reviewDueBy。只改日期不重核 = 假绿。",
    "gate": "pnpm run audit:file-local-fidelity — L4 pre-commit 条件块 + L5 经 prepublishOnly（pr-checks.yml 在 PR 与 push:master 两侧都跑它，满足 INFRA-F61 gate 平权）",
    "ack": {
      "addedAt": "2026-07-31",
      "verifiedAt": "2026-07-30",
      "reviewDueBy": "2026-10-28",
      "signedOffBy": "owner (Vue 与 React 双侧看过 A/B 后签核)",
      "evidence": [
        "commit 5a2df7ae — UserMenu 按 Figma 6423:85 收口，owner 拍「值一律以 Figma 为准」",
        "commit f24e1fbe — 分隔线 4→2 那一轮（本闸要防复发的正是这条）",
        "Figma 产品文件 DtZcMkhNy6qh6jbQQnhreQ node 6423:85 『DD · finance · Healthy · menu』",
        "验收 Vue/React 各 10 项断言 20/20，并拿修改前实测数据当负控证断言真能红 12/12",
        "本快照的每个值均由 scripts/audit-file-local-fidelity.mjs 的解析器从活源抽出，非手打"
      ]
    },
    "notComponentExemptions": {
      "APP Icons (file-local)": "Figma 侧 mockup 用的 component set（18 变体，Tag×Color），本仓库没有对应 code 组件 —— `grep -rniE 'app.icons|3:14402|fefd6aac…' src/` 实测 0 命中。没有代码可守，故不进本闸；它是 mockup 侧的 catalog 条目，归 mockup 链的规则管。"
    }
  },
  "components": [
    {
      "component": "UserMenu",
      "file": "src/components/UserMenu/UserMenu.vue",
      "catalogHeading": "User Menu (file-local)",
      "figma": {
        "fileKey": "DtZcMkhNy6qh6jbQQnhreQ",
        "nodeId": "6423:85",
        "url": "https://www.figma.com/design/DtZcMkhNy6qh6jbQQnhreQ/Micro-Apps-20250923?node-id=6423-85"
      },
      "structure": [
        {
          "class": "user-menu__divider",
          "expectedCount": 1,
          "figmaNodeId": "6423:95",
          "note": "1px light-divider，全设计里只有这一条。历史 bug = 每个 section 各 prepend 一条 → 4 条分隔线藏了近 4 周；这条计数就是防它复发的判据"
        },
        {
          "class": "user-menu__slice-line",
          "expectedCount": 1,
          "figmaNodeId": "6423:111",
          "note": "2px deep-divider，只在 Language 行上方。与 __divider 刻意是两条不同规则：设计用了两种分隔线"
        },
        {
          "class": "user-menu__panel-avatar",
          "expectedCount": 1,
          "figmaNodeId": "6423:87",
          "note": "面板内 34px 头像（不是触发器的 32px）。曾整个缺失，owner 2026-07-30 要求补回"
        },
        {
          "class": "user-menu__body",
          "expectedCount": 1,
          "figmaNodeId": "6423:96",
          "note": "dd-footer 层。它的存在本身是承重的 —— 两条分隔线缩进差 8px 正是由它的 4px padding 造成"
        }
      ],
      "declarations": [
        { "selector": ".user-menu__panel", "property": "width", "value": "360px", "figmaNodeId": "6423:85", "note": "owner 取 Figma 的固定 360，不是 min-width" },
        { "selector": ".user-menu__panel", "property": "border-radius", "value": "var(--r-l)", "figmaNodeId": "6423:85", "note": "radius 12" },
        { "selector": ".user-menu__panel", "property": "padding", "value": "var(--sp-xxs) var(--sp-xxs) var(--sp-xs)", "figmaNodeId": "6423:85", "note": "4/4/8" },
        { "selector": ".user-menu__panel", "property": "gap", "value": "var(--sp-xxs)", "figmaNodeId": "6423:85", "note": "4 —— 分隔线刻意不自带 margin，上下 4px 全由这个 gap 给（否则每条实占 17px 而非 9px）" },
        { "selector": ".user-menu__panel", "property": "background", "value": "var(--bg-layer3)", "figmaNodeId": "6423:85", "note": "Figma 绑 ux/grey/grey-11 #262626；用 --bg-layer3 而非 --color-grey-11 是值形态映射，否则浅色主题下面板仍是深色" },
        { "selector": ".user-menu__panel", "property": "box-shadow", "value": "var(--shadow-l3)", "figmaNodeId": "6423:85" },
        { "selector": ".user-menu__header", "property": "padding", "value": "var(--sp-s)", "figmaNodeId": "6423:86", "note": "p-12。header 高 73 = 12 + max(34,49) + 12" },
        { "selector": ".user-menu__header", "property": "gap", "value": "var(--sp-xs)", "figmaNodeId": "6423:86", "note": "头像与 name-col 之间 8" },
        { "selector": ".user-menu__panel-avatar", "property": "width", "value": "34px", "figmaNodeId": "6423:87" },
        { "selector": ".user-menu__panel-avatar", "property": "height", "value": "34px", "figmaNodeId": "6423:87" },
        { "selector": ".user-menu__name-col", "property": "padding", "value": "var(--sp-xxs)", "figmaNodeId": "6423:89", "note": "name-col 自带 p-4 —— 正是它让 name 文本落在面板 x=62（4+12+34+8+4）" },
        { "selector": ".user-menu__body", "property": "padding", "value": "var(--sp-xxs)", "figmaNodeId": "6423:96" },
        { "selector": ".user-menu__body", "property": "gap", "value": "var(--sp-xxs)", "figmaNodeId": "6423:96" },
        { "selector": ".user-menu__divider", "property": "height", "value": "1px", "figmaNodeId": "6423:95" },
        { "selector": ".user-menu__divider", "property": "background", "value": "var(--line-divider-light)", "figmaNodeId": "6423:95", "note": "#434343" },
        { "selector": ".user-menu__slice-line", "property": "height", "value": "2px", "figmaNodeId": "6423:111" },
        { "selector": ".user-menu__slice-line", "property": "background", "value": "var(--line-divider-deep)", "figmaNodeId": "6423:111", "note": "#353535" },
        { "selector": ".user-menu__body :deep(.menu-list--vertical .menu-list__item)", "property": "min-height", "value": "32px", "figmaNodeId": "6423:108/109", "note": "menu-item 32 高。用 min-height 而非 padding 5.5px 避开半像素；scope 在 UserMenu 内不推广到 MenuList 全局（证据只覆盖这一处面板）" },
        { "selector": ".user-menu__body :deep(.menu-list--vertical .menu-list__item)", "property": "padding-top", "value": "0", "figmaNodeId": "6423:108/109", "note": "文本 21 居中、行自身无纵向 padding" },
        { "selector": ".user-menu__body :deep(.menu-list--vertical .menu-list__item:not(.is-active):not(.is-disabled))", "property": "color", "value": "var(--text-body)", "figmaNodeId": "6423:108/109", "note": "#f8f8f8（不是 --text-2 #cccccc）。两个 :not() 与 hover 重述是承重的 —— 无保护的 color 会与 MenuList.vue:175-217 同特异性、把激活行从品牌色悄悄改成正文色" }
      ]
    }
  ]
}
