@htechcs/harness-kit 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,33 @@
1
+ <!--
2
+ Một "golden task" = một việc đại diện + tiêu chí đậu KHÁCH QUAN, để chạy lại được sau mỗi
3
+ lần sửa harness. Copy file này cho mỗi case. Đặt tên theo việc: add-endpoint.md, fix-flaky-test.md...
4
+
5
+ Nguyên tắc: done-criteria phải đo được bằng MÁY nếu có thể (lệnh chạy ra pass/fail), chỉ rớt
6
+ xuống chấm bằng người khi bất khả kháng. "Trông có vẻ đúng" KHÔNG phải tiêu chí.
7
+ Xoá comment này khi dùng thật.
8
+ -->
9
+
10
+ # Case: <tên việc — vd "thêm endpoint GET /users/:id">
11
+
12
+ ## Task (đưa nguyên văn cho agent)
13
+ <Lời nhắc y như bạn sẽ gõ cho agent. Càng giống thực tế càng tốt.>
14
+
15
+ ## Setup (repo phải ở trạng thái nào trước khi chạy)
16
+ <Branch/commit nền, dữ liệu seed, biến môi trường. Để case lặp lại được giống nhau mỗi lần.>
17
+ - Base: <commit/branch>
18
+ -
19
+
20
+ ## Done-criteria (KHÁCH QUAN — đậu/rớt rõ ràng)
21
+ <Cái PHẢI đúng sau khi agent xong. Ưu tiên lệnh chạy ra pass/fail.>
22
+ - [ ] `<lệnh test>` xanh
23
+ - [ ] `<lệnh lint / typecheck>` sạch
24
+ - [ ] <thay đổi cụ thể tồn tại — vd "route mới trả 200 với id hợp lệ, 404 nếu không">
25
+ - [ ] KHÔNG đụng <file/đường ngoài phạm vi>
26
+
27
+ ## Cách chấm
28
+ <Tự động được thì ghi đúng lệnh. Phải chấm tay thì ghi rubric ngắn, đừng để "tự hiểu".>
29
+ - Tự động: `<lệnh trả exit code>`
30
+ - Tay (nếu cần): <1–2 câu rubric>
31
+
32
+ ## Tham chiếu (tùy chọn)
33
+ <Commit/PR "đúng" để so, nếu có. Giúp thấy agent lệch ở đâu.>
@@ -0,0 +1,42 @@
1
+ # Observability — thấy agent đã làm gì (Mức 5)
2
+
3
+ Khi một eval rớt — hoặc agent "làm gì đó lạ" — bạn cần *nhìn* được nó đã làm gì, không đoán.
4
+ Đây là các chỗ mà nhìn, từ rẻ tới sâu.
5
+
6
+ ## Chỗ nào mà nhìn
7
+
8
+ 1. **Transcript phiên** — bản ghi *mọi* tool call agent đã gọi (đọc file nào, chạy lệnh gì,
9
+ sửa gì). Đây là nguồn sự thật số một khi truy "vì sao nó làm vậy". Claude Code lưu transcript
10
+ mỗi session dưới thư mục project trong `~/.claude/`.
11
+ 2. **`/cost`** — token & chi phí của phiên. Tăng vọt bất thường = dấu hiệu context phình
12
+ (đọc lại file thừa, MCP nhồi tool) → kéo về Mức 2.
13
+ 3. **Telemetry (cho cả team / chạy nền)** — Claude Code xuất được metrics/logs qua OpenTelemetry:
14
+ bật bằng biến môi trường `CLAUDE_CODE_ENABLE_TELEMETRY` rồi trỏ OTLP exporter sang backend
15
+ của bạn (Grafana, Honeycomb, Datadog…). Dùng khi cần theo dõi nhiều phiên/agent, không chỉ một.
16
+ → Tra cấu hình chính xác trong docs Claude Code mục *monitoring / telemetry*.
17
+ 4. **Log hook (audit chủ động)** — gắn `PostToolUse` hook ghi mỗi tool call ra file (xem
18
+ [guardrails/README.md](../guardrails/README.md) mục Hooks). Hữu ích khi chạy nền và muốn xem lại sau.
19
+
20
+ ## Dấu hiệu bệnh & nó chỉ về mức nào
21
+
22
+ Observability không chỉ để debug một phiên — nó **lộ ra lỗ hổng harness**:
23
+
24
+ | Thấy gì trong trace | Bệnh | Sửa ở mức |
25
+ |---------------------|------|-----------|
26
+ | Đọc đi đọc lại cùng file; token leo thang | context bẩn | **Mức 2** (subagent / `/clear` / cắt MCP) |
27
+ | Bị hỏi quyền liên tục cho lệnh an toàn | allowlist thiếu | **Mức 3** (thêm vào `allow`) |
28
+ | Suýt chạy lệnh phá hoại | thiếu chốt | **Mức 3** (thêm vào `deny`/`ask`) |
29
+ | Mất ngữ cảnh giữa phiên dài, làm lại từ đầu | không checkpoint | **Mức 4** (`TASK.md`) |
30
+ | Làm sai mà không ai biết tới lúc muộn | thiếu eval | **Mức 5** (thêm golden task) |
31
+ | Mơ hồ "build/test chạy sao" | chỉ dẫn thiếu | **Mức 1** (CLAUDE.md) |
32
+ | Lặp đi lặp lại cùng một lỗi qua nhiều phiên | luật chưa được ghi | **Mức 1** (thêm 1 dòng vào CLAUDE.md) |
33
+
34
+ **Đóng vòng về Mức 1 — `CLAUDE.md` là tài liệu sống.** Khi trace cho thấy agent **lặp lại** một
35
+ lỗi Z (vd quên chạy migration, sửa nhầm file generated), đừng chỉ sửa tay lần này: thêm **một dòng**
36
+ guardrail/quy ước vào `CLAUDE.md` để chặn lần sau, rồi **chạy lại golden task** xác nhận hết
37
+ regression. Đó là cách `CLAUDE.md` lớn lên *từ lỗi thật*, thay vì phình theo phỏng đoán.
38
+
39
+ ## Nguyên tắc
40
+
41
+ > Đừng cải thiện harness bằng cảm giác. **Đọc trace, để nó chỉ đúng mức cần sửa**, sửa, rồi
42
+ > chạy lại golden task để xác nhận tốt lên thật. Đó là toàn bộ vòng lặp Mức 5.
@@ -0,0 +1,120 @@
1
+ # Guardrails — permission baseline (Mức 3)
2
+
3
+ Mức 3 kiểm soát agent **được phép làm gì** — ranh giới an toàn. Khác Mức 2 (context *sạch*),
4
+ Mức 3 lo hành động *an toàn*: chặn lệnh phá hoại, hỏi trước việc rủi ro, cho việc an toàn chạy thẳng.
5
+
6
+ ## Cài đặt
7
+
8
+ Copy `settings.json` vào `.claude/` của repo:
9
+
10
+ ```bash
11
+ mkdir -p .claude
12
+ cp settings.json .claude/settings.json
13
+ ```
14
+
15
+ **Vì sao là `.claude/settings.json` (không phải `settings.local.json`):** file này **check vào repo**,
16
+ nên cả team clone về là **tự thừa hưởng cùng một bộ guardrail**. Còn `settings.local.json` là
17
+ ghi đè cá nhân (đã gitignore) — để dành cho tinh chỉnh riêng máy bạn, không ép lên team.
18
+
19
+ > ⚡ **Việc ĐẦU TIÊN sau khi copy — thêm lệnh test/lint/build vào `allow`.** Baseline cố ý chỉ
20
+ > allow git read-only. Nếu không thêm vòng feedback của repo, Claude sẽ hỏi quyền *mỗi lần* chạy
21
+ > test → bạn rơi đúng thói quen "bấm yes cho xong" mà mục *Insight* bên dưới gọi là nguy hiểm nhất.
22
+ > Mở `.claude/settings.json`, thêm vào `allow` đúng lệnh của stack bạn:
23
+ >
24
+ > - **Node:** `"Bash(npm run test:*)"`, `"Bash(npm run lint:*)"`, `"Bash(npm run build:*)"`
25
+ > - **Python:** `"Bash(pytest:*)"`, `"Bash(ruff:*)"`, `"Bash(mypy:*)"`
26
+ > - **Go:** `"Bash(go test:*)"`, `"Bash(go build:*)"`, `"Bash(go vet:*)"`
27
+ >
28
+ > Đây là **bắt buộc**, không phải tuỳ chọn — vòng feedback nhanh là tinh tuý xuyên suốt cả kit.
29
+
30
+ ## Mô hình 3 rổ
31
+
32
+ Mọi hành động (chạy bash, đọc/sửa file) rơi vào 1 trong 3 rổ:
33
+
34
+ | Rổ | Nghĩa | Ví dụ trong baseline |
35
+ |----|-------|----------------------|
36
+ | **deny** | cấm tuyệt đối, agent không gọi được | `rm -rf`, đọc `.env`/`secrets/**`, đọc key `*.pem` |
37
+ | **ask** | dừng lại hỏi bạn trước | `git push`, `git reset --hard`, `git clean`, `rm` |
38
+ | **allow** | chạy thẳng, không hỏi | `git status`, `git diff`, `git log`, `git branch` |
39
+
40
+ Cú pháp rule: `Tool(specifier)`.
41
+ - Bash khớp theo **tiền tố**: `Bash(npm run test:*)` khớp mọi lệnh bắt đầu bằng `npm run test`.
42
+ - File theo kiểu **gitignore**: `Read(./secrets/**)`, `Edit(./dist/**)`.
43
+
44
+ ## Cách mở rộng cho repo của bạn
45
+
46
+ Baseline cố ý **tối giản và universal**. Hãy thêm cái đặc thù repo:
47
+
48
+ - **Vào `allow`** — lệnh chạy hằng ngày, an toàn, để khỏi bị hỏi liên tục:
49
+ `Bash(npm run test:*)`, `Bash(npm run lint:*)`, `Bash(pytest:*)`, `Bash(make:*)`.
50
+ - **Vào `ask`** — việc đặc thù repo mà *hệ quả lớn*:
51
+ chạy migration (`Bash(npm run migrate:*)`), deploy, `Bash(docker compose down:*)`.
52
+ - **Vào `deny`** — path không bao giờ được sửa/đọc:
53
+ `Edit(./dist/**)`, `Edit(./vendor/**)`, `Read(./**/*.key)`.
54
+
55
+ > Mẹo: đừng nhồi `allow` quá rộng. Mỗi lần Claude hỏi là một lần bạn *review* — nhồi allow
56
+ > nhiều quá là tự bỏ chốt review của chính mình.
57
+
58
+ ## Insight: deny ≠ bảo mật kín kẽ
59
+
60
+ Deny-list **không** chống được kẻ địch (agent có thể lách: viết `rm` qua script, base64…).
61
+ Nó là **lưới an toàn + giảm ma sát**:
62
+ - `deny`/`ask` chặn **tai nạn** (xoá nhầm, push nhầm) — phòng *lỗi*, không phòng *tấn công*.
63
+ - `allow` cho lệnh an toàn chạy thẳng → bạn đỡ thói quen "bấm yes cho xong" (thói quen đó mới
64
+ là cái nguy hiểm thật).
65
+
66
+ **An toàn thật sự** vẫn là **review diff + plan mode** trước khi cho agent hành động — đó là
67
+ kỷ luật runtime, không gói thành file được.
68
+
69
+ ## Nội dung ngoài & prompt injection
70
+
71
+ `deny`/`ask` ở trên chặn *tai nạn*, **không** chặn được prompt injection. Nội dung agent đọc từ
72
+ web, issue, PR, log… là **dữ liệu — không phải lệnh**, nhưng kẻ xấu có thể giấu chỉ thị trong đó
73
+ để lái agent. Đây là **kỷ luật runtime** (nên kit không nhồi hook/CI-scan sẵn), vài chốt thực dụng:
74
+
75
+ - Đọc nội dung ngoài (web/issue/PR) trong **plan mode** — agent *đề xuất* trước khi *hành động*.
76
+ - KHÔNG cho agent tự chạy lệnh / `curl` lấy ra từ nội dung nó vừa fetch về.
77
+ - Input không tin cậy → tách **session riêng**, đừng trộn vào session đang có quyền cao.
78
+
79
+ Quét tự động ở CI và phần nền sâu hơn: xem `docs/harness-engineering-tutorial.md` (link **Lurkr**
80
+ cho CI-scan, **OpenHands — mitigating prompt injection** cho nền tảng).
81
+
82
+ ## Nâng cao (tùy chọn): Hooks
83
+
84
+ Khi cần *logic* phức tạp hơn allow/deny tĩnh — vd "chặn mọi edit vào path bảo vệ", "tự chạy
85
+ lint sau mỗi lần sửa" — dùng **hook**: một script chạy *trước/sau* mỗi tool call. Khai báo trong
86
+ `settings.json`:
87
+
88
+ ```json
89
+ {
90
+ "hooks": {
91
+ "PreToolUse": [
92
+ { "matcher": "Bash", "hooks": [{ "type": "command", "command": ".claude/hooks/guard.sh" }] }
93
+ ]
94
+ }
95
+ }
96
+ ```
97
+
98
+ Script đọc JSON tool-call từ stdin; **exit code 2 = chặn**, kèm message ra stderr. Vì hook chặn
99
+ là script đặc thù từng repo, kit này **không nhồi sẵn** — chỉ chỉ đường. Viết khi bạn thật sự có
100
+ một quy tắc lặp lại mà allow/deny tĩnh không diễn đạt nổi.
101
+
102
+ **Audit-log (`PostToolUse`)** — ghi lại *mọi* tool call để xem lại sau. Đây là thứ
103
+ `evals/observability.md` (Mức 5) trỏ tới; hook này **generic, không đặc thù repo**:
104
+
105
+ ```json
106
+ {
107
+ "hooks": {
108
+ "PostToolUse": [
109
+ { "matcher": "*", "hooks": [{ "type": "command", "command": ".claude/hooks/audit-log.sh" }] }
110
+ ]
111
+ }
112
+ }
113
+ ```
114
+
115
+ `audit-log.sh` chỉ cần nối payload stdin vào một file — mỗi dòng là JSON một tool call:
116
+
117
+ ```bash
118
+ #!/usr/bin/env bash
119
+ cat >> .claude/audit.log
120
+ ```
@@ -0,0 +1,48 @@
1
+ # Long-running — việc kéo dài quá một session (Mức 4)
2
+
3
+ Mức 4 giữ agent làm việc *đáng tin* trên thứ dài hơi: refactor nhiều ngày, migration lớn,
4
+ agent chạy nền hàng giờ. Việc dài chết vì 3 thứ — mỗi file ở đây chặn một thứ.
5
+
6
+ ## 3 file, 3 vấn đề
7
+
8
+ | File | Chặn cái chết nào |
9
+ |------|-------------------|
10
+ | [`setup.sh`](../setup.sh) | Không về được trạng thái chạy-được (clone/agent mới không biết cài gì) |
11
+ | [`TASK.md`](./TASK.md) | Mất trạng thái qua session / sau `/clear` |
12
+ | [`new-worktree.sh`](../new-worktree.sh) | Nhiều task song song giẫm chân nhau |
13
+
14
+ ## Cài đặt
15
+
16
+ ```bash
17
+ cp setup.sh new-worktree.sh . # vào root repo
18
+ chmod +x setup.sh new-worktree.sh
19
+ cp long-running/TASK.md . # khi bắt đầu một việc dài cụ thể
20
+ ```
21
+
22
+ Rồi **trỏ `CLAUDE.md`** vào chúng để agent biết dùng:
23
+
24
+ ```md
25
+ ## Build / Test / Run
26
+ - Dựng môi trường: ./setup.sh (idempotent — chạy lại an toàn)
27
+
28
+ ## Long-running
29
+ - Việc dài đang chạy ghi ở TASK.md — đọc trước khi bắt đầu, cập nhật khi tới mốc.
30
+ ```
31
+
32
+ ## Khi nào dùng cái nào (đây là phần discipline)
33
+
34
+ File chỉ là công cụ — biết *khi nào* rút ra mới là kỹ năng:
35
+
36
+ - **`setup.sh`** — chạy ngay khi vào checkout mới, và bất cứ khi nào nghi môi trường lệch.
37
+ Giữ nó **idempotent + fail-fast**: agent nền phải dựng lại được mà không cần hỏi.
38
+ - **`TASK.md`** — mở ra khi việc *sẽ vắt qua nhiều session* hoặc bạn sắp `/clear`. Cập nhật ở
39
+ **mốc** (xong một phần, chốt một quyết định), không phải mỗi dòng code. Nó là thứ agent kế
40
+ tiếp đọc để tiếp tục — viết cho "người lạ" hiểu, không viết tốc ký cho riêng mình.
41
+ - **`new-worktree.sh`** — tách worktree khi chạy **≥2 hướng song song**, hoặc khi một task dài
42
+ cần cô lập khỏi nhánh chính. Một task = một worktree. Xong: `git worktree remove <dir>`.
43
+
44
+ ## Chạy nền & quay lại kiểm
45
+
46
+ Việc thật dài có thể chạy ở background rồi quay lại xem (không ngồi canh). Nguyên tắc:
47
+ khúc việc phải **resume được** — nếu đứt giữa chừng, `TASK.md` + `setup.sh` đủ để bắt lại từ
48
+ mốc gần nhất, không phải làm lại từ đầu. Đó là lý do hai file kia tồn tại.
@@ -0,0 +1,33 @@
1
+ <!--
2
+ TASK.md — bộ nhớ sống sót của một việc dài (Mức 4).
3
+
4
+ Việc kéo dài quá một session sẽ mất trạng thái khi `/clear` hoặc khi mở session mới.
5
+ File này là nơi agent (và agent kế tiếp) đọc để TIẾP TỤC, thay vì dựng lại từ đầu.
6
+
7
+ Cách dùng:
8
+ - Đặt ở root repo hoặc thư mục task. Cập nhật KHI có mốc đáng nhớ, không cập nhật mỗi dòng.
9
+ - Trỏ CLAUDE.md tới nó: "Việc dài đang chạy ghi ở TASK.md — đọc trước khi bắt đầu."
10
+ - Xong việc thì xoá / archive, đừng để TASK.md cũ làm nhiễu.
11
+ Xoá phần comment này khi bắt đầu dùng thật.
12
+ -->
13
+
14
+ # Task: <một dòng — đang làm gì>
15
+
16
+ ## Mục tiêu
17
+ <Định nghĩa "xong" đo được. Việc này coi là hoàn thành khi nào?>
18
+
19
+ ## Đang ở đâu (cập nhật mỗi mốc)
20
+ <Trạng thái hiện tại trong 2–4 gạch đầu dòng. Cái gì đã chạy được, cái gì chưa.>
21
+ -
22
+
23
+ ## Bước tiếp theo
24
+ <Hành động cụ thể kế tiếp, đủ rõ để bắt tay ngay mà không phải nghĩ lại.>
25
+ - [ ]
26
+
27
+ ## Quyết định đã chốt (đừng mở lại)
28
+ <Lựa chọn đã quyết + LÝ DO ngắn. Để agent sau không bàn lại từ đầu.>
29
+ -
30
+
31
+ ## Cạm bẫy đã gặp
32
+ <Thứ đã thử mà hỏng, ngõ cụt — để khỏi đâm lại.>
33
+ -
@@ -0,0 +1,26 @@
1
+ # MCP hygiene — checklist dọn tool thừa (Mức 2)
2
+
3
+ Mỗi MCP server bạn cắm sẽ **bơm toàn bộ định nghĩa tool của nó vào context ở MỌI lượt** —
4
+ dù lượt đó không dùng tới. 5 server ít dùng có thể ngốn hàng nghìn token mỗi lượt và làm
5
+ model "rối tay" khi chọn tool. Đây là **thuế context vĩnh viễn**, không phải vấn đề an toàn
6
+ (an toàn là Mức 3).
7
+
8
+ ## Cách xem đang cắm những gì
9
+
10
+ ```bash
11
+ claude mcp list # liệt kê MCP server đang cấu hình
12
+ ```
13
+
14
+ ## Với MỖI server, hỏi 3 câu
15
+
16
+ - [ ] **Tháng qua có thực sự gọi tool của nó không?** Không → gỡ.
17
+ - [ ] **Nó thêm bao nhiêu tool vào mỗi lượt, mà bạn chỉ dùng 1–2?** Thừa nhiều → gỡ hoặc tìm bản gọn hơn.
18
+ - [ ] **Có thể thay bằng CLI sẵn có không?** (vd `gh` thay vì MCP GitHub cho việc đơn giản) → ưu tiên CLI, gỡ MCP.
19
+
20
+ ## Nguyên tắc
21
+
22
+ > **Tool ít mà rõ > nhiều tool chồng chéo.** Giữ đúng cái bạn dùng hằng tuần.
23
+ > Cắm thêm khi *thật sự* cần một nguồn/khả năng mới, không cắm "để sẵn cho chắc".
24
+
25
+ Cắm MCP "để sẵn cho chắc" là cái bẫy phổ biến nhất ở Mức 2: nó âm thầm làm mọi session
26
+ đắt hơn và kém chính xác hơn, mà không ai để ý.
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env bash
2
+ # new-worktree.sh — tạo một git worktree + branch riêng cho một task song song/dài.
3
+ #
4
+ # Mức 4 (long-running): mỗi task nặng nên có cây làm việc RIÊNG, để chạy song song
5
+ # mà không giẫm chân (không stash/switch liên tục, không sửa đè lên nhau).
6
+ #
7
+ # Dùng: ./new-worktree.sh <tên-task>
8
+ # Vd: ./new-worktree.sh refactor-auth
9
+ # -> tạo branch 'refactor-auth' + thư mục ../<repo>-refactor-auth (cạnh repo, KHÔNG lồng trong).
10
+
11
+ set -euo pipefail
12
+
13
+ name="${1:-}"
14
+ [ -n "$name" ] || { echo "dùng: $0 <tên-task> (vd: refactor-auth)"; exit 1; }
15
+
16
+ # Phải đứng trong một git repo.
17
+ git rev-parse --is-inside-work-tree >/dev/null 2>&1 || { echo "✗ không phải git repo"; exit 1; }
18
+
19
+ root="$(git rev-parse --show-toplevel)"
20
+ repo="$(basename "$root")"
21
+ dir="$root/../${repo}-${name}" # đặt CẠNH repo, không lồng -> git status không nhiễu
22
+
23
+ [ -e "$dir" ] && { echo "✗ đã tồn tại: $dir"; exit 1; } # fail-fast, không ghi đè
24
+
25
+ # Tách từ base mới nhất. Đổi 'main' nếu repo bạn dùng tên khác.
26
+ base="$(git symbolic-ref --quiet --short HEAD || echo main)"
27
+
28
+ if git show-ref --verify --quiet "refs/heads/${name}"; then
29
+ git worktree add "$dir" "$name" # branch đã có -> checkout vào worktree mới
30
+ else
31
+ git worktree add -b "$name" "$dir" "$base" # branch mới tách từ base hiện tại
32
+ fi
33
+
34
+ echo "✓ worktree: $dir (branch: $name)"
35
+ echo " cd \"$dir\" để bắt đầu. Xong việc: git worktree remove \"$dir\""
@@ -0,0 +1,24 @@
1
+ {
2
+ "permissions": {
3
+ "deny": [
4
+ "Bash(rm -rf:*)",
5
+ "Read(./.env)",
6
+ "Read(./.env.*)",
7
+ "Read(./secrets/**)",
8
+ "Read(./**/id_rsa)",
9
+ "Read(./**/*.pem)"
10
+ ],
11
+ "ask": [
12
+ "Bash(git push:*)",
13
+ "Bash(git reset --hard:*)",
14
+ "Bash(git clean:*)",
15
+ "Bash(rm:*)"
16
+ ],
17
+ "allow": [
18
+ "Bash(git status)",
19
+ "Bash(git diff:*)",
20
+ "Bash(git log:*)",
21
+ "Bash(git branch:*)"
22
+ ]
23
+ }
24
+ }
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env bash
2
+ # setup.sh — bootstrap môi trường: "một lệnh là repo chạy được".
3
+ #
4
+ # Mức 4 (long-running): agent chạy nền/lặp KHÔNG thể dừng hỏi "cài gì giờ".
5
+ # Script này phải đưa một checkout sạch về trạng thái chạy-được, không cần tương tác.
6
+ #
7
+ # 2 nguyên tắc bắt buộc:
8
+ # - IDEMPOTENT: chạy lại nhiều lần vẫn an toàn (không nhân đôi, không hỏng state).
9
+ # - FAIL-FAST: thiếu thứ gì báo NGAY rồi dừng, đừng chạy tiếp trong trạng thái mù.
10
+ #
11
+ # ĐÂY LÀ SKELETON — mỗi repo cài thứ khác nhau. Điền phần TODO cho repo của bạn,
12
+ # rồi trỏ CLAUDE.md tới đây ("Build / Test / Run: chạy ./setup.sh trước").
13
+
14
+ set -euo pipefail # lỗi -> dừng; biến chưa set -> dừng; lỗi giữa pipe -> dừng
15
+
16
+ cd "$(dirname "$0")"
17
+
18
+ # --- 1. Kiểm điều kiện cần (fail-fast) -------------------------------------
19
+ # Thiếu tool nền là báo ngay, đừng để lỗi mơ hồ ở bước sau.
20
+ require() {
21
+ command -v "$1" >/dev/null 2>&1 || { echo "✗ thiếu '$1' — cài rồi chạy lại"; exit 1; }
22
+ }
23
+ # TODO: liệt kê tool repo bạn cần
24
+ require git
25
+ # require node
26
+ # require python3
27
+
28
+ # --- 2. Cài dependencies (idempotent) --------------------------------------
29
+ # Hầu hết package manager đã idempotent sẵn — chạy lại chỉ no-op.
30
+ # TODO: thay bằng lệnh của repo bạn
31
+ # npm ci
32
+ # uv sync
33
+ # go mod download
34
+
35
+ # --- 3. Cấu hình / secrets (fail-fast, KHÔNG tự tạo bừa) --------------------
36
+ # Có .env.example mà chưa có .env -> báo cho người chạy tự điền, đừng đoán giá trị.
37
+ # if [ -f .env.example ] && [ ! -f .env ]; then
38
+ # echo "✗ chưa có .env — copy .env.example rồi điền secrets"; exit 1
39
+ # fi
40
+
41
+ # --- 4. Dịch vụ phụ trợ (idempotent) ---------------------------------------
42
+ # TODO: DB/queue... — dùng lệnh chỉ-tạo-nếu-chưa-có
43
+ # docker compose up -d
44
+
45
+ # --- 5. Verify: chứng minh môi trường thật sự chạy được --------------------
46
+ # Đừng kết thúc bằng "xong" mù — chạy một check rẻ để chắc chắn.
47
+ # TODO: lệnh smoke-test nhanh nhất của repo
48
+ # npm run build --silent
49
+ # python -c "import yourpkg"
50
+
51
+ echo "✓ môi trường sẵn sàng"
@@ -0,0 +1,40 @@
1
+ <!--
2
+ FEATURE.md — spec một feature TRƯỚC khi viết code (spec-driven development).
3
+
4
+ Đây là nửa "Specs" của trụ cột "Repo-local instructions & Specs": CLAUDE.md (Mức 1) lo luật bền
5
+ xuyên MỌI task; FEATURE.md lo định nghĩa MỘT feature cụ thể trước khi bắt tay. Spec rõ → agent
6
+ chạy đúng hướng, ít lạc; acceptance criteria rõ → có cái để eval (Mức 5) chấm đậu/rớt.
7
+
8
+ Cách dùng: copy cho mỗi feature đáng-kể (vd docs/specs/<feature>.md). Việc nhỏ thì bỏ qua —
9
+ đừng nghi thức hoá. Điền đủ phần, xoá comment này khi dùng thật.
10
+ -->
11
+
12
+ # Feature: <tên ngắn gọn>
13
+
14
+ ## Vấn đề / vì sao
15
+ <Giải quyết cái gì cho ai. 1–3 câu. Nếu không nói rõ được "vì sao", khoan code.>
16
+
17
+ ## Phạm vi
18
+
19
+ ### Trong phạm vi
20
+ -
21
+
22
+ ### NGOÀI phạm vi (quan trọng — chặn scope creep)
23
+ -
24
+
25
+ ## Ràng buộc
26
+ <Bắt buộc kỹ thuật/nghiệp vụ: API phải giữ nguyên, không thêm dependency, giới hạn hiệu năng…>
27
+ -
28
+
29
+ ## Acceptance criteria (đo được — đậu/rớt rõ ràng)
30
+ <Cái PHẢI đúng thì feature mới coi là xong. Đây cũng là nguồn cho golden task ở Mức 5.>
31
+ - [ ]
32
+ - [ ]
33
+
34
+ ## Test hooks
35
+ <Sẽ kiểm bằng test nào / lệnh nào. Ưu tiên tự động hoá được.>
36
+ -
37
+
38
+ ## Ghi chú thiết kế & quyết định đã chốt
39
+ <Hướng tiếp cận + lý do ngắn. Để người sau (và agent) không bàn lại từ đầu.>
40
+ -
@@ -0,0 +1,34 @@
1
+ # Specs — spec-driven development (nửa còn lại của Trụ cột 2)
2
+
3
+ Trụ cột 2 là **"Repo-local instructions & Specs"**. Mức 1 (`/init-harness` → `CLAUDE.md`) lo nửa
4
+ **instructions**: luật bền, đúng xuyên *mọi* task. `FEATURE.md` lo nửa **specs**: định nghĩa **một
5
+ feature cụ thể TRƯỚC khi code**. Spec rõ → agent ít lạc hướng; acceptance criteria rõ → có sẵn cái
6
+ cho eval Mức 5 chấm đậu/rớt.
7
+
8
+ ## Khi nào dùng
9
+
10
+ - Feature **đủ lớn để dễ lạc** / nhiều bước / nhiều người → viết spec trước.
11
+ - Việc nhỏ, một-phát-xong → **bỏ qua**. Đừng biến spec thành nghi thức.
12
+
13
+ ## `FEATURE.md` vs `TASK.md` (đừng nhầm — chúng bổ sung nhau)
14
+
15
+ | | `FEATURE.md` (ở đây) | `TASK.md` (Mức 4) |
16
+ |---|---|---|
17
+ | Mục đích | **plan TRƯỚC khi code**: cái gì + tiêu chí đậu | **trạng thái sống còn QUA session** |
18
+ | Vòng đời | viết 1 lần đầu feature, ít đổi | cập nhật liên tục ở mỗi mốc |
19
+ | Trả lời | "feature này *là gì*, xong khi nào" | "*đang* làm tới đâu, bước kế" |
20
+
21
+ Feature lớn thường dùng **cả hai**: `FEATURE.md` chốt đích, `TASK.md` theo dõi đường đi.
22
+
23
+ ## Cài
24
+
25
+ ```bash
26
+ mkdir -p docs/specs
27
+ cp FEATURE.md docs/specs/<tên-feature>.md
28
+ ```
29
+
30
+ ## Nâng cao
31
+
32
+ Feature lớn / cả team → dùng framework chuyên: **GitHub Spec Kit**, **12-Factor Agents** (link
33
+ trong `docs/harness-engineering-tutorial.md`). `FEATURE.md` chỉ là bản tối giản để bắt đầu — đủ
34
+ để có spec, không bắt bạn nuốt cả framework.