Agent-almanac monitor-binary-version-baselines
git clone https://github.com/pjt222/agent-almanac
T=$(mktemp -d) && git clone --depth=1 https://github.com/pjt222/agent-almanac "$T" && mkdir -p ~/.claude/skills && cp -r "$T/i18n/zh-CN/skills/monitor-binary-version-baselines" ~/.claude/skills/pjt222-agent-almanac-monitor-binary-version-baselines-fae295 && rm -rf "$T"
i18n/zh-CN/skills/monitor-binary-version-baselines/SKILL.md监控二进制版本基线
为 CLI 测试台二进制中的特性系统标记建立可比较的、以版本为键的记录,让新增、移除与暗启动能力能够在跨版本间被机械化地检测出来。
何时使用
- 在一个闭源 CLI 测试台的多个发布之间追踪某项特性的生命周期
- 探测暗启动的能力(已发货但被闸门关闭)或悄然移除的能力
- 验证标记扫描器仍能在旧二进制上检出已知良好的标记(对扫描器自身做回归测试)
- 构建第 1 阶段的底座,供后续阶段(标志位发现、暗启动检测、线路抓包)消费
- 任何临时用
只能回答"X 今天在不在",而你真正需要的是"由 X、Y、Z 组成的系统如何在版本间演进"的场景grep
输入
- 必需:同一个 CLI 测试台的一个或多个已安装二进制版本(或抽取出的打包产物)
- 必需:一个工作中的目录文件,用于存放标记定义(首次运行时创建,跨版本逐步扩展)
- 可选:来自先前运行的已记录基线文件(就地扩展,绝不重写)
- 可选:已知从未发布的版本清单(被跳过的发布、被撤回的构建)
- 可选:已经在追踪中的特性系统清单,用于扩展而不是重新发现
步骤
步骤 1:按类别选择标记
选择能在重新构建中幸存的字符串。挑选稳定、语义上有意义的标识符 — 不要挑选打包器在下个版本就会重命名的压缩名。
推荐的六个类别:
- API — 端点路径、测试台网络表面暴露的方法名
- 身份(Identity) — 内部产品名、代号、版本哨兵
- 配置(Config) — 面向用户的配置文件中被识别的键
- 遥测(Telemetry) — 发送到分析管道的事件名
- 标志(Flag) — 被闸门谓词消费的特性闸门键
- 函数(Function) — 特定处理器内部使用的著名字符串常量(错误消息、日志标签)
避免:看起来像被压缩过的短标识符(例如
_a1、bX、两个字母后跟数字)、任何一次文本修订就会改变的内联字面量,以及任何匹配打包器自身内部命名惯例的字符串。
预期: 每个候选标记都带有一个类别标签和一段简短的理由说明("出现在面向用户的文档中"、"在前 N 个发布中稳定存在"等)。一次典型的初轮筛选能为每个系统产出 20-50 个标记。
失败时: 如果标记在相邻的小版本之间就消失了,说明目录捕获的是易受重新构建影响的易变字符串,而不是稳定的标识符。删除这些条目;改为采用更长、更具语义锚点的子串。
步骤 2:按特性系统对标记分组
把标记打包进每个独立演化能力对应的系统表中。一个"系统"是一组连贯的标记,它们的存在/缺失会协同移动,因为它们共享一次特性生命周期(例如,属于某个假想
acme_widget_v3 能力的全部标记)。
分组的意义:逐系统打分可防止交叉污染。某个系统的标记缺失绝不可压制对另一个系统的检测;不相关系统之间的聚合计数也没有信息量。
一个工作目录的形态(伪代码):
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 重新引入它时同样具有信息量。
基线形态:
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 = 缺失(或尚未存在,取决于时间推进方向)。
thresholds: acme_widget_v3: full: 25 partial: 10
如何选阈值:把
full 设为一次健康安装预期会发出的权重之和;把 partial 设为一个诊断性标记加上一个佐证信号的分数。当你有了多个版本的证据后再重新调校。
预期: 每次扫描对每个系统产出一个带标签的发现:
full | partial | absent。partial 发现需要调查 — 它们正是暗启动与移除的候选。
失败时: 如果每个系统在每个版本都报告
partial,说明阈值过于敏感(很可能被设置得比标记总和还高)。对照某个已验证处于激活状态的版本重新校准。
步骤 6:使用 strings -n 8
扫描
strings -n 8把
strings 配合最短长度过滤作为抽取原语。-n 8 下限能滤掉多数噪声(短片段、填充、地址表垃圾),同时不丢失有意义的标识符 — 它们几乎总是长于 8 个字符。
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 时是否已存在),把它作为独立的附录来记录:
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
与absentscore - 从未发布的版本有显式加注,而非默默省略
- 每个系统都有
与full
两个阈值;发现被相应标注partial - 使用
作为抽取原语(或对非文本二进制使用有文档记载的等价物)strings -n 8 - 最新扫描未改变过去版本的记录;事后新发现放在附录块中
常见陷阱
- 把具体发现当作目录。 目录应描述标记的类别与形态,而不是罗列绑定到具体版本的字面量。塞满"发现"形态条目的目录衰败极快,且如不慎公开发布,是最大的泄漏风险。
- 捕获被压缩的标识符。 像
或_p3a
这样的名字在每次重新构建时都会重命名。即使今天匹配上,明天就是噪声。坚持采用语义上有意义的标识符。q9X - 把遥测事件与特性标志混为一谈。 它们在许多测试台中共享命名惯例,但扮演不同角色。在第 1 步按类别打标签,以便逐类分析保持干净。
- 默默跳过从未发布的版本。 版本序列中的无注释空洞看起来像是漏扫。显式加注:
。_annotation: "never-published" - 在有任何基线数据之前就设置阈值。 首次扫描确立经验上的权重总和;之后再对着它调校阈值,而不是提前拍脑袋。
- 目录增长时重写先前版本的记录。 过去的记录是证据;附录才是事后扫描的受支持模式。
- 信任空白扫描输出。 标记数为零并不总是意味着"缺失"。在宣布移除之前,先确认二进制可读、目录 ID 完全一致。
- 把
当成比strings -n 4
更彻底。 更低的下限加噪声的速度比加信号更快。诊断性标记几乎总是 8 个以上字符。-n 8
相关技能
— 共享的学术纪律;两条流水线都把标记存在当作发现,但下游消费者不同security-audit-codebase
— 把相同的版本追踪严谨性扩展到外部依赖清单;本技能把它应用到二进制工件audit-dependency-versions
— 第 2-3 阶段的后续;消费基线以对标志位推广状态(live / opt-in / dark / removed)进行分类probe-feature-flag-state
— 第 4 阶段的后续;用真实测试台流量验证推断出的行为conduct-empirical-wire-capture
— 第 5 阶段的后续;规定哪些发现可以离开私有工作区redact-for-public-disclosure