关于 fdk-uat
fdk-uat 面向框架维护者,用于验收发布版本。它先从真实远程源在 canary 阶段测试空项目安装,再检查主分支的默认安装路径;失败时要求从远程撤回。
主要功能
- 01分两阶段检查 canary 和主分支
- 02安装到空的临时项目
- 03检查版本新增能力
- 04等待 CDN 哨兵确认传播
- 05从远程撤回失败版本
适用场景
- 01验收包含新能力的框架版本
- 02检查远程全新安装是否收到文档所述文件
- 03验证新规则能否拦截错误输入
- 04记录 UAT 结果或发布故障
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ rheinmir/ setup --skill fdk-uat安装后在 Claude Code 中输入/fdk-uat确认已加载。
详细介绍
技能:fdk-uat
是什么
目的与背景
- 目的: 从“新手通过
curl安装”的角度验收即将发布的 framework:搭建一个空项目,通过真实远程路径安装 overstack,验证该版本面向用户的新能力——分为两个阶段(合并前 canary,合并后立即进行 main-URL smoke);若失败,则从远程端撤下。 - 触发条件(何时使用):
- 即将发布具备新能力的版本(新技能、新规则、新引擎)。
- 怀疑“文档承诺了,但用户拿不到”(GH#77 这类问题)。
medic --ci已通过之后——UAT 是它之上的一层,不会取代它。- 用户提到“uat”“真实测试”“下游测试”“检查新手安装后能不能运行”“验收”“/fdk-uat”;或者
/ship的级别为 release/pr/mr,且包含新能力。
- 用户提到“uat”“真实测试”“下游测试”“检查新手安装后能不能运行”“验收”“/fdk-uat”;或者
- 非目标: 不取代
medic --ci(UAT 是它之上的一层),不是fresh-install-smoke --local(使用工作树file://),不实时运行真实 LLM 模型(有意设定上限——验收由人工完成),不修复发现的问题(撤下并记录到 problem-tree)。
背景——它要解决的问题(原文):
medic --ci 和 fresh-install-smoke --local 都通过 file:// 从工作树安装。它们证明“我机器上的代码没问题”,却不能证明“陌生人通过 curl 安装后能运行”。这是两回事:tarball 缺少文件、.gitignore 把需要发布的内容排除、skill 的 frontmatter 损坏导致 CLI 静默忽略、全局 engine 没有刷新——这些问题只有在真实远程路径上才会暴露。
远程路径只有在推送后才存在——但不一定要推到主分支。临时分支也有 raw URL。只需满足一个条件,先有鸡还是先有蛋的悖论就会消失:必须覆盖全部三个源变量,因为 install.sh 默认会从主分支拉取 skill。
而且 canary 仍然不够:它使用不同的 ref 和覆盖变量运行,因此对默认值本身一无所知——而默认值正是硬编码分支名称字符串的藏身之处。因此必须有第二阶段:运行新手实际会输入的命令,不做任何覆盖。
思维模型
medic --ci 通过 → 列出新能力检查清单(每一项 = 一条命令)→ 阶段 1:push HEAD:uat/ → 空项目通过 raw canary 安装(HARNESS_BASE + REPO_RAW + SKILLS_REF)→ UAT 检查清单 + 真实 Orca 工作区(assert)→ 阶段 2:push 主分支 → 等待 CDN sentinel → 执行 README 中的原始命令,不做覆盖 → smoke → 最终 PASS | FAIL 时撤下(revert/reset --force-with-lease)+ problem-tree。Canary 检查内容;main-URL smoke 检查默认值。
输入与输出契约
| 字段 | 必填? | 含义 | |
|---|---|---|---|
| 输入 | framework 仓库、medic --ci 通过、Git 干净 | 是 | 步骤 0 的前提;未通过则不推送 |
| 输入 | <owner>/<repo> + 主分支 | 是 | 构建 RAW + SKILLS_REF |
| 输入 | 新能力检查清单 | 是 | 推送前编写;无法列为命令的内容不计入 |
| 输入 | 推送审批(canary / 主分支) | 是 | 推送会产生远程副作用 |
| 输入 | --keep | 否 | 保留 UAT 项目以供检查 |
| 输出 | BASE + 即将推送的提交列表 | 是 | 失败时的撤下基准 |
| 输出 | 每项 UAT 的结果(实际测量值) | 是 | 3 个支柱 · test-broad PASS · 新能力 · orchestration-ready · Orca worktree assert · dot 布局 |
| 输出 | 判定结果 | 是 | PASS → 记录到 wiki/log.md;FAIL → 已从远程撤下提交 + llmwiki/html/fdk-problem-tree.html 中的 status: open 节点 |
| 输出 | canary 分支 | — | 两种退出路径下都会删除 |
规则与能力
- RULE-01 (MUST):主分支绝不接收未经 canary 验证的版本。 阶段 1 的存在就是为了确保这一点。
- RULE-02 (MUST):Canary 不能替代 main-URL 冒烟测试。 它使用不同的 ref 和覆盖变量运行 → 无法发现默认值的问题,而硬编码分支名称恰恰就在默认值中。跳过阶段 2,就是跳过真正用户会接触到的内容。
- RULE-03 (MUST):绝不推送后置之不理。 为了 UAT 推送后,必须在同一会话中运行 UAT。推送后去睡觉,就等于留下一份未经验收的版本供他人拉取。
- RULE-04 (MUST):必须在 canary 分支的两种退出路径中都清理它(通过和失败)。步骤 0 会列出
git branch -r | grep uat/,以清理中途退出的会话留下的垃圾。 - RULE-05 (MUST):CDN 的传播并不同步——等待哨兵,不要在推送后立即测量。
raw.githubusercontent可能在同一次安装中返回文件 A 的新版和文件 B 的旧版 → 形成混合版本,导致门禁误报红色(或更危险的误报绿色)。轮询仅存在于新版中的字符串,直到 raw 返回正确内容,然后再测量。2026-07-14 实际测得:新引擎 + 旧策略 → test-broad 为 72/74,新规则未生效;等待几分钟后重新运行 → 74/74。 - RULE-06 (MUST):不要凭记忆统计 skill/rule——要在刚安装的项目中实时统计。仓库磁盘上的数量不等于交付给用户的数量(曾出过问题:文档称有 74 个 skill,安装器却交付了 67 个——有 7 个 frontmatter 格式错误的 skill 被 CLI 静默忽略)。
- RULE-07 (MUST):新规则必须能拦截,而不只是存在。
policy.yaml中有一行规则 ≠ 规则能拦截问题。始终使用一个真实的 BAD 文件进行测试。 - RULE-08 (MUST):上限是刻意设定的: 无头 UAT 无法启动 LLM 模型,因此“
/orchestration可以运行”只按确定性标准检查(安装完整 + 可访问 + 运行时 ping)。使用真实模型进行实时运行仍需手动验收。 - RULE-09 (MUST):失败就撤销,不要“留着以后再修”。远程内容会被其他人拉取。
- RULE-10 (MUST):必须使用实际的 Orca workspace,并通过 assert 检查名称;不能只是“看起来可选”。 之前因被写成可选项,在赶时间运行时遭到忽略,因此曾两次被提醒(2026-07-21、2026-07-24)——仅测试文件系统不算完整的 UAT,即使其他所有 CLI 命令都通过也是如此。
- 能力:运行本地门禁;推送/删除远程分支并回退/重置主分支(需要远程写入权限);从 raw CDN 下载;创建临时项目并在 Orca 中注册 repo/worktree;记录 wiki 日志 + problem-tree。
失败边界
medic --ci报红 / git 不干净 → 在步骤 0 阻塞,不推送。- 无法将新能力落实为命令 → 需要澄清:那只是空头承诺,不算数。
- 阶段 1 失败 → 失败,删除 canary;主分支尚未被污染。
- Orca worktree 的 assert 未找到名称 → 暂时失败:停止,不要报告通过;修复
worktree create后重新验证。 - CDN 哨兵轮询 30 次后仍未更新 → 不测量(不判定红/绿);先对本地文件执行 grep-verify MARK。
- 阶段 2 失败 → 失败,立即撤销提交(步骤 5)+ 记录 problem-tree。
如何操作
主要工作流
| 步骤 | 类型 | 输入 | 操作 | 输出/退出 | 失败/后续 |
|---|---|---|---|---|---|
| W01 | 确定性 | 仓库 | 步骤 0 — 前提:medic --ci、git status --short、git log origin/<nhánh>..HEAD,记录 BASE | BASE 基准 | 失败/工作区不干净 → 阻塞 |
| W02 | 判断 | diff | 步骤 1 — 列出新能力检查清单,每行一条命令 | 检查清单 | 无法转化为命令 → 删除该行 |
| W03 | 副作用 | HEAD | 步骤 2 — 阶段 1:推送 HEAD:uat/<ts>,搭建空项目,使用全部 3 个变量从 raw canary 执行 curl | UAT 项目 | 缺少 SKILLS_REF = 虚假 UAT |
| W04 | 确定性 | UAT 项目 | 步骤 3 — 运行检查清单(3 个支柱、test-broad、新能力、编排、Orca 工作区 + assert、dot 布局) | PASS/FAIL | FAIL → 删除 canary,停止(B01) |
| W05 | 副作用 | 阶段 1 PASS | 步骤 4 — 阶段 2:推送主分支,删除 canary,等待 CDN sentinel,运行 README 中原样的命令,不覆盖任何选项,进行约 1 分钟的 smoke 测试 | PASS/FAIL | FAIL → W06 |
| W06 | 副作用 | 阶段 2 FAIL | 步骤 5 — 撤销提交(revert 或 reset --force-with-lease)+ 记录问题树(B02) | 远程干净 | — |
| W07 | 副作用 | PASS | 步骤 6 — 收尾:清理 UAT 项目(或使用 --keep),将测量数据记录到 wiki/log.md | 已验收版本 | — |
各步骤详情(W01–W07 的事实来源):
0. 前提 — 不可省略
python3 fdk/tools/medic.py --ci # PHẢI xanh. Đỏ mà vẫn push là cố ý đẩy rác lên remote.
git status --short # sạch (không untracked lạc)
git log --oneline origin/<nhánh>..HEAD # in ĐÚNG những commit sắp đẩy — ghi lại, đây là thứ sẽ gỡ nếu fail
记录 BASE=$(git rev-parse origin/<nhánh>) — 作为回退基准。
1. 列出此版本的新能力(UAT 检查清单)
推送之前,具体写明此版本承诺提供哪些新能力。每一行都必须能用命令验证:
- 新 Skill → 安装后
~/.claude/skills/<tên>/SKILL.md必须存在,并且出现在新项目的CAPABILITIES.md中。 - 新 Rule → 必须在新项目中确实生效(写入 BAD 文件 → 被拦截;GOOD 文件 → 通过)。
- 新 Engine/工具 → 在全局 harness home 中可访问。
无法列成命令的,不算能力——那只是空头承诺。
2. 阶段 1 — CANARY 分支(主分支不会收到任何内容)
远程路径只有推送后才存在——但不一定要推送到主分支。临时分支也有 raw URL,而且 curl 不关心是哪条分支。
CANARY="uat/$(date +%y%m%d-%H%M)"
git push origin HEAD:"$CANARY"
搭建一个空项目,从canary 自身分支的 raw 内容进行安装——三个变量都必须指定,否则它会悄悄拉取主分支的内容:
D=~/orca/overstack-uat-$(date +%y%m%d-%H%M)
mkdir -p "$D" && cd "$D" && git init
RAW="https://raw.githubusercontent.com/<owner>/<repo>/$CANARY"
curl -fsSL "$RAW/harness/poc-vendor-neutral/bootstrap.sh" \
| HARNESS_BASE="$RAW/harness/poc-vendor-neutral" \
REPO_RAW="$RAW" \
SKILLS_REF="<owner>/<repo>#$CANARY" \
bash
git add -A && git commit -m "fresh overstack install" # Orca cần ≥1 commit mới nhận repo
缺少
SKILLS_REF,UAT 就成了幻觉。install.sh默认从<repo>#<nhánh chính>安装 Skill——因此如果不覆盖该设置,canary 就会安装主分支的 Skill,也就是用旧版本打分,却为新版本报告 PASS。一个撒谎却仍显示绿色的关卡,比没有关卡更糟。
运行步骤 3 的检查清单。PASS → 进入阶段 2。FAIL → 删除 canary,主分支完全不受影响:
git push origin --delete "$CANARY"
3. UAT — 在该项目中运行步骤 1 检查清单
最低要求,参照 fdk/docs/fresh-install-gate.md:
- 3 个支柱均已就位:
.harness/poc-vendor-neutral/policy.yaml·.claude/settings.json·.pre-commit-config.yaml·.llmwiki/wiki/index.md·CAPABILITIES.md。 - Harness 确实生效:
bash .harness/poc-vendor-neutral/test-broad.sh→ PASS(验证器 GOOD-pass / BAD-block)。 - 新能力已交付 — 本版本独有的部分(步骤 1 检查清单)。这是最容易被漏掉的项目,也是唯一能证明新版本有价值的项目。
- 已可进行编排: 可访问
orchestration/orca-cli/orca-dispatch-reference技能。 - 独立仓库模块已就位(orca-graph): 在空白项目 HOME 中运行
python3 ~/.claude/harness/harness/scripts/orca-graph.py --version,必须输出orca-graph X.Y.Z(rc 0)。rc 3 = shim 已就位但 engine 未就位 → FAIL,即使其他所有项目都是绿色。如果使用--no-graph安装,则预期相反:rc 3,并附带且仅附带一条安装命令。fresh-install-smoke.sh的 (G) 项会自动检查;UAT 中应将结果记入检查清单。 - 必做(不是可选项 — 2026-07-24 反馈,因跳过此步骤已是第 2 次被提醒):搭建真实的 Orca 工作区 + 自行验证它显示在应用中,别只在文件系统层面运行测试。 仅 curl 安装到临时目录,再用 CLI 测试,不能算完整 UAT — 用户什么都看不到,“没有可视化 = 无法使用”。这是硬性门槛:跳过此步骤,UAT 不得判为 PASS。
WT_NAME="uat-$(date +%y%m%d-%H%M)" # tên xác định, dùng để verify ở bước sau
REPO_ID=$(orca repo add --path "$D" --json | python3 -c "import json,sys; print(json.load(sys.stdin)['result']['repo']['id'])")
orca worktree create --repo "id:$REPO_ID" --name "$WT_NAME" --setup skip --no-parent --activate --json
已踩过的陷阱:
orca repo add <path>位置参数 →Unknown command。仓库尚无 commit → Orca 不会识别。repo add不会让工作区显示在应用中 — 必须运行worktree create。
验证门槛 — 必须在上述命令后立即运行,这不是可选步骤:
orca worktree list --repo "id:$REPO_ID" --json | python3 -c "
import json, sys
d = json.load(sys.stdin)
names = [w['displayName'] for w in d['result']['worktrees']]
assert '$WT_NAME' in names, f'KHÔNG THẤY worktree tên {\"$WT_NAME\"} — UAT KHÔNG ĐƯỢC TÍNH LÀ PASS. Có: {names}'
print(f'✓ worktree \"$WT_NAME\" hiện thật trong Orca — {names}')
"
这里的断言 FAIL → 停止,不得继续向用户报告 PASS — 返回修正 worktree create 命令(通常是 --repo 的 selector 格式不正确,或 repo add 尚未 commit),然后重新验证。这正是用结构性门槛取代“每次都要记得自觉搭建工作区”的做法 — 靠手动记忆已连续失败 2 次(2026-07-21、2026-07-24),现在用断言强制执行。
6. 布局符合 dot 标准: UAT 项目根目录下不存在裸的 llmwiki/ 或 harness/ — 只有 .llmwiki/ · .harness/(AP-7 布局幻觉)。
4. 阶段 2 — 合并后立即进行 main-URL 冒烟测试(canary 无法替代此步骤)
阶段 1 使用不同的 ref和三个覆盖变量运行 → 它对默认值本身是盲的。而默认值正是硬编码分支字符串所在之处(bootstrap.sh 的 BASE=、install.sh 的 REPO_RAW=、SKILLS_REF=)。只有此阶段能验证新用户实际会输入的确切命令。
git push origin HEAD:<nhánh chính> # merge — bản này ĐÃ qua canary
git push origin --delete "$CANARY" # dọn nhánh tạm
测量前必须等待 CDN 传播完成。 raw.githubusercontent.com 不会在 push 后立即提供新版本,而且延迟因文件而异:曾测得 engine(bin/llmwiki-validate.py)已经是新版,而 policy.yaml 仍是旧版 → 安装出来的是一种混合版本,门槛会误报为红色。更危险的是,同样的机制也可能导致误报为绿色,如果旧文件碰巧仍能通过测试。
使用一个哨兵确认 — 持续轮询,直到 raw 返回刚推送的确切内容:
RAW="https://raw.githubusercontent.com/<owner>/<repo>/<nhánh chính>"
MARK="<một chuỗi CHỈ có ở bản mới>" # vd tên rule/field vừa thêm
# ⚠ GREP-VERIFY MARK trên file bản mới TRƯỚC (grep "$MARK" <file local>) — chọn pattern đoán
# mà không khớp file → vòng lặp chờ hết lượt rồi tưởng raw còn cũ, dù raw ĐÃ mới (p-32).
for i in $(seq 1 30); do
curl -fsSL "$RAW/harness/poc-vendor-neutral/policy.yaml" | grep -q "$MARK" && break
echo " raw còn cũ, chờ CDN… ($i)"; sleep 10
done
然后再运行README 中的确切命令 — 不覆盖任何变量:
D2=$(mktemp -d) && cd "$D2" && git init -q
curl -fsSL "$RAW/harness/poc-vendor-neutral/bootstrap.sh" | bash
精简版(~1 分钟):3 个支柱均已就位 · bash .harness/poc-vendor-neutral/test-broad.sh PASS · 新技能可在 ~/.claude/skills/ 中访问。
FAIL → 立即回滚(步骤 5)。主分支的风险窗口从“整套冗长 UAT”缩短到“一次 ~1 分钟的冒烟测试”。
5. 未通过 → 立即从远程移除 commit
仅在阶段 2 未通过(或有人已经误直接 push、跳过 canary)时使用。根据情况选择:
# (a) Nhánh chia sẻ / đã có người kéo → REVERT (không viết lại lịch sử):
git revert --no-edit <sha>..HEAD && git push origin <nhánh>
# (b) Nhánh chỉ mình dùng, vừa đẩy xong, chắc chắn chưa ai kéo → RESET:
git reset --hard "$BASE" && git push --force-with-lease origin <nhánh>
用 --force-with-lease,不要用 --force:如果期间有人推送了内容,该命令会拒绝执行,而不是把他们的工作删掉。
移除后:在 llmwiki/html/fdk-problem-tree.html 中记录失败原因(新节点,status: open)— UAT 失败是数据,不是羞耻的事。
6. PASS → 收尾
- 清理 UAT 项目(如果需要检查,可保留
--keep)。 - 将结果(真实测量值,而非承诺)记录到
wiki/log.md。 - 远程上的版本视为已通过“新手安装”阶段的验收。
分支
| ID | 类型 | 条件 | 行为 | 跳过 / 失败 | 回到 |
|---|---|---|---|---|---|
| B01 | 恢复 | PHA 1 FAIL | git push origin --delete "$CANARY";不动主分支 | — | 结束(失败) |
| B02 | 恢复 | PHA 2 FAIL(或误将内容直接推送,跳过 canary) | 共享分支 → git revert --no-edit <sha>..HEAD;尚未被他人拉取的私有分支 → git reset --hard "$BASE" + --force-with-lease;记录 problem-tree 节点 | 绝不裸用 --force | 结束(失败) |
| B03 | 恢复 | Orca worktree 断言 FAIL | 停止,修正 --repo 选择器 / 在 repo add 之前提交,然后重新验证 | 不得报告 PASS | W04 第 5 项 |
| B04 | 用户可选 | --keep | 保留 UAT 项目以供检查,而不是清理 | — | W07 |
| B05 | 恢复 | 步骤 0 列出 git branch -r 时发现仍有旧的 uat/ 分支 | 清理上次中途终止的会话留下的废弃 canary | — | W01 |
验证与停止
只有当步骤 3 的检查清单所有项目都通过命令验证为绿色(包括对 $WT_NAME 名称的 worktree 断言),且 sentinel 之后的 PHA 2 smoke test 通过时,才能判定为 PASS。统计刚安装的项目中 LIVE 状态的 skill/rule 数量,不要凭记忆判断。Sentinel 最多轮询 30 × 10s。绝不推送后就置之不理:UAT 必须在同一会话中运行。
示例
- 正向: 新增
record-episodeskill → 检查清单:“~/.claude/skills/record-episode/SKILL.md存在 + 已列入CAPABILITIES.md” → 推送uat/260919-1030,使用SKILLS_REF="<owner>/<repo>#uat/260919-1030"执行 curl → test-broad PASS、skill 可访问,orca worktree list显示uat-260919-1030→ 推送主分支,sentinel 匹配,smoke test 未覆盖 PASS 状态 → 删除 canary,记录到wiki/log.md。 - 边界/失败: PHA 2 在推送后立即测量,未等待 sentinel → 引擎已更新,但
policy.yaml仍旧 → test-broad 72/74,新规则未生效 → 这是假红:等待 sentinel 后重新测量(74/74)。如果 sentinel 之后仍然失败 → 通过 revert 撤销 commit,记录status: open节点。
来源
- 流程提炼自
fdk/docs/fresh-install-gate.md(§“标准流程:搭建新的 Orca workspace,然后测试 fresh-install”)+ 用户 2026-07-14 的要求:允许先推送以便真实测试,未通过则从远程撤销 commit。 - 两阶段是同一天用户提出质疑后的结论:“这样的话,它能测试 main 的 curl 命令本身吗?”——不能。Canary 证明远程安装的内容可用;只有 main-URL smoke test 才能证明默认命令可运行。还发现
install.sh将#orca硬编码,且没有覆盖途径——缺少这一点会导致 canary 错误地判定为旧版本。