# Prompt — INFRA-F27: build-icon-dist 倒退 fill="currentColor" 修复 (npm publish 阻塞)

> **角色**：executor（被 plan owner 调用 / 或单独跑）
> **范围**：在 `figma-sync/icon-artifacts.mjs` 加 `normalizeFillToCurrentColor` 函数 + 在 `buildCanonicalRecords` 调用，让 `pnpm build` 输出的 `src/icons/catalog/generated/*.ts` / `dist/icons/svg/*.svg` / `dist/icons/esm/*.js` 都不再含 hardcoded `fill="#DBDBDB"`
>
> ⚠️ **不要 commit / 不要 git add** — dirty 累积到 plan owner 复审通过后由 user 决定 commit
> ⚠️ 完成后 **STOP**，按 §4 报告格式回报
> ⚠️ **不扩范围**：仅改 `figma-sync/icon-artifacts.mjs`（+ 可能的 README/CHANGELOG 不在此 prompt）— **不改** figma-data/raw/ / 不改 figma 真源 export pipeline / 不改 `src/icons/raw.ts` / 不改 `src/icons/catalog/generated/*.ts` 的手动版本（让 build 自动 regen 即可）

---

## §0 — Plan owner 已定决策（user 拍板"全推荐"）

User 在 INFRA-F27 backlog entry 修复方向 + 4 个 sub-decision 上 2026-05-09 plan owner 对话拍板：

- **D1 = A 路线** — 在 `icon-artifacts.mjs` 加 `normalizeFillToCurrentColor` step（不动 figma 真源 export pipeline / 不反 CANONICAL-003 决策）
- **D2 = A.1a** 替换位置 — 在 `buildCanonicalRecords` line 75 后立刻替换（影响 `record.svg`，下游 catalog generated + dist svg + dist esm 自动一致）
- **D3 = A.2a** regex 范围 — 仅替换 6 位 hex `fill="#[A-Fa-f0-9]{6}"`（不碰 `fill="none"` / 不需黑名单）
- **D4 = A.4** 验证 — 三步 smoke（build + 再 build + audit）

executor 不需重新决策，**直接按 §2 执行**。

---

## §1 — 上下文 / 现状基线

### 1.1 根因链路（plan owner 已 verify）

