@@ -145,67 +145,83 @@ export const SHADOW_RESIDUAL: readonly { file: string; kind: ShadowResidualKind;
145145 * · 同一次提交必须手工抬高 `FROZEN_SURFACE_TAG_TOTAL` **并**同步 `SURFACE_TAG_ANCHOR.entries`
146146 * (只许抬高到「Σ 登记值」;凭空抬高总数而不登记 ⇒ 红)。
147147 * ③ **两条共同**:未登记文件的命中仍然 = 0;登记的文件必须**真实存在**(在扫描面内)且
148- * **此刻仍命中 == count**(防僵尸登记);`file` 唯一;键按字典序(与 `FROZEN_*_BY_FILE` 同范式);
148+ * **此刻仍命中 == count**(防僵尸登记);**`(file, tier)` 唯一**(同一文件**可以两行** —— `legacy`
149+ * 一行 + `new` 一行,这正是 T19 开的合法路径);键按字典序(与 `FROZEN_*_BY_FILE` 同范式);
149150 * `reason` 必须写明**哪个视图 / 为什么必须用 `<Surface>`**(≥ 12 字,照 `SHADOW_RESIDUAL` 范式)。
151+ * ④ **T19:同一文件两行时的算术**(键 `file` → `(file, tier)` 的**唯一**目的):
152+ * · legacy 档:`实测 − 该文件 new 档登记值之和` = 残存的 legacy 面 ⇒ 仍须 `≤` 登记值(且 ≠ 0);
153+ * · new 档:`实测 − 该文件 legacy 档登记值之和` 必须**恰等于** new 档登记值之和(不是 ≤);
154+ * · 全局「Σ 登记值 == 总数 == Σ 实测」+ 逐文件「登记值之和 ≥ 实测」⇒ 两侧恒等式同时成立。
155+ * ⇒ **legacy 文件内新增一处 `<Surface>`** 的合法路径 = 加一行 `tier: "new"`(值 = 新增处数)
156+ * + 抬 `FROZEN_SURFACE_TAG_TOTAL` + 同步 `SURFACE_TAG_ANCHOR.entries`;🔴 **不许**抬 legacy 面
157+ * (那条路被 §C9.5 / §C35.2 明文封死,抬了也过不了 `SURFACE_TAG_FROZEN_LEGACY_COUNT` 之和锁)。
150158 *
151159 * ★ 为什么本表住这里而不是 `surfaceBaseline.ts`:基线件已贴近 300 行硬限,而「登记为什么」本来就是
152160 * 本件的归处(`BORDER_RESIDUAL` / `RADIUS_RESIDUAL` / `SHADOW_RESIDUAL` 同住);冻结的**数**仍在
153161 * 基线件(`FROZEN_SURFACE_TAG_TOTAL` / `SURFACE_TAG_FROZEN_LEGACY_COUNT` / `SURFACE_TAG_ANCHOR`)。
154162 *
155- * ★ 新登记的写法(照抄本行并同步总数与锚;**顺序按字典序插入**):
156- * `{ file: "views/<新视图>.tsx", count: N, reason: "<哪个视图>:<为什么必须用 Surface(而不是 token 变量)>" }`
163+ * ★ 新登记的写法(照抄本行并同步总数与锚;**顺序按 `(file, tier)` 字典序插入** —— 同一文件时
164+ * `legacy` 行在前):
165+ * `{ file: "views/<新视图>.tsx", count: N, tier: "new", reason: "<哪个视图>:<为什么必须用 Surface>" }`
157166 *
158167 * 副作用:无(纯数据)。边界:`file` 是**相对 `app/src` 的正斜杠路径**,与棘轮扫描面的键同一形态。
159168 */
169+ /** 登记行的**档位** —— T19 起它是键的第二元(`键 = \`${file}|${tier}\``) */
170+ export type SurfaceTagTier = "legacy" | "new";
171+
160172export interface SurfaceTagRegistryEntry {
161173 readonly file: string;
162- /** 该文件允许的 `<Surface>` 开标签处数:legacy 行 = 上限(只许降)· 新登记行 = **恰等于**实测 */
174+ /** 该文件允许的 `<Surface>` 开标签处数:` legacy` 档 = 上限(只许降)· `new` 档 = **恰等于**实测 */
163175 readonly count: number;
164176 /**
165- * `true` = T17-B 迁移快照的既有文件(登记值 = `≤` 上限、受「legacy 之和 == 冻结迁移面」锁);
166- * 省略 = **本批新登记**(登记值必须**恰等于**实测,且同批须抬高总数与锚)—— 这条区分就是二档判据的开关。
177+ * 档位 = 键的第二元(**显式必填**:可选字段的「漏写 = new 档」在两种语义间摇摆)。
178+ * `"legacy"` = T17-B 迁移快照的既有文件(登记值 = `≤` 上限、受「legacy 档之和 == 冻结迁移面」锁);
179+ * `"new"` = 批 5 起**新登记**(登记值必须**恰等于**实测,且同批须抬高总数与锚)—— 这条区分就是二档判据的开关。
167180 */
168- readonly legacy?: true ;
181+ readonly tier: SurfaceTagTier ;
169182 /** 哪个视图 / 为什么需要 `<Surface>`(判据要求 ≥ 12 字,不许空理由) */
170183 readonly reason: string;
171184}
172185
173186/**
174187 * T17-B 迁移快照的 9 个文件(`count` = 当时**实测值**;其和 = 14 = `SURFACE_TAG_FROZEN_LEGACY_COUNT`
175- * = `FROZEN_SURFACE_TAG_TOTAL`)。这 9 行是**冻结面**:只许在真迁走时手工收紧,**不许**为容纳新调用点而抬高。
176- * (`legacy: true` 是那 9 行的**唯一**标记 —— 判据据此把它们与新登记行分开;总数由常数另行核验。)
188+ * = 该档在 `FROZEN_SURFACE_TAG_TOTAL` 里的份额)。这 9 行是**冻结面**:只许在真迁走时手工收紧,**不许**
189+ * 为容纳新调用点而抬高。(`tier: "legacy"` 是那 9 行的**唯一**标记 —— 判据据此把它们与 new 档分开;
190+ * 总数由常数另行核验。)
191+ *
192+ * ★ T19:键 = `(file, tier)` ⇒ 同一文件可**两行并存**(`legacy` + `new`);`legacy` 行仍只许降。
177193 */
178194export const SURFACE_TAG_REGISTRY: readonly SurfaceTagRegistryEntry[] = [
179195 // ⚠️ 全表按键**字典序**(判据 ⑪ 牙 5a)⇒ 新行插在字典序位置,不是追加在尾部。
180- // `legacy: true ` = T17-B 迁移快照的 9 行(`≤` 上限、只许降、其和锁在 `SURFACE_TAG_FROZEN_LEGACY_COUNT`);
181- // 不带 `legacy ` = 批 5/7 的新登记行(`count` **恰等于实测**)。新增 = 加行 + 抬总数 + 同步锚(三处同批)。
182- { file: "components/AiProviderSettings.tsx", count: 1, reason: "Provider 卡片容器(整圈边框 + 圆角 + 无底色):批 7 T7 迁入,走 `level=\"none\"` 只出边框不出底" },
183- { file: "components/AiServicePanel.tsx", count: 1, reason: "调用记录滚动容器(无底色 + 整圈边框):批 7 T7 迁入 `level=\"none\"`,滚动口是布局口、仍走 `style`" },
184- { file: "components/AiTaskPanel.tsx", count: 1, reason: "任务记录滚动容器(无底色 + 整圈边框):批 7 T7 迁入 `level=\"none\"`,形态同 `AiServicePanel`" },
185- { file: "components/ChatMessageList.tsx", count: 2, legacy: true , reason: "T17-B 第 2 批迁移:聊天消息流的两处卡片容器(`Surface` 出面 + 调用点只留排布)⇒ 属迁移面,非本批新增" },
186- { file: "components/ClassroomCapturePanel.tsx", count: 1, reason: "实时捕获卡片容器(原 `const panel` 共享样式):批 7 T7 把常量换成原语 `level=\"none\" radius=\"panel\" padded`" },
187- { file: "components/ClassroomRightPane.tsx", count: 1, reason: "右栏「当前配置」卡(原 `const panel` + spread):批 7 T7 换成原语,布局口(marginTop/字色)留在 `style`" },
188- { file: "components/EnrichPanel.tsx", count: 2, legacy: true , reason: "T17-B 第 2 批迁移:增强面板的两处卡片容器(整圈 `1px solid #e5e7eb` + 底色 + 非交互)⇒ 属迁移面" },
189- { file: "components/FeedFragmentList.tsx", count: 1, legacy: true , reason: "T17-B 第 2 批迁移:信息流片段的卡片容器 ⇒ 属迁移面" },
190- { file: "components/GoalAiSection.tsx", count: 1, legacy: true , reason: "T17-B 第 2 批迁移:目标页 AI 区块的卡片容器 ⇒ 属迁移面" },
191- { file: "components/KnowledgeSampleView.tsx", count: 1, legacy: true , reason: "T17-B 第 2 批迁移:知识样例视图的阅读面 ⇒ 属迁移面" },
192- { file: "components/LiveImageStrip.tsx", count: 1, legacy: true , reason: "T17-B 第 2 批迁移:实时图像条的容器面 ⇒ 属迁移面" },
193- { file: "components/MaterialInputPanel.tsx", count: 1, reason: "学习素材卡容器(原 `const panel` 共享样式):批 7 T7 换成原语 `level=\"none\" radius=\"panel\" padded`" },
194- { file: "components/NotePreviewView.tsx", count: 2, reason: "AI 精修版 / 规则版预览的 markdown 阅读面(两处 `dangerouslySetInnerHTML`):批 7 T7 用受控槽 `html` 迁入" },
195- { file: "components/NoteTagsEditor.tsx", count: 1, reason: "标签编辑浮层(批 7 T18:`level=\"raised\"` 锚定面板,底/边/圆角/阴影四件都得走原语——该文件为新建件,无任何冻结键可承接字面量)" },
196- { file: "components/PhotoCapturePanel.tsx", count: 1, reason: "图文采集卡容器(原 `const panel` 共享样式):批 7 T7 换成原语 `level=\"none\" radius=\"panel\" padded`" },
197- { file: "components/PracticeQuestionsOverlays.tsx", count: 2, reason: "练习条目行容器(整圈边框 + 无底色,两处):批 7 T7 迁入 `level=\"none\"`,行内排布留在 `style`" },
198- { file: "components/ProfileDetector.tsx", count: 1, reason: "视频档案卡容器(整圈边框 + 无底色):批 7 T7 迁入 `level=\"none\" radius=\"panel\" padded`" },
199- { file: "components/ReadyCheckCard.tsx", count: 1, reason: "就绪检查卡容器(整圈边框 + 无底色):批 7 T7 迁入 `level=\"none\" radius=\"panel\" padded`" },
200- { file: "components/RefineLaunchDialog.tsx", count: 1, reason: "画面理解开关行容器(整圈边框 + 无底色):批 7 T7 迁入 `level=\"none\"`,flex 排布留在 `style`" },
201- { file: "components/RefineStrategyPicker.tsx", count: 1, reason: "「高级微调」旋钮层容器(整圈边框 + 无底色):批 7 T7 迁入 `level=\"none\"`" },
202- { file: "components/RefineWorkbench.tsx", count: 1, reason: "差异单列视图的 diff 阅读面(`dangerouslySetInnerHTML`):批 7 T7 用受控槽 `html` 迁入" },
203- { file: "components/SessionSearchBar.tsx", count: 1, reason: "标题/内容/画面三档分组按钮的容器(整圈边框 + 无底色):批 7 T7 迁入 `level=\"none\"`" },
204- { file: "components/TaskConversationView.tsx", count: 3, legacy: true , reason: "T17-B 第 2 批迁移:任务对话视图的 3 处面(**同时是登记制的锚文件**,值 3 不许动)⇒ 属迁移面" },
205- { file: "components/VersionPanel.tsx", count: 2, legacy: true , reason: "T17-B 第 2 批迁移:版本面板的两处卡片容器 ⇒ 属迁移面" },
206- { file: "components/VideoImportPanel.tsx", count: 1, reason: "视频导入卡容器(整圈边框 + 无底色):批 7 T7 迁入 `level=\"none\" radius=\"panel\" padded`" },
207- { file: "components/WeekContractCard.tsx", count: 1, legacy: true , reason: "T17-B 第 2 批迁移:周契约卡片的面 ⇒ 属迁移面" },
208- { file: "components/action-center/ActionCenterPanel.tsx", count: 3, reason: "待提炼行 / SOP 模板区 / 模板行三个容器(整圈边框 + 无底色):批 7 T7 迁入 `level=\"none\"`" },
209- { file: "components/session-detail/SessionScreenCards.tsx", count: 1, reason: "OCR 屏卡容器(带锚点 `id` + 近白底):批 7 T7 用受控槽 `domId` 迁入,`level=\"canvas\"`" },
210- { file: "pages/SettingsPage.tsx", count: 10, reason: "设置页 10 个面板容器(原 `const panel` + spread):批 7 T7 换成原语 `level=\"none\" radius=\"panel\" padded`" },
196+ // `tier: "legacy" ` = T17-B 迁移快照的 9 行(`≤` 上限、只许降、其和锁在 `SURFACE_TAG_FROZEN_LEGACY_COUNT`);
197+ // `tier: "new" ` = 批 5/7 的新登记行(`count` **恰等于实测**)。新增 = 加行 + 抬总数 + 同步锚(三处同批)。
198+ { file: "components/AiProviderSettings.tsx", count: 1, tier: "new", reason: "Provider 卡片容器(整圈边框 + 圆角 + 无底色):批 7 T7 迁入,走 `level=\"none\"` 只出边框不出底" },
199+ { file: "components/AiServicePanel.tsx", count: 1, tier: "new", reason: "调用记录滚动容器(无底色 + 整圈边框):批 7 T7 迁入 `level=\"none\"`,滚动口是布局口、仍走 `style`" },
200+ { file: "components/AiTaskPanel.tsx", count: 1, tier: "new", reason: "任务记录滚动容器(无底色 + 整圈边框):批 7 T7 迁入 `level=\"none\"`,形态同 `AiServicePanel`" },
201+ { file: "components/ChatMessageList.tsx", count: 2, tier: "legacy" , reason: "T17-B 第 2 批迁移:聊天消息流的两处卡片容器(`Surface` 出面 + 调用点只留排布)⇒ 属迁移面,非本批新增" },
202+ { file: "components/ClassroomCapturePanel.tsx", count: 1, tier: "new", reason: "实时捕获卡片容器(原 `const panel` 共享样式):批 7 T7 把常量换成原语 `level=\"none\" radius=\"panel\" padded`" },
203+ { file: "components/ClassroomRightPane.tsx", count: 1, tier: "new", reason: "右栏「当前配置」卡(原 `const panel` + spread):批 7 T7 换成原语,布局口(marginTop/字色)留在 `style`" },
204+ { file: "components/EnrichPanel.tsx", count: 2, tier: "legacy" , reason: "T17-B 第 2 批迁移:增强面板的两处卡片容器(整圈 `1px solid #e5e7eb` + 底色 + 非交互)⇒ 属迁移面" },
205+ { file: "components/FeedFragmentList.tsx", count: 1, tier: "legacy" , reason: "T17-B 第 2 批迁移:信息流片段的卡片容器 ⇒ 属迁移面" },
206+ { file: "components/GoalAiSection.tsx", count: 1, tier: "legacy" , reason: "T17-B 第 2 批迁移:目标页 AI 区块的卡片容器 ⇒ 属迁移面" },
207+ { file: "components/KnowledgeSampleView.tsx", count: 1, tier: "legacy" , reason: "T17-B 第 2 批迁移:知识样例视图的阅读面 ⇒ 属迁移面" },
208+ { file: "components/LiveImageStrip.tsx", count: 1, tier: "legacy" , reason: "T17-B 第 2 批迁移:实时图像条的容器面 ⇒ 属迁移面" },
209+ { file: "components/MaterialInputPanel.tsx", count: 1, tier: "new", reason: "学习素材卡容器(原 `const panel` 共享样式):批 7 T7 换成原语 `level=\"none\" radius=\"panel\" padded`" },
210+ { file: "components/NotePreviewView.tsx", count: 2, tier: "new", reason: "AI 精修版 / 规则版预览的 markdown 阅读面(两处 `dangerouslySetInnerHTML`):批 7 T7 用受控槽 `html` 迁入" },
211+ { file: "components/NoteTagsEditor.tsx", count: 1, tier: "new", reason: "标签编辑浮层(批 7 T18:`level=\"raised\"` 锚定面板,底/边/圆角/阴影四件都得走原语——该文件为新建件,无任何冻结键可承接字面量)" },
212+ { file: "components/PhotoCapturePanel.tsx", count: 1, tier: "new", reason: "图文采集卡容器(原 `const panel` 共享样式):批 7 T7 换成原语 `level=\"none\" radius=\"panel\" padded`" },
213+ { file: "components/PracticeQuestionsOverlays.tsx", count: 2, tier: "new", reason: "练习条目行容器(整圈边框 + 无底色,两处):批 7 T7 迁入 `level=\"none\"`,行内排布留在 `style`" },
214+ { file: "components/ProfileDetector.tsx", count: 1, tier: "new", reason: "视频档案卡容器(整圈边框 + 无底色):批 7 T7 迁入 `level=\"none\" radius=\"panel\" padded`" },
215+ { file: "components/ReadyCheckCard.tsx", count: 1, tier: "new", reason: "就绪检查卡容器(整圈边框 + 无底色):批 7 T7 迁入 `level=\"none\" radius=\"panel\" padded`" },
216+ { file: "components/RefineLaunchDialog.tsx", count: 1, tier: "new", reason: "画面理解开关行容器(整圈边框 + 无底色):批 7 T7 迁入 `level=\"none\"`,flex 排布留在 `style`" },
217+ { file: "components/RefineStrategyPicker.tsx", count: 1, tier: "new", reason: "「高级微调」旋钮层容器(整圈边框 + 无底色):批 7 T7 迁入 `level=\"none\"`" },
218+ { file: "components/RefineWorkbench.tsx", count: 1, tier: "new", reason: "差异单列视图的 diff 阅读面(`dangerouslySetInnerHTML`):批 7 T7 用受控槽 `html` 迁入" },
219+ { file: "components/SessionSearchBar.tsx", count: 1, tier: "new", reason: "标题/内容/画面三档分组按钮的容器(整圈边框 + 无底色):批 7 T7 迁入 `level=\"none\"`" },
220+ { file: "components/TaskConversationView.tsx", count: 3, tier: "legacy" , reason: "T17-B 第 2 批迁移:任务对话视图的 3 处面(**同时是登记制的锚文件**,值 3 不许动)⇒ 属迁移面" },
221+ { file: "components/VersionPanel.tsx", count: 2, tier: "legacy" , reason: "T17-B 第 2 批迁移:版本面板的两处卡片容器 ⇒ 属迁移面" },
222+ { file: "components/VideoImportPanel.tsx", count: 1, tier: "new", reason: "视频导入卡容器(整圈边框 + 无底色):批 7 T7 迁入 `level=\"none\" radius=\"panel\" padded`" },
223+ { file: "components/WeekContractCard.tsx", count: 1, tier: "legacy" , reason: "T17-B 第 2 批迁移:周契约卡片的面 ⇒ 属迁移面" },
224+ { file: "components/action-center/ActionCenterPanel.tsx", count: 3, tier: "new", reason: "待提炼行 / SOP 模板区 / 模板行三个容器(整圈边框 + 无底色):批 7 T7 迁入 `level=\"none\"`" },
225+ { file: "components/session-detail/SessionScreenCards.tsx", count: 1, tier: "new", reason: "OCR 屏卡容器(带锚点 `id` + 近白底):批 7 T7 用受控槽 `domId` 迁入,`level=\"canvas\"`" },
226+ { file: "pages/SettingsPage.tsx", count: 10, tier: "new", reason: "设置页 10 个面板容器(原 `const panel` + spread):批 7 T7 换成原语 `level=\"none\" radius=\"panel\" padded`" },
211227];
0 commit comments