Bắt đầu nhanh, tham chiếu sản phẩm và Git profile có bản tiếng Việt. Các tài liệu còn lại dùng tiếng Anh.
Git profile
English | Tiếng Việt
Đúng một cái núm trong .babysit/git-flow.yaml quyết định việc đã xong thì đi
đâu, và QA test gắt tới mức nào:
profile: startup # pet | startup | enterprise
base_branch: mainsetup-project hỏi đúng một câu — ở repo này, sai một lần thì trả giá bao
nhiêu? — rồi ghi lại câu trả lời. Mọi thứ còn lại suy ra từ đó. Xem mình đang
có gì:
bbs autopilot git-flow # in ra BBS_PROFILE / BBS_MODE / BBS_LAND / BBS_RIGOR / …pet | startup | enterprise | |
|---|---|---|---|
| branch sống lâu | main | develop + main | develop + staging + main |
| việc về đích kiểu gì | push chính là release | PR vào develop, tác giả tự merge | PR vào develop, người khác merge |
| review diễn ra ở đâu | không ở đâu cả | tại máy, trong browser | tại máy và trên GitHub |
| độ gắt QA | smoke, 3–5 ca | standard, 5–10 | strict, 8–12 |
Độ gắt chỉ nới bề rộng. PASS mang đúng một nghĩa ở cả ba: mọi chiều rubric
áp dụng được phải từ B trở lên, và phải có một lượt chạy end-to-end mới tinh
trên đúng code cuối cùng. Dự án nghiệp dư chạy ít ca hơn — chứ không bao giờ
chạy không ca nào.
Một repo không có key profile: sẽ tự suy ra pet. Chưa ai cấu hình nó,
nên nó xử sự y như git trần. Key mode:/land:/push:/ticket_branch: viết
tay vẫn thắng preset, nên một config viết từ hồi chưa có profile vẫn giữ nguyên
hình dạng nó đã ghi ra.
Thứ không profile nào đụng tới: bạn làm việc ở đâu
2build làm việc ngay trên cái branch bạn đang đứng. Không profile nào cắt
branch, không profile nào lôi bạn vào worktree — bbs autopilot git-flow in ra
BBS_MODE='trunk' ở cả ba. Đây là chủ ý: một công cụ lặng lẽ dời việc của bạn
đi chỗ khác là công cụ bạn không quản được. Nên sự cô lập là thứ từng lần chạy
phải xin, không bao giờ là thứ file config tự bật sau lưng bạn:
bbs ticket ensure --slug-hint thing --mode=worktree # ticket này có checkout riêng
bbs ticket ensure --slug-hint thing --mode=branch # …hoặc cắt branch feat/… tại chỗ
/bbs:foreman # chạy lô: foreman tạo worktree cho mỗi ticketRepo nào lúc nào cũng muốn một trong hai hình dạng đó thì viết tay
mode: worktree (hoặc mode: branch) — xem Chạy nhiều ticket song
song. Phần còn lại của tài liệu này nói về những
thứ profile thật sự quyết định.
Cái bẫy: danh tính ticket nằm trong một biến môi trường
Không có branch cho ticket thì trong git chẳng có gì nhận diện nó. Danh tính
chuyển sang BABYSIT_TICKET, do ensure in ra cho bạn:
export BABYSIT_TICKET=bs-xxxxxxxxMất biến đó — shell mới, pane mới, một session crash rồi quay lại — thì hook
pre-push không resolve ra ticket nào và bỏ qua. Verdict qa và review-pr thôi
không còn chặn gì nữa, một cách âm thầm. Ở pet, nghĩa là một cú push không ai
gác, mà push chính là release. Khôi phục bằng:
bbs ticket session list
bbs ticket session attach <id> # in lại dòng exportCấu trúc branch mà mỗi profile giả định
base_branch là thứ duy nhất profile không suy ra hộ bạn được — nó phụ thuộc
vào cách bạn release. Đặt nó khớp với hình dưới đây.
pet — một branch
main ────────●────●────●──────► push chính là releaseprofile: pet
base_branch: maincreate-pr cố tình BLOCK ở đây (land: none); không có bước PR nào cả, nên
verdict qa + review-pr là thứ duy nhất đứng giữa một session và code đã phát
hành của bạn.
startup — develop để tích hợp, main để release
feat/bs-a ──┐
feat/bs-b ──┼──► develop ───────────────► PR về đích ở đây
feat/bs-c ──┘ │
└──► main ──► release có tagprofile: startup
base_branch: developSao không dùng thẳng main: với base_branch: main, merge PR chính là ship,
nên QA ở máy bạn là cửa duy nhất từng chạy. develop tách đã tích hợp khỏi
đã ship thành hai sự kiện — một merge tồi bị giữ lại trên nhánh tích hợp, và
main luôn ở trạng thái release được.
Nếu bạn deploy mỗi lần merge và không có thời điểm release riêng thì develop
chỉ là thủ tục — đặt base_branch: main và biết rằng bạn vừa biến QA ở máy
thành cửa cuối cùng.
enterprise — thêm một môi trường tích hợp được deploy ở giữa
feat/bs-a ──┐
feat/bs-b ──┼──► develop ──► staging ──► main
feat/bs-c ──┘ (CI) (QA đã deploy) (release có tag)profile: enterprise
base_branch: developHai chỗ review trả lời hai câu khác nhau: lượt xem trong browser ở máy là QA
sản phẩm (nó có chạy không?), PR trên GitHub vào develop là review code
do người khác làm. staging là nơi kết quả đã tích hợp được QA như một bản
deploy chứ không phải như một checkout. QA strict còn trả luôn surface trước
khi bàn giao — bằng chứng đi trong body của PR, nên đừng mong app còn chạy sau
một lượt QA (smoke/standard để app chạy và đưa bạn URL).
Việc đẩy lên bậc trên không bao giờ là việc của babysit
create-pr nhắm vào base_branch rồi dừng — cùng lý do nó không bao giờ merge.
Mọi bước đẩy lên phía trên là của bạn hoặc của CI:
git switch main && git merge --ff-only develop && git tag v1.2.0 && git push --follow-tagsNgoại lệ duy nhất là hotfix, fork thẳng từ production thay vì xếp hàng sau mọi
thứ đang nằm trên develop:
BBS_BASE_BRANCH=main bbs ticket ensure --slug-hint hotfix-thing --type hotfix --mode=branch
# …fix, QA, PR vào main, rồi merge ngược main về developKhi đã có branch: base ở máy là bề mặt test, không phải base của git
Luật này chỉ có tác dụng khi đã thật sự cắt cái gì đó — một ticket
--mode=branch, hoặc một lô chạy worktree.
Mọi thao tác ghi lên branch ticket đều tham chiếu origin/<base>, không bao giờ
là <base> ở máy. Merge base ở máy vào branch ticket sẽ kéo mọi thứ đang dang
dở khác vào PR của ticket đó. Muốn lấy thay đổi mới từ upstream thì dùng:
bbs ticket refresh # fetch + merge origin/<base>; BLOCK nếu cây bẩn hoặc conflictChỉ bốn lệnh động vào base ở máy — surface compose, surface revert,
serve, land — và không lệnh nào ghi lên branch ticket.
ensure fetch origin/<base> trước khi cắt, rồi fork từ ref remote-tracking
với --no-track. Hai đường lui, theo thứ tự:
- Không có ref
origin/<base>nào cả (không có remote, hoặc remote không có branch đó) → fork từ<base>ở máy. - Fetch lỗi nhưng ref có sẵn → fork từ
origin/<base>biết-lần-cuối và nói rõ:ensure: warning — fetch failed, using the last-known origin/<base>.
bbs ticket board khép phần chân bằng vị trí thật của base ở máy, để độ lệch
hiện ra trước khi nó cắn:
BASE: main — 3 ahead / 12 behind origin/main (last fetch 2h ago)
↑ 3 commit(s) on local 'main' are not on origin/main — mode: trunk cuts no branch, so tickets build on them; they are only unpushed
↓ origin/main moved on — bbs ticket refresh (in a ticket) or bbs ticket surface revert (on the primary)Nó không bao giờ chặn — base lệch giữa chừng là chuyện bình thường. Board không
fetch, nên các con số mới tới mức tuổi ghi trong ngoặc. Dòng ↑ tự đổi lời theo
mode: khi không cắt gì, ticket dựng trên mấy commit đó và điều duy nhất chưa
ổn là chúng chưa được push; còn khi ticket đã cắt từ origin/<base> thì chúng
vô hình với mọi ticket cắt sau đó.
Chạy nhiều ticket song song
Nhiều session trong một checkout nghĩa là mọi thay đổi đan vào nhau trên một
branch: bạn không thể review riêng một ticket, không thể bỏ một cái tồi, không
thể quy trách nhiệm cho một regression. Worktree mua đúng sự tách bạch đó, và
trả giá bằng một commit + bbs ticket surface compose cho mỗi vòng test thay
vì sửa-rồi-refresh. Chính vì cái giá đó mà không ai tự bật nó cho bạn.
Luật duy nhất
Checkout chính ở nguyên trên
base_branch. Đó là bề mặt test dùng chung —node_modulesvà dev server sống ở đó. Nếu một branch ticket chiếm chỗ đó thì không gộp được gì ở đó nữa.
serve thực thi luật này chứ không đoán:
STATUS: BLOCKED
REASON: primary checkout … is on 'feat/…', not base 'main'.Cách A — để foreman giao việc hộ
Cần Orca với orchestration được bật. Đưa cho nó một dự án lớn; nó tách requirement cha thành Task DAG, tạo worktree cho từng ticket, rồi giám sát các Dispatch autopilot plan và build/QA — nên nó chạy y hệt nhau trên mọi repo, đã cấu hình hay chưa:
/bbs:foremanNó gác design trước khi có code, tuần tự hóa QA surface dùng chung, chạy QA tích hợp cho các ticket tương tác, rồi áp dụng finish policy theo thứ tự phụ thuộc.
Cách B — tự giao việc
OUT=$(bbs ticket ensure --slug-hint <slug> --mode=worktree) # mỗi ticket một worktree
cd "$(printf '%s\n' "$OUT" | sed -n 's/^WORKTREE=//p')" # vào đúng path nó in ra
/bbs:autopilot "<yêu cầu>"Repo nào lần nào cũng làm kiểu này thì khỏi gõ flag nữa:
profile: startup
base_branch: develop
mode: worktree # mỗi ticket có checkout riêng
land: local # …và lô đã xong được gộp ở máy trước khi mở PRland: local là thứ biến điểm-chốt QA-người-gộp thành bàn giao mặc định. Nó chỉ
có nghĩa khi đi kèm mode: worktree: một branch cắt tại chỗ kéo checkout chính
rời base_branch, và mọi lần gộp sau đó sẽ BLOCK.
Rồi: gộp, review, ship
bbs ticket board # trạng thái: ticket, verdict, PR, ai đang giữ lease
bbs ticket serve # gộp TẤT CẢ ticket đã xong (qa + review-pr DONE) lên base ở máy
bbs ticket serve <t1> <t2> # …hoặc đúng những cái bạn gọi tên
bbs ticket serve --release # trả lại surfaceserve <t1> <t2> gộp đúng những ticket bạn gọi tên — phần thưởng là bạn có thể
bỏ một cái tồi ra ngoài mà mỗi cái còn lại vẫn về đích bằng PR sạch của nó.
| lệnh | làm gì |
|---|---|
bbs ticket surface compose | chạy trần từ worktree — đưa ticket đó lên bề mặt dùng chung để QA |
bbs ticket surface compose <t…> | chạy từ checkout chính — reset về base rồi merge đúng các ticket được gọi tên |
bbs ticket surface revert | sau khi PR merge trên remote, kéo base ở máy về đúng origin/<base> |
bbs ticket surface clear --ticket <t> --head <sha> | chỉ để khôi phục: xóa một marker cũ sau khi ticket đã được giữ lại; chứng minh ticket đã done và head đã review nằm trên base, không đổi cây làm việc |
bbs ticket surface acquire | mỗi lúc một phiên QA trên bề mặt dùng chung; người khác BLOCK kèm tên chủ lease |
bbs ticket land <t…> | merge các ticket đã xong vào base ở máy và giữ merge đó (xem dưới) |
Tất cả đều từ chối lớn tiếng chứ không làm mất việc.
surface clear không thay thế surface revert. Chỉ dùng nó khi ticket đã được
giữ lại trên base ở máy nhưng marker cũ còn sót, còn revert sẽ làm mất commit hợp
lệ. Lệnh giữ surface lease và sẽ BLOCK nếu marker không gọi tên đúng một ticket
đã hoàn tất hoặc <sha> không phải ancestor của base hiện tại. Mọi thay đổi
tracked và untracked được giữ nguyên.
Vòng QA khi ticket sống trong worktree
- Code và commit trong worktree.
bbs ticket surface composetừ worktree.- QA thấy lỗi → sửa trong worktree, commit, chạy lại
surface compose. Đừng bao giờ sửa trong checkout base — QA phải test một trạng thái ticket đã commit. - Push branch ticket;
create-prnhắm vàobase_branch. - Sau khi PR merge:
bbs ticket surface reverttừ checkout chính; các worktree còn dang dở chạy lạisurface compose.
finish — để ticket đã kiểm chứng tự khép lại
serve là một lượt nhìn, không phải một lượt hạ cánh: nó reset base rồi gộp
lại từ đầu mỗi lần, nên không gì nó đặt ở đó sống sót qua lần surface revert
kế tiếp. land thì ngược lại — một merge --no-ff vào base ở máy và ở lại đó.
Repo có thể xin cho việc đó tự xảy ra:
profile: startup
finish: land # review (mặc định) | land | prGiờ foreman merge từng ticket vào base ở máy ngay khi worker của nó xong, thay vì để lại một đống branch cho bạn tự gộp. Bạn review branch base — vốn đã chạy sẵn trên dev server — rồi push.
finish: pr là nửa còn lại của cùng key: foreman chạy create-pr cho từng
ticket ngay khi nó xong, nên cả batch kết thúc bằng các PR đã mở thay vì một
đống branch. Nó cần có chỗ để mở PR — dưới land: none thì config bị từ chối
ngay lúc đọc, vì create-pr vốn BLOCK ở đó.
Mặc định finish là review và phải bật thủ công theo từng repo, vì đây là key
duy nhất cho phép một lượt chạy tự hành động trên chính verdict của nó trong lúc
bạn ngủ — dịch chuyển branch base, hoặc push ra chỗ cả team nhìn thấy. Cả hai
giá trị đều chặn ở cùng một chỗ: qa và review-pr đều phải DONE, theo từng
ticket, không có --force nào để với tới. Việc chưa được kiểm chứng không bao
giờ tự khép lại.
Những chốt chặn còn lại là của riêng land, và chúng là thứ khiến một lần merge
bạn không ngồi xem vẫn sống được:
- Cả lô được kiểm trước khi bất kỳ cái nào merge — một ticket thứ ba bị chặn không để lại hai cái đầu trên một base bạn không hề yêu cầu.
- Nó giữ QA lease, nên không thể dịch base dưới chân một phiên QA đang chạy.
- Nó không bao giờ push. Base kết thúc ở phía trước origin và cú push vẫn là của bạn.
- Chạy lại là no-op (
LANDED=0 … already on main).
finish: pr là giá trị có push — mở PR thì phải push — nhưng nó chỉ mở PR chứ
không hơn: phần review, và cú merge, vẫn là của con người.
Đừng chạy serve sau khi đã land: serve gộp lại từ origin/<base>, kéo base
về origin và vứt hết các merge — branch ticket vẫn giữ việc, nhưng bề mặt
review của bạn biến mất.
Đổi profile
Chạy lại /bbs:setup-project — trên repo đã cấu hình, nó mời bạn đổi và chỉ ghi
lại profile:. Đổi profile là đổi chỗ review và độ gắt QA; nó không bao giờ dời
việc đang dang dở.
Đổi base_branch sang develop trên repo chưa có nhánh đó: tạo và push nó từ
main trước khi đổi, không thì lần cắt đầu tiên không thấy origin/develop
và sẽ fork từ base ở máy.
git switch -c develop main && git push -u origin developViết tay mode:, land:, push: hay finish: sẽ đè lên preset của profile.
Đó là cửa thoát hiểm, không phải hình dạng bình thường — một cái núm viết ra tay
là một cái núm thôi bám theo profile của nó. mode: được đọc tại thời điểm cắt
branch, nên thêm hay bỏ nó chỉ ảnh hưởng ticket mới; trước khi chuyển vào lối
làm việc worktree thì checkout chính phải sạch và đứng trên base_branch, còn
trước khi chuyển ra thì phải xong hoặc gác các worktree dang dở (bbs ticket board) và trả mọi surface lease.
Schema đầy đủ và bảng suy dẫn: .claude/skills/references/git-flow.md;
phần máy móc worktree: references/worktrees.md.
