# Next Session Pickup — Select 下拉 Teleport（C）+ 场景化强制 G1–G4

> 生成: 2026-06-10 session 收尾。两条独立线,**依次做**:先 C(单组件重构,有界),再 G1–G4(enforcement,多部件)。
> 起手第一句: `按 docs/internal/_plans/next-session-pickup-2026-06-10-select-teleport-and-enforcement.md 依次做 C 和 G1-G4`

---

## 0. 起手必读（mandatory onboarding）

6-step onboarding（FIGMA_AS_SOURCE_OF_TRUTH → STATUS → PROJECT_GOAL → tracker → 本 pickup → AGENTS §0-10）+ `git log --oneline -8` + `git status`（注意:工作区可能有并行 session 的 dirty 文件 `.claude/settings.json` / `skills/shared-vocab-rules/vocabulary.md` + 一个 stash → **别动**,假设别 session 进行中）。

## 上个 session 已 ship（context）

- **INFRA-F42**（commit 14c69d2c）: 图标导出 diff 化(内容 hash + file-version 快门 + orphan 定向清理 + 中断安全)。已 shipped。
- **commit c91e1bc3**（docs QA + 组件保真批）: Chart 展示 2 列无滚动 / PopupBox 换 DS Button + 表单弹窗 demo + 删冗余 alert/confirm/open / base PopupBox footer 按钮 bug(死 legacy prop→canonical) / Button 自带 DS 字体(修 Teleport 字体) / SelectBox value 左对齐 / canonical-compliance 白名单识别 root-fix / docs nav 把 Feedback 拆 Overlays&Messaging + Progress→Data / **`docs/internal/ds-enforcement-scenarios-spec.md`(G1-G4 真源 spec)**。

---

## 任务 C — Select 下拉 Teleport（先做,有界,Refactor 类）

### 问题（已实证,别重查）
`SelectBoxFilled`/`SelectBoxLine` 的下拉列表(`DropDownListSelect`)在 **PopupBox 里被裁切**;在 **Select 独立页正常**（user 截图实证）。

### 根因（已定位）
- `src/canonical/SelectBoxBase.vue` 里 `<DropDownListSelect v-if="isOpen" class="select-dropdown">` 是 trigger `<button>` 的**兄弟节点**,在 `.select-wrap`(position:relative)内。
- `.select-dropdown`(SelectBoxBase ~L466)**已是** `position:absolute; top:calc(100%+8px); left:0; z-index:30; min-width:100%; width:max-content` → 独立页能正常浮起。
- **但** `src/components/PopupBox/PopupBox.vue` 的 `.popup-box__body`(~L329)是 `flex:1; min-height:0; overflow:auto`(height-bounded modal 的滚动容器,**长表单真需要**滚动,不能简单删 overflow)→ 绝对定位的下拉被 body 的 `overflow:auto` 裁掉。

### 正解 = Teleport 下拉到 body（核心组件重构,影响所有 Select）
1. `SelectBoxBase.vue`: 用 `<Teleport to="body">` 包 `<DropDownListSelect>`。
2. 用 trigger 的 `getBoundingClientRect()` 算 **fixed 定位**(top/left/width),open 时设到 teleport 的下拉容器。
3. open 期间监听 `scroll`(capture)+ `resize` 重算定位(关闭或重定位)。
4. **关键陷阱**: click-outside 在 `handleDocumentPointerDown`(SelectBoxBase ~L133-137)用 `rootRef.value.contains(target)` 判定 → teleport 后下拉在 rootRef 外 → 点选项会被判 outside → close 先于 select 触发 → **选不中**。必须加 `dropdownRef` 并改成 `!rootRef.contains(t) && !dropdownRef.contains(t)` 才 close。
5. `width:300px`(L variant,L281)等宽度规则迁移到 fixed 定位时按 trigger 实测宽度走。

