Skip to content
Back to skills

Infocard Pub Hardening

ASecurity

Infocard-pub 硬故障加固:截图 base64 截断/guard 误杀/npm 门禁/视觉验收失败。

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 19, 2026
developmentjavascriptpythonrustgojavabashnode

Works with

  • terminal

Security analysis

A100/100

Scanned October 19, 2026

npx -y skills add ccwq/infocard-pub --skill infocard-pub-hardening --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Infocard Pub Hardening?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Infocard Pub Hardening
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/ccwq-infocard-pub-hardening/badge)](https://www.skillsdirectory.com/skills/ccwq-infocard-pub-hardening)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: infocard-pub-hardening
description: "Infocard-pub 硬故障加固:截图 base64 截断/guard 误杀/npm 门禁/视觉验收失败。"
category: infocard
tags: [infocard, build, screenshot, preflight, hardening]
---

# infocard-pub-hardening · 发布流程硬故障加固

## 已知故障速查表

| ID | 症状 | 根因 | 修复 |
|----|------|------|------|
| H-8 | 子智能体写的 `card.html.meta.yaml` 缺少必要字段(`path`/`title`/`category`/`desc`/`tags`),build 失败 | 子智能体不掌握 build 索引的必需字段规范 | 主线自行写 docs/ 的 meta.yaml:slug / path / title / category / desc / tags / updated / date 八字段;子智能体产出只作 facts 来源参考,不作发布用 |
| H-9 | incremental build 报 "target path missing" 后删掉了已有的 HTML 文件 | build 脚本在 meta.yaml 存在但 HTML 不存在时先触发 validate,validate 失败导致 HTML 被跳过但 meta 仍在 index,待 incremental snapshot 发现 HTML 不匹配就报错;再次 build 时 HTML 已被跳过只清掉 meta,反复查 | **写 meta.yaml → 立即 copy HTML 到 docs/ → 再 build**;不要先 copy HTML 再写 meta |
| H-10 | info-leak 门禁误杀代码示例中的内网 IP(如 `192.168.1.100`)| check-info-leak.js 对 `192.168.x.x` 格式的私有 IP 也标记为 IP 地址泄露 | 代码示例中的内网 IP 改为公开示例域名,如 `server.example.com`、`bastion.example.com`、`192.168.1.100` → `server.example.com` |
| H-2 | `python3 -m http.server` 被识别为长驻进程拦截 | 终端 guard 误杀 | **强制使用 `live-server`**(见下方正确操作) |
| H-3 | `browser_cdp` desktop 截图 PNG/JPEG 文件损坏(broken chunk / 非 base64 字符) | persisted-output 对超长 base64 中间插入 `...` 截断 | **必须指定 `screenshot_path=绝对路径` 走文件落盘**,禁止 base64 返回 |
| H-4 | vision_analyze 接收截图失败或误判 | 直接传 base64 字符串或 file:// URL | **始终用已落盘的绝对路径**传给 vision_analyze,禁止传 base64 |
| H-5 | info-leak 门禁误杀推文 ID(`status/2108956141325811828` 等长数字串被判为中国大陆手机号) | check-info-leak.js 手机号正则对 18-19 位数字串误匹配 | 在 HTML 中**把 `status/<id>` 改为链接形式** `<a href="https://x.com/<user>/status/<id>" style="color:inherit;text-decoration:none">推文原文</a>`,两处同步(`docs/` 和 `.docs/<run-id>/<slug>/card.html`);改后重新过门禁 |
| H-7 | 子智能体选了禁用主题或虚构主题名(如 `q-style`),仍被发布 | 委派时未用 `infocard-theme-assignment`,成品未核验 `data-theme` 与 decision record 一致性 | 见 `infocard-publish-sop` Theme Enforcement Gate;发布前必须 `grep data-theme=` 与 `theme-decision.json.selected_theme` 比对;不符即 `BLOCKED_AT_THEME_MISMATCH` |
| H-6 | 移动端布局翻车(hero 双栏 `1fr 340px` 在 ≤640px 溢出,右侧 stat-block 被切) | HTML 缺少 `@media (max-width: 640px)` 响应式规则 | 在 `</style>` 前插入响应式折叠 CSS(见 H-6 详细节);同步 `.docs/` 副本后再 build |
| H-11 | 子智能体委派超时 card.html 未写出 | write_file 25s 超时,owner exit 中断写入 | 主线直接创作(H-12 补强路径);委派 completed ≠ 写入成功 |
| H-12 | 子智能体产出 < 27KB | 受 token 预算或模板不熟截断内容节 | 主线 patch 补节(竞品对比表/技术细节/代码示例/Pipeline 示例等) |
| H-13 | 子智能体产出缺 `data-theme` 属性 | 知道但漏写根标签属性 | `grep -o 'data-theme='` 强制门禁;无则主线 patch `<html>` 标签 |
| H-14 | 委派 completed 但文件不存在 | write/execute_code 超时中断 | 每次收口先 `ls -la + wc -c` 验证 existence + size,再决定走补强还是主线创作 |
| H-15 | info-leak 误杀推文 ID 手机号 | 18 位数字串被手机号正则匹配 | ID 全在 `<a href>` 内则 PASS;裸 ID 必须外套链接;Anatomy/GEV 实测通过 |
| H-16 | 子智能体只覆盖模板自带类的 media query,自建类在移动端全部失效 | 委派要求「复用模板类名」,子智能体另建 `.risk-grid`/`.grid-3`/`.matrix`/`.flow`/`.comparison-table`/`.data-table`/`.env-table`/`.analysis-block`/`.code-block` 等类却不为它们写窄屏规则 | 发布前跑批量扫描脚本(见 H-16 详细节):表格数 vs `.table-wrap` 数量、`<pre>` 数 vs 720px media 是否覆盖代码块类、三栏栅格类是否在 media 内改单列;发现即主线补 720px 规则并包 `table-wrap` |
| H-17 | 宽表格在移动端挤爆、第三列被裁 | `<table>` 无最小宽度、无横向滚动容器,浏览器强行压缩列宽导致文字逐字换行 | 所有 `<table>` 必须包 `<div class="table-wrap">`,并在 720px media 内设 `min-width:600px`(多列对比表 720px)+ 可见滚动条样式 |

---

## H-8 · 子智能体 meta.yaml 永远不完整

**症状**:`build` 报错 "missing fields title" / "missing fields category" / "missing fields path",子智能体产出的 `card.html.meta.yaml` 格式是 markdown 注释或缺少必要索引字段。

**根因**:子智能体不知道 build-site.js 的 YAML 必需字段规范(slug / path / title / category / desc / tags / updated / date),也不掌握 `---` 头禁止规则。

**修复路径**:

主线自行写 `docs/<slug>.html.meta.yaml`,不依赖子智能体产出:

```bash
# ❌ 禁止:直接用子智能体写的 meta.yaml
cp .docs/<run-id>/<slug>/card.html.meta.yaml docs/<slug>.html.meta.yaml

# ✅ 正确:主线自行按规范写入
cat > docs/<slug>.html.meta.yaml << 'META'
slug: <slug>
path: docs/<slug>.html
title: <实际标题>
category: <分类:开源工具/AI产品/设计产品/开发者资源>
desc: <一句话描述,60-120字>
tags:
  - <标签1>
  - <标签2>
updated: "YYYY-MM-DD HH:MM:SS"
date: "YYYY-MM-DD HH:MM:SS"
META

# copy HTML → build
cp .docs/<run-id>/<slug>/card.html docs/<slug>.html
node scripts/build-site.js
```

**子智能体 meta.yaml 只作 facts 来源参考**,不作发布用。发布前 preflight 必须确认 docs/ 下的 meta.yaml 格式正确。

---

## H-9 · incremental build "target path missing" 循环

**症状**:第一次 build 报 "missing fields title",修复后第二次报 "target path missing → docs/<slug>.html",之后每次 build 都重复这两个错误。

**根因**:incremental build 流程是:发现 docs/<slug>.html.meta.yaml → 检查 docs/<slug>.html 是否存在 → 若 HTML 不存在则报错并停止;此时 meta.yaml 已被 sync-build-timestamps 写入,但 HTML 未被 index 处理。下次 build 时:meta.yaml 存在但 HTML 仍不存在,validate 又失败,meta.yaml 被标记为 new 但 index 不更新。修复时若先写 meta 再 copy HTML,HTML 会因为 meta 的 path 字段指向它而被认可;若先 copy HTML 再写 meta,HTML 可能被 incremental snapshot 意外清理。

**正确流程(H-8 的反面)**:

```bash
# 步骤1:写 meta.yaml 到 docs/(先确认所有字段)
cat > docs/<slug>.html.meta.yaml << 'YAML'
slug: <slug>
path: docs/<slug>.html
title: ...
YAML

# 步骤2:立即 copy HTML 到 docs/(meta 写完后立即跟上)
cp .docs/<run-id>/<slug>/card.html docs/<slug>.html

# 步骤3:build(此时 meta+HTML 原子存在)
node scripts/build-site.js
```

**禁止**:先 copy HTML,再写 meta.yaml,再 build。

---

## H-10 · info-leak 误杀内网 IP

**症状**:`check-info-leak.js` 报告 `[MEDIUM] IP地址` 在代码示例行,如 `sshrs forward --host jump-server --remote 192.168.1.100:80`。

**根因**:check-info-leak.js 对 `192.168.x.x` / `10.x.x.x` / `172.16-31.x.x` 格式的私有 IP 地址均标记为泄露风险。

**修复**:代码示例中的内网 IP 替换为公开示例域名:

| 内网 IP | 替换为 |
|---|---|
| `192.168.1.100` | `server.example.com` |
| `192.168.0.1` | `gateway.example.com` |
| `10.0.0.1` | `internal.example.com` |
| `172.16.0.1` | `bastion.example.com` |

---

## H-7 · 主题违规发布

**症状**:成品 `card.html` 的 `data-theme` 与 `theme-decision.json.selected_theme` 不符,或使用了 `theme/themes.json` 中不存在的名称。

**根因**:跳过了 `infocard-theme-assignment`,未生成 decision record,委派 prompt 中预置了 `data-theme=`。

**发布前必查**:
```bash
# 1. decision record 存在
ls .docs/<run-id>/<slug>/theme-decision.json

# 2. 成品 data-theme
grep -o 'data-theme="[^"]*"' .docs/<run-id>/<slug>/card.html

# 3. 核对
jq -r '.selected_theme' .docs/<run-id>/<slug>/theme-decision.json
# → 不符 = BLOCKED_AT_THEME_MISMATCH,退回子智能体重做

# 4. 验证主题已在 themes.json 注册
grep -o '"[a-z-]+":' theme/themes.json | grep -v 'version\|label\|capability\|struct\|implement'
# 不在列表中的名称无效
```

**修复路径**:
- 成品用了禁用主题 → 退回子智能体,从 `infocard-theme-assignment` 重新分配
- 成品忘了写 `data-theme` → 主线直接 `patch` `<html>` 标签补上(若主题名称已确认)
- 虚构主题名(如 `q-style`)→ 退回,从 `themes.json` 候选中重选

---

## H-1 · npm check-wiki-duplicate 被门禁拦截

**症状**:命令超时未响应,或返回权限错误。

**临时绕行**(不依赖门禁白名单):
```bash
# 手动 grep 查重(repo 根目录执行)
grep -r "slug\|title" docs/*.html.meta.yaml | grep -i "<关键词>"
grep -r "title:" docs/*.html.meta.yaml | grep -i "<关键词>"
```

**长期解决**:向门禁 owner 申请将 `npm run check-wiki-duplicate` 加入只读命令白名单。

---

## H-2 · python http.server 被 guard 误杀

**症状**:前台 `python3 -m http.server` 被识别为长驻进程,触发审批拦截。

**正确操作**:

```bash
# ❌ 禁止:前台 python http.server(会被 guard 误杀)
python3 -m http.server 5588

# ✅ 正确:使用 live-server
cd /path/to/wt-<slug>
command -v live-server >/dev/null || { echo "live-server is required on PATH" >&2; exit 1; }
live-server --port=5588 --host=10.6.8.14 .
```

验证:`curl -s --max-time 5 http://10.6.8.14:5588/docs/<slug>.html | head -5`

---

## H-3 · browser_cdp desktop 截图文件损坏

**症状**:PNG 文件无法打开("broken PNG chunk")、JPEG 解码失败(66 个非 base64 字符)。

**根因**:`browser_cdp` 返回的 base64 输出超过长度阈值时,persisted-output 在中间插入 `...` 截断,导致 base64 字符串损坏。

**正确操作**:

```javascript
// ✅ 正确:指定 screenshot_path 走文件落盘
browser_cdp({
  method: 'Page.captureScreenshot',
  params: { format: 'png', fullPage: false },
  screenshot_path: '/tmp/card-desktop.png'   // ← 必须指定
})

// ❌ 错误:返回 base64 到 terminal(会被截断)
browser_cdp({ method: 'Page.captureScreenshot', params: { format: 'png' } })
// → 超过长度时 base64 中间插入 "..." → 文件损坏
```

**验证截图完整性**:
```bash
file /tmp/card-desktop.png       # 期望:PNG image data
identify /tmp/card-desktop.png   # 期望:显示尺寸,无 error
```

---

## H-4 · vision_analyze 接收截图失败

**症状**:vision_analyze 报告无法读取图像、返回空结果,或误判页面内容。

**根因**:传入了 base64 字符串或 `file://` 协议路径。

**正确操作**:

```javascript
// Step 1:先用 browser_cdp 落盘截图(H-3 规则)
browser_cdp({
  method: 'Page.captureScreenshot',
  params: { format: 'png' },
  screenshot_path: '/tmp/card-desktop.png'
})

// Step 2:vision_analyze 用绝对路径,禁止 base64
vision_analyze({
  image_url: '/tmp/card-desktop.png',   // ✅ 绝对路径
  question: '桌面端视觉验收:critical/major/minor 并说明'
})
```

**严禁**:
- `image_url: 'data:image/png;base64,...'`(会截断)
- `image_url: 'file:///tmp/card-desktop.png'`(file:// 在部分环境下不可用)

---

## H-5 · info-leak 门禁误杀推文 ID

**症状**:`node scripts/check-info-leak.js` 报告 `[HIGH] 手机号(中国大陆) (line N): 154***198`;检查 HTML 发现该行只有 `status/2109083154682351988` 形式的 X 帖子 ID,无真实手机号。

**根因**:X/Twitter status ID 是 18-19 位纯数字字符串,check-info-leak.js 的手机号正则对连续 11+ 位数字串也会部分匹配导致误判。

**已确认案例**(2026-10-11 同日复现 4 次):
- ste-zh: `status/2108956141325811826` → `141***182`
- Pulse: 同上模式
- ai-dev-workflow: `2108551891005394944` → `189***949`
- JiuwenSwarm: `2109083154682351988` → `154***198`

**修复**(两处同步):

```python
# docs/<slug>.html 和 .docs/<run-id>/<slug>/card.html 两处同时修
old = '<span>status/2108956141325811826</span>'
new = '<a href="https://x.com/<user>/status/2108956141325811826" style="color:inherit;text-decoration:none">推文原文</a>'
raw = open(path).read().replace(old, new, 1)
open(path, 'w').write(raw)
```

**规则**:推文 ID 串不允许以纯文本形式出现在 card.html 中,必须外套 `<a href>` 或改为其他表述(日期、作者名等),否则 info-leak 门禁必拦。

---

## H-13 · 子智能体产出缺 `data-theme` 属性

**症状**:子智能体 `status: completed`,card.html 存在且 size 正常,但 `<html>` 根标签缺少 `data-theme` 属性,导致 build 后页面无主题样式、index 记录缺失。

**根因**:子智能体在委派 prompt 里知道 `data-theme="graph-paper"`(写在类名、注释或 theme-decision.json 里),但输出 HTML 时漏写根标签属性。darkblue 和 graph-paper 模板的 `<html>` 标签均无默认 `data-theme` 值。

**已确认案例**:Anatomy(graph-paper, 44,602B)和 God's Eye View(darkblue, 39,617B)均漏写,发布前均由主线 `patch` 补上。

**修复路径**:

```bash
# 发布前强制 grep 检查 data-theme
grep -o 'data-theme="[^"]*"' .docs/<run-id>/<slug>/card.html

# 若输出为空 → 主线直接 patch(主题名已在 theme-decision.json 中确认)
patch .docs/<run-id>/<slug>/card.html \
  '<html lang="zh-CN">' \
  '<html lang="zh-CN" data-theme="<selected_theme>">'
```

**规则**:`grep data-theme=` 是发布前强制门禁,不通过不得 build。子智能体 `status: completed` 不等于 `data-theme` 存在——必须实际检查文件。

---

## H-14 · 委派完成后必须验证文件 existence + size

**症状**:子智能体 `status: completed`(owner exit)但 `card.html` 不存在(size = 0 或文件缺失),或文件写出但 size 远低于预期(如 2KB)。

**根因**:`write_file` 工具 25s 超时硬限制;大文件在慢 I/O 下无法时限内写入;子智能体改用 `execute_code` 后 kernel write 同样受 25s 限制;owner exit 时写入被中断。

**已确认案例**:Unlimited OCR 委派超时,card.html 未写出(deleg_8b373256,owner exit)。

**主线程兜底流程**:

```bash
# 步骤1:检查文件 existence + size
ls -la .docs/<run-id>/<slug>/card.html
wc -c .docs/<run-id>/<slug>/card.html

# 步骤2:判断
# size = 0 或不存在  → 主线直接创作(H-12 补强路径)
# size 1KB-26KB      → H-12 主线补节
# size ≥ 27KB        → 进入 H-13 data-theme 检查

# 步骤3:主线程创作用 execute_code(主线无 25s 内核限制)
# 素材已在主线程 context,open(path,'w').write(html) 直接写
```

**关键教训**:委派 `status: completed` ≠ 写入成功。检查文件 existence + size 是每次委派收口的第一个动作。

---

## H-15 · 推文 ID 安全格式判定规则

**症状**:推文 ID(18-19 位纯数字)出现在 HTML 中,info-leak 误判为手机号;但 ID 全在 `<a href>` 内时无实际风险。

**已确认安全案例**:Anatomy 中 2 个推文 ID 全在 href 内 → info-leak PASS;God's Eye View 中 2 个推文 ID 全在 href 内 → info-leak PASS。

**判定规则**:

```
IF  所有推文 ID 出现都在 href 属性内  → info-leak PASS,无风险
IF  任意裸 ID 纯文本出现(不在 href)  → info-leak FAIL,必须修复
```

```bash
# 验证脚本
python3 -c "
content=open('card.html').read()
ids=['2109800865854881808']  # 替换为实际 ID
for id in ids:
    in_href=all('href=' in content[max(0,m.start()-80):m.start()+len(id)+10]
                 for m in __import__('re').finditer(id, content))
    total=content.count(id)
    print(f'{id}: total={total} all_in_href={in_href}')
"
```

**强制规则**:推文 ID 串不允许以纯文本形式出现在 card.html 中,必须外套 `<a href>` 链接或改为其他表述(日期、作者名等)。

---

## H-11 · 子智能体委派超时导致 card.html 未写出

**症状**:子智能体委派完成(owner exit)但 `.docs/<run-id>/<slug>/card.html` 不存在或 size = 0;日志显示 `write_file` 超时或 execute_code 超时。

**根因**:`write_file` 工具有 25s 超时硬限制;大文件(25KB+)在慢 I/O 下无法在时限内写入。子智能体改用 `execute_code` 后,kernel write 同样受 25s 限制。子智能体 owner exit 时写入操作被中断,文件未落盘。

**主线程兜底流程**:

```bash
# 步骤1:检查子智能体是否写出了文件(H-14)
ls -la .docs/<run-id>/<slug>/card.html

# 步骤2:若文件不存在或 size < 1KB → 主线程直接创作
# 用 execute_code 在主线程写入,不走子智能体委派
# 主线程 execute_code 无子智能体 25s 内核限制,文件可写成功

# 步骤3:verify 后按 H-9 流程发布(copy HTML → copy meta → build)
```

**关键教训**:子智能体 `write_file` 超时后,该委派的 HTML 不可信;主线程直接创作是最高效的兜底方案,不要反复重委派。委派超时 ≠ 素材丢失——主线程持有完整素材上下文。

---

## H-12 · 子智能体输出 size 不足 27KB

**症状**:子智能体产出的 `card.html` 通过 info-leak 但 size < 27KB(软下限),`wc -c` 显示 20-26KB。

**根因**:子智能体受 token 预算限制或主题模板结构不熟悉,创作时截断内容节。27KB 软下限是信息密度的经验阈值:低于此值的卡视觉单薄、读者体验差。

**主线程补强流程**:

```bash
# 步骤1:检查 size
wc -c .docs/<run-id>/<slug>/card.html

# 步骤2:若 < 27KB,主线 patch 补节(不要重委派):
# 典型补节类型:
#   - 竞品横向对比表(+1.5-2KB)
#   - 技术细节深入节(+1-2KB)
#   - 快速上手 / 代码示例节(+1-2KB)
#   - Pipeline 示例节(+1-2KB)
#   - R-SWA 数学分析节(+1-2KB)

# 示例 patch:
# patch .docs/<run-id>/<slug>/card.html \
#   '<\/section>\s*<section class="dark-closing">' \
#   '<h2 class="section-title">N · 补充节标题</h2>...内容...<\/section>\n\n    <section class="dark-closing">'

# 步骤3:verify 后 publish
```

**规则**:主线程是最终的 size 把关人;子智能体输出只作骨架和 facts 来源,补节是主线的标准操作,不算绕过子智能体。

---

## H-6 · 移动端布局溢出

**症状**:桌面双栏 hero(`grid-template-columns: 1fr 340px`)在移动端(≤640px)水平溢出,右侧 stat-block 被切,左侧文字与图标重叠。

**已修复案例**:`microsoft-litebox`(commit `3fcc827a`)。

**修复模板**(插入 `</style>` 前):

```css
    @media (max-width: 640px) {
      .hero {
        grid-template-columns: 1fr !important;
      }
      .hero-copy {
        border-right: none !important;
        border-bottom: 3px solid var(--ink);
        padding-bottom: 14px !important;
      }
      .hero-visual {
        padding: 12px 10px !important;
      }
      .stat-block .num {
        font-size: 28px !important;
      }
      .badge-row {
        flex-wrap: wrap;
        gap: 4px;
      }
      .page {
        padding: 0 8px !important;
      }
    }
```

**流程**:修 docs/ → 同步 .docs/ 副本 → `npm run build` → verify → commit → push。

---

## 发布前 preflight 清单

执行 `npm run build` 前,依次确认:

- [ ] H-1 查重:若 `npm run check-wiki-duplicate` 超时 → 改手动 `grep` 查重
- [ ] H-3 截图:browser_cdp 必须用 `screenshot_path` 落盘,验证 `file` 命令确认完整
- [ ] H-4 验收:vision_analyze 必须用绝对路径,禁止 base64
- [ ] H-5 info-leak:card.html 中无裸 `status/<id>` 纯文本推文 ID(已外套 `<a href>` 链接形式则通过)
- [ ] H-6 移动端:已发布卡片若使用 hero 双栏布局,须已含 `@media (max-width: 640px)` 规则
- [ ] H-2 预览:使用 `live-server`,禁止 `python3 -m http.server`
- [ ] meta.yaml:`date` 为 ISO 格式 `"YYYY-MM-DD HH:MM:SS"`,无尾部 `---`
- [ ] build 后:`npm run build` 成功输出 `wrote _index.yaml`

---

## H-16 详细节 · 移动端适配批量扫描脚本

**何时跑**:任何子智能体产出 card.html 之后、build 之前。子智能体只覆盖模板自带类的窄屏规则,自建类一律漏写 —— 2026-10-13 一天内连续在 Anatomy / RustGIS / LLM Wiki v2 / Qwen Image / God's Eye View 五张卡复现。

**扫描脚本**(Python,直接对 `docs/<slug>.html` 跑):

```python
import re, os, glob

base = '/home/ccwq/qbox/opendir/project/infocard-pub/docs'

def media_block(style, key):
    m = re.search(r'@media[^{]*' + key + r'[^{]*\{', style)
    if not m:
        return None
    d = 0; i = m.end() - 1
    while i < len(style):
        if style[i] == '{': d += 1
        elif style[i] == '}':
            d -= 1
            if d == 0: break
        i += 1
    return style[m.end():i]

for f in sorted(glob.glob(os.path.join(base, '2026*.html'))):
    c = open(f, encoding='utf-8').read()
    style = c.split('</style>')[0]
    blk = media_block(style, '720px')
    nt = len(re.findall(r'<table', c))
    wrap = c.count('<div class="table-wrap">')
    pre = len(re.findall(r'<pre', c))
    flags = ''
    if nt and not wrap:            flags += ' TABLE-NOWRAP'
    if pre and blk and 'code' not in blk.lower(): flags += ' CODE-NOMOBILE'
    if nt and blk and 'table-wrap' not in blk:      flags += ' TW-NOMOBILE'
    if not blk:                    flags += ' NO-720'
    if flags:
        print(f'{os.path.basename(f)[:48]:50} t={nt:2} wrap={wrap:2} pre={pre:2}{flags}')
```

**判定**:`TABLE-NOWRAP` = 表格没有滚动容器(必炸);`NO-720` = 卡片根本没有窄屏断点;`CODE-NOMOBILE` / `TW-NOMOBILE` = 有 720px media 但没覆盖对应类。

**修复动作**(主线执行,勿交回子智能体):

```python
# 1) 所有表格包滚动容器
tables = re.findall(r'<table\b.*?</table>', c, re.S)
out = []; last = 0
for t in tables:
    pos = c.index(t, last); out.append(c[last:pos])
    if 'table-wrap' in c[max(0, pos-40):pos]:
        out.append(t)
    else:
        out.append('<div class="table-wrap">' + t + '</div>')
    last = pos + len(t)
out.append(c[last:]); c = ''.join(out)

# 2) 在 720px media 收尾 } 前插入规则(找不到就整段新增 media)
```

**标准修复 CSS**:

```css
@media (max-width: 720px){
  .table-wrap{overflow-x:auto;-webkit-overflow-scrolling:touch;margin:12px -6px;padding:0 6px}
  .table-wrap::-webkit-scrollbar{height:6px}
  .table-wrap::-webkit-scrollbar-thumb{background:rgba(0,0,0,.18);border-radius:3px}
  .table-wrap table{min-width:600px}
  .table-wrap td,.table-wrap th{padding:7px 9px;font-size:11.5px}
}
```

暗色主题把滚动条颜色改成 `rgba(255,255,255,.22)`,轨道 `rgba(255,255,255,.06)`。

**收尾必查**:`.section-head` 在 720px 下补 `padding-left:14px`(编号方块贴边,历史高频 bug);三栏栅格类(`.risk-grid`/`.grid-2`/`.grid-3`/`.matrix`/`.flow`)改 `grid-template-columns:1fr`;标签平衡用 `for t in ['div','table','section','ul','li']` 校验开闭数量一致。

---

## 关联 skill

- `infocard-authoring-workflow` — 完整发布流程(worktree / meta.yaml / build / commit)
- `browser-cdp-only-operations` — browser/CDP 操作边界
- `infocard-visual-pass-loop` — 视觉验收循环

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.