Skill: br — dây chuyền sản xuất Ralph (BR-kỹ → frames → loop có harness)
BR = Business Requirements — bản đặc tả yêu cầu đã chốt, nằm ở
. Skill
đặt tên theo artifact trung tâm mà nó sản xuất, không theo động từ. Mọi frame và mọi
clause đều truy ngược về file đó, nên "lỗi sản phẩm ⇔ lỗi BR" (GH#15, 2026-07-03) là
tiền đề của cả dây chuyền chứ không phải khẩu hiệu.
Hub 1-tên: user mô tả phạm vi (interview / compile / slice / run / status), KHÔNG
phải nhớ nhiều lệnh. Nền tảng: council 028 (thin-slice, người-trong-vòng-lặp) +
council 031/032 (loop v1 với 6 điều kiện bắt buộc — winner Taleb mean-rank 1.0).
WHAT
Purpose và context
- Purpose: biến tài liệu yêu cầu thô của user thành sản phẩm có kỷ luật qua dây chuyền BR-kỹ → frames → loop có harness, mọi frame và clause truy ngược về .
- Trigger (when to use):
- User có tài liệu yêu cầu (thô/không cấu trúc) và muốn biến thành sản phẩm có kỷ luật.
- User nói: interview / phỏng vấn yêu cầu / soạn BR / slice frame / chạy frame / trạng thái dây chuyền / ralph.
- Non-goals: không ghi vào ; không viết lại đồ nghề tất định đã build (, , …); không tự commit/merge thay người; không sửa UI tay ngoài ; không hook auto-fire.
Mental model
llmwiki/raw/ → interview (spec-filled S1–S10 + câu hỏi hỏi-bù) → compile (BR.md có clause_id + "Giả định đang gánh" + BR.clauses.json) → slice (frames có depends_on, frame-lint gác) → run (loop-runner 6 phanh, commit per-frame, sổ trace) → qc / find / status (truy ngược lỗi→frame→clause)
. User chỉ mô tả PHẠM VI; hub chọn mode.
Input và output contract
| Field | Required? | Ý nghĩa |
|---|
| In | tài liệu thô trong | có (mode interview) | chỉ đọc; trống → hỏi user bỏ tài liệu vào |
| In | br/interview/NNN-answers.md
đã điền | có (mode compile) | câu trả lời thật của user theo field-id |
| In | phạm vi user mô tả | có | interview · compile · slice · run · find · qc · status · sync · contract · rail |
| In | cờ · · · · | không | nhánh tuỳ chọn (xem Branches) |
| Out | , br/interview/NNN-{questions.html,answers.md}
| mode interview | field có + |
| Out | , , (nếu có UI) | mode compile | check-contract xanh |
| Out | , , | mode slice | frame-lint xanh |
| Out | commit , , | mode run | tóm tắt một dòng + medic |
| Out | br/line-status.{json,html}
+ llmwiki/html/line-status.html
| mode status | tóm tắt 3 dòng |
Rules và capabilities
- RULE-01 (MUST): Người là cổng cuối — KHÔNG tự commit/merge output frame; dry-run + người duyệt (council 028).
- RULE-02 (MUST): Phanh bất khả xâm phạm — không bao giờ nâng max_iter hay tắt guard "để nó xong". Thừa phanh rẻ hơn thiếu phanh (council 031/032).
- RULE-03 (MUST): Prompt-dặn KHÔNG phải lớp an toàn — scope-jail/test-hash là code tất định (loop-runner guard 5/6), không phải lời dặn trong prompt (council 5/5 ghế).
- RULE-04 (MUST): Lens-fill luôn — không trộn với câu trả lời thật; luôn hiện ở bảng "Giả định đang gánh".
- RULE-05 (MUST): Slicer người-trong-vòng-lặp ở đợt đầu — chưa full-auto (lỗi slicer là lỗi tương quan, blast-radius = toàn BR).
- RULE-06 (MUST): File-first, on-demand — mọi artifact là file travel-được; skill gọi tay, KHÔNG hook auto-fire (ADR-004).
- RULE-07 (MUST): HTML sinh ra cho người xem phải giải nghĩa thuật ngữ + in full-path (R16).
- Capabilities: đọc tài liệu thô + codebase; ghi artifact dưới ; chạy tool tất định (lint, loop, monitor) cục bộ; gọi adapter model có tools bó hẹp; tạo issue ở tracker ngoài chỉ sau xác nhận.
Failure boundaries
- trống → clarify: hỏi user bỏ tài liệu vào.
br-fill.py check-contract
ĐỎ → compile failed, sửa BR tới khi xanh.
- đỏ, hoặc working-tree bẩn → blocked (từ chối chạy).
- Frame
tier: compensable/irreversible
chưa có → blocked (TIER-GATE).
- Điều kiện KILL (vd diff-jail cắn ≥2 lần / test-hash cắn 1 lần) → cancelled cả run, không cứu, không nâng phanh.
- Frame đỏ → nhánh dưới (partial), nhánh không liên quan vẫn chạy.
- Kiểm UI không có FILE ẢNH thật + → coi là failed (chưa từng làm).
HOW
Main workflow
| Step | Type | Inputs | Action | Outputs/exit | Failure/next |
|---|
| W01 | judgment | lời user | Chọn mode theo phạm vi user mô tả (hub 1-tên) | mode | mơ hồ → hỏi lại |
| W02 | judgment | + spec-template | Mode 1 interview: bóc raw vào S1–S10, gap-diff, sinh questions.html + answers.md | 2 file NNN | raw trống → clarify; → B01 |
| W03 | deterministic | — | STOP — user mở HTML + điền answers.md | answers đã điền | → B02 |
| W04 | judgment | spec-filled + answers | Mode 2 compile: BR.md + BR.clauses.json (+ DESIGN.md nếu có UI) | BR | — |
| W05 | deterministic | BR | Gate br-fill.py check-contract
| xanh | đỏ → sửa BR, lặp W05 |
| W06 | judgment | BR.md | Mode 3 slice: đề xuất lát cắt, STOP cho user duyệt TỪNG lát, ghi frame theo template | frames + index | — |
| W07 | deterministic | frames | Gác + | lint xanh | đỏ → sửa frame, lặp W07 |
| W08 | effect | frame | Mode 4 run: (tier-gate, lint, tree sạch, loop-runner, commit, run_log_ref, sổ trace) | commit frame + run log | đỏ → chặn nhánh dưới; KILL → vứt run |
| W09 | deterministic | frames + run log + BR | Mode 5 status: build-line-status.py build
+ tóm tắt 3 dòng | line-status.html | lỗi rơi vào assumed → quay W02 vòng sau |
Chi tiết từng bước (nguồn chân lý cho W01–W09 và mọi mode phụ):
Đồ nghề tất định (đã build + selftest — KHÔNG viết lại)
| Tool | Việc | selftest |
|---|
skills/br/assets/spec-template.md
| bộ specs chuẩn S1–S10 (khung tham chiếu mọi project) | — |
skills/br/assets/frame-template.md
| template TẤT ĐỊNH cho mọi frame (frontmatter + 4 section body người-đọc-hiểu) | — |
skills/br/assets/design-template.md
| template thiết kế tái dùng (khung Google design-doc + design system nạp từ ) → copy thành | — |
| gác frame 7 luật (schema · scope · test-first · freshness · DAG · exclusive-scope có lease · content) | |
harness/scripts/loop-runner.py
| loop 6 phanh (max_iter·budget·no_progress·escalate·diff-jail·test-hash) | |
fdk/tools/build-line-status.py
| monitor tất định (frame·run-log·BR → json+html, ) | build-line-status.py selftest
|
Runtime artifacts sống ở
tại gốc project (không phải trong skill):
,
br/interview/NNN-{questions.html,answers.md}
,
,
,
,
,
,
br/line-status.{json,html}
.
Mode 1 —
- Đọc TẤT CẢ file trong (chỉ đọc — luật cấm ghi raw/). Nếu trống → hỏi user bỏ tài liệu vào.
- Đối chiếu bộ specs chuẩn
skills/br/assets/spec-template.md
: bóc thông tin từ raw map vào từng field S1–S10 → ghi . MỖI field ghi (filled|missing|conflict|assumed
) + ().
- Gap-diff: field / → sinh bộ câu hỏi. Mỗi field là MỘT spec rõ (rigor /orca-workflow): câu hỏi nêu field LÀM GÌ + "trả lời thế nào là ĐỦ" (tiêu chí chấp nhận của field), không hỏi mơ hồ. Ghi 2 file (cùng số thứ tự NNN):
br/interview/NNN-questions.html
— xem theo section, có giải nghĩa thuật ngữ + tiêu chí-đủ mỗi field, in full-path (R16), dark-mode. CHỈ hỏi phần thiếu.
br/interview/NNN-answers.md
— block điền theo field-id (vd … khoảng trống), để user gõ câu trả lời.
3b. (T-260714-01, gọi tắt ) — user muốn máy điền phần thiếu thay vì hỏi hết: chạy python3 fdk/tools/br-fill.py fill --root .
(tất định, không model). Tool tra registry defaults (skills/br/assets/defaults.yaml
— bảng 26 điều kiện loop + convention github/spec-kit; project override thắng khi trùng field) và in 2 nhóm:
- ĐỀ XUẤT MÁY ĐIỀN → dán vào , GIỮ NGUYÊN
filled_by: <source>:<refs>
+ từng mục (kỷ luật lens-fill áp cho mọi tầng máy điền).
- CÂU HỎI THẬT (field carve-out — auth/quyền, trust boundary, tiền — và field không có default): chọn ≤5 câu theo Impact × Uncertainty (carve-out đã xếp trước, chiếm suất hỏi trước) đưa vào questions.html; phần vượt trần thì lens-fill (bước 5). Carve-out KHÔNG BAO GIỜ nhận giá trị máy — kể cả registry có entry, kể cả lens.
- STOP — báo user mở HTML xem + điền answers.md.
- Nếu user bật (hoặc nói "cho chuyên gia điền mục chưa chắc"): bốc lens bằng
python3 harness/scripts/council.py roster --case product --json
, điền các field mà user đánh dấu "không chắc" → mỗi field đóng dấu + . KHÔNG trộn với câu trả lời thật của user.
Mode 2 —
- Đọc +
br/interview/*-answers.md
đã điền.
- Sinh : mỗi điều khoản có (kế thừa field-id, vd ) + provenance (). Đầu file là bảng "Giả định đang gánh" liệt kê mọi clause / (fail-fast: nhìn một phát biết đang cược gì).
- Sản phẩm có UI (S7.5): chốt một clause NFR (vd ) TRỎ tới , KHÔNG chép luật design vào BR. Đồng thời copy
skills/br/assets/design-template.md
→ và điền §1/§2/§4/§6/§7 cho sản phẩm (giữ nguyên §3/§5 kế thừa). BR = phải có theme/design; DESIGN.md = design trông ra sao.
- Sinh :
{clause_id: {provenance, assumed: bool, fields: ["S4.2", …]}}
— khai field spec S1–S10 mà clause hiện thực (khoá của checksum hợp đồng); monitor () đọc file này để tô cam clause assumed. Bảng "Giả định đang gánh" nhóm theo nguồn fill ( / / ) để người duyệt thấy đang cược gì từ đâu.
- Gate checksum hợp đồng (vá G1 — council c9dc13d):
python3 fdk/tools/br-fill.py check-contract --root .
— mọi required field S1–S10 phải có clause đối ứng; ĐỎ là compile FAIL, sửa BR cho tới khi xanh, không có chuyện "compile xong mà hợp đồng thủng".
Mode 3 —
- Đọc . ĐỀ XUẤT danh sách lát cắt: mỗi lát = {clause_ids, scope_code dự kiến (≤3 file), scope_test, acceptance_test, depends_on}. STOP cho user duyệt/sửa/gộp/tách TỪNG lát (người-trong-vòng-lặp — chốt chặn lỗi tương quan slicer). Lô đầu ≤ 3–5 frame.
- là bắt buộc nghĩ, không bắt buộc có (GH#75, distill Atomic Task Graph): dây chuyền là ĐỒ THỊ chứ không phải danh sách. Frame B khai khi B không thể xanh nếu A chưa xanh (B gọi API/store/schema A dựng). Không dựa ai → . Khai đúng thì được ba thứ miễn phí ở mode 4: thứ tự chạy tự đúng, frame đỏ tự chặn nhánh dưới, và sửa một frame chỉ phải chạy lại đúng nhánh của nó. Khai bừa (nối cho "có vẻ hợp lý") thì mất song song và tạo chờ giả — chỉ nối khi có phụ thuộc THẬT.
- Ghi
br/frames/frame-NNN-<slug>.md
THEO TEMPLATE skills/br/assets/frame-template.md
(schema v0 + 5 section body bắt buộc: Nghiệp vụ · Input/Output · Spec (FR/SC) · Tiêu chí nghiệm thu · Ngoài phạm vi — viết cho NGƯỜI VỀ SAU đọc-hiểu, frame-lint R7 gác cứng: frame_id phải có slug nghiệp vụ, muc_tieu không được generic, mỗi frame có FR-id/SC-id rõ, section không được rỗng) + parent_br_hash = sha256(br/BR.md)
.
- Sinh registry (bảng frame_id · clause_ids · scope_code · status · run_log_ref) từ frontmatter các frame.
- Gác:
python3 fdk/tools/frame-lint.py check br/frames --root .
— xanh hết mới coi là slice xong (gồm R6 exclusive-scope: 2 frame chưa xong không được giẫm cùng file).
- R6 là HỢP ĐỒNG THUÊ, không phải sổ đỏ: frame đã xong (run-log ) nhả quyền giữ file. Nhờ vậy frame mới được nối vào điểm khởi động (, , …) mà frame cũ từng giữ — không có lease thì code mới xanh mà không tới tay người dùng (walleye 2026-09-10: 4 frame xanh, người chơi không thấy gì). Hai frame chưa xong cùng giữ một file vẫn ĐỎ — trừ khi đã xếp chúng nối tiếp (frame này là tổ tiên của frame kia): khi đó chúng không bao giờ cùng sửa một lúc, và nhiều tính năng lần lượt nối vào cùng một được. trao file cho người thuê sau cùng (hậu duệ).
- Mỗi tính năng phải có một lát NỐI DÂY tới điểm khởi động, và frame UI phải nghiệm thu bằng test đi qua giao diện thật (e2e/route-shots), không bằng unit test của module đứng riêng.
python3 fdk/tools/br-prompts.py sync
— bổ sung mục prompt cho các frame mới vào sổ (user sửa tay được ngay).
Mode 4 —
Cách gọi tất định (đã wire — mặc định IN-PLACE, MỘT cây làm việc duy nhất):
python3 fdk/tools/br-run.py run br/frames/<frame>.md --root .
# xem prompt sẽ gửi mà không chạy: thêm --print-prompt
# chạy cả hàng đợi resumable: python3 fdk/tools/br-queue.py run --queue br/queue.yaml
# sửa 1 frame → chạy lại ĐÚNG nhánh của nó, không cả dây chuyền:
# python3 fdk/tools/br-queue.py affected <frame_id> --queue br/queue.yaml --reset
# python3 fdk/tools/br-queue.py run --queue br/queue.yaml
# cần cô lập thư mục thật sự (hiếm): thêm --worktree
Queue chạy theo ĐỒ THỊ, không theo thứ tự gõ tay (GH#75 — distill Atomic Task Graph, arXiv 2607.01942):
- Topo order: đọc của từng frame và tự sắp thứ tự (không phụ thuộc thì giữ nguyên thứ tự khai — queue cũ chạy y như trước).
- Frame đỏ CHẶN cả nhánh dưới nó (, ghi ): input của nhánh đó đã hỏng, chạy tiếp chỉ tốn lượt model cho một cái chắc chắn fail. Nhánh KHÔNG liên quan vẫn chạy bình thường — một frame hỏng không còn đứng chặn cả dây chuyền.
- Sửa cục bộ (): sửa một frame → chỉ frame đó + nhánh dưới nó về , mọi frame xanh khác giữ nguyên. Đây là chỗ trả tiền: trước đây một frame hỏng giữa chuỗi buộc chạy lại nhiều hơn mức cần.
- KHÔNG làm subgraph-reuse cache (paper có, ta bỏ có chủ ý): đã bỏ qua frame xanh rồi; thêm cache theo hash ở quy mô vài chục frame chỉ đẻ bug stale. Giới hạn của chính paper cũng áp ở đây — lợi ích còn nguyên khi đồ thị nhỏ và acceptance_test tất định; graph rất lớn hoặc test/tool trả kết quả bấp bênh thì lịch chạy này mất tác dụng.
tự: (0) TIER-GATE — frame khai
tier: compensable|irreversible
trong frontmatter (effect không-đảo: gọi API ngoài/gửi mail/ghi DB) bị CHẶN cho tới khi người chạy lại với (SHEPHERD gate-before-materialize), (1) frame-lint, (2) kiểm working-tree sạch, (3) chạy loop-runner NGAY TRONG cây hiện tại với revise đã trỏ tới (render skills/br/assets/revise-prompt.md
→ tools bó hẹp), (4) frame xanh → commit vào nhánh hiện tại, message gắn frame_id (mốc revert/blame), (5) in tóm tắt 1 dòng, (6) ghi vào frame, (7) ghi mốc vào SỔ TRACE (checkpoint-trace/SHEPHERD — record trỏ commit sẵn, không double-commit) → xem cả dây chuyền: python3 fdk/tools/checkpoint.py list
; rollback cả pipeline về frame bất kỳ theo TÊN: checkpoint.py rollback <frame_id>
(giữ lịch sử, cảnh báo effect tier không-đảo sau mốc).
Vì sao in-place là mặc định (feedback user 05/07): N frame = N worktree = N folder ma không ai lục; luồng người thường là bật app lên xem, sửa phải hiện ngay trong cây đang chạy app. Kiểm soát không-phạm-scope nằm ở HARNESS (diff-jail revert mỗi vòng + FINAL SCOPE SWEEP + test-hash + scope_clean + R6 exclusive-scope), không nằm ở cô lập thư mục. Không ưng kết quả → git revert <commit frame>
.
Bước thủ công tương đương (nếu cần tinh chỉnh tay):
- frame đó xanh. Kiểm working-tree SẠCH ( rỗng) — bẩn thì từ chối.
- Tạo git worktree riêng từ của frame (jail filesystem — frame đầu KHÔNG đụng harness/hook/CI).
- Chạy loop — commit per-frame VÀO WORKTREE BRANCH (không phải main); người vẫn merge tay:
python3 harness/scripts/loop-runner.py run \
--verify "<acceptance_test>" --revise "<adapter claude -p>" \
--state "<scope_code>" --scope "<scope_code>" --protect "<scope_test>" \
--baseline "<baseline_ref>" --commit-on-success \
--commit-message "frame(<frame_id>): <muc_tieu> [<clause_ids>]" \
--max-iter <g> --budget-seconds <g> --no-progress-k <g> --escalate-after <g> \
--log br/frames/<id>.run.json --cwd <worktree>
Adapter revise = với tools bó hẹp (--allowedTools Edit,Write,Read,Grep,Glob
— KHÔNG Bash tự do, không network), prompt dựng từ + + output verify fail gần nhất.
Truy vết frame→code (điều kiện tiên quyết — chắc frame tạo code nào + không phạm scope):
- Run-log ghi = file THẬT frame đã đổi (≠ scope dự định); =true khi mọi file ⊆ ; = file frame ĐỊNH ghi ngoài scope nhưng đã bị revert.
- FINAL SCOPE SWEEP: mọi lần loop dừng (kể cả PROTECT_VIOLATION), loop-runner revert sạch mọi file ngoài → worktree LUÔN scope-clean, LUÔN ⊆ . Kiểm soát hoàn toàn, không chỉ dựa lời dặn prompt.
- chỉ commit khi verdict=SUCCESS VÀ scope_clean; message gắn → truy đúng frame. = commit gốc để tính diff (thường HEAD của worktree lúc bắt đầu).
Prompt & queue (chạy nhiều frame, resume được)
- 📒 SỔ PROMPT TỔNG (nguồn ƯU TIÊN #1, user sửa tay không cần model): — MỘT file, mỗi frame một mục . Mở ra, tìm frame, sửa/THÊM nội dung thoải mái (placeholder giữ nguyên để máy điền lúc chạy).
python3 fdk/tools/br-prompts.py sync
tự thêm mục cho frame mới, KHÔNG BAO GIỜ đè mục đã sửa tay (chạy sau mỗi ); / để tra. Thứ tự ưu tiên khi run: sổ prompt > queue inline > prompt_file > template mặc định.
- Template gốc (seed cho sổ):
skills/br/assets/revise-prompt.md
. Adapter render nguồn thắng cuộc → gọi (tools bó hẹp). Xem prompt sẽ gửi: br-revise.py run --frame <f> --verify "<cmd>" --print
.
- File QUEUE (, mẫu
skills/br/assets/queue.example.yaml
): DANH SÁCH frame để chạy tuần tự; mỗi mục hoặc (trỏ file template) HOẶC (ghi text trực tiếp, vẫn hỗ trợ placeholder). Driver fdk/tools/br-queue.py run --queue br/queue.yaml
chạy từng mục, ghi back sau MỖI mục → chạy lại queue = bỏ qua , chạy tiếp (resume). xem mỗi mục dùng prompt nào; xem trạng thái.
- Adapter là ranh giới (BNAL) — template + queue + render đều tất định + selftest (, ).
- Điều kiện KILL viết TRƯỚC (via negativa): vd diff-jail cắn ≥2 lần / test-hash cắn 1 lần → vứt cả run, không cứu, không nâng phanh.
- Loop dừng → in tóm tắt MỘT DÒNG (verdict · số vòng · diffstat · lệnh verify cuối) + chạy
python3 fdk/tools/medic.py --ci
. STOP — người đọc diff trong worktree, tự quyết merge/chạy-tiếp/vứt.
- Điền + vào frame + registry. Ghi lesson (kể cả điều KHÔNG xảy ra — phanh nào chưa từng cắn).
Mode 4b — /br find <file-hoặc-từ-khoá>
— trỏ vào chỗ lỗi, ra frame/prompt phụ trách
Luồng người thường: bật app → thấy lỗi → trỏ vào khúc đó:
python3 fdk/tools/br-find.py "src/auth/login.py" # theo file
python3 fdk/tools/br-find.py "đăng nhập" # theo từ khoá / clause
In ra: frame phụ trách (khớp theo file THẬT đã đổi > scope_code > từ khoá) · điều khoản BR ·
prompt nằm ở đâu (inline trong queue.yaml / prompt_file / template mặc định) · lệnh chạy lại ĐÚNG frame đó sau khi sửa prompt. Hoạt động được vì R6 exclusive-scope (frame-lint) ép mỗi file chỉ thuộc MỘT frame tại một thời điểm — file đã đổi chủ qua lease thì frame phụ trách là người thuê sau cùng (
), còn lịch sử thì
theo commit
.
Mode 4c — — audit senior QC (LLM 4-mục) trên mockup/frame
Sau khi có mockup/frame xanh, chạy CẶP MẮT SENIOR (đắt=LLM, gọi tay — phần rẻ tất định đã tự chạy sau mỗi
):
- Code: gọi (Skill tool → ) soi diff frame — 4 mục security/performance/naming/logic + sinh test tái hiện .
- UI/UX: gọi (Skill tool → ) audit mockup — 4 mục a11y/hierarchy/consistency/antipattern; phần đo được (contrast/tap-target/overlap/misalign/missing-label) chạy qua engine visual-qa headless:
node skills/visual-qa/assets/route-shots.mjs <base-url-mockup> --audit
- Verdict CẦN SỬA → sửa trước khi chốt frame. Advisory — verdict LLM là ý kiến; số đo engine + test mới gác cứng (đã tự chạy ở và bước 3b). Không có frame chỉ định → audit các route mockup hiện có.
Mode 5 —
python3 fdk/tools/build-line-status.py build --root .
→ + llmwiki/html/line-status.html
.
- In tóm tắt 3 dòng (tổng quan trạng thái · frame đáng chú ý nhất · đường dẫn HTML). Trang cho truy ngược lỗi→frame→clause; clause assumed tô cam → quay vòng sau nếu lỗi rơi vào assumed.
Mode 6 — (frame ↔ GitHub sub-issue)
Distill
: mỗi frame → một GitHub sub-issue để team thấy tiến độ + hand-off. KHÔNG làm lại provenance (clause_id/manifest đã sâu hơn ccpm); chỉ lớp đồng bộ mỏng trên
.
python3 fdk/tools/br-sync.py sync br/frames --root . --repo <owner/repo> --dry-run
— LUÔN dry-run trước (không gọi gh, không ghi mapping).
- Bỏ khi chắc → tạo sub-issue cho frame chưa map, ghi
br/frames/issue-mapping.json
. Idempotent. tạo epic-issue. / có sẵn.
- Tạo issue là hành động ra-ngoài repo → xác nhận trước khi bỏ .
Mode 7 — (UI Contract chốt thực hiện, md+html)
Tách trục CODE (frame) khỏi trục HIỂN THỊ (screen/route). Chuẩn mọi dự án:
- Frame khai
ui_role: none|screen|panel|widget|form|action
+ (frame-template) + section "## UI hoạt động ra sao".
- : + (id·title·route thật·frames[]). Sửa file này gom màn KHÔNG đụng frame.
python3 fdk/tools/br-contract.py build br/frames --layout br/ui-layout.yaml --root .
→ + . Bảng frame·làm gì·ui_role·màn·route·clause·acceptance + đếm frame/màn/route + cảnh báo lệch. có sẵn.
Branches
| ID | Kind | Guard | Hành vi | Skip / failure | Rejoin |
|---|
| B01 | user_optional | (gọi tắt ) ở interview | đề xuất máy điền () + ≤5 câu hỏi thật theo Impact × Uncertainty; carve-out không nhận giá trị máy | vượt trần → lens-fill | W03 |
| B02 | user_optional | hoặc "cho chuyên gia điền mục chưa chắc" | council.py roster --case product --json
, điền field missing user đánh dấu không chắc, đóng dấu + | không trộn với câu trả lời thật | W04 |
| B03 | conditional_required | sản phẩm có UI (S7.5) | clause NFR trỏ ; copy → , điền §1/§2/§4/§6/§7 | không có UI → skip | W05 |
| B04 | conditional_required | frame có khác none | trước khi code; là visual-qa | thiếu mục ## Thiết kế (design-twice)
→ frame chưa xong | W08 |
| B05 | conditional_required | frame khai hoặc | chặn cho tới khi chạy lại với | không ack → không chạy | W08 |
| B06 | capability_optional | cần cô lập thư mục thật sự (hiếm) | thêm , hoặc bước thủ công tương đương (worktree riêng từ ) | — | W08 |
| B07 | user_optional | chạy nhiều frame / sửa một frame | br-queue.py run --queue br/queue.yaml
theo topo order; affected <frame_id> --reset
chạy lại đúng nhánh | frame đỏ chặn nhánh dưới | W09 |
| B08 | user_optional | /br find <file-hoặc-từ-khoá>
| Mode 4b: in frame phụ trách + clause + chỗ prompt + lệnh chạy lại | — | W08 (sửa prompt rồi chạy lại) |
| B09 | user_optional | | Mode 4c: + (advisory); verdict CẦN SỬA → sửa trước khi chốt frame | — | W08 |
| B10 | user_optional | | Mode 6: br-sync.py sync --dry-run
trước, xác nhận rồi mới bỏ | không xác nhận → dừng ở dry-run | kết thúc |
| B11 | user_optional | | Mode 7: → + | — | kết thúc |
| B12 | user_optional | hoặc | tắt gate MỀM (R7 content · visual-qa UI · assumption-gate) qua ; phanh cứng giữ nguyên | skip tự bật lại sau 1 lần | W08 |
Validation và stopping
Tất định:
(7 luật),
br-fill.py check-contract
, loop-runner 6 phanh + FINAL SCOPE SWEEP,
sau mỗi loop, selftest của từng tool (
,
,
build-line-status.py selftest
,
,
). Cần review (người là cổng cuối): duyệt answers, duyệt TỪNG lát ở slice, đọc diff frame rồi tự quyết merge/chạy-tiếp/vứt. Mode dừng ở các điểm
STOP đã ghi; loop dừng theo phanh, không nâng phanh.
Examples
- Positive: user bỏ 3 file yêu cầu vào , nói "soạn BR" → interview sinh
br/interview/001-questions.html
hỏi 7 field thiếu → user điền → compile ra + check-contract xanh → slice đề xuất 4 lát, user duyệt → frame-lint.py check br/frames --root .
xanh → br-run.py run br/frames/frame-001-dang-nhap.md --root .
→ SUCCESS, commit frame(frame-001-dang-nhap): …
, tóm tắt một dòng + medic.
- Boundary/failure: khi không rỗng → từ chối chạy. Frame gửi mail khai → TIER-GATE chặn tới khi . Frame-003 đỏ → frame-004 () thành , frame-005 không liên quan vẫn chạy.
Reference — Stream UI thống nhất + Spec rõ ràng (distill hallmark + orca-workflow)
Hai lỗ hổng của dây chuyền nhiều-frame, vá bằng hai cơ chế distill:
A. UI/UX thống nhất XUYÊN FRAME (distill ). Mỗi frame chạy độc lập qua loop-runner → dễ mỗi frame một kiểu, mockup chắp vá. Cơ chế hallmark:
= HỆ TOKEN KHOÁ, đọc trước và đè mọi lựa chọn;
luật ĐẢO NGƯỢC — frame phải GIỐNG nhau (chia sẻ hệ), không đa dạng hoá;
locked-token — mọi màu/font/spacing trỏ token có tên (
), CẤM inline. Áp vào /br:
- copy → với bảng token §4 + §4.1 (token khoá) là NGUỒN CHÂN LÝ.
- Frame UI ở chỉ dùng token DESIGN.md; thêm giá trị mới = thêm token vào DESIGN.md rồi trỏ, không tự chế tại frame.
- Drift token bị (consistency) + engine visual-qa bắt → "stream" chạy bằng gate tất định, không bằng lời dặn. Kết quả: frame-01 và frame-09 trông như MỘT sản phẩm.
B. Mọi frame + màn interview cần SPEC rõ (học ). Propose sinh FR/SC id-bền + acceptance; /br mang rigor đó xuống mọi mắt xích:
- Frame: frame-template có section (frame-lint R7 gác cứng) — mỗi frame khai FR-id (hệ thống PHẢI…) + SC-id (đo NGƯỜI), acceptance_test là bằng chứng của SC, mỗi dòng nghiệm thu truy về một FR. Frame thiếu FR/SC rõ = chưa xong, y như interview thiếu field.
- Interview: mỗi field S1–S10 hỏi ra phải là MỘT spec rõ — câu hỏi nêu field làm gì + tiêu chí "trả lời thế nào là ĐỦ" (không hỏi mơ hồ). Field còn = spec chưa đủ, hiện ở bảng "Giả định đang gánh" để không trôi.
Reference — Vòng THIẾT KẾ cho frame UI (skill ) — chạy TRƯỚC khi code
Bắt buộc với mọi frame : trước khi viết một dòng code UI, chạy
để có
2–3 phương án KHÁC NHAU TẬN GỐC, so sánh, chọn 1, rồi ghi mục
## Thiết kế (design-twice)
vào chính frame (3 phương án + phương án chọn + lý do + ý ghép từ bản thua).
- Vì sao: ý đầu tiên là ý dễ nghĩ nhất, không phải ý tốt nhất. Không có phương án để so thì
agent chỉ biết vá phương án duy nhất — mỗi vòng vá đẻ một lỗi mới (theme Lume: 5 vòng vá).
- Tín hiệu phải quay lại vòng thiết kế: đang vá lần thứ 2 cho cùng một chỗ, hoặc user chê
"xấu/bựa/không ưng" mà không chỉ ra lỗi cụ thể ⇒ hình dạng sai, vá thêm chỉ tốn công.
- Frame UI thiếu mục
## Thiết kế (design-twice)
= chưa xong (R7 content coi là thiếu nội dung).
- Thiết kế xong mới tới → code → gate bên dưới.
Reference — Vòng tự-kiểm THỊ GIÁC cho frame UI (skill )
Nguyên tắc (không phải luật re-check riêng): sửa UI = edit spec + code CỦA FRAME đó rồi
lại. Muốn "chạy lại frame là tự re-verify UI" thì
của frame UI
phải CHÍNH LÀ visual-qa — test hit vào UI, không phải unit-test cạnh bên. Đây là điểm đòn bẩy
cao: không cần dặn agent "nhớ kiểm lại", vì loop verify của chính frame đã ôm luôn cái kiểm UI.
- Frame đặt:
acceptance_test: "node skills/visual-qa/assets/route-shots.mjs --base <url> --route <route> --assert --user <u> --pass <p> --out <dir>"
→ exit 1 nếu route không 200/rỗng → frame ĐỎ tới khi UI render đúng. lại sau mỗi sửa = tự re-verify.
- Sau green, agent ảnh (skill ) → (route·mức·cách sửa) → mỗi finding là một sửa spec+code của frame → lại. Bắt lỗi theme/brand/empty-state unit-test không thấy; dùng khi extension MCP lỗi localhost.
- Cấm sửa UI tay ngoài (nợ p28 05/07): sửa tay = mất gate + không re-verify. Mọi sửa UI phải qua frame.
Reference — Rail — công tắc harness của dây chuyền ()
"On the rails" = đang trong tầm gác. MẶC ĐỊNH ON — auto-on cả phiên khi dùng /br.
python3 fdk/tools/br-rail.py status --root .
→ trạng thái + LUÔN nhắc bật/tắt được.
- (gác đủ) · (tắt gate MỀM tới khi bật lại) · (bỏ gate mềm ĐÚNG 1 lần rồi tự bật lại + cảnh báo).
- AN TOÀN (bất biến): off/skip CHỈ tắt gate mềm — R7 content · visual-qa UI · assumption-gate. PHANH CỨNG (diff-jail · test-hash · scope-clean) + cấu trúc R1–R6 + hook bảo mật KHÔNG BAO GIỜ tắt qua đây.
- Wiring: đầu , đọc → nếu off/skip thì gọi (bỏ R7+assumption), loop-runner phanh cứng vẫn nguyên; IN trạng thái Rail + dòng nhắc mỗi lần chạy.
Reference — Bằng chứng > lời (luật kiểm UI — feedback 2026-07-13)
Kiểm UI/thị giác chỉ hợp lệ khi có
FILE ẢNH thật + liệt kê route→ảnh→bytes (skill
).
Không có file = CHƯA TỪNG LÀM = fail, bất kể model nói gì. Sau khi sửa UI phải chụp LẠI + đọc lại trước khi green. Cấm "cho pass" bằng suy luận. (Cũng là luật /fdk.)