@educa-corp/fw 0.6.0 → 0.6.2
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.
- package/CHANGELOG.md +12 -0
- package/bin/fw.js +16 -0
- package/commands/prd.md +4 -4
- package/commands/product.md +1 -1
- package/docs/guide/01-bat-dau.md +99 -0
- package/docs/guide/02-khai-niem.md +66 -0
- package/docs/guide/README.md +59 -0
- package/docs/guide/lenh/fw.md +51 -0
- package/docs/guide/lenh/prd.md +80 -0
- package/docs/guide/lenh/product.md +66 -0
- package/docs/guide/vai-tro/po-ba.md +27 -0
- package/docs/guide/xu-ly-su-co.md +31 -0
- package/package.json +2 -1
- package/ref/prd/change.md +3 -3
- package/ref/prd/new.md +8 -8
- package/ref/product/epic.md +4 -4
- package/templates/prd.md +5 -5
- package/templates/product-epic.md +4 -4
- package/templates/product.md +1 -1
- package/tools/spec_edit.py +72 -16
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
> `fw install` in các mục của những version mới hơn bản đang cài trong dự án.
|
|
4
4
|
> Mỗi lần sửa lệnh, khuôn, bộ câu hỏi hay công cụ đều phải tăng version và thêm một mục ở đây. Test sẽ báo lỗi nếu version chưa có mục.
|
|
5
5
|
|
|
6
|
+
## 0.6.2 — 2026-09-30
|
|
7
|
+
|
|
8
|
+
- **Hướng dẫn sử dụng** được cài vào dự án ở `.fw/guide/`, đúng với version đang dùng và đọc được khi không có mạng. Bắt đầu từ `.fw/guide/README.md`. Có thể hỏi Claude, ví dụ *"Đọc .fw/guide và cho biết cách đổi PRD"*.
|
|
9
|
+
- `fw install` tự thêm `.fw/backup/` vào `.gitignore`, để các bản sao file đã sửa không bị commit nhầm.
|
|
10
|
+
|
|
11
|
+
## 0.6.1 — 2026-09-30
|
|
12
|
+
|
|
13
|
+
- Thuật ngữ: **Acceptance Criteria (điều kiện nghiệm thu)**, viết tắt AC, thay cho "tiêu chí chấp nhận". **Business Rule (BR)** thay cho "rule". Cột "Logic" của bảng BR đổi thành **Business Logic**.
|
|
14
|
+
- Epic đánh số Business Rule là `BR1.`, `BR2.`… (trước đây là `R1.`). `spec_edit upgrade` tự đổi mã trong epic cũ, và trong phần `(nguồn: …)` của PRD cũ. `coverage` coi `R3` và `BR3` là cùng một mã.
|
|
15
|
+
- `(nguồn: …)` **chỉ được ghi mã**: `BR3` / `AC1` của epic, `CON-02`, hoặc `PRD` (mục thêm khi viết PRD, PO đã duyệt). `spec_edit` chặn khi tạo hoặc sửa PRD còn ghi nguồn bằng lời. `coverage` báo lỗi khi nguồn là mã không có trong epic.
|
|
16
|
+
- PRD đã tạo bằng bản cũ: lần sửa đầu tiên sẽ yêu cầu đổi các chỗ ghi nguồn bằng lời thành mã (thường là `PRD`).
|
|
17
|
+
|
|
6
18
|
## 0.6.0 — 2026-09-30
|
|
7
19
|
|
|
8
20
|
- Sẵn sàng publish lên npmjs với tên **`@educa-corp/fw`**. Cài lần đầu: `npx @educa-corp/fw install`.
|
package/bin/fw.js
CHANGED
|
@@ -26,11 +26,13 @@ const SOURCES = [
|
|
|
26
26
|
{ from: 'templates', to: '.fw/core/templates' },
|
|
27
27
|
{ from: 'tools', to: '.fw/core/tools' },
|
|
28
28
|
{ from: 'bin/fw.js', to: '.fw/core/bin/fw.js' },
|
|
29
|
+
{ from: 'docs/guide', to: '.fw/guide' },
|
|
29
30
|
];
|
|
30
31
|
// Mọi file gói npm phải mang theo để fw.js chạy được — test đối chiếu với `files` của package.json.
|
|
31
32
|
const RUNTIME_FILES = ['package.json', 'CHANGELOG.md', 'bin/config.template.yaml', ...SOURCES.map((s) => s.from)];
|
|
32
33
|
const CONFIG = '.fw/config.yaml';
|
|
33
34
|
const SETTINGS = '.claude/settings.json';
|
|
35
|
+
const GITIGNORE_LINE = '.fw/backup/';
|
|
34
36
|
|
|
35
37
|
// Lệnh AI dùng để sửa file spec (xem tools/spec_edit.py). Cho phép sẵn để PO không bị hỏi quyền mỗi lần.
|
|
36
38
|
const SPEC_EDIT = '.fw/core/tools/spec_edit.py';
|
|
@@ -98,6 +100,18 @@ function allowSpecEdit(target) {
|
|
|
98
100
|
return { added };
|
|
99
101
|
}
|
|
100
102
|
|
|
103
|
+
// .fw/backup/ chứa bản sao file người dùng đã sửa, giữ lại khi nâng cấp — không nên vào git.
|
|
104
|
+
function ignoreBackup(target) {
|
|
105
|
+
const p = path.join(target, '.gitignore');
|
|
106
|
+
const text = fs.existsSync(p) ? fs.readFileSync(p, 'utf8') : '';
|
|
107
|
+
const lines = text.split(/\r?\n/).map((l) => l.trim());
|
|
108
|
+
if (lines.includes(GITIGNORE_LINE) || lines.includes('/' + GITIGNORE_LINE)) return false;
|
|
109
|
+
const sep = text && !text.endsWith('\n') ? '\n' : '';
|
|
110
|
+
const block = '\n# @educa-corp/fw — bản sao file đã sửa, tạo khi nâng cấp\n' + GITIGNORE_LINE + '\n';
|
|
111
|
+
fs.writeFileSync(p, text + sep + block);
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
101
115
|
function install(target) {
|
|
102
116
|
const old = readManifest(target);
|
|
103
117
|
const stamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
|
@@ -163,6 +177,7 @@ function install(target) {
|
|
|
163
177
|
changes: changesSince(old.version),
|
|
164
178
|
python: findPython(),
|
|
165
179
|
permissions: allowSpecEdit(target),
|
|
180
|
+
gitignore: ignoreBackup(target),
|
|
166
181
|
};
|
|
167
182
|
}
|
|
168
183
|
|
|
@@ -193,6 +208,7 @@ function printReport(r) {
|
|
|
193
208
|
}
|
|
194
209
|
console.log(` thêm ${r.added.length} · cập nhật ${r.updated.length} · gỡ ${r.removed.length}`);
|
|
195
210
|
if (r.configCreated) console.log(` tạo ${CONFIG} — hãy mở file này và khai specs, tracker, units`);
|
|
211
|
+
if (r.gitignore) console.log(` thêm ${GITIGNORE_LINE} vào .gitignore`);
|
|
196
212
|
if (r.permissions.error) console.log(` ⚠ ${r.permissions.error}`);
|
|
197
213
|
else if (r.permissions.added.length) console.log(` thêm ${r.permissions.added.length} luật cho phép vào ${SETTINGS} (AI sửa spec không phải hỏi quyền)`);
|
|
198
214
|
if (r.python) {
|
package/commands/prd.md
CHANGED
|
@@ -5,7 +5,7 @@ argument-hint: "<EPIC-ID> [change <mô tả thay đổi>] [--force]"
|
|
|
5
5
|
|
|
6
6
|
# /prd
|
|
7
7
|
|
|
8
|
-
Mục đích: biến epic đã làm rõ (`/product`) thành **PRD chính thức**, gồm các use case,
|
|
8
|
+
Mục đích: biến epic đã làm rõ (`/product`) thành **PRD chính thức**, gồm các use case, Business Rule (BR) và **Acceptance Criteria (điều kiện nghiệm thu)**, viết tắt **AC**, có **mã ổn định**. Sau đó PRD là nguồn cho BDD, TDD và code.
|
|
9
9
|
|
|
10
10
|
- `/prd <EPIC-ID>`: tạo PRD từ epic `ready`.
|
|
11
11
|
- `/prd <EPIC-ID> change <mô tả>`: thêm, sửa hoặc bỏ nội dung trong PRD đã có.
|
|
@@ -21,7 +21,7 @@ Nếu **trước lệnh này** phiên đã có hội thoại khác, dòng đầu
|
|
|
21
21
|
- Không có `.fw/config.yaml`: dừng và báo *"Chưa cài framework. Chạy `npx @educa-corp/fw install` ở thư mục gốc dự án."*
|
|
22
22
|
2. Tìm file epic: `{specs}/product/epics/{EPIC-ID}-*.md`. Đọc frontmatter để lấy `slug`, `domain`, `status`.
|
|
23
23
|
3. File PRD: `D = {specs}/{domain}/{slug}/prd.md`.
|
|
24
|
-
4. Đọc `{specs}/product/glossary.md` nếu có, và `SE section {specs}/product/product.md constraints`. Viết đúng thuật ngữ. PRD không được đi ngược ràng buộc nào.
|
|
24
|
+
4. Đọc `{specs}/product/glossary.md` nếu có, và `SE section {specs}/product/product.md constraints`. Viết đúng thuật ngữ. PRD không được đi ngược ràng buộc nào. BR bắt nguồn từ ràng buộc thì ghi `(nguồn: CON-01)`. `SE` báo không có mục đó (khuôn cũ): coi như chưa có ràng buộc, và nhắc một dòng *"Chạy `/product` để bổ sung Ràng buộc và Giai đoạn cho tầng sản phẩm."*
|
|
25
25
|
|
|
26
26
|
## Bước 2 — Chọn chế độ
|
|
27
27
|
|
|
@@ -36,7 +36,7 @@ Nếu **trước lệnh này** phiên đã có hội thoại khác, dòng đầu
|
|
|
36
36
|
## Bước 3 — Luật chung
|
|
37
37
|
|
|
38
38
|
1. **Mã ổn định.** `UC-NNN` đánh số trên toàn sản phẩm. `UC-NNN-BRnn`, `UC-NNN-ACnn` đánh số trong từng UC. Mã đã cấp thì **không bao giờ đổi, không dùng lại**. Thêm mới thì lấy số kế tiếp trong UC đó. Mục bỏ đi thì giữ dòng, gạch ngang nội dung, và ghi *"Đã bỏ (v…)"*.
|
|
39
|
-
2. **Nguồn.** Mọi
|
|
39
|
+
2. **Nguồn.** Mọi BR và AC phải ghi `(nguồn: …)`, và **chỉ ghi mã**: mã epic (`BR3`, `AC1`), ràng buộc (`CON-02`), hoặc `PRD` (thêm khi viết PRD, PO đã duyệt). Không ghi lời giải thích. `SE` chặn nếu sai.
|
|
40
40
|
3. **Dấu.** Nội dung chuyển nguyên ý từ mục `✅` của epic thì giữ `✅`. Chỗ nào AI **tự thêm, tách hoặc suy ra** thì gắn `🤖`. Chỉ PO mới đổi được `🤖` thành `✅`.
|
|
41
41
|
4. **Ngôn ngữ nghiệp vụ.** Không nói API, bảng dữ liệu hay framework.
|
|
42
42
|
5. **Tối đa 4 câu hỏi mỗi lượt, tính cả câu phụ.**
|
|
@@ -56,7 +56,7 @@ In ra đúng khối sau:
|
|
|
56
56
|
---
|
|
57
57
|
Trạng thái : {✅ Đã duyệt v{version} | 🟡 Nháp v{version} — còn {n} mục 🤖, {k} câu hỏi mở}
|
|
58
58
|
Đã ghi : {D} {· epic → handed-off · product.md nếu có}
|
|
59
|
-
Kiểm : coverage {đủ | thiếu …} · {số UC} UC · {số BR}
|
|
59
|
+
Kiểm : coverage {đủ | thiếu …} · {số UC} UC · {số BR} BR · {số AC} AC
|
|
60
60
|
Luồng : Product → [PRD ◀ bạn ở đây] → BDD → TDD · Design-spec → Code → Test → QC
|
|
61
61
|
Bước tiếp : {/bdd UC-xxx | trả lời / xác nhận các mục trên | `/clear` rồi `/prd {id}` để tiếp}
|
|
62
62
|
```
|
package/commands/product.md
CHANGED
|
@@ -63,7 +63,7 @@ Trước checkpoint 1 của epic, AI tự điền mục **Bối cảnh hệ th
|
|
|
63
63
|
- Tạo file mới: `SE create <file> <<'EOF'` … nội dung … `EOF`
|
|
64
64
|
- Sửa nội dung: `SE edit <file> <<'EOF'` `[{"old": "…", "new": "…"}]` `EOF`. Gom mọi chỗ sửa của một lượt vào **một** lần gọi. Đoạn lặp giống nhau thì thêm `"all": true`.
|
|
65
65
|
- Frontmatter: `SE set <file> checkpoint=1 open_questions=3 updated=YYYY-MM-DD`
|
|
66
|
-
- Xem các mục còn chờ PO: `SE pending <file>`. PO xác nhận các mục có mã: `SE confirm <file> AC2 AC3
|
|
66
|
+
- Xem các mục còn chờ PO: `SE pending <file>`. PO xác nhận các mục có mã: `SE confirm <file> AC2 AC3 BR8b`
|
|
67
67
|
- Đọc một mục: `SE section <file> <mã>` (mã là `<!-- sec:… -->` ở tiêu đề). **Luôn tìm mục theo mã**, không theo số hay tên. Khi sửa, giữ nguyên `<!-- sec:… -->`.
|
|
68
68
|
- `SE` báo lỗi thì **không có gì được ghi**. Đọc lại file, sửa lệnh rồi chạy lại. Không bỏ qua lỗi. Lỗi mà **không in dòng nào** là Python chưa kịp chạy: chạy lại đúng lệnh đó một lần.
|
|
69
69
|
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
[← Hướng dẫn](README.md)
|
|
2
|
+
|
|
3
|
+
# Bắt đầu
|
|
4
|
+
|
|
5
|
+
## 1. Máy cần có
|
|
6
|
+
|
|
7
|
+
| Phần mềm | Bản | Kiểm tra | Cài |
|
|
8
|
+
|---|---|---|---|
|
|
9
|
+
| Node.js | 18 trở lên | `node --version` | https://nodejs.org |
|
|
10
|
+
| Python | 3.8 trở lên | `python --version` | Windows: `winget install Python.Python.3.12` · macOS: `brew install python` |
|
|
11
|
+
| Claude Code | — | `claude --version` | https://claude.com/claude-code |
|
|
12
|
+
|
|
13
|
+
**Vì sao cần Python:** AI sửa tài liệu spec qua một công cụ viết bằng Python. Công cụ này chặn các lỗi như sửa không trúng chỗ, hỏng chữ có dấu. Thiếu Python thì mọi lệnh AI sẽ dừng ngay và báo cài.
|
|
14
|
+
|
|
15
|
+
**Windows:** nên cài Python bằng `winget` như trên (bản python.org). Bản cài từ Microsoft Store thỉnh thoảng thoát lỗi mà không chạy. Lệnh AI tự chạy lại được, nhưng bản python.org ổn định hơn.
|
|
16
|
+
|
|
17
|
+
## 2. Cài vào dự án
|
|
18
|
+
|
|
19
|
+
Mở terminal **ở thư mục gốc của dự án**. Với dự án kiểu umbrella, đó là thư mục chứa các repo con.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npx @educa-corp/fw install
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Kết quả mẫu:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
fw 0.6.2 — đã cài
|
|
29
|
+
thêm 20 · cập nhật 0 · gỡ 0
|
|
30
|
+
tạo .fw/config.yaml — hãy mở file này và khai specs, tracker, units
|
|
31
|
+
thêm .fw/backup/ vào .gitignore
|
|
32
|
+
thêm 3 luật cho phép vào .claude/settings.json (AI sửa spec không phải hỏi quyền)
|
|
33
|
+
Python: python
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Lệnh tạo ra:
|
|
37
|
+
|
|
38
|
+
| Thư mục / file | Là gì | Commit vào git? |
|
|
39
|
+
|---|---|---|
|
|
40
|
+
| `.claude/commands/` | Các lệnh `/product`, `/prd`… | ✅ Có |
|
|
41
|
+
| `.claude/settings.json` | Quyền cho AI sửa spec mà không hỏi mỗi lần | ✅ Có |
|
|
42
|
+
| `.fw/core/` | Bộ câu hỏi, khuôn tài liệu, công cụ sửa spec, bản `fw` chạy offline | ✅ Có |
|
|
43
|
+
| `.fw/config.yaml` | **Cấu hình của dự án. Bạn được sửa** | ✅ Có |
|
|
44
|
+
| `.fw/guide/` | Bộ hướng dẫn này, đúng với version đang cài | ✅ Có |
|
|
45
|
+
| `.fw/backup/` | Bản sao file bạn đã tự sửa, được giữ lại khi nâng cấp | ❌ Không. `fw install` đã tự thêm vào `.gitignore` |
|
|
46
|
+
|
|
47
|
+
Commit xong, đồng đội clone về là **có ngay**, không cần tự cài.
|
|
48
|
+
|
|
49
|
+
## 3. Khai cấu hình
|
|
50
|
+
|
|
51
|
+
Mở `.fw/config.yaml`:
|
|
52
|
+
|
|
53
|
+
```yaml
|
|
54
|
+
workspace: LMS
|
|
55
|
+
|
|
56
|
+
# Thư mục chứa mọi tài liệu (product, PRD, …). Umbrella: trỏ vào repo spec, ví dụ spec-repo/specs
|
|
57
|
+
specs: specs
|
|
58
|
+
|
|
59
|
+
# [jira] | [builtin] | [jira, builtin]
|
|
60
|
+
tracker: [builtin]
|
|
61
|
+
|
|
62
|
+
# Mỗi unit là một codebase build/deploy độc lập
|
|
63
|
+
units: []
|
|
64
|
+
# - { name: web-phuhuynh, path: apps/parent-web, stack: react }
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
| Trường | Cần khai lúc nào |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `specs` | **Ngay.** Mọi tài liệu sẽ được ghi vào thư mục này |
|
|
70
|
+
| `tracker` | **Ngay.** `builtin` thì AI tự đánh mã epic `EP-01`, `EP-02`…. `jira` thì AI hỏi Jira key (ví dụ `EDU-100`) |
|
|
71
|
+
| `units` | Chưa cần. Các lệnh hiện có chưa dùng tới trường này. Khai sẵn cũng được |
|
|
72
|
+
|
|
73
|
+
## 4. Chạy lần đầu
|
|
74
|
+
|
|
75
|
+
Mở Claude Code ở thư mục gốc dự án rồi gõ:
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
/product
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
AI sẽ hỏi về sản phẩm theo từng lượt, tối đa 4 câu mỗi lượt. Có sẵn tài liệu (BRD, đề án, slide) thì dán vào, AI sẽ trích trước để bạn chỉ cần xác nhận. Chi tiết xem [hướng dẫn /product](lenh/product.md).
|
|
82
|
+
|
|
83
|
+
Một vòng làm việc đầy đủ:
|
|
84
|
+
|
|
85
|
+
| # | Gõ | Kết quả |
|
|
86
|
+
|---|---|---|
|
|
87
|
+
| 1 | `/product` | `specs/product/product.md`: nhóm người dùng, danh sách epic |
|
|
88
|
+
| 2 | `/clear` → `/product EP-01` | File làm rõ yêu cầu của EP-01 |
|
|
89
|
+
| 3 | `/clear` → `/prd EP-01` | `specs/{domain}/{slug}/prd.md` |
|
|
90
|
+
| 4 | Khi cần đổi: `/clear` → `/prd EP-01 change khoá tài khoản sau 3 lần sai` | PRD lên version mới |
|
|
91
|
+
|
|
92
|
+
## 5. Nâng cấp
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npx @educa-corp/fw upgrade # lên bản mới nhất
|
|
96
|
+
npx @educa-corp/fw upgrade 0.7.0 # lên đúng một version
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Lệnh in ra version cũ → mới kèm danh sách thay đổi. File bạn đã tự sửa trong `.claude/commands/` hay `.fw/core/` sẽ được **sao lưu vào `.fw/backup/`** trước khi bị thay. `config.yaml` **không bao giờ** bị ghi đè. Chi tiết: [fw](lenh/fw.md).
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
[← Hướng dẫn](README.md)
|
|
2
|
+
|
|
3
|
+
# Khái niệm
|
|
4
|
+
|
|
5
|
+
Chỉ những khái niệm bạn **gặp khi dùng** các lệnh hiện có.
|
|
6
|
+
|
|
7
|
+
## Ba tầng tài liệu
|
|
8
|
+
|
|
9
|
+
| Tầng | Trả lời câu hỏi | Lệnh | File |
|
|
10
|
+
|---|---|---|---|
|
|
11
|
+
| **Sản phẩm** | Sản phẩm cho ai, gồm những tính năng lớn nào? | `/product` | `specs/product/product.md` |
|
|
12
|
+
| **Epic** (làm rõ yêu cầu) | Tính năng này thật ra cần gì? Còn chỗ nào chưa rõ? | `/product EP-01` | `specs/product/epics/EP-01-….md` |
|
|
13
|
+
| **PRD** | Đặc tả chính thức để làm BDD, thiết kế và code | `/prd EP-01` | `specs/{domain}/{slug}/prd.md` |
|
|
14
|
+
|
|
15
|
+
**Epic là nháp, PRD là bản chính thức.** Khi PRD được tạo, epic chuyển sang `handed-off` và chỉ còn là lịch sử quyết định. Muốn đổi yêu cầu thì sửa PRD, **không sửa epic**.
|
|
16
|
+
|
|
17
|
+
## Thuật ngữ
|
|
18
|
+
|
|
19
|
+
| Thuật ngữ | Nghĩa | Ví dụ |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| **Actor** (nhóm người dùng) | Nhóm người trực tiếp dùng hệ thống, mã `ACT-01`… | ACT-01 Admin |
|
|
22
|
+
| **Epic** | Một nhóm tính năng lớn của sản phẩm | EP-01 Quản trị tài khoản và phân quyền |
|
|
23
|
+
| **UC** (Use case) | Một mục tiêu của một actor, xong trong một lần tương tác | UC-005 Đổi mật khẩu và quên mật khẩu |
|
|
24
|
+
| **BR** (Business Rule) | Luật nghiệp vụ hệ thống phải tuân theo, dạng *"Hệ thống PHẢI / KHÔNG ĐƯỢC …"* | UC-001-BR01: Email PHẢI bắt buộc và duy nhất |
|
|
25
|
+
| **Business Logic** | Cách luật được thực hiện: rẽ nhánh, công thức, thông báo khi lỗi | Email trống hoặc đã có tài khoản → từ chối, báo lý do |
|
|
26
|
+
| **AC** (Acceptance Criteria, điều kiện nghiệm thu) | Điều kiện kiểm được là đạt hay không đạt, dạng *"Khi … thì …"* | UC-002-AC01: Khi tài khoản mới quá 7 ngày chưa đăng nhập thì bị chuyển inactive, đăng nhập bị từ chối |
|
|
27
|
+
| **Ràng buộc** | Điều kiện áp cho **mọi epic**, mã `CON-01`… | CON-01: Audit log không được xoá hay sửa |
|
|
28
|
+
| **Checkpoint** | Một điểm dừng để bạn chốt. `/product` có 2 (sản phẩm) hoặc 3 (epic) checkpoint | — |
|
|
29
|
+
|
|
30
|
+
## Mã ổn định
|
|
31
|
+
|
|
32
|
+
| Đối tượng | Mã | Đánh số |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| UC | `UC-001` | Toàn sản phẩm, mọi epic dùng chung một dãy |
|
|
35
|
+
| Business Rule trong PRD | `UC-003-BR02` | Trong từng UC |
|
|
36
|
+
| AC trong PRD | `UC-003-AC04` | Trong từng UC |
|
|
37
|
+
| Business Rule / AC trong epic | `BR1.`, `AC1.` | Trong epic. Chèn giữa thì dùng `BR8a.` |
|
|
38
|
+
|
|
39
|
+
**Mã đã cấp không bao giờ đổi và không dùng lại.** Mục bỏ đi được giữ lại, gạch ngang và ghi *"Đã bỏ (v1.1)"*. Nhờ vậy BDD, code và test sau này luôn trỏ đúng mục.
|
|
40
|
+
|
|
41
|
+
## Dấu 🤖 và ✅
|
|
42
|
+
|
|
43
|
+
| Dấu | Nghĩa |
|
|
44
|
+
|---|---|
|
|
45
|
+
| 🤖 | AI trích hoặc tự thêm, **chờ bạn chốt** |
|
|
46
|
+
| ✅ | Bạn đã xác nhận |
|
|
47
|
+
|
|
48
|
+
Cách xác nhận: trả lời như bình thường (*"Đúng"*, *"OK"*), hoặc xác nhận theo mã (*"OK UC-003-BR04, UC-003-AC02"*).
|
|
49
|
+
|
|
50
|
+
Tài liệu chỉ được đặt trạng thái "xong" (`ready` hoặc `approved`) khi **không còn 🤖**, **không còn câu hỏi mở**, và **đã ghi người duyệt**. Công cụ sẽ tự chặn nếu chưa đủ, AI không lách được.
|
|
51
|
+
|
|
52
|
+
## Nguồn của từng mục trong PRD
|
|
53
|
+
|
|
54
|
+
Mỗi Business Rule và AC trong PRD ghi rõ nó lấy từ đâu, và **chỉ ghi mã**:
|
|
55
|
+
|
|
56
|
+
| Ghi | Nghĩa |
|
|
57
|
+
|---|---|
|
|
58
|
+
| `(nguồn: BR3, AC1)` | Lấy từ BR3 và AC1 của epic |
|
|
59
|
+
| `(nguồn: CON-02)` | Lấy từ ràng buộc CON-02 của tầng sản phẩm |
|
|
60
|
+
| `(nguồn: PRD)` | Thêm trong lúc viết PRD, bạn đã duyệt |
|
|
61
|
+
|
|
62
|
+
Nhờ vậy công cụ kiểm được **mọi BR và AC của epic đều đã vào PRD**, không rơi mục nào.
|
|
63
|
+
|
|
64
|
+
## Vì sao phải `/clear`
|
|
65
|
+
|
|
66
|
+
Mỗi lượt hỏi-đáp, AI đọc lại **toàn bộ** hội thoại từ đầu phiên. Phiên càng dài thì mỗi lượt càng đắt. Đo thực tế: chạy tiếp trong phiên cũ đắt hơn khoảng **56%** mỗi lượt. Mọi thứ đã chốt đều nằm trong file, nên mở phiên mới **không mất gì**. Lệnh sẽ nhắc bạn đúng lúc nên `/clear`.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Hướng dẫn sử dụng `@educa-corp/fw`
|
|
2
|
+
|
|
3
|
+
> Framework làm việc với Claude Code: đưa một tính năng đi từ **ý tưởng → làm rõ yêu cầu → PRD → …**, AI hỏi và viết, con người xác nhận ở mỗi bước.
|
|
4
|
+
> Hướng dẫn này viết cho **bản 0.6.2**. Chỉ mô tả những lệnh **đã có**. Lệnh mới có thì hướng dẫn mới được bổ sung.
|
|
5
|
+
> Trong dự án đã cài framework, bản hướng dẫn đúng với version đang dùng nằm ở **`.fw/guide/`**. Có thể hỏi Claude: *"Đọc .fw/guide và cho biết cách đổi PRD"*.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Bạn muốn…
|
|
10
|
+
|
|
11
|
+
| Bạn muốn | Đọc |
|
|
12
|
+
|---|---|
|
|
13
|
+
| Cài vào dự án và chạy lần đầu | [Bắt đầu](01-bat-dau.md) |
|
|
14
|
+
| Hiểu vài khái niệm trước khi dùng (UC, BR, AC, 🤖/✅…) | [Khái niệm](02-khai-niem.md) |
|
|
15
|
+
| Biết **vai trò của mình** dùng lệnh nào | [PO / BA](vai-tro/po-ba.md) · *Dev, QC: sắp có* |
|
|
16
|
+
| Tra cứu một lệnh cụ thể | Bảng lệnh ngay bên dưới |
|
|
17
|
+
| Gặp lỗi | [Xử lý sự cố](xu-ly-su-co.md) |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Pipeline hiện có
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
cài framework ──► /product ──► /product EP-xx ──► /prd EP-xx ┄┄► (bước tiếp: /bdd — sắp có)
|
|
25
|
+
tầng làm rõ một viết PRD
|
|
26
|
+
sản phẩm tính năng ▲ │
|
|
27
|
+
└─┘ /prd EP-xx change (đổi PRD)
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Mỗi lệnh kết thúc bằng một khối giống nhau. Bạn chỉ cần đọc dòng **Bước tiếp**:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
---
|
|
34
|
+
Trạng thái : 🟡 Đang làm rõ — checkpoint 1/3, còn 2 câu hỏi mở
|
|
35
|
+
Đã ghi : specs/product/epics/EP-01-quan-tri-tai-khoan-va-phan-quyen.md
|
|
36
|
+
Luồng : [Product ◀ bạn ở đây] → PRD → BDD → TDD · Design-spec → Code → Test → QC
|
|
37
|
+
Bước tiếp : `/clear` rồi `/product EP-01`
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Bảng lệnh
|
|
43
|
+
|
|
44
|
+
| Lệnh | Ai | Làm gì | Hướng dẫn |
|
|
45
|
+
|---|---|---|---|
|
|
46
|
+
| `npx @educa-corp/fw install` | Người cài dự án | Cài framework vào dự án (một lần) | [fw](lenh/fw.md) |
|
|
47
|
+
| `npx @educa-corp/fw upgrade [version]` | Người phụ trách dự án | Nâng cấp framework | [fw](lenh/fw.md) |
|
|
48
|
+
| `/product` | PO / BA | Làm rõ **tầng sản phẩm**: tầm nhìn, nhóm người dùng, danh sách epic, ràng buộc | [/product](lenh/product.md) |
|
|
49
|
+
| `/product <EPIC-ID>` | PO / BA | **Làm rõ yêu cầu** một tính năng trước khi viết PRD | [/product](lenh/product.md) |
|
|
50
|
+
| `/prd <EPIC-ID>` | PO / BA | Viết **PRD chính thức** từ epic đã làm rõ | [/prd](lenh/prd.md) |
|
|
51
|
+
| `/prd <EPIC-ID> change <mô tả>` | PO / BA | Thêm, sửa hoặc bỏ nội dung PRD | [/prd](lenh/prd.md) |
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Ba điều nên nhớ
|
|
56
|
+
|
|
57
|
+
1. **AI đề xuất, bạn xác nhận.** Mục AI tự trích hoặc tự thêm mang dấu 🤖. Chỉ khi bạn đồng ý thì nó mới thành ✅. Không có gì được coi là "đã chốt" nếu bạn chưa xác nhận.
|
|
58
|
+
2. **Mọi thứ đã chốt đều nằm trong file.** Vì vậy bạn dừng lúc nào cũng được, và nên **`/clear` giữa các bước** để phiên mới rẻ hơn.
|
|
59
|
+
3. **Mã không bao giờ đổi.** `UC-003-BR02` hôm nay và sau 10 lần sửa vẫn là cùng một Business Rule. Mục bỏ đi thì gạch ngang, không bị xoá.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
[← Hướng dẫn](../README.md) · [Bảng lệnh](../README.md#bảng-lệnh)
|
|
2
|
+
|
|
3
|
+
# `fw` — cài và nâng cấp framework
|
|
4
|
+
|
|
5
|
+
> Lệnh chạy ở **terminal**, không chạy trong Claude Code, và không tốn token.
|
|
6
|
+
|
|
7
|
+
| Lệnh | Làm gì |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `npx @educa-corp/fw install` | Cài framework vào dự án. Chạy **một lần**, ở thư mục gốc dự án |
|
|
10
|
+
| `npx @educa-corp/fw upgrade` | Nâng cấp lên bản mới nhất |
|
|
11
|
+
| `npx @educa-corp/fw upgrade 0.7.0` | Nâng cấp (hoặc quay về) đúng một version |
|
|
12
|
+
| `node .fw/core/bin/fw.js --version` | Xem dự án đang dùng bản nào |
|
|
13
|
+
|
|
14
|
+
> ⚠️ Luôn gõ **tên đầy đủ** `@educa-corp/fw`. Trên npm có một gói khác tên `fw` của người khác, gõ `npx fw` sẽ tải nhầm gói đó.
|
|
15
|
+
|
|
16
|
+
## `install` làm những gì
|
|
17
|
+
|
|
18
|
+
1. Tải framework từ npm.
|
|
19
|
+
2. Chép vào dự án: lệnh vào `.claude/commands/`; bộ câu hỏi, khuôn, công cụ sửa spec và **một bản `fw` chạy offline** vào `.fw/core/`; **hướng dẫn sử dụng** vào `.fw/guide/`.
|
|
20
|
+
3. Tạo `.fw/config.yaml` nếu chưa có. File này **không bao giờ** bị ghi đè.
|
|
21
|
+
4. Thêm quyền vào `.claude/settings.json` để AI sửa file spec mà không hỏi mỗi lần. Cấu hình khác trong file giữ nguyên.
|
|
22
|
+
5. Thêm `.fw/backup/` vào `.gitignore` (nếu chưa có).
|
|
23
|
+
6. Kiểm Python. Thiếu thì báo ❌ kèm lệnh cài.
|
|
24
|
+
|
|
25
|
+
Bản `fw` chạy offline trong `.fw/core/bin/` có hai tác dụng: các việc tự động sau này (hook khi commit, CI) chạy **không cần mạng**, và luôn **đúng version** đã commit trong repo.
|
|
26
|
+
|
|
27
|
+
## `upgrade` làm những gì
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
fw 0.6.0 → 0.6.1 — đã nâng cấp
|
|
31
|
+
Có gì mới:
|
|
32
|
+
- Thuật ngữ: Acceptance Criteria (điều kiện nghiệm thu) …
|
|
33
|
+
thêm 0 · cập nhật 9 · gỡ 0
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
| Tình huống | Kết quả |
|
|
37
|
+
|---|---|
|
|
38
|
+
| File framework bạn **chưa** sửa | Thay bằng bản mới |
|
|
39
|
+
| File framework bạn **đã tự sửa** | Sao lưu vào `.fw/backup/<ngày giờ>/` rồi mới thay, và báo tên file |
|
|
40
|
+
| File bản mới **bỏ đi** | Gỡ, nếu bạn chưa sửa. Đã sửa thì giữ lại và báo |
|
|
41
|
+
| Lệnh có sẵn trong dự án **trùng tên** lệnh framework | Sao lưu trước khi thay |
|
|
42
|
+
| `.fw/config.yaml` | Không đụng tới |
|
|
43
|
+
|
|
44
|
+
Nâng cấp xong, commit các thay đổi để cả team cùng dùng bản mới.
|
|
45
|
+
|
|
46
|
+
## Muốn giữ thay đổi riêng của dự án
|
|
47
|
+
|
|
48
|
+
Đừng sửa file trong `.claude/commands/` hay `.fw/core/`, vì lần nâng cấp sau sẽ thay chúng (dù có sao lưu). Chỗ đúng để đặt thay đổi riêng:
|
|
49
|
+
- `.fw/config.yaml`: cấu hình dự án.
|
|
50
|
+
- `specs/product/glossary.md`: thuật ngữ của dự án.
|
|
51
|
+
- Góp ý để sửa trong chính framework, rồi nâng cấp.
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
[← Hướng dẫn](../README.md) · [Bảng lệnh](../README.md#bảng-lệnh)
|
|
2
|
+
|
|
3
|
+
# `/prd` — viết PRD chính thức
|
|
4
|
+
|
|
5
|
+
> Biến epic đã làm rõ thành **PRD**: chia thành các use case, mỗi UC có Business Rule và AC riêng với **mã ổn định**. PRD là nguồn cho BDD, thiết kế và code ở các bước sau.
|
|
6
|
+
|
|
7
|
+
| | Tạo PRD | Đổi PRD |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| **Gõ** | `/prd EP-01` | `/prd EP-01 change <mô tả thay đổi>` |
|
|
10
|
+
| **Ai** | PO / BA | PO / BA |
|
|
11
|
+
| **Cần có trước** | Epic `ready` (xem [/product](product.md)) | PRD đã có |
|
|
12
|
+
| **Số lượt** | 2 | 2 |
|
|
13
|
+
| **Ghi ra** | `specs/{domain}/{slug}/prd.md` · epic chuyển `handed-off` | `prd.md`, version tăng (1.0 → 1.1) |
|
|
14
|
+
| **Bước tiếp** | Duyệt PRD → `/bdd UC-xxx` *(sắp có)* | Duyệt lại bản mới |
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Tạo PRD: `/prd EP-01`
|
|
19
|
+
|
|
20
|
+
**Lượt 1: chia use case.** AI đề xuất danh sách UC, mỗi UC lấy những BR và AC nào của epic. Ví dụ thật với EP-01 của LMS (21 BR, 21 AC, chia thành 10 UC):
|
|
21
|
+
|
|
22
|
+
| UC | Tên | Lấy từ epic |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| UC-001 | Tạo tài khoản nhân sự | BR1, BR2, BR3, BR4, BR8a, BR8b, BR19, AC1, AC21 |
|
|
25
|
+
| UC-005 | Đổi mật khẩu và quên mật khẩu | BR4, BR6, BR12, AC15 |
|
|
26
|
+
| … | … *(10 UC)* | … |
|
|
27
|
+
|
|
28
|
+
Một BR có thể thuộc nhiều UC, ví dụ BR4 (mật khẩu mặc định) nằm ở cả UC-001 và UC-005.
|
|
29
|
+
|
|
30
|
+
Bạn đồng ý, gộp hoặc tách UC.
|
|
31
|
+
|
|
32
|
+
**Lượt 2: viết và duyệt.**
|
|
33
|
+
1. AI ghi toàn bộ PRD trong một lần.
|
|
34
|
+
2. Công cụ **kiểm độ phủ**: mọi BR và AC của epic đều phải có trong PRD. Thiếu thì AI bổ sung cho tới khi đủ. AI không được tự bỏ mục nào; muốn bỏ thì phải hỏi bạn.
|
|
35
|
+
3. AI **chỉ in các mục 🤖**, tức những chỗ AI tự thêm, tách hoặc suy ra khi viết PRD. Không in lại cả PRD.
|
|
36
|
+
4. Bạn mở file để đọc toàn bộ, rồi trả lời hoặc xác nhận theo mã: *"OK UC-003-BR04, UC-003-AC02"*.
|
|
37
|
+
5. Hết 🤖 và hết câu hỏi mở thì AI hỏi *"Ai duyệt PRD này?"*, sau đó đặt `status: approved`.
|
|
38
|
+
|
|
39
|
+
## PRD trông như thế nào
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
## 2. Use case
|
|
43
|
+
### UC-001 — Tạo tài khoản nhân sự
|
|
44
|
+
- Actor · Điều kiện trước · Kết quả sau · Luồng chính
|
|
45
|
+
|
|
46
|
+
Business Rule (BR)
|
|
47
|
+
| Mã | Business Rule | Business Logic | Nguồn |
|
|
48
|
+
| UC-001-BR01 | Email PHẢI bắt buộc và duy nhất trên toàn hệ … | Email trống hoặc đã có tài khoản → từ chối, báo lý do | (nguồn: BR1) |
|
|
49
|
+
|
|
50
|
+
Acceptance Criteria (điều kiện nghiệm thu)
|
|
51
|
+
- UC-001-AC01. Khi Admin import file 10 dòng, trong đó 2 dòng trùng email thì 8 tài khoản được tạo, 2 dòng được báo lỗi kèm lý do. (nguồn: AC1)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Gồm 5 mục: **Tổng quan** (mục tiêu, actor, phạm vi, ràng buộc áp dụng) · **Use case** · **Màn hình** · **Câu hỏi còn mở** · **Lịch sử thay đổi**. Cột `Nguồn` chỉ ghi mã (xem [Khái niệm](../02-khai-niem.md#nguồn-của-từng-mục-trong-prd)).
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Đổi PRD: `/prd EP-01 change <mô tả>`
|
|
59
|
+
|
|
60
|
+
Dùng cho **mọi** thay đổi: thêm, sửa, bỏ.
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
/prd EP-01 change khoá tài khoản sau 3 lần đăng nhập sai
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**Lượt 1: kế hoạch.** AI tìm mọi mục bị ảnh hưởng, kể cả những mục kéo theo, rồi trình bảng *(ví dụ minh hoạ)*:
|
|
67
|
+
|
|
68
|
+
| Mã | Loại | Hiện tại | Sẽ thành |
|
|
69
|
+
|---|---|---|---|
|
|
70
|
+
| UC-001-BR05 | sửa | Khoá sau 5 lần sai | Khoá sau 3 lần sai |
|
|
71
|
+
| UC-001-AC07 | thêm | — | Khi sai 3 lần thì … |
|
|
72
|
+
| UC-001-BR06 | bỏ | … | ~~…~~ Đã bỏ (v1.1) |
|
|
73
|
+
|
|
74
|
+
**Lượt 2: áp dụng.** AI sửa đúng các mục đó, thêm dòng vào Lịch sử thay đổi, tăng version. PRD đang `approved` thì quay về `draft` và **cần duyệt lại**.
|
|
75
|
+
|
|
76
|
+
## Lưu ý
|
|
77
|
+
|
|
78
|
+
- **Bạn được tự sửa tay PRD.** Nhưng giữ nguyên mã, dấu ✅ / 🤖 và đoạn `<!-- sec:… -->`. Sửa nhiều thì nên dùng `change`, để AI tìm giúp các mục bị kéo theo.
|
|
79
|
+
- **Không sửa epic sau khi đã có PRD.** Epic lúc này chỉ còn là lịch sử.
|
|
80
|
+
- PRD tạo bằng bản framework cũ (trước 0.6.1) có thể còn ghi nguồn bằng lời, ví dụ *"(nguồn: PO chốt khi viết PRD)"*. Lần sửa đầu tiên AI sẽ được yêu cầu đổi các chỗ đó thành mã, thường là `PRD`.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
[← Hướng dẫn](../README.md) · [Bảng lệnh](../README.md#bảng-lệnh)
|
|
2
|
+
|
|
3
|
+
# `/product` — làm rõ yêu cầu
|
|
4
|
+
|
|
5
|
+
> AI **hỏi ngược lại** để bạn nói ra những điều đã biết nhưng chưa viết: phạm vi, trường hợp đặc biệt, luật nghiệp vụ. Làm việc này **trước** khi viết PRD, để AI không phải đoán.
|
|
6
|
+
|
|
7
|
+
| | Tầng sản phẩm | Một epic |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| **Gõ** | `/product` | `/product EP-01` (hoặc tên tính năng) |
|
|
10
|
+
| **Ai** | PO / BA | PO / BA |
|
|
11
|
+
| **Cần có trước** | Đã cài framework | Nên có tầng sản phẩm `ready` |
|
|
12
|
+
| **Số checkpoint** | 2 | 3 |
|
|
13
|
+
| **Ghi ra** | `specs/product/product.md`, `glossary.md` | `specs/product/epics/EP-01-….md` |
|
|
14
|
+
| **Bước tiếp** | `/product <epic ưu tiên cao nhất>` | `/prd EP-01` |
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Tầng sản phẩm: `/product`
|
|
19
|
+
|
|
20
|
+
| Checkpoint | AI hỏi | Bạn chốt |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| 1. Tầm nhìn và người dùng | Sản phẩm giải quyết gì, cho ai · khác cách làm hiện tại ở đâu · có những nhóm người dùng nào · đo thành công bằng gì · những gì **không** làm | Tầm nhìn, nhóm người dùng `ACT-xx`, mục tiêu, ngoài phạm vi |
|
|
23
|
+
| 2. Epic và ràng buộc | Các nhóm tính năng lớn · ưu tiên · chia **giai đoạn** (MVP, Phase 2…) · **ràng buộc** áp cho mọi epic (pháp lý, bảo mật, vận hành, dữ liệu) | Bảng epic, ràng buộc `CON-xx` |
|
|
24
|
+
|
|
25
|
+
Cuối cùng AI hỏi **ai duyệt** tầng sản phẩm, mặc định là bạn.
|
|
26
|
+
|
|
27
|
+
**Có sẵn tài liệu (BRD, đề án…)?** Dán vào ngay lượt đầu. AI trích trước và gắn 🤖, bạn chỉ cần xác nhận hoặc sửa. Các mục **Stakeholders, Giả định, Rủi ro** chỉ được điền khi tài liệu có, AI không hỏi riêng.
|
|
28
|
+
|
|
29
|
+
## Một epic: `/product EP-01`
|
|
30
|
+
|
|
31
|
+
| Checkpoint | AI hỏi | Ví dụ thật (dự án LMS, EP-01) |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| 1. Hiểu đúng vấn đề | Bối cảnh · vấn đề · mục tiêu · actor · trong và ngoài phạm vi · phụ thuộc | "Học sinh, phụ huynh có đăng nhập không?" → chỉ có tài khoản ở hệ thống đăng nhập tập trung, chưa dùng LMS |
|
|
34
|
+
| 2. Luồng và chỗ hở | Điểm vào, luồng chính, màn hình · **soi chỗ hở**: lỗi, ranh giới phạm vi, phụ thuộc ngầm, mâu thuẫn, trường hợp nhiều | "Một phụ huynh có 2 con thì sao?" → mỗi con một email riêng |
|
|
35
|
+
| 3. Chốt để sang PRD | AI **đề xuất** Business Rule (`BR1.`…) và AC (`AC1.`…), tự kiểm độ phủ trước khi trình | 21 Business Rule, 21 AC |
|
|
36
|
+
|
|
37
|
+
Bước soi chỗ hở **luôn chạy ít nhất một vòng**, kể cả khi tài liệu bạn đưa rất dày. Tài liệu càng dày thì càng dễ tạo cảm giác "đã đủ".
|
|
38
|
+
|
|
39
|
+
**Khi bạn đổi ý giữa chừng:** nếu điều mới trái với tầng sản phẩm (ví dụ `product.md` ghi "học sinh không đăng nhập"), AI sẽ báo, mở lại các mục liên quan (về 🤖), hạ checkpoint, và đưa phương án để bạn chọn.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Trong một lượt hỏi-đáp
|
|
44
|
+
|
|
45
|
+
- **Tối đa 4 câu hỏi**, tính cả câu phụ. Câu chưa trả lời được lưu vào mục "Câu hỏi còn mở" và được hỏi lại ở lượt sau.
|
|
46
|
+
- File được **ghi ngay sau mỗi lượt**, nên bạn dừng lúc nào cũng được.
|
|
47
|
+
- Thuật ngữ mới lặp từ 2 lần trở lên: AI hỏi nghĩa, và hỏi có thêm vào `glossary.md` không.
|
|
48
|
+
|
|
49
|
+
## Dừng và chạy tiếp
|
|
50
|
+
|
|
51
|
+
- Muốn dừng: nói *"dừng"*. AI lưu lại và báo cách chạy tiếp.
|
|
52
|
+
- Chạy tiếp: `/clear` rồi gõ lại **đúng lệnh cũ**. AI đọc file và tiếp tục từ checkpoint đang dở, hỏi các câu còn mở trước.
|
|
53
|
+
- Sau mỗi checkpoint, lệnh sẽ gợi ý `/clear`. Nên làm theo, vì phiên mới rẻ hơn và không mất gì.
|
|
54
|
+
|
|
55
|
+
## Trạng thái của file
|
|
56
|
+
|
|
57
|
+
| `status` | Nghĩa | Chạy lại lệnh thì |
|
|
58
|
+
|---|---|---|
|
|
59
|
+
| `in-progress` | Đang làm rõ | Tiếp từ checkpoint đang dở |
|
|
60
|
+
| `ready` | Đã chốt hết, không còn 🤖, không còn câu hỏi mở, đã có người duyệt | Chỉ sửa khi bạn nói rõ mục cần sửa |
|
|
61
|
+
| `handed-off` | *(chỉ epic)* Đã có PRD | **Dừng.** Muốn đổi yêu cầu thì dùng `/prd EP-01 change …` |
|
|
62
|
+
|
|
63
|
+
## Lưu ý
|
|
64
|
+
|
|
65
|
+
- **Đừng xoá dấu ✅ / 🤖 hay đoạn `<!-- sec:… -->`** khi tự sửa file. Đoạn `<!-- sec:… -->` là mã của từng mục, lệnh dùng nó để tìm mục (khi xem trên GitLab hay VS Code thì không thấy).
|
|
66
|
+
- File tạo từ bản framework cũ hơn sẽ được **tự bổ sung** các mục và cột còn thiếu khi chạy lại lệnh. Nội dung đã chốt giữ nguyên.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[← Hướng dẫn](../README.md)
|
|
2
|
+
|
|
3
|
+
# PO / BA
|
|
4
|
+
|
|
5
|
+
> Bạn quyết định **làm cái gì**. Mọi bước sau (BDD, thiết kế, code, test) đều dựa trên những gì bạn chốt ở đây.
|
|
6
|
+
|
|
7
|
+
## Đường đi của bạn
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
/product ──► /product EP-xx ──► /prd EP-xx ──► duyệt PRD ──► (giao cho BDD — sắp có)
|
|
11
|
+
▲ │
|
|
12
|
+
└─┘ /prd EP-xx change khi yêu cầu thay đổi
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
| Bước | Lệnh | Bạn làm gì | Hướng dẫn |
|
|
16
|
+
|---|---|---|---|
|
|
17
|
+
| 1 | `/product` | Trả lời về sản phẩm, nhóm người dùng, epic, ràng buộc. Chốt từng mục | [/product](../lenh/product.md) |
|
|
18
|
+
| 2 | `/product EP-xx` | Trả lời AI hỏi về tính năng. Đây là chỗ **quan trọng nhất**: câu trả lời càng rõ thì PRD càng ít phải sửa | [/product](../lenh/product.md#một-epic-product-ep-01) |
|
|
19
|
+
| 3 | `/prd EP-xx` | Duyệt cách chia UC, xác nhận các mục 🤖, ghi người duyệt | [/prd](../lenh/prd.md) |
|
|
20
|
+
| 4 | `/prd EP-xx change …` | Khi yêu cầu đổi. Duyệt kế hoạch thay đổi, rồi duyệt lại PRD | [/prd](../lenh/prd.md#đổi-prd-prd-ep-01-change-mô-tả) |
|
|
21
|
+
|
|
22
|
+
## Mẹo
|
|
23
|
+
|
|
24
|
+
- **Có tài liệu thì dán vào ngay lượt đầu.** AI trích trước, bạn chỉ xác nhận. Nhưng AI vẫn sẽ hỏi những gì tài liệu **chưa nói**.
|
|
25
|
+
- **Chưa biết câu trả lời thì nói "chưa biết".** Câu đó được lưu vào "Câu hỏi còn mở" và hỏi lại lần sau. Không nên trả lời đại cho xong.
|
|
26
|
+
- **`/clear` sau mỗi checkpoint.** Rẻ hơn, và không mất gì.
|
|
27
|
+
- Một epic thường cần **2–3 buổi**. Bạn dừng lúc nào cũng được.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
[← Hướng dẫn](README.md)
|
|
2
|
+
|
|
3
|
+
# Xử lý sự cố
|
|
4
|
+
|
|
5
|
+
## Khi cài và nâng cấp
|
|
6
|
+
|
|
7
|
+
| Bạn thấy | Nguyên nhân | Làm gì |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| `npm error 404 … @educa-corp/fw` ngay sau khi có bản mới | Registry npm cần vài phút mới đọc được bản vừa phát hành | Đợi 3–5 phút. Vẫn lỗi thì chạy `npm cache clean --force` rồi thử lại |
|
|
10
|
+
| `❌ KHÔNG TÌM THẤY PYTHON 3.8+` | Máy chưa có Python, hoặc có nhưng chưa nằm trong PATH | Cài Python (xem [Bắt đầu](01-bat-dau.md#1-máy-cần-có)), **mở terminal mới**, chạy lại lệnh |
|
|
11
|
+
| `⚠ Python này cài từ Microsoft Store` | Bản Store thỉnh thoảng thoát lỗi mà không chạy | Vẫn dùng được. Nên cài bản python.org: `winget install Python.Python.3.12` |
|
|
12
|
+
| `⚠ … file bạn đã sửa bị ghi đè, bản cũ ở .fw/backup/…` | Bạn từng sửa file của framework | Mở bản sao để lấy lại phần đã sửa nếu cần. Xem [fw](lenh/fw.md#muốn-giữ-thay-đổi-riêng-của-dự-án) |
|
|
13
|
+
| `Đây là bản fw đã cài trong dự án …` | Bạn chạy `install` từ `.fw/core/bin/fw.js` | Dùng `npx @educa-corp/fw upgrade` |
|
|
14
|
+
|
|
15
|
+
## Khi chạy lệnh trong Claude Code
|
|
16
|
+
|
|
17
|
+
| Bạn thấy | Nghĩa là | Làm gì |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| Dòng bắt đầu bằng `spec_edit: …` | Công cụ sửa spec vừa **chặn** một chỗ sửa sai (không tìm thấy đoạn cần sửa, giá trị không hợp lệ…). **Không có gì bị ghi** | Không cần làm gì. AI đọc lại file rồi sửa đúng |
|
|
20
|
+
| AI chạy lại cùng một lệnh `spec_edit` | Lần trước Python thoát mà không chạy (thường gặp với bản Store) | Bình thường, AI được dặn chạy lại một lần |
|
|
21
|
+
| `không đặt được status=approved: còn 3 dòng 🤖` | Còn mục chưa được bạn xác nhận | Xác nhận các mục AI liệt kê |
|
|
22
|
+
| `… chưa có approved_by` | Chưa ghi người duyệt | Trả lời câu *"Ai duyệt?"* |
|
|
23
|
+
| `chưa được PRD dùng tới: BR7, AC12` | Có BR hoặc AC của epic chưa được đưa vào PRD | AI sẽ bổ sung. Nếu bạn muốn bỏ thì nói rõ |
|
|
24
|
+
| `(nguồn: …) chỉ được ghi mã …` | PRD đang ghi nguồn bằng lời (thường là PRD tạo bằng bản trước 0.6.1) | AI sẽ đổi thành mã, thường là `PRD` |
|
|
25
|
+
| `💡 Phiên này đã dài. Nên /clear …` | Phiên đang mang theo hội thoại cũ | Gõ `/clear` rồi gọi lại lệnh |
|
|
26
|
+
| `EP-xx chưa làm rõ xong. Chạy /product EP-xx trước` | Epic chưa `ready` | Chạy tiếp `/product EP-xx`. Thật sự cần PRD sớm thì thêm `--force`, PRD sẽ được ghi chú là tạo khi epic chưa xong |
|
|
27
|
+
| `PRD đã có …` | Gọi `/prd EP-xx` khi PRD đã tồn tại | Dùng `/prd EP-xx change <mô tả>` |
|
|
28
|
+
|
|
29
|
+
## Vẫn chưa được?
|
|
30
|
+
|
|
31
|
+
Ghi lại **lệnh đã gõ** và **dòng lỗi**, rồi gửi cho người phụ trách framework.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@educa-corp/fw",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Framework làm việc với Claude Code cho phòng PTPM",
|
|
5
5
|
"bin": {
|
|
6
6
|
"fw": "bin/fw.js"
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"ref/",
|
|
19
19
|
"templates/",
|
|
20
20
|
"tools/",
|
|
21
|
+
"docs/guide/",
|
|
21
22
|
"CHANGELOG.md"
|
|
22
23
|
],
|
|
23
24
|
"engines": {
|
package/ref/prd/change.md
CHANGED
|
@@ -5,9 +5,9 @@ Dùng cho **mọi** thay đổi: thêm, sửa, bỏ. Mã không bao giờ đánh
|
|
|
5
5
|
## Lượt 1 — Kế hoạch thay đổi
|
|
6
6
|
|
|
7
7
|
1. Đọc `D`. Lấy mô tả thay đổi từ `$ARGUMENTS`. Không có mô tả thì hỏi PO *"Bạn muốn đổi gì?"* rồi dừng.
|
|
8
|
-
2. Xác định các mục bị ảnh hưởng. Mô tả thay đổi có thể kéo theo mục khác (một
|
|
8
|
+
2. Xác định các mục bị ảnh hưởng. Mô tả thay đổi có thể kéo theo mục khác (một Business Rule (BR) đổi thì AC liên quan cũng phải đổi), nên phải tìm cả những mục đó.
|
|
9
9
|
3. Cấp mã cho mục mới:
|
|
10
|
-
-
|
|
10
|
+
- BR hoặc AC mới trong UC có sẵn: số lớn nhất **đang có trong UC đó**, kể cả dòng đã bỏ, cộng một.
|
|
11
11
|
- UC mới: `SE next-uc {specs}`.
|
|
12
12
|
4. Trình kế hoạch:
|
|
13
13
|
|
|
@@ -25,7 +25,7 @@ Dùng cho **mọi** thay đổi: thêm, sửa, bỏ. Mã không bao giờ đánh
|
|
|
25
25
|
Khi PO đồng ý kế hoạch:
|
|
26
26
|
|
|
27
27
|
1. **Một** lần `SE edit D` cho mọi chỗ sửa:
|
|
28
|
-
- Mục sửa và mục thêm mang `✅`, vì PO vừa duyệt kế hoạch.
|
|
28
|
+
- Mục sửa và mục thêm mang `✅`, vì PO vừa duyệt kế hoạch. Mục thêm ghi `(nguồn: PRD)`, trừ khi lấy từ một mã epic hay ràng buộc cụ thể.
|
|
29
29
|
- Mục bỏ: giữ dòng, gạch ngang nội dung, ghi *"Đã bỏ (v{mới})"*.
|
|
30
30
|
- Thêm dòng vào **5. Lịch sử thay đổi**, dạng *"v1.1 — UC-003: khoá sau 3 lần sai (BR02 sửa, AC05 thêm, BR04 bỏ)"*.
|
|
31
31
|
2. Tăng version phụ (1.0 → 1.1): `SE set D version=1.1 updated=…`. PRD đang `approved` thì đặt luôn `status=draft approved_by=— approved_at=—`.
|
package/ref/prd/new.md
CHANGED
|
@@ -8,15 +8,15 @@ Chỉ **2 lượt** hỏi-đáp, vì epic đã được làm rõ kỹ. Không h
|
|
|
8
8
|
2. Chạy `SE next-uc {specs}` để lấy mã UC đầu tiên. Các UC tiếp theo tăng dần từ mã đó.
|
|
9
9
|
3. Đề xuất cách chia UC.
|
|
10
10
|
- **Một UC = một mục tiêu của một actor**, xong trong một lần tương tác. Ví dụ "Đổi và quên mật khẩu", không phải "Quản lý bảo mật".
|
|
11
|
-
- Mỗi UC nên có khoảng 2–8
|
|
12
|
-
- **Mọi `
|
|
11
|
+
- Mỗi UC nên có khoảng 2–8 Business Rule (BR). Nhiều hơn thì cân nhắc tách, ít hơn một thì cân nhắc gộp.
|
|
12
|
+
- **Mọi `BR…` và `AC…` của epic phải thuộc ít nhất một UC.** Một BR áp cho nhiều UC thì ghi ở tất cả các UC đó.
|
|
13
13
|
4. Trình cho PO dạng bảng:
|
|
14
14
|
|
|
15
15
|
| UC | Tên | Actor | Lấy từ epic |
|
|
16
16
|
|---|---|---|---|
|
|
17
|
-
| UC-001 | … | ACT-01 |
|
|
17
|
+
| UC-001 | … | ACT-01 | BR8, BR9, BR10, AC4, AC5 |
|
|
18
18
|
|
|
19
|
-
Kèm tối đa 3 câu hỏi khác, chỉ khi cần. Ví dụ: một
|
|
19
|
+
Kèm tối đa 3 câu hỏi khác, chỉ khi cần. Ví dụ: một BR nên đặt vào UC nào, hoặc hai UC có nên gộp không. Không có gì cần hỏi thì chỉ hỏi *"Cách chia này được chưa?"*.
|
|
20
20
|
|
|
21
21
|
## Lượt 2 — Viết PRD
|
|
22
22
|
|
|
@@ -24,10 +24,10 @@ Khi PO đồng ý cách chia UC:
|
|
|
24
24
|
|
|
25
25
|
1. Tạo `D` bằng **một** lần `SE create`, theo khuôn `.fw/core/templates/prd.md`:
|
|
26
26
|
- **1. Tổng quan:** chuyển từ checkpoint 1 của epic. Ghi các ràng buộc `CON-xx` áp cho epic này.
|
|
27
|
-
- **2. Use case:** mỗi UC gồm điều kiện trước, kết quả sau, luồng chính (lấy các bước liên quan ở checkpoint 2 của epic), bảng
|
|
28
|
-
- **Cột Rule:** một
|
|
29
|
-
- **Cột Logic:** rẽ nhánh, công thức, thông báo khi lỗi. Lấy từ edge case và nhật ký làm rõ của epic.
|
|
30
|
-
- **
|
|
27
|
+
- **2. Use case:** mỗi UC gồm điều kiện trước, kết quả sau, luồng chính (lấy các bước liên quan ở checkpoint 2 của epic), bảng BR, Acceptance Criteria (điều kiện nghiệm thu).
|
|
28
|
+
- **Cột Business Rule:** một BR mỗi dòng, dạng *"Hệ thống PHẢI / KHÔNG ĐƯỢC …"*.
|
|
29
|
+
- **Cột Business Logic:** rẽ nhánh, công thức, thông báo khi lỗi. Lấy từ edge case và nhật ký làm rõ của epic.
|
|
30
|
+
- **AC:** dạng *"Khi … thì …"*, mô tả **kết quả nhìn thấy được**, không mô tả cơ chế.
|
|
31
31
|
- **3. Màn hình:** chuyển từ "Màn hình chính" của epic, kèm cột UC.
|
|
32
32
|
- **4. Câu hỏi còn mở:** chỉ những gì phát sinh khi viết PRD.
|
|
33
33
|
- **5. Lịch sử thay đổi:** dòng `1.0`.
|
package/ref/product/epic.md
CHANGED
|
@@ -39,12 +39,12 @@ Mỗi câu hỏi và câu trả lời được ghi vào **Nhật ký làm rõ**.
|
|
|
39
39
|
|
|
40
40
|
## Checkpoint 3 — Chốt để sang PRD
|
|
41
41
|
|
|
42
|
-
1. Từ checkpoint 1–2 và nhật ký, AI **đề xuất** danh sách
|
|
43
|
-
2. AI **đề xuất**
|
|
42
|
+
1. Từ checkpoint 1–2 và nhật ký, AI **đề xuất** danh sách Business Rule (BR), dạng *"Hệ thống PHẢI / KHÔNG ĐƯỢC …"*. Đánh số `BR1.`, `BR2.`… ở đầu dòng. Chèn giữa thì dùng `BR8a.`, **không đánh số lại**, vì `/prd` dùng các mã này để kiểm không rơi BR nào.
|
|
43
|
+
2. AI **đề xuất** Acceptance Criteria (điều kiện nghiệm thu), dạng *"Khi … thì …"*, đánh số `AC1.`, `AC2.`…. Mỗi AC phải kiểm được là đạt hay không đạt.
|
|
44
44
|
3. **Tự kiểm độ phủ** trước khi trình cho PO:
|
|
45
|
-
- Mỗi bước trong luồng chính có ít nhất một
|
|
45
|
+
- Mỗi bước trong luồng chính có ít nhất một BR hoặc AC.
|
|
46
46
|
- Mỗi edge case có kết quả mong muốn.
|
|
47
|
-
- Không có hai
|
|
47
|
+
- Không có hai BR mâu thuẫn nhau.
|
|
48
48
|
4. Trình cho PO. PO chốt từng mục. Mục nào PO chưa chốt thì giữ dấu `🤖`.
|
|
49
49
|
|
|
50
50
|
Chỉ đặt `status: ready` khi đủ cả hai điều kiện: **mọi mục đều mang dấu `✅`** và **mục `sec:open` = "Không còn"**. Còn câu hỏi mở thì vẫn ghi file, `status` giữ `in-progress`.
|
package/templates/prd.md
CHANGED
|
@@ -14,7 +14,7 @@ updated: {YYYY-MM-DD}
|
|
|
14
14
|
> PRD chính thức. Nguồn làm rõ yêu cầu: `{đường dẫn file epic}` (chỉ còn là lịch sử, không sửa tiếp ở đó).
|
|
15
15
|
> Dấu: `✅` = PO đã xác nhận · `🤖` = AI thêm khi viết PRD, chờ PO chốt.
|
|
16
16
|
> Mã (`UC-…`, `…-BR…`, `…-AC…`) **không bao giờ đổi, không dùng lại**. Mục bỏ đi thì gạch ngang và ghi "Đã bỏ (v…)".
|
|
17
|
-
> `(nguồn:
|
|
17
|
+
> `(nguồn: …)` chỉ ghi mã: `BR3` / `AC1` của epic · `CON-02` (ràng buộc) · `PRD` (thêm khi viết PRD, PO đã duyệt).
|
|
18
18
|
|
|
19
19
|
## 1. Tổng quan <!-- sec:overview -->
|
|
20
20
|
|
|
@@ -42,13 +42,13 @@ updated: {YYYY-MM-DD}
|
|
|
42
42
|
|---|---|---|
|
|
43
43
|
| 1 | {…} | {…} |
|
|
44
44
|
|
|
45
|
-
**Rule**
|
|
45
|
+
**Business Rule (BR)**
|
|
46
46
|
|
|
47
|
-
| Mã | Rule | Logic | Nguồn |
|
|
47
|
+
| Mã | Business Rule | Business Logic | Nguồn |
|
|
48
48
|
|---|---|---|---|
|
|
49
|
-
| ✅ UC-{NNN}-BR01 | {Hệ thống PHẢI / KHÔNG ĐƯỢC …} | {rẽ nhánh, công thức, điều kiện; thông báo khi lỗi} | (nguồn:
|
|
49
|
+
| ✅ UC-{NNN}-BR01 | {Hệ thống PHẢI / KHÔNG ĐƯỢC …} | {rẽ nhánh, công thức, điều kiện; thông báo khi lỗi} | (nguồn: BR1) |
|
|
50
50
|
|
|
51
|
-
**
|
|
51
|
+
**Acceptance Criteria (điều kiện nghiệm thu)**
|
|
52
52
|
|
|
53
53
|
- ✅ UC-{NNN}-AC01. Khi {…} thì {…}. (nguồn: AC1)
|
|
54
54
|
|
|
@@ -49,17 +49,17 @@ updated: {YYYY-MM-DD}
|
|
|
49
49
|
|
|
50
50
|
## Checkpoint 3 — Chốt để sang PRD <!-- sec:cp3 -->
|
|
51
51
|
|
|
52
|
-
**Rule
|
|
53
|
-
- 🤖
|
|
52
|
+
**Business Rule (BR)** (bản nháp, PRD sẽ viết chính thức). Đánh số `BR1.`, `BR2.`…; chèn thêm giữa thì dùng `BR8a.`, không đánh số lại. Sang PRD sẽ thành `UC-xxx-BRnn`:
|
|
53
|
+
- 🤖 BR1. {Hệ thống PHẢI / KHÔNG ĐƯỢC …}
|
|
54
54
|
|
|
55
|
-
**
|
|
55
|
+
**Acceptance Criteria (điều kiện nghiệm thu)** (bản nháp). Đánh số `AC1.`, `AC2.`…:
|
|
56
56
|
- 🤖 AC1. {Khi … thì …}
|
|
57
57
|
|
|
58
58
|
## Nhật ký làm rõ <!-- sec:log -->
|
|
59
59
|
|
|
60
60
|
| Vòng | # | Nhóm | Câu hỏi | PO trả lời |
|
|
61
61
|
|---|---|---|---|---|
|
|
62
|
-
| 1 | 1 | {Phạm vi / Luồng / Rule / Phụ thuộc / Thuật ngữ} | {…} | {…} |
|
|
62
|
+
| 1 | 1 | {Phạm vi / Luồng / Business Rule / Phụ thuộc / Thuật ngữ} | {…} | {…} |
|
|
63
63
|
|
|
64
64
|
## Câu hỏi còn mở <!-- sec:open -->
|
|
65
65
|
|
package/templates/product.md
CHANGED
|
@@ -46,7 +46,7 @@ updated: {YYYY-MM-DD}
|
|
|
46
46
|
|
|
47
47
|
## 6. Ràng buộc <!-- sec:constraints -->
|
|
48
48
|
|
|
49
|
-
> Ràng buộc áp cho **mọi epic**. PRD có
|
|
49
|
+
> Ràng buộc áp cho **mọi epic**. PRD có Business Rule (BR) bắt nguồn từ ràng buộc nào thì ghi `(nguồn: CON-01)`.
|
|
50
50
|
|
|
51
51
|
| Mã | Loại | Ràng buộc |
|
|
52
52
|
|---|---|---|
|
package/tools/spec_edit.py
CHANGED
|
@@ -13,11 +13,11 @@ Cách dùng (nội dung truyền qua stdin):
|
|
|
13
13
|
spec_edit.py edit <file> stdin = JSON [{"old": "...", "new": "...", "all": false}]
|
|
14
14
|
spec_edit.py set <file> key=value… sửa frontmatter
|
|
15
15
|
spec_edit.py pending <file> liệt kê các dòng còn dấu 🤖 (kèm số dòng)
|
|
16
|
-
spec_edit.py confirm <file> <mã>… đổi 🤖 → ✅ trên dòng chứa mã (vd AC2
|
|
16
|
+
spec_edit.py confirm <file> <mã>… đổi 🤖 → ✅ trên dòng chứa mã (vd AC2 BR8b ACT-03)
|
|
17
17
|
spec_edit.py section <file> <mã> in đúng một mục theo mã `<!-- sec:… -->` (vd constraints)
|
|
18
18
|
spec_edit.py upgrade <file> gắn mã mục cho file theo khuôn cũ (chạy một lần)
|
|
19
19
|
spec_edit.py next-uc <thư mục specs> in mã UC kế tiếp (quét mọi prd.md)
|
|
20
|
-
spec_edit.py coverage <epic> <prd> liệt kê
|
|
20
|
+
spec_edit.py coverage <epic> <prd> liệt kê BR / AC của epic chưa được PRD dùng tới
|
|
21
21
|
|
|
22
22
|
Mã thoát: 0 = thành công · 1 = lỗi dữ liệu (không ghi gì) · 2 = sai cách dùng.
|
|
23
23
|
"""
|
|
@@ -42,7 +42,13 @@ COMMON_INT = {"open_questions": (0, 999)}
|
|
|
42
42
|
DATE = re.compile(r"^\d{4}-\d{2}-\d{2}$")
|
|
43
43
|
VERSION = re.compile(r"^\d+\.\d+$")
|
|
44
44
|
UC_HEADING = re.compile(r"^###\s+UC-(\d{3})\b", re.M)
|
|
45
|
-
EPIC_ITEM = re.compile("^\\s*- [\U0001F916\u2705] ((?:R|AC)\\d+[a-z]?)\\.", re.M)
|
|
45
|
+
EPIC_ITEM = re.compile("^\\s*- [\U0001F916\u2705] ((?:BR|R|AC)\\d+[a-z]?)\\.", re.M)
|
|
46
|
+
# Epic theo khu\u00f4n tr\u01b0\u1edbc 0.6.1 \u0111\u00e1nh s\u1ed1 Business Rule l\u00e0 R1\u2026; t\u1eeb 0.6.1 l\u00e0 BR1\u2026. Coi hai ki\u1ec3u l\u00e0 m\u1ed9t m\u00e3.
|
|
47
|
+
OLD_BR = re.compile(r"(?<![\w-])R(\d+[a-z]?)(?![\w])")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def br_code(code):
|
|
51
|
+
return OLD_BR.sub(r"BR\1", code.strip())
|
|
46
52
|
SOURCE_REF = re.compile(r"\(nguồn:\s*([^)]*)\)")
|
|
47
53
|
|
|
48
54
|
|
|
@@ -74,6 +80,7 @@ def cmd_create(path):
|
|
|
74
80
|
fail(path + " đã tồn tại — không ghi đè. Dùng `edit` hoặc `set`.")
|
|
75
81
|
text = stdin_text()
|
|
76
82
|
check_keys(file_type(text), [], sec_keys(text), creating=True)
|
|
83
|
+
check_sources(file_type(text), text)
|
|
77
84
|
write(path, text)
|
|
78
85
|
print("đã tạo " + path)
|
|
79
86
|
|
|
@@ -103,6 +110,7 @@ def cmd_edit(path):
|
|
|
103
110
|
for e in edits:
|
|
104
111
|
text = text.replace(e["old"], e["new"]) if e.get("all") else text.replace(e["old"], e["new"], 1)
|
|
105
112
|
check_keys(file_type(text), before, sec_keys(text), creating=False)
|
|
113
|
+
check_sources(file_type(text), text)
|
|
106
114
|
write(path, text)
|
|
107
115
|
print("đã sửa %d chỗ trong %s" % (len(edits), path))
|
|
108
116
|
|
|
@@ -223,6 +231,34 @@ def template_sections(ftype):
|
|
|
223
231
|
return [(k, normalize_title(t), SEC_KEY.sub("", t).strip()) for _, _, t, k in headings(lines) if k]
|
|
224
232
|
|
|
225
233
|
|
|
234
|
+
# (nguồn: …) chỉ nhận mã, để script truy được BR/AC của PRD về đúng chỗ sinh ra nó:
|
|
235
|
+
# BR3 / AC1 (epic; R3 là kiểu cũ) · CON-02 (ràng buộc tầng sản phẩm) · PRD (thêm khi viết PRD, PO đã duyệt)
|
|
236
|
+
SOURCE_TOKEN = re.compile(r"^(?:BR\d+[a-z]?|R\d+[a-z]?|AC\d+[a-z]?|CON-\d+|PRD)$")
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def bad_sources(text):
|
|
240
|
+
out = []
|
|
241
|
+
# Dòng trích dẫn (>) là chú thích của khuôn (vd "(nguồn: …) chỉ ghi mã"), không phải nguồn thật.
|
|
242
|
+
body = "\n".join(l for l in text.split("\n") if not l.lstrip().startswith(">"))
|
|
243
|
+
for refs in SOURCE_REF.findall(body):
|
|
244
|
+
for r in (x.strip() for x in refs.split(",")):
|
|
245
|
+
if r and not SOURCE_TOKEN.match(r) and r not in out:
|
|
246
|
+
out.append(r)
|
|
247
|
+
return out
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def source_rule_msg(bad):
|
|
251
|
+
return ("(nguồn: …) chỉ được ghi mã — BR3 / AC1 của epic, CON-02, hoặc PRD (mục thêm khi viết PRD, "
|
|
252
|
+
"PO đã duyệt). Đang ghi: " + " · ".join('"%s"' % b for b in bad))
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def check_sources(ftype, text):
|
|
256
|
+
if ftype == "prd":
|
|
257
|
+
bad = bad_sources(text)
|
|
258
|
+
if bad:
|
|
259
|
+
fail(source_rule_msg(bad))
|
|
260
|
+
|
|
261
|
+
|
|
226
262
|
def check_keys(ftype, before, after, creating):
|
|
227
263
|
dup = sorted({k for k in after if after.count(k) > 1})
|
|
228
264
|
if dup:
|
|
@@ -271,10 +307,23 @@ def cmd_upgrade(path):
|
|
|
271
307
|
added.append(k)
|
|
272
308
|
else:
|
|
273
309
|
unknown.append(title)
|
|
274
|
-
|
|
275
|
-
|
|
310
|
+
new_text = "\n".join(lines)
|
|
311
|
+
# Mã Business Rule: R3 → BR3 (khuôn từ 0.6.1). Epic: đổi mọi chỗ. PRD: chỉ trong (nguồn: …).
|
|
312
|
+
renamed = [0]
|
|
313
|
+
if ftype == "epic":
|
|
314
|
+
new_text, renamed[0] = OLD_BR.subn(r"BR\1", new_text)
|
|
315
|
+
elif ftype == "prd":
|
|
316
|
+
def fix(m):
|
|
317
|
+
out = OLD_BR.sub(r"BR\1", m.group(0))
|
|
318
|
+
renamed[0] += out != m.group(0)
|
|
319
|
+
return out
|
|
320
|
+
new_text = SOURCE_REF.sub(fix, new_text)
|
|
321
|
+
if new_text != text:
|
|
322
|
+
write(path, new_text)
|
|
276
323
|
missing = [(k, orig) for k, _, orig in tpl if k not in used]
|
|
277
324
|
print("đã gắn mã: %s" % (", ".join(added) or "không có gì mới"))
|
|
325
|
+
if renamed[0]:
|
|
326
|
+
print("đã đổi mã Business Rule R… → BR…: %d chỗ" % renamed[0])
|
|
278
327
|
if unknown:
|
|
279
328
|
print("⚠ tiêu đề không khớp khuôn (giữ nguyên): " + " · ".join(unknown))
|
|
280
329
|
if missing:
|
|
@@ -293,21 +342,28 @@ def cmd_next_uc(specs_dir):
|
|
|
293
342
|
def cmd_coverage(epic_path, prd_path):
|
|
294
343
|
wanted = []
|
|
295
344
|
for code in EPIC_ITEM.findall(read(epic_path)):
|
|
296
|
-
if code not in wanted:
|
|
297
|
-
wanted.append(code)
|
|
345
|
+
if br_code(code) not in wanted:
|
|
346
|
+
wanted.append(br_code(code))
|
|
298
347
|
used = set()
|
|
299
348
|
for refs in SOURCE_REF.findall(read(prd_path)):
|
|
300
|
-
used.update(r
|
|
349
|
+
used.update(br_code(r) for r in refs.split(","))
|
|
301
350
|
missing = [c for c in wanted if c not in used]
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
351
|
+
bad = bad_sources(read(prd_path))
|
|
352
|
+
# CON-xx (ràng buộc tầng sản phẩm) và PRD (thêm khi viết PRD) hợp lệ dù không có trong epic.
|
|
353
|
+
unknown = sorted(r for r in used - set(wanted) - {""}
|
|
354
|
+
if SOURCE_TOKEN.match(r) and not re.match(r"^(CON-\d+|PRD)$", r))
|
|
355
|
+
print("epic có %d BR/AC · PRD dùng %d" % (len(wanted), len(wanted) - len(missing)), flush=True)
|
|
356
|
+
problems = []
|
|
357
|
+
if bad:
|
|
358
|
+
problems.append(source_rule_msg(bad))
|
|
305
359
|
if unknown:
|
|
306
|
-
|
|
360
|
+
problems.append("PRD ghi nguồn không có trong epic: %s (gõ nhầm mã?)" % ", ".join(unknown))
|
|
307
361
|
if missing:
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
362
|
+
problems.append("chưa được PRD dùng tới: %s. Đưa vào một UC với (nguồn: …), hoặc hỏi PO có bỏ không."
|
|
363
|
+
% ", ".join(missing))
|
|
364
|
+
if problems:
|
|
365
|
+
fail("\n - ".join([""] + problems))
|
|
366
|
+
print("đủ — mọi BR/AC của epic đều có trong PRD")
|
|
311
367
|
|
|
312
368
|
|
|
313
369
|
PENDING, CONFIRMED = "\U0001F916", "✅"
|
|
@@ -327,7 +383,7 @@ def cmd_pending(path):
|
|
|
327
383
|
|
|
328
384
|
def cmd_confirm(path, codes):
|
|
329
385
|
if not codes:
|
|
330
|
-
fail("cần ít nhất một mã, ví dụ: confirm <file> AC2 AC3
|
|
386
|
+
fail("cần ít nhất một mã, ví dụ: confirm <file> AC2 AC3 BR8b", 2)
|
|
331
387
|
lines = read(path).split("\n")
|
|
332
388
|
# Mã khớp nguyên từ: AC2 không được khớp nhầm AC20.
|
|
333
389
|
targets = {}
|