# Monitor Binary Version Baselines

> Establish and maintain longitudinal baselines of CLI binary contents across versions. Covers marker selection by category (API / identity / config / telemetry / flag / function), weighted scoring, threshold-based system-presence detection, and per-version baseline records. Use when tracking a feature's lifecycle across releases, when probing for dark-launched or removed capabilities, or when verifying that a scanning tool itself still catches known-good markers on old binaries.

- Skill: `pjt222/monitor-binary-version-baselines-4` (Agent Skill)
- Install (CLI): `npx skillmds@latest add pjt222/monitor-binary-version-baselines-4`
- Raw SKILL.md: https://api.skillmd.com/api/skills/pjt222/monitor-binary-version-baselines-4/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Integrations & APIs
- License: MIT
- Author: pjt222 (https://skillmd.com/u/pjt222)
- Updated: 2026-09-21
- Page: https://skillmd.com/skills/pjt222/monitor-binary-version-baselines-4

---


# 监控二进制版本基线

为 CLI 测试台二进制中的特性系统标记建立可比较的、以版本为键的记录，让新增、移除与暗启动能力能够在跨版本间被机械化地检测出来。

## 何时使用

- 在一个闭源 CLI 测试台的多个发布之间追踪某项特性的生命周期
- 探测暗启动的能力（已发货但被闸门关闭）或悄然移除的能力
- 验证标记扫描器仍能在旧二进制上检出已知良好的标记（对扫描器自身做回归测试）
- 构建第 1 阶段的底座，供后续阶段（标志位发现、暗启动检测、线路抓包）消费
- 任何临时用 `grep` 只能回答"X 今天在不在"，而你真正需要的是"由 X、Y、Z 组成的系统如何在版本间演进"的场景

## 输入

- **必需**：同一个 CLI 测试台的一个或多个已安装二进制版本（或抽取出的打包产物）
- **必需**：一个工作中的目录文件，用于存放标记定义（首次运行时创建，跨版本逐步扩展）
- **可选**：来自先前运行的已记录基线文件（就地扩展，绝不重写）
- **可选**：已知从未发布的版本清单（被跳过的发布、被撤回的构建）
- **可选**：已经在追踪中的特性系统清单，用于扩展而不是重新发现

## 步骤

### 步骤 1：按类别选择标记

选择能在重新构建中幸存的字符串。挑选稳定、语义上有意义的标识符 — 不要挑选打包器在下个版本就会重命名的压缩名。

推荐的六个类别：

- **API** — 端点路径、测试台网络表面暴露的方法名
- **身份（Identity）** — 内部产品名、代号、版本哨兵
- **配置（Config）** — 面向用户的配置文件中被识别的键
- **遥测（Telemetry）** — 发送到分析管道的事件名
- **标志（Flag）** — 被闸门谓词消费的特性闸门键
- **函数（Function）** — 特定处理器内部使用的著名字符串常量（错误消息、日志标签）

避免：看起来像被压缩过的短标识符（例如 `_a1`、`bX`、两个字母后跟数字）、任何一次文本修订就会改变的内联字面量，以及任何匹配打包器自身内部命名惯例的字符串。

**预期：** 每个候选标记都带有一个类别标签和一段简短的理由说明（"出现在面向用户的文档中"、"在前 N 个发布中稳定存在"等）。一次典型的初轮筛选能为每个系统产出 20-50 个标记。

**失败时：** 如果标记在相邻的小版本之间就消失了，说明目录捕获的是易受重新构建影响的易变字符串，而不是稳定的标识符。删除这些条目；改为采用更长、更具语义锚点的子串。

### 步骤 2：按特性系统对标记分组

把标记打包进每个独立演化能力对应的**系统表**中。一个"系统"是一组连贯的标记，它们的存在/缺失会协同移动，因为它们共享一次特性生命周期（例如，属于某个假想 `acme_widget_v3` 能力的全部标记）。

分组的意义：逐系统打分可防止交叉污染。某个系统的标记缺失绝不可压制对另一个系统的检测；不相关系统之间的聚合计数也没有信息量。

一个工作目录的形态（伪代码）：

```text
catalog:
  acme_widget_v3:
    markers:
      - { id: "acme_widget_v3_init",         category: function, weight: 10 }
      - { id: "acme.widget.v3.dialog.open",  category: telemetry, weight: 5 }
      - { id: "ACME_WIDGET_V3_DISABLE",      category: flag,     weight: 10 }
  acme_other_system:
    markers:
      - ...
```

**预期：** 每个系统拥有自己的标记清单；没有任何标记同时出现在两个系统中。添加一个新系统意味着新增一个顶级条目 — 绝不事后在系统之间搬运标记。

**失败时：** 如果标记难以归入某一个系统（重叠、模糊），说明系统定义太粗了。拆分该系统，或者接受某些标记是"共享底座"并把它们排除在逐系统打分之外。

### 步骤 3：按信号强度为标记赋权

为每个标记赋予一个权重，反映它的存在单独能在多大程度上确认该系统：

- **10 = 单独即可确诊** — 足够独特，以至于仅凭发现这一个标记就足以确认该系统存在（例如一条很长的、系统专用的、其他代码路径不会发出的字符串）
- **3-5 = 仅起佐证作用** — 单独过于普通，无法确诊，但会计入聚合得分（例如测试台在多个特性间复用的一个短遥测后缀）

要教授的是这一惯例，而非具体数字。"确诊"与"佐证"之间的落差比具体整数更重要 — 关键在于第 5 步的阈值能区分"单个强信号"与"多个弱信号"。

**预期：** 每个标记都有一个权重。目录的权重分布偏向佐证性标记（3-5），每个系统只有少量单独即可确诊的标记（10）。

**失败时：** 如果每个标记都被赋值为 10，打分就失去了分辨率 — 部分存在的发现将不可能被识别。降格那些在多个系统间反复出现、或出现在不相关处理器中的标记。

### 步骤 4：记录每个版本的基线

对扫描过的每个版本，都应记录**存在的**与**缺失的**标记，以版本为键。两者都是证据：版本 N 中缺失的某个标记，与版本 N+1 重新引入它时同样具有信息量。

基线形态：

```text
baselines:
  "1.4.0":
    acme_widget_v3:
      present: ["acme_widget_v3_init", "ACME_WIDGET_V3_DISABLE"]
      absent:  ["acme.widget.v3.dialog.open"]
      score:   20
  "1.5.0":
    acme_widget_v3:
      present: ["acme_widget_v3_init", "ACME_WIDGET_V3_DISABLE", "acme.widget.v3.dialog.open"]
      absent:  []
      score:   25
  "1.4.1":
    _annotation: "never-published; skipped from upstream release timeline"
```

从未发布的版本要显式加注，而不是默默省略。默默跳过的版本在下一位读者眼里就像数据丢失。

**预期：** 每个版本对每个被追踪系统都产出一条记录，填充 `present`、`absent`、`score`；如从未发布则填入显式的 `_annotation`。

**失败时：** 如果基线扫描在一个先前存在的系统上产出了零标记，在没有确认二进制路径正确、`strings` 命令有输出、且标记 ID 与目录完全一致之前，不要假定它被移除了。错误的零值会污染纵向记录。

### 步骤 5：为完全与部分检测设置阈值

为每个系统定义两道闸门，作用于聚合得分：

- **`full`** — 高于此分则该系统被视为在本版本中存在且激活
- **`partial`** — 高于此分则该系统被视为已发货但不完整（部分标记存在，但未达 `full` 阈值）

低于 `partial` = 缺失（或尚未存在，取决于时间推进方向）。

```text
thresholds:
  acme_widget_v3:
    full:    25
    partial: 10
```

如何选阈值：把 `full` 设为一次健康安装预期会发出的权重之和；把 `partial` 设为一个诊断性标记加上一个佐证信号的分数。当你有了多个版本的证据后再重新调校。

**预期：** 每次扫描对每个系统产出一个带标签的发现：`full | partial | absent`。`partial` 发现需要调查 — 它们正是暗启动与移除的候选。

**失败时：** 如果每个系统在每个版本都报告 `partial`，说明阈值过于敏感（很可能被设置得比标记总和还高）。对照某个已验证处于激活状态的版本重新校准。

### 步骤 6：使用 `strings -n 8` 扫描

把 `strings` 配合最短长度过滤作为抽取原语。`-n 8` 下限能滤掉多数噪声（短片段、填充、地址表垃圾），同时不丢失有意义的标识符 — 它们几乎总是长于 8 个字符。

```bash
strings -n 8 path/to/binary > /tmp/binary-strings.txt
```

然后对 `/tmp/binary-strings.txt` 运行目录匹配（任何面向行的匹配器：`grep -F -f markers.txt`、`ripgrep` 或一个小脚本都可以）。

注意事项：

- 更低的下限（`-n 4`、`-n 6`）会让输出被二进制垃圾与压缩符号噪声淹没；诊断与佐证的区分会崩塌
- 更高的下限（`-n 12+`）会漏掉较短的标志位标识符与配置键
- 某些打包器会压缩或编码字符串；如果 `strings` 返回几乎空白的输出，二进制可能需要先做打包抽取（超出本技能范围）

**预期：** 每行一个字符串的输出，通常 1k-100k 行，取决于二进制大小。手工检视前 100 行应能辨认出可识别的标识符。

**失败时：** 如果输出为空或难以辨认，二进制很可能是被打包、加密、或以 `strings` 读不懂的字节码格式发货的。停在这里、先在抽取层解决；不要基于一次不可读的扫描记录基线。

### 步骤 7：向前扩展基线，而不重写过去的记录

当目录中新增系统或标记时，**仅对其后的版本**进行扫描。过去版本的记录保持原样。

为什么这样做：先前版本的基线是当时扫描到的经验证据，而不是对该过去版本实际内容的当前模型。用事后新发现的标记去回头改写它们，会把"我们现在知道什么"与"我们当时观察到了什么"混为一谈。两者都有用；基线文件里只应保留其中一者。

如果确实需要做一次事后扫描（例如测试某个新标记在版本 N-3 时是否已存在），把它作为**独立的附录**来记录：

```text
addenda:
  "1.4.0":
    scan_date: "2026-04-15"
    catalog_revision: "v7"
    findings:
      acme_new_system:
        present: ["..."]
```

原本的 `baselines["1.4.0"]` 条目保持不动。读者既能看到原始记录，也能看到后来的事后扫描及各自使用的目录修订。

**预期：** 基线文件只向前单调增长；过去记录只追加、附带可选的附录块。目录修订版本化，使每次扫描都能追溯到它所用的目录状态。

**失败时：** 如果你感到冲动想要直接编辑某个过去版本的 `present` 清单，请停下。改为增加一条附录。篡改过去记录会失去检测扫描器回归的能力（后续任何扫描器校验环节的第 8 步都依赖历史记录的不可变性）。

## 校验

- [ ] 目录对每个标记都显式标注了类别（API / 身份 / 配置 / 遥测 / 标志 / 函数 之一）
- [ ] 每个标记都严格归属于一个系统；没有任何标记同时出现在两个系统中
- [ ] 权重跨越真实区间（既有 10，也有 3-5）；权重不是全都相同
- [ ] 每个被扫描版本都对每个被追踪系统记录了 `present`、`absent` 与 `score`
- [ ] 从未发布的版本有显式加注，而非默默省略
- [ ] 每个系统都有 `full` 与 `partial` 两个阈值；发现被相应标注
- [ ] 使用 `strings -n 8` 作为抽取原语（或对非文本二进制使用有文档记载的等价物）
- [ ] 最新扫描未改变过去版本的记录；事后新发现放在附录块中

## 常见陷阱

- **把具体发现当作目录。** 目录应描述标记的类别与形态，而不是罗列绑定到具体版本的字面量。塞满"发现"形态条目的目录衰败极快，且如不慎公开发布，是最大的泄漏风险。
- **捕获被压缩的标识符。** 像 `_p3a` 或 `q9X` 这样的名字在每次重新构建时都会重命名。即使今天匹配上，明天就是噪声。坚持采用语义上有意义的标识符。
- **把遥测事件与特性标志混为一谈。** 它们在许多测试台中共享命名惯例，但扮演不同角色。在第 1 步按类别打标签，以便逐类分析保持干净。
- **默默跳过从未发布的版本。** 版本序列中的无注释空洞看起来像是漏扫。显式加注：`_annotation: "never-published"`。
- **在有任何基线数据之前就设置阈值。** 首次扫描确立经验上的权重总和；之后再对着它调校阈值，而不是提前拍脑袋。
- **目录增长时重写先前版本的记录。** 过去的记录是证据；附录才是事后扫描的受支持模式。
- **信任空白扫描输出。** 标记数为零并不总是意味着"缺失"。在宣布移除之前，先确认二进制可读、目录 ID 完全一致。
- **把 `strings -n 4` 当成比 `-n 8` 更彻底。** 更低的下限加噪声的速度比加信号更快。诊断性标记几乎总是 8 个以上字符。

## 相关技能

- `security-audit-codebase` — 共享的学术纪律；两条流水线都把标记存在当作发现，但下游消费者不同
- `audit-dependency-versions` — 把相同的版本追踪严谨性扩展到外部依赖清单；本技能把它应用到二进制工件
- `probe-feature-flag-state` — 第 2-3 阶段的后续；消费基线以对标志位推广状态（live / opt-in / dark / removed）进行分类
- `conduct-empirical-wire-capture` — 第 4 阶段的后续；用真实测试台流量验证推断出的行为
- `redact-for-public-disclosure` — 第 5 阶段的后续；规定哪些发现可以离开私有工作区

