SOP — 技能条款第四轮实战验证 + 工程阻断项修复¶
主题目录
skill-verify-r4/。本次变更对象有两类:① 全局技能deployment-tool-engineering(条款补一处盲点);② 本工程lustre_ins的两处阻断级缺陷(门禁遗漏、dry-run 副作用)及其文档口径。前三轮技能改造见
skill-portability/SOP.md、skill-design-refine/SOP.md、skill-clause-fix/SOP.md。本轮触发方式与第三轮相同(以本工程为对象实跑验证),差别是:本轮验的是第三轮修订后的条款,因而同时得到「条款是否站得住」与「工程还差什么」两份结论。
1. 背景¶
第三轮改造新增了三处条款(--force 两类语义、存量工程渐进期不与交付分级冲突、分批按数据面可分性分级),并留下两项工程待办。本轮按修订后的条款把本工程重新对照一遍。
结论:
- 三处新条款在本工程全部经受住检验(实测落点见附录 B.2);
- 逐条对照又抓到 8 项此前未记录的差距,其中 3 项为阻断级(附录 B.1),触发本次工程侧修复;
- 条款自身暴露 1 处盲点:上轮已看到「三态被 bool 包装函数折平」的形态,但当时只记为工程问题,未升格为条款,导致同类形态(含共享库通用幂等工具)本轮仍只能靠人读代码发现。本次补齐。
本次变更属 C 类(改全局技能 + 改工程代码),只动工具层,不触碰任何 Lustre 主机、不重跑任何不可逆动作。
2. 目标¶
变更完成后应达到以下可验证状态:
- 技能正文含「禁止把三态折回两值」的约束,反例表含「bool 折平三态」「关键词子串判定」两条
repo子命令纳入变更门禁(MUTATING_SUBCOMMANDS)add-mdt的lctl set_param mds.*.enable_dne=1经统一变更入口执行,--dry-run下零副作用- 技能 frontmatter 可解析、
name与目录名一致、行数 < 500、引用的references/齐全 deployer.py语法自检通过;SOP / RUNBOOK 与实现体口径一致
3. 前置条件¶
| 项 | 要求 | 检查命令 |
|---|---|---|
| 受改文件 | 技能 ~/.trae-cn/skills/deployment-tool-engineering/SKILL.md、工程 deployer.py、docs/SOP.md 均可写 |
test -w <file> && echo writable |
| 依赖 | python3 与工程 .venv/bin/python 可用 |
python3 --version |
| 备份台账 | 前几轮备份仍在(skill-clause-fix/backup/ 等),本轮与之并列、不混用 |
ls skill-clause-fix/backup skill-verify-r4/backup |
| 作业占用 | 无需检查:本次不接触 Lustre 主机,不涉及 Slurm / LSF 节点 | — |
4. 影响范围¶
| 维度 | 说明 |
|---|---|
| 主机 | 本机(操作机)一处,不涉及 192.168.31.21 / .22 等任何远端主机 |
| 组件 | 全局技能 deployment-tool-engineering 正文;工程编排层 deployer.py;工程手册 docs/SOP.md |
| 受影响资产 | 技能 +3 行(312 → 315 行);deployer.py 门禁清单 1 行、DNE 步骤 16 行;docs/SOP.md 两处说明 |
| 是否停服 | 否。不触碰 edafs 文件系统,无客户端 IO 影响 |
| 窗口期 | 无限制 |
| 在跑业务 | 无 |
| 行为变化 | repo 子命令新增前置门禁(只读预检查):节点不可达或环境不达标时会 BLOCK 而不再直接改仓库文件,属预期收紧 |
| 缓存影响 | 改技能使上游前缀缓存失效,收尾后开新会话 |
5. 分步操作¶
本次由 Agent 直接编辑文件完成,无实现体脚本;以下为与实现等价的操作序列,供复核与手工复现。以实现体(
SKILL.md/deployer.py本身)为准。
# 1. 备份受改文件(高危前置:备份先于一切修改)
TS=$(date +%Y%m%d-%H%M%S) # 备份时间戳
D=/home/rain/Documents/lustre_ins/skill-verify-r4/backup
mkdir -p "$D/global-skills/deployment-tool-engineering" "$D/docs"
cp -p /home/rain/.trae-cn/skills/deployment-tool-engineering/SKILL.md \
"$D/global-skills/deployment-tool-engineering/SKILL.md.bak.$TS" # 技能
cp -p /home/rain/Documents/lustre_ins/deployer.py "$D/deployer.py.bak.$TS" # 工程编排层
cp -p /home/rain/Documents/lustre_ins/docs/SOP.md "$D/docs/SOP.md.bak.$TS" # 工程手册
# 2. 技能:补三态约束 + 两条反例(定点替换,不重写整文件)
# 2a 「幂等与计划先行」节三态表后新增:禁止把三态折回两值
# 2b 「常见反例」表新增两行:bool 包装函数折平三态 / 通用幂等工具关键词子串判定
# 3. 工程 deployer.py:修两处阻断级缺陷(定点替换)
# 3a MUTATING_SUBCOMMANDS 增补 "repo"(实现文件 deployer.py:cmd_repo 与
# modules/repo_setup.py 会写各节点 /etc/yum.repos.d/*.repo)
# 3b cmd_add_mdt 末步「启用 DNE」由裸调 _ssh.run 改为走 apply_change,
# 并去掉 `2>/dev/null || true`(改为校验返回码,失败计入本次失败)
# 4. 工程 docs/SOP.md:与实现体口径对齐
# 4a 5.1 节「变更类子命令」枚举补 repo,并新增「单独执行 repo」小节
# 4b 5.5 节补注:末步 DNE 经统一变更入口,dry-run 下不下发
失败判断依据:任一步返回码非 0,或第 6 章校验任一项不通过,即视为失败,按第 7 章回滚。
6. 验证¶
# 1. 技能结构校验:frontmatter、name、description、行数、references、修订点命中
python3 - <<'PY'
import re, pathlib
root = pathlib.Path('/home/rain/.trae-cn/skills/deployment-tool-engineering')
t = (root/'SKILL.md').read_text(encoding='utf-8')
fm = re.match(r'^---\n(.*?)\n---\n', t, re.S).group(1)
name = re.search(r'^name:\s*(.+)$', fm, re.M).group(1).strip()
desc = re.search(r'^description:\s*(.+)$', fm, re.M).group(1).strip()
print('frontmatter 可解析: True')
print('name 一致/合法:', name == root.name, bool(re.fullmatch(r'[a-z0-9]+(-[a-z0-9]+)*', name)))
print('desc 合规:', ': ' not in desc and '<' not in desc and '>' not in desc and 'Use when' in desc)
print('行数:', len(t.splitlines()), '(<500:', len(t.splitlines()) < 500, ')')
print('缺失 references:', [r for r in set(re.findall(r'references/([\w.\-]+\.md)', t))
if not (root/'references'/r).exists()] or '无')
for k in ['禁止把三态折回两值', '用 bool 包装函数把三态折平', '通用幂等工具用关键词子串判定']:
print(f'修订点命中 [{k}]:', t.count(k))
PY
# 2. 工程语法与门禁清单校验
cd /home/rain/Documents/lustre_ins
.venv/bin/python -m py_compile main.py deployer.py # 预期:无输出(成功)
.venv/bin/python -c "
import deployer
m = deployer.LustreDeployer.MUTATING_SUBCOMMANDS
print('变更子命令清单:', m)
print('repo 已入门禁:', 'repo' in m)
"
# 3. dry-run 零副作用验证(不联网:直接验统一变更入口的行为)
.venv/bin/python -c "
from deployer import LustreDeployer
d = LustreDeployer(config_path='config/deploy.yaml', dry_run=True)
called = []
r = d.apply_change('测试变更', action=lambda: called.append(1), dry_run_desc='将做某事')
print('dry-run 下 apply_change 返回:', r) # 预期 None
print('action 是否被调用:', bool(called)) # 预期 False
"
实测结果(2026-09-24 01:15):
| 校验项 | 结果 |
|---|---|
| 技能 frontmatter 可解析 / name 一致且合法 / desc 合规 | True / True True / True |
| 技能行数 | 315(变更前 312,< 500) |
| 引用的 references 齐全 | True(skeleton.md / python-stack.md / shared-lib-contract.md) |
| 三个修订点命中 | 各 1 次 |
py_compile main.py deployer.py |
通过 |
| 变更子命令清单 | ('bootstrap','add-oss','add-ost','add-mdt','add-client','enable-ha','tune','repo') |
dry-run 下 apply_change |
返回 None、action 未被调用 |
| RUNBOOK 口径 | 无需改动(其 repo 引用为恢复动作,未描述门禁) |
未做项及理由:未做真机 --dry-run 端到端复跑(需 SSH 到 192.168.31.21/.22)。本次改动是编排层的分支归属变更,行为已由第 3 条用例在本地覆盖;端到端复跑留待下一次真实部署窗口执行。
7. 回滚¶
备份位置:skill-verify-r4/backup/,逐条登记见 skill-verify-r4/backup/README.md。
# 高危:覆盖,执行前先确认目标路径与本轮时间戳
B=/home/rain/Documents/lustre_ins/skill-verify-r4/backup
TS=20260924-011524
# 回滚技能正文
cp -p "$B/global-skills/deployment-tool-engineering/SKILL.md.bak.$TS" \
/home/rain/.trae-cn/skills/deployment-tool-engineering/SKILL.md
# 回滚工程编排层
cp -p "$B/deployer.py.bak.$TS" /home/rain/Documents/lustre_ins/deployer.py
# 回滚工程手册
cp -p "$B/docs/SOP.md.bak.$TS" /home/rain/Documents/lustre_ins/docs/SOP.md
回滚后验证:
grep -c "禁止把三态折回两值" /home/rain/.trae-cn/skills/deployment-tool-engineering/SKILL.md # 应为 0
cd /home/rain/Documents/lustre_ins && .venv/bin/python -c "
import deployer
print('repo 已入门禁(回滚后应为 False):', 'repo' in deployer.LustreDeployer.MUTATING_SUBCOMMANDS)"
注意:三处回滚相互独立,可只回滚其一。回滚技能正文后需开新会话使其生效。
8. 风险说明¶
| 风险 | 等级 | 缓解措施 |
|---|---|---|
| 技能定点替换误匹配到别处 | 中 | 匹配段含足够上下文;替换后逐处 diff 复核,并用第 6 章命中计数确认 |
| 新增反例改变既有工程判据 | 低 | 两条反例属「把已有原则落到反例表」,未新增安全要求;三项无开关的红线原文未动 |
repo 入门禁后被既有流程当作"新增校验"而误判 |
中 | 第 4 章已声明属预期收紧;--dry-run 下 BLOCK 降级为告警,预演不受影响 |
DNE 步骤去掉 \|\| true 后,非 DNE 环境上命令失败导致 add-mdt 记失败 |
中 | 仅在确实执行 mds.*.enable_dne=1 时判定;失败会记 enable_dne:<host> 并置本次失败,属显式暴露而非静默吞掉;如需容忍应在配置中显式处理 |
| 技能改动使缓存前缀失效带来成本 | 低 | 变更集中在一次会话内完成,收尾后开新会话 |
附录 A — 交付物清单¶
| 文件 | 说明 |
|---|---|
skill-verify-r4/SOP.md |
本文件 |
skill-verify-r4/backup/README.md |
备份台账(3 条) |
skill-verify-r4/backup/global-skills/deployment-tool-engineering/SKILL.md.bak.20260924-011524 |
技能正文修订前版本 |
skill-verify-r4/backup/deployer.py.bak.20260924-011524 |
工程编排层修订前版本 |
skill-verify-r4/backup/docs/SOP.md.bak.20260924-011524 |
工程手册修订前版本 |
skill-verify-r4/backup/docs/HANDOFF.md.bak.20260924-011653 |
交接快照整体重写前版本 |
改造后资产位置:/home/rain/.trae-cn/skills/deployment-tool-engineering/(SKILL.md 315 行 + references/ 三份)。
附录 B — 第四轮验证发现(对象:本工程 lustre_ins)¶
B.1 本轮抓到的 8 项差距¶
| # | 发现 | 证据 | 级别 | 本次处置 |
|---|---|---|---|---|
| N1 | repo 是变更子命令却不入门禁 |
deployer.py 的 MUTATING_SUBCOMMANDS 原无 repo;modules/repo_setup.py 写 /etc/yum.repos.d/*.repo(且在 ROLLBACK_REMOTE_PATHS 中);docs/SOP.md 第 83 行称"各变更子命令"但枚举里无它,第 5 章亦无小节 |
阻断 | 已修 |
| N2 | add-mdt 在 dry-run 下仍有真实副作用 |
deployer.py:cmd_add_mdt 裸调 _ssh.run("lctl set_param mds.*.enable_dne=1 2>/dev/null \|\| true"),未经 apply_change、未判 dry_run、未校验返回码 |
阻断 | 已修 |
| N3 | 多主机数据面不可逆变更(mkfs)未分批、无失败阈值 |
全工程无批次/失败阈值配置;_run_steps 失败即终止但明写"前序模块已变更的不会自动回滚" |
阻断 | 未修,见 B.3 |
| N4 | 无 plan.yaml 预演产物 |
dry-run 仅打日志,无结构化计划文件 | 建议 | 未修,见 B.3 |
| N5 | 模块普遍不覆盖 sync_status(),幂等判定内嵌 execute() |
modules/ 下无 def sync_status;backup() 因而无法"仅对变更项"执行 |
建议 | 未修,见 B.3 |
| N6 | 共享库通用幂等工具用 bool + 关键词子串判定,异常当"需变更" | base_module.check_idempotent / skip_if_done(expected_output in stdout;异常 return False);当前工程未调用,属潜伏 |
阻断 | 未修,见 B.3(已由本轮技能反例覆盖) |
| N7 | ssh_user 配置项无实效引用,连接身份硬编码 root |
deployer.py:_do_initialize 传 {"username": "root"};ssh_user 仅被 lustre_state.py 塞进 target dict,未用于建立连接 |
建议 | 未修,见 B.3 |
| N8 | lctl dl 按固定字段位置解析 |
utils/lustre_state.py:get_online_targets 用 parts[4] 取 host、parts[-1] 取 state |
建议 | 未修,见 B.3 |
复核说明:N1、N2 是据以动手的证据,已单独做过定向交叉复核(对比实现体、
docs/SOP.md、docs/RUNBOOK.md三处口径),确认非误报;N1 复核时额外发现 SOP 第 83 行"各变更子命令"的表述本身也与实现体不符。
B.2 第三轮三处修订条款的实测落点¶
| 修订 | 本工程实测 | 判定 |
|---|---|---|
--force 两类语义 |
modules/target_format.py 的设备校验在 --force 判定之前完成;--force 只用于 mkfs / rollback 的显式确认,无一处拿它跳过校验 |
条款正确,原措辞确实过窄 |
| 渐进期不与交付分级冲突 | 本工程仍未做 bundle 与环境契约,按新条款应登记差距清单而非判交付未完成 | 条款消解了自相冲突 |
| 分批按数据面可分性分级 | tune / repo / firewall 等仅服务配置面且可重入 → 建议;mkfs 数据面不可逆 → 阻断(即 N3) |
分级在本工程给出了不同且正确的判据 |
B.3 本次未处置项(转为工程待办)¶
| 项 | 为何不在本次范围 | 建议下一步 |
|---|---|---|
| N3 分批与失败阈值 | 需新增配置项与编排改造,属独立变更单元,非"顺手可修" | 优先级高于 N4/N5:先补批次规模与失败阈值配置,再改编排 |
N4 plan.yaml |
属新增能力,需设计产物结构 | 与 N5 一并做:sync_status() 生成变更清单即天然产出计划 |
N5 sync_status() 未覆盖 |
触及全部业务模块,改动面大 | 与 N4 合并规划 |
| N6 共享库幂等工具 | 未被调用,属潜伏;改动共享库影响所有挂载工程 | 单独评估:要么改三态,要么删除 |
N7 ssh_user 幽灵配置 |
涉及执行身份模型(当前为 root-only) | 需先确认是否支持非 root 部署 |
N8 lctl dl 解析 |
lctl dl 无结构化输出,影响面限于巡检展示 |
低优先,随巡检改造一并处理 |
来源:
/home/rain/Documents/lustre_ins/skill-verify-r4/SOP.md| 最后更新:2026-09-24 01:17(由build/sync.py同步,请勿在站点侧修改)