1. `figma-data/published/icons/svg/<category>/<file>.svg` 文件原文是 `fill="#DBDBDB"`（figma plugin export 硬编码灰色）
2. [`figma-sync/icon-artifacts.mjs:75`](../../../figma-sync/icon-artifacts.mjs#L75) `readFileSync(svgAbsolutePath, 'utf8').trim()` 读 SVG 原文，**完全 pass-through 不替换**
3. [`figma-sync/icon-artifacts.mjs:114`](../../../figma-sync/icon-artifacts.mjs#L114) `templateLiteral(record.svg)` inline 写进 `src/icons/catalog/generated/<category>.ts`
4. 同时 `buildDistIconAssets()` (line 283) 把 `record.svg` 写入 `dist/icons/svg/*.svg` + `dist/icons/esm/*.js`——**也含同样的硬编码灰**

### 1.2 当前状态（plan owner 2026-05-09 已 verify，commit `91f43d74` 含证据）

| 文件 | 当前内容 | 跑 `pnpm build` 后 | 期望（修复后） |
|---|---|---|---|
| `src/icons/catalog/generated/*.ts` (28 文件) | `fill="currentColor"` ✅（手动 patch / CANONICAL-003 修复版）| 倒退成 `fill="#DBDBDB"` ❌ | `fill="currentColor"` 持续保持 |
| `figma-data/published/icons/svg/<category>/<file>.svg` | `fill="#DBDBDB"`（figma 真源 export 原值）| 不变 | **不变**（figma 真源不动；本任务范围不含） |
| `dist/icons/svg/*` + `dist/icons/esm/*` | gitignored，发到 npm 时含倒退版本 | 倒退含硬编码 | 修复后含 currentColor |

### 1.3 audit baseline（修复前已是 PASS — 因为仓库 commit 版本是 currentColor）

```
$ pnpm audit:icon-fill-currentcolor
{ "totals": { "findings": 0, "filesWithFindings": 0 } }
exit: 0
```

> ⚠️ **执行陷阱**：如果 executor **fire 前**先跑 `pnpm build`，会让 generated/*.ts 倒退成 `#DBDBDB`，audit 立刻 fail，而这是 INFRA-F27 bug 状态，不是 executor 改坏的。**正确顺序：先改 icon-artifacts.mjs → 再跑 build → 再跑 audit**。

### 1.4 项目位置

- 项目目标：[`AGENTS.md`](../../../AGENTS.md) + [`docs/PROJECT_GOAL.md`](../../PROJECT_GOAL.md)
- INFRA-F27 backlog entry：[`docs/internal/backlog.md`](../backlog.md) §INFRA-F27
- 关联：CANONICAL-003 ✅ Resolved 2026-05-06（本 bug 的 patch 来源）/ INFRA-F25（`audit:icon-fill-currentcolor` 检测器）

---

## §2 — 任务（按顺序执行）

### 2.1 修改 [`figma-sync/icon-artifacts.mjs`](../../../figma-sync/icon-artifacts.mjs)

**改动 1：加 `normalizeFillToCurrentColor` 函数**（位置：在 `templateLiteral` 函数后；即原文件 line 47 后插入）

```js
function normalizeFillToCurrentColor(svg) {
  return svg.replace(/fill="#[A-Fa-f0-9]{6}"/g, 'fill="currentColor"')
}
```

**改动 2：在 `buildCanonicalRecords` 调用**（位置：line 75 当前是 `const svg = readFileSync(svgAbsolutePath, 'utf8').trim()`）

改前：
```js
const svgAbsolutePath = resolve(ROOT, record.path)
const svg = readFileSync(svgAbsolutePath, 'utf8').trim()
```

改后：
```js
const svgAbsolutePath = resolve(ROOT, record.path)
const svg = normalizeFillToCurrentColor(readFileSync(svgAbsolutePath, 'utf8').trim())
```

> 仅一行 inline 改动。**不重命名 record.svg / 不引入中间变量 / 不改其它 record 字段**。

### 2.2 不改其它任何文件

显式禁止：

- ❌ 不改 `figma-data/published/icons/svg/<category>/<file>.svg`（figma 真源 — 硬规则 #1：不修改 Figma；这些 SVG 是 figma plugin export，等价于 figma 真源数据）
- ❌ 不改 `figma-data/published/icons/index.json`
- ❌ 不改 `src/icons/raw.ts` 或 `src/icons/index.ts` 或 `src/icons/catalog/manifest.ts`
- ❌ 不手动改 `src/icons/catalog/generated/*.ts`（让 build 自动 regen，结果应与当前 commit 版本一致）
- ❌ 不改任何 audit 脚本
- ❌ 不删任何文件

### 2.3 不动 git status

执行 §3 验证时 `pnpm build` 会重写 `dist/`（gitignored 不影响）+ 可能短暂产生 `src/icons/catalog/generated/*.ts` dirty（修复正确则 build 完成时 0 dirty）。executor 跑完不要 `git add` / 不要 `git checkout` / 保持原样让 plan owner 复审。

---

## §3 — 验证（D4 三步 smoke）

按下面顺序执行，每步贴关键输出到 §4 报告：

### Step 1 — 单次 build smoke（修复成功的核心证据）

```bash
pnpm build 2>&1 | tail -3
echo "exit: $?"
git status --short -- 'src/icons/catalog/generated/*.ts' 'figma-data/published/icons/manifest.json'
```

**预期**：
- `pnpm build` exit 0
- `git status --short -- 'src/icons/catalog/generated/*.ts'` 输出 **0 行**（修复后 build 输出 = 仓库 commit 版本 = `fill="currentColor"`）
- `figma-data/published/icons/manifest.json` 可能因 timestamp 刷新出现 1 行 `M`（可接受，不阻塞——属于 INFRA-F26 范围）

> 如 `git status` 仍有 28 个 generated icon ts 文件 dirty → **修复未生效**，停下检查 normalizeFillToCurrentColor 是否被调用、regex 是否匹配、record.svg 替换是否进了 templateLiteral 输出。

### Step 2 — 二次 build 幂等性

```bash
pnpm build 2>&1 | tail -3
echo "exit: $?"
git status --short
```

**预期**：与 Step 1 相同（manifest.json timestamp 可能再刷一次，但 generated/*.ts 应仍 0 行 dirty）

### Step 3 — audit 持续 PASS

```bash
pnpm audit:icon-fill-currentcolor 2>&1 | tail -10
echo "exit: $?"
```

**预期**：
- `findings: 0` / `filesWithFindings: 0`
- exit 0

### Step 4 — typecheck 不 regress

```bash
pnpm exec vue-tsc --noEmit
echo "exit: $?"
```

**预期**：exit 0

---

## §4 — 报告格式（完成后 STOP 回报到对话）

```
## INFRA-F27 执行报告

### 文件改动
- 修改：figma-sync/icon-artifacts.mjs
  - 加 normalizeFillToCurrentColor 函数 (在 templateLiteral 后)
  - buildCanonicalRecords line 75 svg 读后调用 normalize
  - diff 行数：+__ / -__
- 其它：无（按 §2.2 显式禁止）

### 验证（§3 四步）
- Step 1 build: exit __ / git status generated/*.ts dirty 行数: __
- Step 2 二次 build: exit __ / git status: ...
- Step 3 audit:icon-fill-currentcolor: exit __ / findings: __
- Step 4 vue-tsc --noEmit: exit __

### 未解决项 / blocker
- 无 / 列出

STOP — 等 plan owner 复审 + user 拍板 commit。
```

---

## §5 — Blocker 自检（pre-flight 强制）

executor 在跑 §2 之前 verify，命中任一 → STOP，不执行：

- [ ] `git status --short` 当前应只含本 prompt 文件（master 已 push 12ed815c）。如有 `src/icons/catalog/generated/*.ts` 已 dirty → 说明前面有人跑过 build 但没修 → executor 先 `git checkout -- src/icons/catalog/generated/` 让基线干净再 fire
- [ ] `wc -l figma-sync/icon-artifacts.mjs` 应是 303 行。如不是 → 代码已变，patch 行号失效，STOP 报告
- [ ] `grep -c "function normalizeFillToCurrentColor" figma-sync/icon-artifacts.mjs` 应是 0。如已存在 → 已被某次提前实现，STOP 报告
- [ ] `pnpm audit:icon-fill-currentcolor` 应 exit 0（baseline）。如 fail → 仓库基线已坏（generated/*.ts 已倒退），先 `git checkout HEAD -- src/icons/catalog/generated/` 恢复

---

## §6 — 反模式自检（plan owner 已过；列在此供审阅）

| # | 反模式 | 自检结论 |
|---|---|---|
| 1 | 硬编码项目级规则到工具 | ✗ "icon 默认 currentColor"是设计系统约定，本 prompt 把这条约定下移进 build pipeline（机制实例化），符合正解 |
| 2 | 打补丁方案 | ✗ 不是为某 icon 加特例；是抽象成 `normalizeFillToCurrentColor` 机制让所有 icon 自动一致 |
| 3 | to-do list 思维（无产出契约） | ✗ §3 含可机械验证的 schema（git diff 0 / audit findings:0 / exit code）|
| 4 | 没问下游怎么消费 | ✗ 下游：`src/icons/catalog/generated/*.ts` 编译进 npm 包 / `dist/icons/svg/` CDN / `dist/icons/esm/` ESM 三路全跟 `record.svg` 改而自动一致 |
| 5 | 扩展时改哪里 | ✗ 未来若 figma export 改成其它色（如黄色品牌 icon）想保留硬编码 — `normalizeFillToCurrentColor` 一处加白名单或调整 regex 即可 |

---

## §7 — 关联

- backlog entry: [`docs/internal/backlog.md`](../backlog.md) §INFRA-F27
- 依赖：CANONICAL-003 ✅ Resolved 2026-05-06（patch 来源）/ INFRA-F25（audit 检测器）
- 阻塞：NPM-003 publish CI（不修则发出去的 npm 包 icon 全是硬编码灰）
- 触发证据：commit `91f43d74` cleanup pre-flight `pnpm build` smoke
- 不依赖：NPM-001 / NPM-002（可与之并行 fire）
