About fdk-uat
fdk-uat is a release-acceptance skill for framework maintainers. It tests a clean install from a real remote source in a canary phase, then checks the default main-branch install path; failures trigger removal from the remote.
Key features
- 01Two-phase canary and main-branch checks
- 02Installs into a clean temporary project
- 03Checks new release capabilities
- 04Waits for CDN sentinel propagation
- 05Removes failed releases from remote
Use cases
- 01Accepting a framework release with new capabilities
- 02Checking whether a remote fresh install receives documented files
- 03Verifying new rules block bad inputs
- 04Recording UAT results or release failures
Install
Copy the command and run it in a terminal.
npx skills add https:/ / github.com/ rheinmir/ setup --skill fdk-uatAfter installing, type/fdk-uatin Claude Code to confirm it loaded.
Details
From the project READMESkill: fdk-uat
WHAT
Purpose và context
- Purpose: nghiệm thu một bản framework sắp phát hành ở tầng "người mới
curlvề": dựng dự án TRỐNG, cài overstack qua đường remote thật, kiểm năng lực MỚI của chính bản đó tới tay người dùng — qua HAI PHA (canary trước merge, main-URL smoke ngay sau merge); fail thì gỡ khỏi remote. - Trigger (when to use):
- Sắp công bố một bản có năng lực mới (skill mới, rule mới, engine mới).
- Nghi ngờ "doc hứa mà user không nhận được" (lớp lỗi GH#77).
- Sau khi
medic --ciđã xanh — UAT là tầng TRÊN nó, không thay thế nó.- User nói "uat", "test thật", "test downstream", "kiểm tra người mới cài có chạy không", "acceptance",
/fdk-uat; hoặc/shipmức release/pr/mr có năng lực mới.
- User nói "uat", "test thật", "test downstream", "kiểm tra người mới cài có chạy không", "acceptance",
- Non-goals: không thay
medic --ci(UAT là tầng TRÊN nó), không phảifresh-install-smoke --local(working-treefile://), không live-run model LLM thật (trần cố ý — acceptance làm tay), không sửa lỗi tìm thấy (gỡ + ghi problem-tree).
Bối cảnh — vấn đề nó giải (nguyên văn):
medic --ci và fresh-install-smoke --local cài từ working-tree qua file://. Chúng chứng minh "code trong máy tôi lành", không chứng minh "người lạ curl về là chạy được". Hai thứ khác nhau: tarball thiếu file, .gitignore nuốt mất thứ cần ship, skill hỏng frontmatter bị CLI bỏ qua trong im lặng, engine global không refresh — mọi lỗi này chỉ lộ ra trên đường remote thật.
Đường remote chỉ tồn tại sau khi push — nhưng không nhất thiết là push lên nhánh chính. Một nhánh tạm cũng có raw URL. Nghịch lý con-gà-quả-trứng biến mất, với một điều kiện: phải override cả ba biến nguồn, vì install.sh mặc định kéo skill từ nhánh chính.
Và canary vẫn không đủ: nó chạy với ref khác + biến override, nên mù với chính các giá trị mặc định — mà mặc định là chỗ chứa chuỗi tên nhánh hardcode. Nên phải có pha hai: chạy đúng cái lệnh người mới gõ, không override gì cả.
Mental model
medic --ci xanh → khai checklist năng lực mới (mỗi dòng = một lệnh) → PHA 1: push HEAD:uat/<ts> → dự án trống curl từ raw canary (HARNESS_BASE + REPO_RAW + SKILLS_REF) → UAT checklist + workspace Orca thật (assert) → PHA 2: push nhánh chính → chờ sentinel CDN → đúng lệnh README không override → smoke → PASS chốt | FAIL gỡ (revert/reset --force-with-lease) + problem-tree. Canary kiểm NỘI DUNG; main-URL smoke kiểm GIÁ TRỊ MẶC ĐỊNH.
Input và output contract
| Field | Required? | Ý nghĩa | |
|---|---|---|---|
| In | repo framework, medic --ci xanh, git sạch | có | tiền đề bước 0; đỏ thì không push |
| In | <owner>/<repo> + nhánh chính | có | dựng RAW + SKILLS_REF |
| In | checklist năng lực mới | có | viết trước khi push; không khai thành lệnh được = không tính |
| In | duyệt push (canary / nhánh chính) | có | push là side-effect remote |
| In | --keep | không | giữ dự án UAT để soi |
| Out | BASE + danh sách commit sắp đẩy | có | mốc gỡ nếu fail |
| Out | kết quả UAT từng mục (số đo thật) | có | 3 trụ · test-broad PASS · năng lực mới · orchestration-ready · worktree Orca assert · layout dot |
| Out | verdict | có | PASS → log vào wiki/log.md; FAIL → commit đã gỡ khỏi remote + node status: open trong llmwiki/html/fdk-problem-tree.html |
| Out | nhánh canary | — | đã xoá ở CẢ HAI lối ra |
Rules và capabilities
- RULE-01 (MUST): Nhánh chính KHÔNG BAO GIỜ nhận bản chưa qua canary. Pha 1 tồn tại để điều đó đúng.
- RULE-02 (MUST): Canary KHÔNG thay được main-URL smoke. Nó chạy với ref khác + biến override → mù với các giá trị mặc định, mà mặc định chính là chỗ hardcode tên nhánh. Bỏ pha 2 là bỏ đúng thứ người dùng thật chạm vào.
- RULE-03 (MUST): Không bao giờ push rồi bỏ mặc. Đã push để UAT thì phải chạy UAT trong cùng phiên. Push xong đi ngủ là để lại một bản chưa nghiệm thu cho người khác kéo về.
- RULE-04 (MUST): Dọn nhánh canary ở CẢ HAI lối ra (pass và fail). Bước 0 liệt kê
git branch -r | grep uat/để dọn rác của phiên chết giữa chừng. - RULE-05 (MUST): CDN propagate không đồng đều — CHỜ SENTINEL, đừng đo ngay sau push.
raw.githubusercontentcó thể trả file A đã mới còn file B còn cũ trong cùng một lần cài → bản lai, và cổng báo đỏ giả (hoặc xanh giả, nguy hiểm hơn). Poll một chuỗi CHỈ có ở bản mới cho tới khi raw trả đúng, rồi mới đo. Đo được thật ngày 2026-07-14: engine mới + policy cũ → test-broad 72/74, luật mới không cắn; chờ vài phút rồi chạy lại → 74/74. - RULE-06 (MUST): Không đếm skill/rule bằng trí nhớ — đếm LIVE trong dự án vừa cài. Con số trên đĩa repo không phải con số tới tay người dùng (đã cháy: doc báo 74 skill, installer giao 67 — 7 skill hỏng frontmatter bị CLI nuốt im lặng).
- RULE-07 (MUST): Rule mới phải CẮN, không chỉ có mặt. File
policy.yamlcó dòng luật ≠ luật chặn được. Luôn test bằng một file BAD thật. - RULE-08 (MUST): Trần (ceiling) cố ý: UAT headless không spin được model LLM, nên "
/orchestrationchạy được" chỉ kiểm ở mức tất định (cài đủ + reachable + ping runtime). Live-run bằng model thật vẫn là acceptance làm tay. - RULE-09 (MUST): Fail thì gỡ, không "để đó sửa sau". Remote là thứ người khác kéo về.
- RULE-10 (MUST): Workspace Orca hiện thật là BẮT BUỘC, có assert kiểm tên, không phải "tuỳ chọn thấy được bằng mắt". Bị nhắc lặp lại 2 lần (2026-07-21, 2026-07-24) vì trước đây ghi là tuỳ chọn nên bị bỏ qua khi chạy vội — filesystem-only test KHÔNG tính là UAT hoàn chỉnh dù mọi lệnh CLI khác xanh hết.
- Capabilities: chạy gate cục bộ; push/xoá nhánh remote và revert/reset nhánh chính (cần quyền ghi remote); tải từ raw CDN; tạo dự án tạm + đăng ký repo/worktree trong Orca; ghi log wiki + problem-tree.
Failure boundaries
medic --ciđỏ / git bẩn → blocked ở bước 0, không push.- Không khai được năng lực mới thành lệnh → clarify: đó là lời hứa suông, không tính.
- PHA 1 FAIL → failed, xoá canary; nhánh chính chưa bị bẩn.
- Assert worktree Orca không thấy tên → failed tạm: DỪNG, không báo PASS, sửa
worktree createrồi verify lại. - Sentinel CDN hết 30 lượt chưa mới → chưa đo (không kết luận đỏ/xanh); grep-verify MARK trên file local trước.
- PHA 2 FAIL → failed, gỡ commit ngay (bước 5) + ghi problem-tree.
HOW
Main workflow
| Step | Type | Inputs | Action | Outputs/exit | Failure/next |
|---|---|---|---|---|---|
| W01 | deterministic | repo | Bước 0 — tiền đề: medic --ci, git status --short, git log origin/<nhánh>..HEAD, ghi BASE | mốc BASE | đỏ/bẩn → blocked |
| W02 | judgment | diff | Bước 1 — khai checklist năng lực mới, mỗi dòng một lệnh | checklist | không thành lệnh → bỏ dòng đó |
| W03 | effect | HEAD | Bước 2 — PHA 1: push HEAD:uat/<ts>, dựng dự án trống, curl từ raw canary với cả 3 biến | dự án UAT | thiếu SKILLS_REF = UAT ảo giác |
| W04 | deterministic | dự án UAT | Bước 3 — chạy checklist (3 trụ, test-broad, năng lực mới, orchestration, workspace Orca + assert, layout dot) | PASS/FAIL | FAIL → xoá canary, dừng (B01) |
| W05 | effect | PASS pha 1 | Bước 4 — PHA 2: push nhánh chính, xoá canary, chờ sentinel CDN, chạy đúng lệnh README không override, smoke ~1 phút | PASS/FAIL | FAIL → W06 |
| W06 | effect | FAIL pha 2 | Bước 5 — gỡ commit (revert hoặc reset --force-with-lease) + ghi problem-tree (B02) | remote sạch | — |
| W07 | effect | PASS | Bước 6 — chốt: dọn dự án UAT (hoặc --keep), ghi số đo vào wiki/log.md | bản đã nghiệm thu | — |
Chi tiết từng bước (nguồn chân lý cho W01–W07):
0. Tiền đề — không được bỏ
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
Ghi lại BASE=$(git rev-parse origin/<nhánh>) — mốc để quay về.
1. Khai năng lực mới của bản này (checklist UAT)
Trước khi push, viết ra cụ thể bản này hứa gì mới. Mỗi dòng phải kiểm được bằng lệnh:
- Skill mới →
~/.claude/skills/<tên>/SKILL.mdtồn tại sau khi cài, và có trongCAPABILITIES.mdcủa dự án mới. - Rule mới → phải CẮN THẬT trong dự án mới (viết một file BAD → bị chặn; file GOOD → qua).
- Engine/tool mới → reachable trong global harness home.
Không khai được thành lệnh thì không tính là năng lực — đó là lời hứa suông.
2. PHA 1 — nhánh CANARY (nhánh chính KHÔNG nhận gì cả)
Đường remote chỉ tồn tại sau khi push — nhưng không nhất thiết phải push lên nhánh chính. Một nhánh tạm cũng có raw URL, và curl không quan tâm nhánh nào.
CANARY="uat/$(date +%y%m%d-%H%M)"
git push origin HEAD:"$CANARY"
Dựng dự án trống, cài từ raw của chính nhánh canary — phải trỏ cả ba biến, không thì nó lặng lẽ kéo nội dung của nhánh chính:
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
Thiếu
SKILLS_REFlà bài UAT thành ẢO GIÁC.install.shmặc định cài skill từ<repo>#<nhánh chính>— nên nếu không override, canary sẽ cài skill của nhánh chính, tức nó chấm bản CŨ rồi báo PASS cho bản MỚI. Một cổng nói dối mà vẫn xanh còn tệ hơn không có cổng.
Chạy checklist bước 3. PASS → sang pha 2. FAIL → xoá canary, nhánh chính chưa hề bị bẩn:
git push origin --delete "$CANARY"
3. UAT — chạy checklist bước 1 trong dự án ĐÓ
Tối thiểu, theo fdk/docs/fresh-install-gate.md:
- 3 trụ có mặt:
.harness/poc-vendor-neutral/policy.yaml·.claude/settings.json·.pre-commit-config.yaml·.llmwiki/wiki/index.md·CAPABILITIES.md. - Harness cắn thật:
bash .harness/poc-vendor-neutral/test-broad.sh→ PASS (validator GOOD-pass / BAD-block). - Năng lực MỚI tới tay — phần riêng của bản này (checklist bước 1). Đây là mục hay bị bỏ nhất, và cũng là mục duy nhất chứng minh bản MỚI có giá trị.
- Orchestration-ready: skill
orchestration/orca-cli/orca-dispatch-referencereachable. - Module repo riêng tới nơi (orca-graph):
python3 ~/.claude/harness/harness/scripts/orca-graph.py --versiontrong HOME của dự án trống phải inorca-graph X.Y.Z(rc 0). rc 3 = shim tới mà engine không tới → FAIL, kể cả khi mọi thứ khác xanh. Cài với--no-graphthì kỳ vọng NGƯỢC LẠI: rc 3 kèm đúng một lệnh cài.fresh-install-smoke.shmục (G) kiểm tự động; UAT ghi kết quả vào checklist. - BẮT BUỘC (không phải tuỳ chọn — feedback 2026-07-24, lần thứ 2 bị nhắc vì skip bước này): dựng workspace Orca thật + TỰ VERIFY nó hiện trong app, đừng chỉ chạy filesystem-level. Chỉ curl-cài vào một thư mục tạm rồi test bằng CLI KHÔNG tính là UAT hoàn chỉnh — user không thấy được gì, "không visual = không dùng được". Đây là cổng CỨNG: UAT KHÔNG được coi là PASS nếu bỏ qua bước này.
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
Cạm bẫy đã dính:
orca repo add <path>positional →Unknown command. Repo chưa có commit → Orca không nhận.repo addkhông làm workspace hiện trong app — phảiworktree create.
Cổng verify — chạy NGAY sau lệnh trên, đây không phải bước phụ:
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}')
"
Assertion FAIL ở đây → DỪNG, không được tiếp tục báo PASS cho user — quay lại sửa lệnh worktree create (thường do --repo sai định dạng selector hoặc repo add chưa commit) rồi verify lại. Đây chính là gate cấu trúc thay cho việc "phải nhớ tự giác dựng workspace mỗi lần" — nhớ tay đã fail 2 lần liền (2026-07-21, 2026-07-24), giờ ép bằng assert.
6. Layout đúng chuẩn dot: không tồn tại llmwiki/ hay harness/ trần ở gốc dự án UAT — chỉ .llmwiki/ · .harness/ (AP-7 layout hallucination).
4. PHA 2 — main-URL smoke, NGAY SAU merge (canary KHÔNG thay được bước này)
Pha 1 chạy với ref khác và ba biến override → nó mù với chính các giá trị mặc định. Mà mặc định là chỗ chứa chuỗi nhánh hardcode (bootstrap.sh BASE=, install.sh REPO_RAW=, SKILLS_REF=). Chỉ pha này kiểm được đúng cái lệnh người mới thật sự gõ.
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
CHỜ CDN PROPAGATE TRƯỚC KHI ĐO — bắt buộc. raw.githubusercontent.com không phục vụ bản mới ngay sau push, và độ trễ không đồng đều giữa các file: đã đo được cảnh engine (bin/llmwiki-validate.py) đã mới trong khi policy.yaml còn cũ → bản cài ra là một thứ lai, và cổng báo ĐỎ GIẢ. Nguy hiểm hơn: cùng cơ chế đó có thể cho XANH GIẢ nếu file cũ tình cờ vẫn qua được test.
Chốt bằng một sentinel — poll tới khi raw trả đúng nội dung mình vừa đẩy:
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
Rồi mới chạy đúng lệnh trong README — không override một biến nào:
D2=$(mktemp -d) && cd "$D2" && git init -q
curl -fsSL "$RAW/harness/poc-vendor-neutral/bootstrap.sh" | bash
Rút gọn (~1 phút): 3 trụ có mặt · bash .harness/poc-vendor-neutral/test-broad.sh PASS · skill mới reachable trong ~/.claude/skills/.
FAIL → gỡ ngay (bước 5). Cửa sổ rủi ro của nhánh chính thu từ "cả bài UAT dài" xuống "một lần smoke ~1 phút".
5. KHÔNG PASS → gỡ commit khỏi remote NGAY
Chỉ dùng khi PHA 2 đỏ (hoặc ai đó đã lỡ push thẳng, bỏ qua canary). Chọn theo tình huống:
# (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 chứ không --force: nếu trong lúc đó có người đẩy lên, lệnh sẽ từ chối thay vì xoá mất việc của họ.
Gỡ xong: ghi lại vì sao fail vào llmwiki/html/fdk-problem-tree.html (node mới, status: open) — thất bại UAT là dữ liệu, không phải chuyện xấu hổ.
6. PASS → chốt
- Dọn dự án UAT (hoặc giữ lại
--keepnếu cần soi). - Ghi kết quả (số đo thật, không phải lời hứa) vào
wiki/log.md. - Bản trên remote coi như đã được nghiệm thu ở tầng "người mới cài".
Branches
| ID | Kind | Guard | Hành vi | Skip / failure | Rejoin |
|---|---|---|---|---|---|
| B01 | recovery | PHA 1 FAIL | git push origin --delete "$CANARY"; nhánh chính không đụng | — | kết thúc (failed) |
| B02 | recovery | PHA 2 FAIL (hoặc lỡ push thẳng bỏ canary) | nhánh chia sẻ → git revert --no-edit <sha>..HEAD; nhánh riêng chưa ai kéo → git reset --hard "$BASE" + --force-with-lease; ghi node problem-tree | không bao giờ --force trần | kết thúc (failed) |
| B03 | recovery | assert worktree Orca FAIL | DỪNG, sửa --repo selector / commit trước repo add, verify lại | không được báo PASS | W04 mục 5 |
| B04 | user_optional | --keep | giữ dự án UAT để soi thay vì dọn | — | W07 |
| B05 | recovery | bước 0 liệt kê git branch -r thấy còn nhánh uat/ cũ | dọn canary rác của phiên chết giữa chừng | — | W01 |
Validation và stopping
PASS chỉ khi mọi mục checklist bước 3 xanh bằng lệnh (kể cả assert worktree tên $WT_NAME) VÀ smoke pha 2 xanh sau sentinel. Đếm skill/rule LIVE trong dự án vừa cài, không theo trí nhớ. Sentinel poll tối đa 30 × 10s. Không bao giờ push rồi bỏ mặc: UAT chạy trong cùng phiên.
Examples
- Positive: bản thêm skill
record-episode→ checklist "~/.claude/skills/record-episode/SKILL.mdtồn tại + có trongCAPABILITIES.md" → pushuat/260919-1030, curl vớiSKILLS_REF="<owner>/<repo>#uat/260919-1030"→ test-broad PASS, skill reachable,orca worktree listthấyuat-260919-1030→ push nhánh chính, sentinel khớp, smoke không override PASS → xoá canary, ghiwiki/log.md. - Boundary/failure: PHA 2 đo ngay sau push không chờ sentinel → engine mới +
policy.yamlcũ → test-broad 72/74, luật mới không cắn → đây là đỏ GIẢ: chờ sentinel rồi đo lại (74/74). Nếu sau sentinel vẫn đỏ → gỡ commit bằng revert, ghi nodestatus: open.
Origin
- Luồng distill từ
fdk/docs/fresh-install-gate.md(§ "Luồng chuẩn: dựng workspace Orca mới rồi test fresh-install") + yêu cầu của user 2026-07-14: cho phép push trước để test thật, không pass thì gỡ commit khỏi remote. - Hai pha là kết quả của một phản biện của user cùng ngày: "nếu vậy thì nó có test được đúng cái curl của main không?" — không. Canary chứng minh nội dung cài được qua remote; chỉ main-URL smoke chứng minh cái lệnh mặc định chạy được. Kèm phát hiện
install.shhardcode#orcakhông có đường override — thiếu nó thì canary chấm nhầm bản cũ.