### Gate（C）
- `pnpm vue-tsc --noEmit` 0 · `pnpm vitest run`(Select 相关绿,219 baseline)· `pnpm test:render-verification` + `pnpm audit:render-drift-gate`(A=0 不回归)· `pnpm audit:canonical-compliance`(注意:R2 regex 会匹配 CSS 注释里的字面 `<button>` → 写注释避开该 token,上个 session 踩过)。
- **视觉双验**: ① Select 独立页(`#/select`)下拉仍正常浮 + 选项可选;② PopupBox 页(`#/popup-box` → Open Output Settings → Protocol 下拉)下拉浮在最上层、不被 modal 裁、不撑开 Auto start、点选项能选中。
- dev server: `pnpm dev`(可能撞并行 session 的 5173 → 自己落 5174,截图认准自己端口;收尾只 kill 自己端口,别动 5173)。

---

## 任务 G1–G4 — 场景化强制（后做,真源 = `docs/internal/ds-enforcement-scenarios-spec.md`）

**先完整读 spec**。它已对齐 owner 既有的 `code-conventions.md` R7(4 场景)/R6 + `design-process.md` M21 —— **指引层已完备,只补自动化强制 + 接线**,且**强制必须场景感知**(场景2 已有产品增量 = 增量/非阻塞,不能 strict-TVU 全量闸,否则违 R6"不修 pre-design-system 债")。

owner 已拍板 **G1-G4 全做**,建议顺序 G1+G3 → G4 → G2,**每个独立 commit**:
- **G1**（High,直击"手搓按钮"）: 新 ESLint 规则 `no-native-element`（消费代码手搓 `<button>/<input>/<select>/<textarea>` 而 DS 有等价物时报错）+ 镜像 `audit-product-code.mjs` R 规则 + 接进 `audit:consumer-code`。`eslint-plugin/rules/` 加新文件 + `index.js` 注册 + recommended config。
- **G3**（High,堵"silent 通过"）: 消费模板 `templates/consumer-product/` ship `eslint.config`(extends recommended)+ `package.json` lint/audit script + pre-commit；`setup-tvu-consumer` 技能按场景接线(场景3 全量严格 / 场景2 只挂 lint-staged 改动文件)。
- **G4**（Medium）: 建 `audit:mockup-conformance` 总闸 = 合并 5 个 `audit-mockup-*.mjs` + 接 orphan 的 `audit-mockup-library-origin.mjs`(当前无 npm script,从不跑)。
- **G2**（Medium,最后,易误报）: ESLint `no-hardcoded-spacing`(px 在有 token 时报)+ audit 镜像;**先建 px↔token 映射表防误报**(消费代码合法 px 多)。

### Gate（每个 G）
vue-tsc 0 · 相关 audit/lint 自测(造正例反例验)· `audit:self-audit-phase2` 不回归 · 新 audit 必挂 pre-commit(memory [[feedback_audit-pre-commit-gate]]:新 audit 不挂 hook = 违例 push 进 master)。

---

## 纪律提醒（避免重蹈本 session 的坑）
- **owner 直 commit master + 含 push**;`src/**`/eslint-plugin 等 consumer-facing 改动**带 changeset**(patch/minor),docs/figma-sync/scripts 内部改动不带。
- **视觉改动 commit 需 `VISUAL_COMMIT_APPROVED=1`**(owner 看过截图后)。
- **verify before build**: 本 session 多次"发现"其实代码已对(close × 已 rest=Default/hover=Active、下拉本就 absolute、side-pop 已发布双主题)。改前先对 live render / 读真源确认,别凭截图猜。
- **小步 commit**: 别堆超大未提交 batch(本 session 教训);尤其 C 是全局组件重构,改完即验即提。
- D 的延伸（可选 follow-up）: render-verifier 不校 `text-align`(所以 Select 居中没被 sync 抓到)→ 可给 verifier 加 text-align 检查,属 INFRA-F41 verifier 覆盖同类。
