@relipa/ai-flow-kit 0.2.0-beta.0 → 0.2.0-beta.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/bin/aiflow.js +64 -4
- package/custom/rules/output-language.md +36 -0
- package/custom/skills/automation-testing/SKILL.md +1 -1
- package/custom/skills/ba-skills/skill-ba-build-business-rules-v1.md +2 -0
- package/custom/skills/ba-skills/skill-ba-initial-analysis-template-v1.md +7 -5
- package/custom/skills/ba-skills/skill-ba-initial-analysis-v1.md +8 -1
- package/custom/skills/ba-skills/skill-ba-mermaid-flowchart-v1.md +2 -0
- package/custom/skills/ba-skills/skill-ba-qna-template-v1.md +6 -5
- package/custom/skills/ba-skills/skill-ba-qna-v1.md +19 -5
- package/custom/skills/ba-skills/skill-ba-ui-prototype-v1.md +2 -0
- package/custom/skills/ba-skills/skill-ba-write-uc-spec-v1.md +2 -0
- package/custom/skills/coverage-check/SKILL.md +2 -0
- package/custom/skills/create-system-requirement/SKILL.md +115 -33
- package/custom/skills/deploy-model/SKILL.md +2 -0
- package/custom/skills/design-experiment/SKILL.md +2 -0
- package/custom/skills/evaluate-model/SKILL.md +2 -0
- package/custom/skills/evidence-aggregation/SKILL.md +2 -0
- package/custom/skills/execute-flow/SKILL.md +2 -0
- package/custom/skills/explore-data/SKILL.md +2 -0
- package/custom/skills/frame-ml-problem/SKILL.md +2 -0
- package/custom/skills/gate-review/SKILL.md +2 -0
- package/custom/skills/generate-spec/SKILL.md +2 -0
- package/custom/skills/generate-test-report/SKILL.md +2 -0
- package/custom/skills/generate-testcase/SKILL.md +2 -0
- package/custom/skills/impact-analysis/SKILL.md +2 -0
- package/custom/skills/improve-algorithm/SKILL.md +2 -0
- package/custom/skills/ingest-data/SKILL.md +117 -3
- package/custom/skills/log-bug/SKILL.md +2 -0
- package/custom/skills/pr-impact-analysis/SKILL.md +2 -0
- package/custom/skills/read-study-requirement/SKILL.md +13 -7
- package/custom/skills/report-customer/SKILL.md +6 -2
- package/custom/skills/retest-orchestration/SKILL.md +2 -0
- package/custom/skills/review-plan/SKILL.md +2 -0
- package/custom/skills/script-sync/SKILL.md +1 -1
- package/custom/skills/test-analysis/SKILL.md +2 -0
- package/custom/skills/test-skills/rules/qa-writing-standards.md +2 -2
- package/custom/skills/train-model/SKILL.md +2 -0
- package/custom/templates/shared/create-spec-workflow.md +93 -31
- package/custom/templates/shared/create-testcase-workflow.md +32 -1
- package/custom/templates/shared/gate-workflow.md +15 -0
- package/docs/common/CHANGELOG.md +23 -0
- package/package.json +1 -1
- package/scripts/create-score-excel.js +4 -111
- package/scripts/init.js +24 -1
- package/scripts/link-resolver.js +101 -4
- package/scripts/task.js +25 -8
- package/scripts/ticket-writer.js +247 -0
- package/scripts/use.js +141 -0
|
@@ -3,7 +3,29 @@
|
|
|
3
3
|
> **For:** `create-spec`
|
|
4
4
|
> **Persona:** Business Analyst (BA)
|
|
5
5
|
> **Goal:** Chuyển đổi yêu cầu thô (Jira / Backlog / file) thành tài liệu Đặc tả Use Case (UC Spec) hoàn chỉnh để bàn giao Dev/Test, tuân theo quy trình UCflow.
|
|
6
|
-
> **Interaction Rules:** Hỏi ONE câu hỏi tại một thời điểm — đợi BA trả lời trước khi tiếp tục. KHÔNG hỏi nhiều câu cùng lúc.
|
|
6
|
+
> **Interaction Rules:** Hỏi ONE câu hỏi tại một thời điểm — đợi BA trả lời trước khi tiếp tục. KHÔNG hỏi nhiều câu cùng lúc. Số lượng câu hỏi KHÔNG cố định — phải phản ánh đúng độ phức tạp thực tế của yêu cầu (xem chi tiết ở Gate 1 Bước 2).
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
### Quy ước Blocking / Non-blocking & đánh dấu PENDING
|
|
11
|
+
|
|
12
|
+
> Áp dụng cho **cả 4 Gate** — dùng để quyết định câu hỏi nào bắt buộc phải Confirmed trước khi tiến gate tiếp theo, và câu nào được phép defer mà vẫn cho qua.
|
|
13
|
+
|
|
14
|
+
Mỗi Gap/Assumption/câu hỏi Q&A phải được gắn nhãn mức độ ảnh hưởng ngay khi phát hiện:
|
|
15
|
+
|
|
16
|
+
- **🔴 Blocking** — chưa trả lời thì KHÔNG thể tiến gate tiếp theo một cách đáng tin cậy. Gồm: ảnh hưởng cấu trúc dữ liệu (thêm/sửa field, quan hệ), luồng chính (happy path) của use case, phân quyền/bảo mật, hoặc quyết định kiến trúc khó đảo ngược sau khi đã code.
|
|
17
|
+
- **🟡 Non-blocking** — có thể tạm ghi Open (deferred) và vẫn đi tiếp. Gồm: chi tiết UI/text hiển thị, giá trị mặc định có thể chỉnh sau, alternative/exception flow hiếm gặp, câu hỏi cần chờ khách hàng xác nhận nhưng không chặn thiết kế các phần khác.
|
|
18
|
+
|
|
19
|
+
**Khi BA/khách hàng defer (chưa trả lời ngay) một câu hỏi:**
|
|
20
|
+
|
|
21
|
+
- 🟡 Non-blocking → đánh dấu **Open**, tiếp tục ngay sang câu hỏi kế tiếp hoặc gate kế tiếp — KHÔNG chặn.
|
|
22
|
+
- 🔴 Blocking → AI phải hiển thị cảnh báo rõ lý do đây là blocking + hệ quả nếu bỏ qua, rồi hỏi xác nhận BA có chắc muốn tiếp tục không. Nếu BA xác nhận đồng ý tiếp tục dù chưa trả lời → vẫn đánh dấu Open nhưng giữ nhãn 🔴 Blocking (không được hạ xuống Non-blocking) — mục "Điều kiện dừng chặn APPROVED" ở mỗi Gate dựa vào nhãn này.
|
|
23
|
+
|
|
24
|
+
**Đánh dấu PENDING trong tài liệu output:** bất kỳ nội dung nào phụ thuộc vào một câu hỏi còn Open (Blocking hoặc Non-blocking) phải ghi rõ marker tại đúng vị trí liên quan trong `Analysis_v(n).md` / `UI-Prototype` / `UC-Spec_v1.md`, thay vì tự suy diễn nội dung để lấp đầy:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
⚠️ [PENDING - QA-0N] <mô tả ngắn nội dung còn thiếu>
|
|
28
|
+
```
|
|
7
29
|
|
|
8
30
|
---
|
|
9
31
|
|
|
@@ -189,40 +211,48 @@ Nếu workspace mở dạng parent folder chứa cả `ak docs` lẫn source cod
|
|
|
189
211
|
- Xem xét các khía cạnh: validate dữ liệu, định dạng nhập liệu, phân quyền, xử lý ngoại lệ, thông báo lỗi
|
|
190
212
|
- Liệt kê **toàn bộ** Gap/Assumption tìm được — không chỉ những điểm "cốt yếu"
|
|
191
213
|
|
|
214
|
+
> ❌ **KHÔNG** tự giới hạn số câu hỏi theo một số cố định (ví dụ luôn dừng ở 5-6 câu) bất kể độ phức tạp của input. Số câu hỏi phải phản ánh đúng số Gap/Assumption thực sự tồn tại: yêu cầu đơn giản (1 field, 1 luồng) có thể chỉ cần 2-3 câu; yêu cầu phức tạp (nhiều actor, nhiều luồng, tích hợp hệ thống khác) có thể cần 15-20+ câu. Nếu trong lúc hỏi/nghe trả lời phát hiện thêm Gap mới, bổ sung ngay câu hỏi mới vào cuối vòng lặp — không đóng vòng lặp sớm chỉ vì đã hỏi "đủ nhiều".
|
|
215
|
+
|
|
216
|
+
**Phân loại mức ảnh hưởng:** trước khi hỏi, gắn nhãn mỗi Gap là 🔴 Blocking hoặc 🟡 Non-blocking theo [Quy ước Blocking / Non-blocking](#quy-ước-blocking--non-blocking--đánh-dấu-pending) ở đầu file.
|
|
217
|
+
|
|
192
218
|
**Vòng lặp hỏi-đáp:** Với từng Gap/Assumption trong danh sách, hỏi **ONE câu một lúc**, đợi BA trả lời trước khi hỏi câu tiếp theo:
|
|
193
219
|
|
|
194
220
|
- BA trả lời đủ rõ → đánh dấu **Confirmed**, tích hợp ngay vào bản phân tích đang xây dựng, chuyển sang câu hỏi tiếp theo
|
|
195
|
-
- BA trả lời "chưa biết" / "để hỏi lại [stakeholder]" / "chưa chốt được ngay"
|
|
196
|
-
-
|
|
221
|
+
- BA trả lời "chưa biết" / "để hỏi lại [stakeholder]" / "chưa chốt được ngay":
|
|
222
|
+
- 🟡 Non-blocking → đánh dấu **Open** (deferred), ghi chú lý do, chuyển ngay sang câu hỏi tiếp theo — KHÔNG chặn
|
|
223
|
+
- 🔴 Blocking → hiển thị cảnh báo lý do đây là blocking + hệ quả nếu bỏ qua, hỏi BA xác nhận có chắc muốn tiếp tục không; BA xác nhận → đánh dấu **Open** nhưng giữ nhãn 🔴 Blocking, vẫn chuyển sang câu tiếp theo
|
|
224
|
+
- Lặp lại cho tới khi **mọi** Gap/Assumption trong danh sách đã được hỏi (mỗi câu ở trạng thái Confirmed hoặc Open tường minh, kèm nhãn Blocking/Non-blocking)
|
|
197
225
|
|
|
198
|
-
Chỉ sau khi vòng lặp này hoàn tất (không còn Gap nào **chưa được hỏi**) mới chuyển sang Bước 3. Số lượng câu hỏi Open còn lại
|
|
226
|
+
Chỉ sau khi vòng lặp này hoàn tất (không còn Gap nào **chưa được hỏi**) mới chuyển sang Bước 3. Số lượng câu hỏi Open còn lại (và nhãn Blocking/Non-blocking của chúng) chính là input cho Gate 2.
|
|
199
227
|
|
|
200
228
|
#### Bước 3: Soạn thảo Kết quả phân tích sơ bộ
|
|
201
229
|
- **READ template:** `.claude/skills/ba-skills/skill-ba-initial-analysis-template-v1.md`
|
|
202
230
|
- Điền đầy đủ các mục theo template: Actors, Facts, Assumptions, Gap Analysis table — dùng câu trả lời **Confirmed** từ vòng lặp Bước 2 làm nội dung chính, không phải giả định của AI
|
|
203
|
-
- Gap Analysis table chỉ còn liệt kê các Gap **Open** (deferred) từ vòng lặp Bước 2
|
|
231
|
+
- Gap Analysis table chỉ còn liệt kê các Gap **Open** (deferred) từ vòng lặp Bước 2, kèm nhãn 🔴 Blocking / 🟡 Non-blocking
|
|
232
|
+
- Với mỗi Gap Open, chèn marker `⚠️ [PENDING - QA-0N]` tại đúng vị trí nội dung liên quan (mục 3/4 của template) thay vì bỏ trống hoặc tự suy diễn
|
|
233
|
+
- Nếu tài liệu còn ít nhất 1 Gap Open → thêm banner đầu tài liệu: `⚠️ TÀI LIỆU CHƯA ĐẦY ĐỦ — còn [N] câu hỏi Open ([X] Blocking, [Y] Non-blocking), xem QnA-Log_v1.md`
|
|
204
234
|
- Lưu: `02.BA-Specs/01.Analysis/[functionId]/Analysis_v1.md`
|
|
205
235
|
|
|
206
236
|
#### Bước 4: Soạn thảo danh sách Q&A
|
|
207
237
|
|
|
208
238
|
- **READ skill:** `.claude/skills/ba-skills/skill-ba-qna-v1.md`
|
|
209
239
|
- **READ template:** `.claude/skills/ba-skills/skill-ba-qna-template-v1.md`
|
|
210
|
-
- Ghi lại **toàn bộ** câu hỏi đã hỏi ở vòng lặp Bước 2 dưới dạng log: mỗi câu kèm câu trả lời và trạng thái cuối cùng
|
|
240
|
+
- Ghi lại **toàn bộ** câu hỏi đã hỏi ở vòng lặp Bước 2 dưới dạng log: mỗi câu kèm câu trả lời, nhãn Blocking/Non-blocking, và trạng thái cuối cùng
|
|
211
241
|
- **Confirmed** — BA đã trả lời trong Bước 2, ghi kèm câu trả lời
|
|
212
|
-
- **Open** — BA đã xác nhận chưa trả lời được ngay (cần hỏi lại stakeholder khác), ghi kèm lý do deferred
|
|
242
|
+
- **Open** — BA đã xác nhận chưa trả lời được ngay (cần hỏi lại stakeholder khác), ghi kèm lý do deferred + nhãn 🔴 Blocking hoặc 🟡 Non-blocking
|
|
213
243
|
- Không tạo câu hỏi mới ở bước này — đây là bản ghi (log) của vòng lặp đã chạy ở Bước 2, không phải một vòng hỏi mới
|
|
214
244
|
- Lưu: `02.BA-Specs/02.QnA/[functionId]/QnA-Log_v1.md`
|
|
215
245
|
|
|
216
246
|
#### Bước 5: Gate Review & Pause
|
|
217
247
|
- **INVOKE** `gate-review` skill (generate mode) — ghi `.aiflow/review/gate-1-[functionId].md`
|
|
218
|
-
- **Kiểm tra Q&A bắt buộc:** Đọc `QnA-Log_v1.md` — đếm số câu
|
|
248
|
+
- **Kiểm tra Q&A bắt buộc:** Đọc `QnA-Log_v1.md` — đếm số câu **Open**, tách riêng theo nhãn 🔴 Blocking và 🟡 Non-blocking
|
|
219
249
|
- Hiển thị gate pause message — chờ phản hồi từ BA
|
|
220
250
|
|
|
221
|
-
**Nếu
|
|
251
|
+
**Nếu còn câu Open 🔴 Blocking → từ chối APPROVED ngay, hiển thị:**
|
|
222
252
|
|
|
223
|
-
⛔ GATE 1 BLOCKED — Còn [N] câu hỏi chưa được trả lời trong QnA-Log_v1.md.
|
|
253
|
+
⛔ GATE 1 BLOCKED — Còn [N] câu hỏi Blocking chưa được trả lời trong QnA-Log_v1.md.
|
|
224
254
|
|
|
225
|
-
| ID | Câu hỏi còn Open |
|
|
255
|
+
| ID | Câu hỏi còn Open (Blocking) |
|
|
226
256
|
|----|-----------------|
|
|
227
257
|
| [Q_ID] | [nội dung câu hỏi] |
|
|
228
258
|
|
|
@@ -231,17 +261,26 @@ Vui lòng cung cấp câu trả lời theo một trong 3 cách:
|
|
|
231
261
|
→ Option B: paste nội dung bảng Q&A đã điền vào chat
|
|
232
262
|
→ Option C: đặt file `QnA-Log_v1_response.md` vào thư mục `02.QnA/[functionId]/` rồi gõ NEXT
|
|
233
263
|
|
|
234
|
-
❌ APPROVED không được chấp nhận cho đến khi
|
|
264
|
+
❌ APPROVED không được chấp nhận cho đến khi mọi câu hỏi **Blocking** đã Confirmed.
|
|
235
265
|
|
|
236
|
-
**
|
|
266
|
+
**Nếu chỉ còn câu Open 🟡 Non-blocking (không còn Blocking) → cho phép APPROVED, nhưng hiển thị cảnh báo ngay trong gate pause message trước khi BA gõ APPROVED:**
|
|
267
|
+
|
|
268
|
+
⚠️ Còn [N] câu hỏi Non-blocking chưa Confirmed — có thể APPROVED để tiếp tục, tài liệu sẽ giữ marker `[PENDING - QA-0N]` tại các phần liên quan cho tới khi được bổ sung ở Gate 2.
|
|
269
|
+
|
|
270
|
+
| ID | Câu hỏi còn Open (Non-blocking) |
|
|
271
|
+
|----|-----------------|
|
|
272
|
+
| [Q_ID] | [nội dung câu hỏi] |
|
|
273
|
+
|
|
274
|
+
**Khi APPROVED (không còn câu Blocking Open — câu Non-blocking Open được phép tồn tại):**
|
|
237
275
|
→ **INVOKE** `gate-review` skill (verify mode) — chạy `ak review check --gate 1 --ticket [functionId]`
|
|
238
276
|
→ Nếu passed: `ak gate 1 approved --ticket [functionId]` → chuyển Gate 2
|
|
239
277
|
→ Nếu blocked: làm theo gate-review skill response protocol
|
|
240
278
|
|
|
241
279
|
**Definition of Done:**
|
|
242
280
|
- [ ] `functionId` đã được xác định và thư mục đầu ra đã sẵn sàng
|
|
243
|
-
- [ ] File `Analysis_v1.md` tạo đúng vị trí, cấu trúc đúng template, có bảng Gap Analysis
|
|
244
|
-
- [ ] File `QnA-Log_v1.md` có ít nhất 1 câu hỏi cho mỗi Gap,
|
|
281
|
+
- [ ] File `Analysis_v1.md` tạo đúng vị trí, cấu trúc đúng template, có bảng Gap Analysis, số Gap phản ánh đúng độ phức tạp thực tế (không bị gò về một số cố định)
|
|
282
|
+
- [ ] File `QnA-Log_v1.md` có ít nhất 1 câu hỏi cho mỗi Gap, mỗi câu có nhãn Blocking/Non-blocking rõ ràng
|
|
283
|
+
- [ ] Không còn câu hỏi 🔴 Blocking ở trạng thái Open
|
|
245
284
|
- [ ] Không có giả định nào được tự ý chốt thành Spec mà không đưa vào Q&A
|
|
246
285
|
|
|
247
286
|
> **Telemetry:** Run `ak gate 1 start --ticket [functionId]` khi bắt đầu gate này.
|
|
@@ -270,8 +309,8 @@ Vui lòng cung cấp câu trả lời theo một trong 3 cách:
|
|
|
270
309
|
#### Bước 1: Tiếp nhận và đánh giá câu trả lời
|
|
271
310
|
- Đối chiếu từng câu trả lời với câu hỏi trong `QnA-Log_v(m).md`
|
|
272
311
|
- Phân loại:
|
|
273
|
-
- Đủ thông tin → cập nhật trạng thái: **Confirmed
|
|
274
|
-
- Chưa rõ hoặc phát sinh nghiệp vụ mới → giữ trạng thái **Open**, bổ sung ghi
|
|
312
|
+
- Đủ thông tin → cập nhật trạng thái: **Confirmed**, xóa marker `[PENDING - QA-ID]` liên quan
|
|
313
|
+
- Chưa rõ hoặc phát sinh nghiệp vụ mới → giữ trạng thái **Open**, bổ sung ghi chú, giữ nguyên nhãn 🔴 Blocking / 🟡 Non-blocking đã gắn từ Gate 1 (hoặc gắn mới nếu là Gap phát sinh)
|
|
275
314
|
|
|
276
315
|
#### Bước 2: Tích hợp thông tin vào tài liệu phân tích
|
|
277
316
|
- Lấy thông tin từ các câu Confirmed → cập nhật vào `Analysis_v(n).md`
|
|
@@ -280,7 +319,7 @@ Vui lòng cung cấp câu trả lời theo một trong 3 cách:
|
|
|
280
319
|
|
|
281
320
|
#### Bước 3: Rẽ nhánh theo trạng thái Q&A
|
|
282
321
|
|
|
283
|
-
**Kịch bản A —
|
|
322
|
+
**Kịch bản A — Còn câu hỏi Open 🔴 Blocking:**
|
|
284
323
|
1. Kiểm tra `Analysis_v(n+1).md` có phát sinh Gap mới không
|
|
285
324
|
2. Tạo: `02.BA-Specs/02.QnA/[functionId]/QnA-Log_v(m+1).md` — chỉ giữ câu Open và câu mới phát sinh
|
|
286
325
|
3. **INVOKE** `gate-review` skill (generate mode) — ghi `.aiflow/review/gate-2-[functionId]-round-[m+1].md`
|
|
@@ -289,7 +328,8 @@ Vui lòng cung cấp câu trả lời theo một trong 3 cách:
|
|
|
289
328
|
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
290
329
|
⏸️ GATE 2 — VÒNG Q&A [m+1]
|
|
291
330
|
|
|
292
|
-
Câu hỏi còn Open: [N]
|
|
331
|
+
Câu hỏi còn Open (🔴 Blocking): [N]
|
|
332
|
+
Câu hỏi còn Open (🟡 Non-blocking): [M]
|
|
293
333
|
File Q&A mới: [02.BA-Specs/02.QnA/[functionId]/QnA-Log_v(m+1).md](02.BA-Specs/02.QnA/[functionId]/QnA-Log_v(m+1).md)
|
|
294
334
|
File phân tích: [02.BA-Specs/01.Analysis/[functionId]/Analysis_v(n+1).md](02.BA-Specs/01.Analysis/[functionId]/Analysis_v(n+1).md)
|
|
295
335
|
|
|
@@ -299,23 +339,40 @@ File phân tích: [02.BA-Specs/01.Analysis/[functionId]/Analysis_v(n+1).md](02
|
|
|
299
339
|
|
|
300
340
|
5. Lặp lại Bước 1 khi nhận phản hồi mới
|
|
301
341
|
|
|
302
|
-
**Nếu BA gõ APPROVED khi đang ở Kịch bản A (còn câu hỏi Open) → từ chối ngay:**
|
|
342
|
+
**Nếu BA gõ APPROVED khi đang ở Kịch bản A (còn câu hỏi Open 🔴 Blocking) → từ chối ngay:**
|
|
303
343
|
|
|
304
|
-
⛔ GATE 2 BLOCKED — Còn [N] câu hỏi chưa Confirmed.
|
|
344
|
+
⛔ GATE 2 BLOCKED — Còn [N] câu hỏi Blocking chưa Confirmed.
|
|
305
345
|
|
|
306
|
-
| ID | Câu hỏi còn Open |
|
|
346
|
+
| ID | Câu hỏi còn Open (Blocking) |
|
|
307
347
|
|----|-----------------|
|
|
308
348
|
| [Q_ID] | [nội dung câu hỏi] |
|
|
309
349
|
|
|
310
350
|
→ Cung cấp câu trả lời (Option A/B/C) rồi gõ NEXT để AI xử lý tiếp.
|
|
311
|
-
❌ APPROVED chỉ được chấp nhận khi
|
|
351
|
+
❌ APPROVED chỉ được chấp nhận khi mọi câu hỏi **Blocking** đã Confirmed.
|
|
352
|
+
|
|
353
|
+
**Kịch bản B — Chỉ còn câu hỏi Open 🟡 Non-blocking (không còn Blocking):**
|
|
354
|
+
|
|
355
|
+
1. Tạo `QnA-Log_v(m+1).md` như Kịch bản A (giữ câu Non-blocking Open + câu mới phát sinh)
|
|
356
|
+
2. Hiển thị cho BA 2 lựa chọn, không tự chọn thay:
|
|
357
|
+
|
|
358
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
359
|
+
⏸️ GATE 2 — CÒN [M] CÂU HỎI NON-BLOCKING CHƯA CONFIRMED
|
|
360
|
+
|
|
361
|
+
Các câu hỏi này không chặn thiết kế Gate 3/4, nhưng output sẽ giữ marker `[PENDING - QA-ID]` tại các phần liên quan cho tới khi được bổ sung.
|
|
362
|
+
|
|
363
|
+
→ Cách 1: cung cấp câu trả lời (Option A/B/C) rồi gõ NEXT để xử lý tiếp
|
|
364
|
+
→ Cách 2: gõ APPROVED để chấp nhận tiếp tục dù output chưa đầy đủ ở các mục này
|
|
365
|
+
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
366
|
+
|
|
367
|
+
1. BA gõ NEXT với phản hồi mới → lặp lại Bước 1
|
|
368
|
+
2. BA gõ APPROVED → coi là đã xác nhận chấp nhận output chưa đầy đủ, chuyển sang xử lý APPROVED bên dưới
|
|
312
369
|
|
|
313
|
-
**Kịch bản
|
|
370
|
+
**Kịch bản C — Tất cả đã Confirmed:**
|
|
314
371
|
1. Chốt tài liệu phân tích (phiên bản cuối cùng là đầu vào Gate 3)
|
|
315
372
|
2. **INVOKE** `gate-review` skill (generate mode) — ghi `.aiflow/review/gate-2-[functionId].md`
|
|
316
373
|
3. Hiển thị gate pause message — đợi **APPROVED**
|
|
317
374
|
|
|
318
|
-
**Khi APPROVED (
|
|
375
|
+
**Khi APPROVED (Kịch bản B hoặc C — không còn câu Blocking Open):**
|
|
319
376
|
→ **INVOKE** `gate-review` skill (verify mode) — chạy `ak review check --gate 2 --ticket [functionId]`
|
|
320
377
|
→ Nếu passed: `ak gate 2 approved --ticket [functionId]` → chuyển Gate 3
|
|
321
378
|
→ Nếu blocked: làm theo gate-review skill response protocol
|
|
@@ -323,7 +380,8 @@ File phân tích: [02.BA-Specs/01.Analysis/[functionId]/Analysis_v(n+1).md](02
|
|
|
323
380
|
**Definition of Done:**
|
|
324
381
|
- [ ] Mọi câu trả lời đều được phân tích và phân loại trạng thái
|
|
325
382
|
- [ ] Tài liệu phân tích phiên bản mới tích hợp đầy đủ thông tin nghiệp vụ mới nhất
|
|
326
|
-
- [ ] Không còn câu hỏi
|
|
383
|
+
- [ ] Không còn câu hỏi 🔴 Blocking ở trạng thái Open trước khi tiến Gate 3
|
|
384
|
+
- [ ] Nếu còn câu 🟡 Non-blocking Open, BA đã xác nhận rõ ràng (gõ APPROVED sau khi thấy cảnh báo) và các mục liên quan trong Analysis đã có marker `[PENDING - QA-ID]`
|
|
327
385
|
|
|
328
386
|
> **Telemetry:** Run `ak gate 2 start --ticket [functionId]` khi bắt đầu gate này.
|
|
329
387
|
> Run `ak gate 2 approved --ticket [functionId]` sau khi gate-review verify passed. Run as-is — không thêm shell redirects.
|
|
@@ -334,7 +392,7 @@ File phân tích: [02.BA-Specs/01.Analysis/[functionId]/Analysis_v(n+1).md](02
|
|
|
334
392
|
|
|
335
393
|
**Mục tiêu:** Dựa trên tài liệu phân tích đã chốt, thiết kế bản mẫu giao diện (Prototype) độc lập bằng HTML/CSS để trực quan hóa các thành phần và luồng tương tác.
|
|
336
394
|
|
|
337
|
-
**Điều kiện vào Gate 3:**
|
|
395
|
+
**Điều kiện vào Gate 3:** Không còn câu hỏi 🔴 Blocking Open và Gate 2 đã **APPROVED** (câu 🟡 Non-blocking Open, nếu còn, mang marker `[PENDING - QA-ID]` sang cùng Analysis).
|
|
338
396
|
|
|
339
397
|
**Đầu vào (Inputs):**
|
|
340
398
|
- `02.BA-Specs/01.Analysis/[functionId]/Analysis_v(n).md` — phiên bản cuối
|
|
@@ -349,6 +407,7 @@ File phân tích: [02.BA-Specs/01.Analysis/[functionId]/Analysis_v(n+1).md](02
|
|
|
349
407
|
#### Bước 1: Liệt kê thành phần giao diện
|
|
350
408
|
- Đọc tài liệu phân tích đã chốt
|
|
351
409
|
- Xác định: ô nhập liệu, nút bấm, liên kết, vùng hiển thị lỗi, vùng hiển thị dữ liệu
|
|
410
|
+
- Nếu Analysis còn marker `[PENDING - QA-ID]` liên quan đến thành phần giao diện nào → giữ nguyên placeholder trực quan cho thành phần đó (không tự bịa nội dung/behavior để lấp đầy) và ghi chú `<!-- PENDING - QA-ID -->` cạnh phần tử HTML tương ứng
|
|
352
411
|
|
|
353
412
|
#### Bước 2: Thiết kế và viết code HTML/CSS
|
|
354
413
|
- **READ skill:** `.claude/skills/ba-skills/skill-ba-prototype-v1.md` và làm theo
|
|
@@ -406,6 +465,7 @@ File phân tích: [02.BA-Specs/01.Analysis/[functionId]/Analysis_v(n+1).md](02
|
|
|
406
465
|
- **READ skill:** `.claude/skills/ba-skills/skill-ba-write-uc-spec-v1.md`
|
|
407
466
|
- Tạo: `02.BA-Specs/04.UC-Specs/[functionId]/UC-Spec_v1.md`
|
|
408
467
|
- Điền General Information: Use Case ID, Title, Version, Actors, Preconditions, Postconditions
|
|
468
|
+
- Nếu Analysis/Prototype đầu vào còn marker `[PENDING - QA-ID]` (câu Non-blocking chưa Confirmed) → giữ nguyên marker tại đúng vị trí liên quan (Business Rule, UI Component, Flow step) trong UC Spec, không tự bịa nội dung để lấp đầy; thêm banner đầu tài liệu: `⚠️ UC SPEC CHƯA ĐẦY ĐỦ — còn [N] mục PENDING, xem QnA-Log_v(m).md`
|
|
409
469
|
|
|
410
470
|
#### Bước 2: Mô tả luồng sự kiện (Flow of Events)
|
|
411
471
|
- **Main Flow** — happy path
|
|
@@ -450,7 +510,7 @@ File phân tích: [02.BA-Specs/01.Analysis/[functionId]/Analysis_v(n+1).md](02
|
|
|
450
510
|
- [ ] 100% element HTML được mô tả trong bảng UI Components (id, loại, ràng buộc)
|
|
451
511
|
- [ ] Mã Mermaid hợp lệ, thể hiện đủ Main/Alternative/Exception Flow
|
|
452
512
|
- [ ] Business Rules đầy đủ với mã `[BR-NNN]` và thông báo lỗi chính xác
|
|
453
|
-
- [ ]
|
|
513
|
+
- [ ] Không còn giả định ngầm chưa Confirm bị viết như thể đã chốt; mọi nội dung Non-blocking Open còn lại đã có marker `[PENDING - QA-ID]` rõ ràng tại đúng vị trí (không phải ghi chú mơ hồ chung)
|
|
454
514
|
|
|
455
515
|
> **Telemetry:** Run `ak gate 4 start --ticket [functionId]` khi bắt đầu gate này.
|
|
456
516
|
> Run `ak gate 4 approved --ticket [functionId]` sau khi gate-review verify passed. Run as-is — không thêm shell redirects.
|
|
@@ -497,9 +557,11 @@ Sau khi Gate 4 đã APPROVED (UC Spec hoàn thành):
|
|
|
497
557
|
- ❌ **KHÔNG** bỏ qua thứ tự Gate — luôn đi từ Gate 1 → 2 → 3 → 4
|
|
498
558
|
- ❌ **KHÔNG** tự chốt giả định nghiệp vụ mà không đưa vào Q&A
|
|
499
559
|
- ❌ **KHÔNG** dùng `superpowers:brainstorming` ở Gate 1 — terminal state của skill đó invoke `writing-plans`, bypass cấu trúc BA workflow
|
|
500
|
-
- ✅ **BẮT BUỘC** ở Gate 1 Bước 2: hỏi hết **toàn bộ** Gap/Assumption xác định được, từng câu một, ngay trong hội thoại — không giới hạn ở "điểm mơ hồ cốt yếu". Chỉ được ghi Open (deferred sang QnA-Log) khi BA xác nhận rõ ràng là chưa trả lời được ngay
|
|
501
|
-
-
|
|
502
|
-
- ❌ **KHÔNG
|
|
560
|
+
- ✅ **BẮT BUỘC** ở Gate 1 Bước 2: hỏi hết **toàn bộ** Gap/Assumption xác định được, từng câu một, ngay trong hội thoại — không giới hạn ở "điểm mơ hồ cốt yếu" và **không giới hạn theo một số câu hỏi cố định** bất kể độ phức tạp input. Chỉ được ghi Open (deferred sang QnA-Log) khi BA xác nhận rõ ràng là chưa trả lời được ngay
|
|
561
|
+
- ✅ **BẮT BUỘC** gắn nhãn 🔴 Blocking / 🟡 Non-blocking cho mỗi Gap/câu hỏi ngay khi phát hiện (xem [Quy ước Blocking / Non-blocking](#quy-ước-blocking--non-blocking--đánh-dấu-pending))
|
|
562
|
+
- ❌ **KHÔNG** tiến Gate 3 khi vẫn còn câu hỏi 🔴 Blocking ở trạng thái Open
|
|
563
|
+
- ❌ **KHÔNG chấp nhận APPROVED** ở Gate 1 hoặc Gate 2 khi còn bất kỳ câu hỏi 🔴 **Blocking** nào ở trạng thái **Open** — từ chối và hiển thị danh sách câu hỏi cần trả lời
|
|
564
|
+
- ✅ **ĐƯỢC PHÉP chấp nhận APPROVED** ở Gate 1/2/4 khi chỉ còn câu hỏi 🟡 **Non-blocking** Open, với điều kiện: đã hiển thị cảnh báo rõ ràng cho BA trước khi nhận APPROVED, và mọi nội dung phụ thuộc câu hỏi đó được đánh dấu `[PENDING - QA-ID]` tại đúng vị trí trong output — không được tự suy diễn nội dung để lấp đầy
|
|
503
565
|
- ✅ **BẮT BUỘC** chạy Bước 0 ở Gate 1 — xác nhận `functionId` và thư mục đầu ra trước khi làm bất cứ điều gì
|
|
504
566
|
- ✅ **BẮT BUỘC** chạy Bước 0.5 ở Gate 1 — đảm bảo AK-Docs đang ở branch `feature/[functionId]/[taskId]` trước khi ghi file đầu tiên
|
|
505
567
|
- ✅ **BẮT BUỘC** đọc skill từ `.claude/skills/ba-skills/` — không suy luận từ bộ nhớ
|
|
@@ -90,7 +90,7 @@ Ví dụ: `functionId = AD06` → thư mục gốc là `03.Testing/`
|
|
|
90
90
|
```
|
|
91
91
|
(AI-Artifacts/ = 03.Testing/07.AI-Artifacts/ — viết tắt cho ngắn gọn)
|
|
92
92
|
|
|
93
|
-
[Input: SRS / Jira / Backlog / Spec file] → Pre-flight: BẮT BUỘC xác định functionId
|
|
93
|
+
[Input: UC Spec + System Requirement (bắt buộc, cảnh báo nếu thiếu) / SRS / Jira / Backlog / Spec file] → Pre-flight: BẮT BUỘC xác định functionId
|
|
94
94
|
↓
|
|
95
95
|
Gate 1: Phân tích yêu cầu & Đánh giá rủi ro → AI-Artifacts/[functionId]/
|
|
96
96
|
↓ APPROVED
|
|
@@ -169,8 +169,37 @@ Trước khi ghi bất kỳ file nào vào `03.Testing/`, đảm bảo AK-Docs
|
|
|
169
169
|
|
|
170
170
|
> ❌ Không tự thêm `--yes` khi chưa thấy QA gõ xác nhận rõ ràng trong hội thoại.
|
|
171
171
|
|
|
172
|
+
#### Bước 0.6: Kiểm tra UC Spec & System Requirement (input bắt buộc, cảnh báo non-blocking)
|
|
173
|
+
|
|
174
|
+
**Mục tiêu:** Test Case phải được thiết kế dựa trên cả UC Spec (yêu cầu nghiệp vụ do BA chốt) **và** System Requirement (bản dịch kỹ thuật cho Dev — Validation Rule, Exception/Error Handling, Acceptance Test) — thiếu một trong hai, TC dễ bỏ sót case mà Dev đã implement hoặc case BA đã đặc tả.
|
|
175
|
+
|
|
176
|
+
1. Tìm UC Spec: `AK-Docs/02.BA-Specs/04.UC-Specs/[functionId]/UC-Spec_v{N}.md` (version cao nhất, không phải bản archive).
|
|
177
|
+
2. Tìm System Requirement: `AK-Docs/02.BA-Specs/00.Requirements/[functionId]/System-Requirement_v*.md` (version cao nhất).
|
|
178
|
+
3. Nếu tìm thấy System Requirement, đọc 2 header của nó: `UC-Spec-Version` (phải khớp version UC Spec tìm được ở bước 1) và `Status` (phải là `✅ Approved`).
|
|
179
|
+
|
|
180
|
+
Nếu **bất kỳ** điều kiện sau không thỏa mãn → hiển thị cảnh báo tương ứng, **không dừng/không cancel Gate 1** — vẫn tiếp tục với dữ liệu hiện có:
|
|
181
|
+
|
|
182
|
+
| Tình huống | Cảnh báo hiển thị |
|
|
183
|
+
|---|---|
|
|
184
|
+
| Không tìm thấy UC Spec | `⚠️ CẢNH BÁO: Không tìm thấy UC Spec cho [functionId] — Test Case sẽ chỉ dựa trên System Requirement (nếu có) + input khác, thiếu ngữ cảnh nghiệp vụ gốc.` |
|
|
185
|
+
| Không tìm thấy System Requirement | `⚠️ CẢNH BÁO: Không tìm thấy System Requirement cho [functionId] — Test Case sẽ chỉ dựa trên UC Spec, có thể thiếu Validation Rule/Exception Handling/Acceptance Test mà Dev đã chốt riêng.` |
|
|
186
|
+
| Có System Requirement nhưng `UC-Spec-Version` không khớp UC Spec hiện tại | `⚠️ CẢNH BÁO: System Requirement đang trace theo UC Spec v[X], nhưng UC Spec hiện tại là v[Y] — nội dung có thể lỗi thời.` |
|
|
187
|
+
| Có System Requirement, khớp version, nhưng `Status` ≠ `✅ Approved` | `⚠️ CẢNH BÁO: System Requirement cho [functionId] chưa Approved (Status: [giá trị hiện tại]) — nội dung có thể còn thay đổi.` |
|
|
188
|
+
|
|
189
|
+
→ Với mọi cảnh báo trên: ghi lại tình huống này vào `[functionId]_01_Requirement_Analysis_QA.md` (mục Assumptions, Bước 4) để QA/Dev biết rõ giới hạn của phân tích — không chỉ hiển thị rồi bỏ qua.
|
|
190
|
+
|
|
191
|
+
Nếu tất cả điều kiện thỏa mãn → thông báo ngắn rồi tiếp tục:
|
|
192
|
+
|
|
193
|
+
```
|
|
194
|
+
✓ UC Spec: v[N]
|
|
195
|
+
✓ System Requirement: v[N] (✅ Approved, khớp UC Spec v[N])
|
|
196
|
+
→ Tiếp tục Gate 1...
|
|
197
|
+
```
|
|
198
|
+
|
|
172
199
|
#### Bước 1: Đọc và tổng hợp đầu vào
|
|
173
200
|
- Đọc `.aiflow/context/current.json` — tiêu đề, mô tả, acceptance criteria, liên kết tài liệu
|
|
201
|
+
- Đọc `UC-Spec_v{N}.md` (tìm thấy ở Bước 0.6) — yêu cầu nghiệp vụ gốc: Main/Alternative/Exception Flow, Business Rules (BR-NNN), UI Components
|
|
202
|
+
- Nếu tìm thấy `System-Requirement_v{N}.md` ở Bước 0.6 → đọc toàn bộ, dùng làm input kỹ thuật bổ sung cho Bước 2 (Phân tích yêu cầu): đối chiếu Functional/Non-Functional Requirements (`FR-*`, `NFR-*`), Validation Rules (`VR-*`), Exception & Error Handling (`ER-*`), Acceptance Test Scenarios (`AT-*`) — TC ở Gate 3 phải bao phủ cả các `AT-*`/`ER-*` này, không chỉ Business Rules trong UC Spec
|
|
174
203
|
- Nếu description có URL → chạy `ak fetch-links <url>` để tải nội dung
|
|
175
204
|
- Nếu có `supplementaryContext[]` → đọc từng item (SRS file, Figma link, API spec, spec MD file)
|
|
176
205
|
- Nếu có file yêu cầu thô được chỉ định → đọc file đó
|
|
@@ -227,6 +256,7 @@ Trước khi ghi bất kỳ file nào vào `03.Testing/`, đảm bảo AK-Docs
|
|
|
227
256
|
|
|
228
257
|
**Definition of Done:**
|
|
229
258
|
- [ ] `functionId` đã được xác định, thư mục đầu ra đã sẵn sàng
|
|
259
|
+
- [ ] Đã chạy Bước 0.6 (kiểm tra UC Spec + System Requirement); nếu có cảnh báo, đã ghi vào `01_Requirement_Analysis_QA.md`
|
|
230
260
|
- [ ] File `01_Requirement_Analysis_Result.md` có đủ 16 mục + Risk Matrix + Test Scope
|
|
231
261
|
- [ ] Toàn bộ Critical/Major đã được hỏi trực tiếp (từng câu một) và có câu trả lời từ QA — không còn nghi vấn bỏ ngỏ
|
|
232
262
|
- [ ] File `01_Requirement_Analysis_QA.md` đã tạo nếu có issue, ghi lại đầy đủ câu hỏi + câu trả lời (Bước 2b)
|
|
@@ -530,6 +560,7 @@ Sau khi Gate 4 đã APPROVED (bộ Test Case hoàn thành):
|
|
|
530
560
|
- ❌ **KHÔNG** tiến gate tiếp theo khi còn issue Critical hoặc Major chưa giải quyết
|
|
531
561
|
- ✅ **BẮT BUỘC** chạy Bước 0 ở Gate 1 — xác nhận `functionId` và thư mục đầu ra trước khi làm bất cứ điều gì
|
|
532
562
|
- ✅ **BẮT BUỘC** chạy Bước 0.5 ở Gate 1 — đảm bảo AK-Docs đang ở branch `feature/[functionId]/[taskId]` trước khi ghi file đầu tiên
|
|
563
|
+
- ✅ **BẮT BUỘC** chạy Bước 0.6 ở Gate 1 — kiểm tra UC Spec + System Requirement (tồn tại, khớp version, Approved); thiếu/lệch → cảnh báo yellow **không chặn Gate**, ghi vào QA artifact
|
|
533
564
|
- ✅ **BẮT BUỘC** đọc skill từ `.claude/skills/test-skills/` — không suy luận từ bộ nhớ
|
|
534
565
|
- ✅ **BẮT BUỘC** invoke `gate-review` cuối mỗi gate và chờ APPROVED
|
|
535
566
|
- ✅ **BẮT BUỘC** tuân thủ 100% `testcase-template.md` ở Gate 3 và Gate 4
|
|
@@ -719,3 +719,18 @@ Bugs logged: [N] | Skipped: [N]
|
|
|
719
719
|
7. Display: `GATE 2 DONE: Entry ghi vào AK-Docs/01.QnA/[file] — MR: [url hoặc "tạo thủ công"]`
|
|
720
720
|
|
|
721
721
|
> **Telemetry:** Run `ak gate 2 start --ticket [ticket-id]` when starting. Run `ak gate 2 approved --ticket [ticket-id]` when Gate 2 completes.
|
|
722
|
+
|
|
723
|
+
### GATE 3 — Sinh Task & Tạo Ticket (auto, chạy ngay sau Gate 2 xong)
|
|
724
|
+
|
|
725
|
+
> Implements "Vấn đề 5" trong `docs/internal/PM Workflow_v1.0.md`. **Không chờ APPROVED để bắt đầu** — chạy tự động ngay sau Gate 2 Bước 7, không cần lệnh riêng và không cần chờ PM merge MR (merge là việc độc lập của PM). `INVOKE`: `ingest-data` skill § 5 cho toàn bộ logic phân tích/đề xuất — mục này chỉ nêu gate mechanics.
|
|
726
|
+
|
|
727
|
+
1. Đọc lại entry vừa ghi + điều tra source/docs liên quan (skill § 5.1).
|
|
728
|
+
2. Quyết định content này có cần task không (skill § 5.2). Nếu KHÔNG cần task → hiển thị thông báo tương ứng, chạy `ak gate 3 skip --ticket [ticket-id] --reason "<1 câu lý do>"` (không chạy `ak gate 3 start`), và **kết thúc luôn, không có Gate 3 Display**. `skip` vẫn đóng gate đúng cách (advance `currentGate` giống `approved`) — task hiện đúng "done" trên dashboard, không treo ở trạng thái "sẵn sàng chạy Gate 3" mãi.
|
|
729
|
+
3. Nếu CẦN task → chạy `ak gate 3 start --ticket [ticket-id]`, rồi đề xuất danh sách task (skill § 5.3), cho PM sửa/thêm/xoá (skill § 5.4), lặp tới khi PM hài lòng.
|
|
730
|
+
4. Điểm dừng xác nhận trước khi tạo ticket (skill § 5.5) — ❌ không tự thêm `--yes`, phải có xác nhận rõ ràng trong hội thoại.
|
|
731
|
+
5. Xác định project đích trên Backlog/Jira (skill § 5.6, dùng `ak backlog-projects`/`ak jira-projects` + `ak backlog-set-default-project`/`ak jira-set-default-project`).
|
|
732
|
+
6. Tạo ticket bằng `ak tasks create-tickets <file>.json --json` (skill § 5.7). Nếu thiếu WRITE credentials, hỏi PM nhập ngay trong chat rồi `ak credentials set <field> "<value>"` và thử lại đúng 1 lần.
|
|
733
|
+
7. Ghi ngược link ticket vào entry gốc + hiển thị tổng kết (skill § 5.8).
|
|
734
|
+
8. Run: `ak gate 3 approved --ticket [ticket-id]` để đóng task.
|
|
735
|
+
|
|
736
|
+
> **Telemetry:** Run `ak gate 3 start --ticket [ticket-id]` chỉ khi Bước 2 kết luận CẦN task. Run `ak gate 3 approved --ticket [ticket-id]` khi Gate 3 hoàn tất, hoặc `ak gate 3 skip --ticket [ticket-id] --reason "..."` khi Bước 2 kết luận KHÔNG cần task.
|
package/docs/common/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,29 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
+
## [0.2.2] - 2026-08-06
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`ak gate <n> skip --ticket <id> [--reason <text>]`** — a gate can now close as "skipped" (legitimately produced no deliverable) instead of only `start`/`approved`. First consumer: `ingest-data` Gate 3, when the AI decides the ingested content is reference-only and no task list is needed — it now runs `ak gate 3 skip` instead of leaving the gate untouched. `scripts/task.js`'s `updateTaskGateState()` gained a `skip` branch that records `skippedGates[gate] = {at, reason}` in `task-state.json` and advances `currentGate` exactly like `approved` does, so every existing "is this task done" check (which only ever inspects `currentGate`) keeps working with zero changes. Prompted by an impact assessment for the `ai-flow-ex` VS Code extension's dashboard, which was showing skipped-Gate-3 ingest-data tasks stuck as "ready to run Gate 3" forever — see `docs/superpowers/specs/2026-08-06-ingest-data-gate3-skipped-state-design.md` in `ai-flow-ex` for the paired UI-side change (reads `skippedGates` to render a distinct "⏭ Skipped" badge instead of the generic "✓ Approved").
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- **`ak task next` treated `ingest-data` as a 2-gate flow** — a stale assumption left over from before Gate 3 (task creation & ticket generation) was added to `ingest-data` earlier in the same release cycle. `scripts/task.js` grouped it with `gen-doc`/`create-system-requirement` (`maxGate = 2`), so pausing via `ak task next` on Gate 2 would prematurely mark the task `status: 'done'` and `gateLabel(3, 'ingest-data')` returned `'Done'` instead of `'Sinh Task & Tạo Ticket'`. Split into its own 3-gate branch (`maxGate = 3`); `gen-doc`/`create-system-requirement` are unaffected.
|
|
19
|
+
|
|
20
|
+
## [0.2.1] - 2026-08-05
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- **`ingest-data` gained Gate 3 — auto-triggered task proposal & real ticket creation on Backlog/Jira, closing the gap after Gate 2 where the flow used to just stop once an entry landed in `AK-Docs/01.QnA/`.** Implements "Vấn đề 5" in `docs/internal/PM Workflow_v1.0.md`, decided to run **automatically** right after Gate 2 (not on-demand as the doc's earlier tentative proposal had it):
|
|
25
|
+
- AI re-reads the entry just ingested plus related source code/docs to judge impact, then decides whether it needs a task at all — pure-reference content (FYI, status update) ends the gate with no task proposed, no ticket created.
|
|
26
|
+
- When action is needed, proposes a task list (`type`: spec/coding/test/other, `title`, `track`, `description` built from the PM Workflow §6.1 template — spec-type tasks additionally require a PM-supplied "Nguồn tham chiếu" link, never inferred). The user can add/edit/remove tasks directly during review — not just approve/reject the whole list — before the single explicit confirmation point ("Danh sách task như trên — đồng ý tạo trên Backlog/Jira?", never auto-`--yes`'d).
|
|
27
|
+
- New `scripts/ticket-writer.js` — `ak tasks create-tickets <file>.json [--json]` posts the approved list as real Backlog/Jira issues. `scripts/link-resolver.js` gained the write-side API calls it needed (`createBacklogIssue`, `createJiraIssue`, `fetchBacklogIssueTypes`, `fetchBacklogPriorities`) plus a generic `httpsPost` helper (form-encoded for Backlog, JSON for Jira) — the existing `httpsGet`-only file had no POST capability before this.
|
|
28
|
+
- **Deliberately separate WRITE credentials** (`BACKLOG_API_KEY_WRITE` / `JIRA_API_TOKEN_WRITE`) from the read-only keys `ak fetch-links`/`ak use` already rely on, per the PM Workflow doc's policy of never letting the read key create tickets. When missing, `ak tasks create-tickets` returns a structured `{"error":"missing-write-credentials", field, message}` instead of failing silently — the AI surfaces this in chat, asks the user to paste the key, saves it via the new `ak credentials set <key> <value>` (allowlisted to the keys this feature introduced — dedicated adapter credentials still go through `ak init --adapter`), and retries once.
|
|
29
|
+
- Project-id resolution reuses the existing `BACKLOG_DEFAULT_PROJECT_ID`/`ak backlog-projects`/`ak backlog-set-default-project` pattern; added the Jira equivalents (`ak jira-projects`, `ak jira-set-default-project`, `fetchJiraProjects` in `scripts/use.js`) so both adapters check local config before ever prompting.
|
|
30
|
+
- Approved tickets get their link written back into the originating `Meetings-Log`/`QnA-Log`/`Confirmations-Log` entry (`→ Tasks created: TICKET-101 (coding), ...`), keeping the traceability chain from PM Workflow §6 intact end-to-end.
|
|
31
|
+
- **Still open, tracked in the PM Workflow doc's Vấn đề 5 implementation note:** no fixed feedback→task-type rule table (AI still judges per-context); new tickets don't auto-attach `functionId`/labels (Dev/BA/QA still assign on `ak use TICKET-XXX`); Backlog issueType/priority default to the project's first issueType and a "Normal"-named priority unless overridden explicitly in the task JSON.
|
|
32
|
+
|
|
10
33
|
## [0.2.0] - 2026-08-04
|
|
11
34
|
|
|
12
35
|
### Added
|
package/package.json
CHANGED
|
@@ -24,25 +24,6 @@ function norm(cmd) {
|
|
|
24
24
|
return ALIAS_MAP[c] || c;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
// ── Version helpers ─────────────────────────────────────────────────────────
|
|
28
|
-
const CURRENT_VERSION = '0.1.6';
|
|
29
|
-
|
|
30
|
-
function parseVersion(v) {
|
|
31
|
-
if (!v || v === '-' || v === '' || v.toLowerCase() === 'unknown') return null;
|
|
32
|
-
const core = v.split('-')[0]; // strip prerelease suffix, e.g. 0.1.5-beta.1 -> 0.1.5
|
|
33
|
-
const parts = core.split('.').map(n => parseInt(n, 10));
|
|
34
|
-
if (parts.some(n => Number.isNaN(n))) return null;
|
|
35
|
-
return parts;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function compareVersion(a, b) {
|
|
39
|
-
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
40
|
-
const diff = (a[i] || 0) - (b[i] || 0);
|
|
41
|
-
if (diff !== 0) return diff;
|
|
42
|
-
}
|
|
43
|
-
return 0;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
27
|
// ── Parse log file ─────────────────────────────────────────────────────────
|
|
47
28
|
function parseLog(filePath) {
|
|
48
29
|
const lines = fs.readFileSync(filePath, 'utf-8').split(/\r?\n/);
|
|
@@ -58,19 +39,17 @@ function parseLog(filePath) {
|
|
|
58
39
|
if (!user || user === '-' || user === '') continue;
|
|
59
40
|
|
|
60
41
|
const timestamp = cols[0] || '';
|
|
61
|
-
const version = cols[5] || '';
|
|
62
42
|
const command = cols[6] || '';
|
|
63
43
|
const ticket = cols[9] || '';
|
|
64
44
|
|
|
65
45
|
const date = timestamp.split(' ')[0]; // dd/MM/yyyy
|
|
66
46
|
|
|
67
47
|
if (!users[user]) {
|
|
68
|
-
users[user] = { email: user, days: new Set(), events: 0, rawCmds: new Set(), gateCmds: []
|
|
48
|
+
users[user] = { email: user, days: new Set(), events: 0, rawCmds: new Set(), gateCmds: [] };
|
|
69
49
|
}
|
|
70
50
|
const u = users[user];
|
|
71
51
|
u.events++;
|
|
72
52
|
if (date) u.days.add(date);
|
|
73
|
-
if (version.trim()) u.versions.add(version.trim());
|
|
74
53
|
|
|
75
54
|
const cmd = norm(command);
|
|
76
55
|
if (cmd) u.rawCmds.add(cmd);
|
|
@@ -151,19 +130,6 @@ const SCORE_LABELS = [
|
|
|
151
130
|
'Sử dụng thường xuyên workflow',
|
|
152
131
|
];
|
|
153
132
|
|
|
154
|
-
// ── Version status for a user ──────────────────────────────────────────────
|
|
155
|
-
function getVersionStatus(u) {
|
|
156
|
-
const all = [...u.versions];
|
|
157
|
-
const parsed = all.map(v => ({ raw: v, parts: parseVersion(v) })).filter(v => v.parts);
|
|
158
|
-
if (parsed.length === 0) {
|
|
159
|
-
return { status: all.length ? 'unknown' : 'none', latest: null, all };
|
|
160
|
-
}
|
|
161
|
-
parsed.sort((a, b) => compareVersion(a.parts, b.parts));
|
|
162
|
-
const latest = parsed[parsed.length - 1];
|
|
163
|
-
const isOutdated = compareVersion(latest.parts, parseVersion(CURRENT_VERSION)) < 0;
|
|
164
|
-
return { status: isOutdated ? 'outdated' : 'ok', latest: latest.raw, all };
|
|
165
|
-
}
|
|
166
|
-
|
|
167
133
|
// ── Build summary row for each user ───────────────────────────────────────
|
|
168
134
|
function buildRow(email, u) {
|
|
169
135
|
const { events, days, rawCmds, gateCmds } = u;
|
|
@@ -232,7 +198,6 @@ function buildRow(email, u) {
|
|
|
232
198
|
impactLabel: '',
|
|
233
199
|
totalScore: score,
|
|
234
200
|
notes,
|
|
235
|
-
versionStatus: getVersionStatus(u),
|
|
236
201
|
};
|
|
237
202
|
}
|
|
238
203
|
|
|
@@ -331,78 +296,11 @@ function writeExcel(rows, workbook, sheetLabel, fromLabel, toLabel) {
|
|
|
331
296
|
return sheet;
|
|
332
297
|
}
|
|
333
298
|
|
|
334
|
-
// ── Version warning sheet ───────────────────────────────────────────────────
|
|
335
|
-
function writeVersionSheet(workbook, rows) {
|
|
336
|
-
const sheet = workbook.addWorksheet('Version Warning');
|
|
337
|
-
sheet.columns = [
|
|
338
|
-
{ key: 'no', width: 5 },
|
|
339
|
-
{ key: 'email', width: 30 },
|
|
340
|
-
{ key: 'status', width: 16 },
|
|
341
|
-
{ key: 'latest', width: 16 },
|
|
342
|
-
{ key: 'allVersions', width: 30 },
|
|
343
|
-
{ key: 'note', width: 50 },
|
|
344
|
-
];
|
|
345
|
-
|
|
346
|
-
const border = {
|
|
347
|
-
top: { style: 'thin' }, left: { style: 'thin' },
|
|
348
|
-
bottom: { style: 'thin' }, right: { style: 'thin' },
|
|
349
|
-
};
|
|
350
|
-
|
|
351
|
-
sheet.addRow([`Cảnh báo phiên bản — Yêu cầu phiên bản hiện tại: ${CURRENT_VERSION}`]);
|
|
352
|
-
sheet.mergeCells('A1:F1');
|
|
353
|
-
const title = sheet.getCell('A1');
|
|
354
|
-
title.font = { bold: true, size: 13, color: { argb: 'FF1F3864' } };
|
|
355
|
-
title.alignment = { vertical: 'middle', horizontal: 'center' };
|
|
356
|
-
title.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FFD6DCE4' } };
|
|
357
|
-
sheet.getRow(1).height = 28;
|
|
358
|
-
|
|
359
|
-
const HEADERS = ['#', 'Email', 'Trạng thái', 'Phiên bản mới nhất', 'Các phiên bản dùng trong tuần', 'Ghi chú'];
|
|
360
|
-
const hRow = sheet.addRow(HEADERS);
|
|
361
|
-
hRow.height = 20;
|
|
362
|
-
hRow.eachCell(cell => {
|
|
363
|
-
cell.fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: 'FF4F81BD' } };
|
|
364
|
-
cell.font = { bold: true, color: { argb: 'FFFFFFFF' }, size: 11 };
|
|
365
|
-
cell.alignment = { vertical: 'middle', horizontal: 'center', wrapText: true };
|
|
366
|
-
cell.border = border;
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
const warnings = rows.filter(row => row.versionStatus.status === 'outdated' || row.versionStatus.status === 'unknown');
|
|
370
|
-
|
|
371
|
-
const STATUS_LABEL = { outdated: 'CŨ HƠN', unknown: 'KHÔNG XÁC ĐỊNH', ok: 'OK', none: 'Không có dữ liệu' };
|
|
372
|
-
const STATUS_COLOR = { outdated: 'FFFFC7CE', unknown: 'FFFFEB9C' };
|
|
373
|
-
|
|
374
|
-
warnings.forEach((row, idx) => {
|
|
375
|
-
const vs = row.versionStatus;
|
|
376
|
-
const note = vs.status === 'outdated'
|
|
377
|
-
? `Đang dùng ${vs.latest}, chưa cập nhật lên ${CURRENT_VERSION}`
|
|
378
|
-
: 'Không đọc được phiên bản (giá trị "-" hoặc "unknown") — cần kiểm tra lại';
|
|
379
|
-
const r = sheet.addRow([
|
|
380
|
-
idx + 1, row.email, STATUS_LABEL[vs.status], vs.latest || '-', vs.all.join(', ') || '-', note,
|
|
381
|
-
]);
|
|
382
|
-
r.height = 20;
|
|
383
|
-
const fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: STATUS_COLOR[vs.status] || 'FFFFFFFF' } };
|
|
384
|
-
r.eachCell({ includeEmpty: true }, (cell, col) => {
|
|
385
|
-
cell.border = border;
|
|
386
|
-
cell.fill = fill;
|
|
387
|
-
cell.alignment = (col <= 4)
|
|
388
|
-
? { vertical: 'middle', horizontal: 'center' }
|
|
389
|
-
: { vertical: 'middle', horizontal: 'left', wrapText: false };
|
|
390
|
-
});
|
|
391
|
-
});
|
|
392
|
-
|
|
393
|
-
sheet.views = [{ state: 'frozen', ySplit: 2 }];
|
|
394
|
-
sheet.autoFilter = { from: 'A2', to: 'F2' };
|
|
395
|
-
|
|
396
|
-
return warnings;
|
|
397
|
-
}
|
|
398
|
-
|
|
399
299
|
async function writeExcelFile(rows, outputPath, sheetLabel, fromLabel, toLabel) {
|
|
400
300
|
const workbook = new ExcelJS.Workbook();
|
|
401
301
|
writeExcel(rows, workbook, sheetLabel, fromLabel, toLabel);
|
|
402
|
-
const warnings = writeVersionSheet(workbook, rows);
|
|
403
302
|
await workbook.xlsx.writeFile(outputPath);
|
|
404
|
-
console.log(`Written: ${outputPath} (${rows.length} users
|
|
405
|
-
return warnings;
|
|
303
|
+
console.log(`Written: ${outputPath} (${rows.length} users)`);
|
|
406
304
|
}
|
|
407
305
|
|
|
408
306
|
// ── Canonical user list (fixed order) ─────────────────────────────────────
|
|
@@ -437,7 +335,7 @@ const LOG_EMAIL_ALIAS = {
|
|
|
437
335
|
};
|
|
438
336
|
|
|
439
337
|
const EMPTY_USER = (email) => ({
|
|
440
|
-
email, events: 0, days: new Set(), rawCmds: new Set(), gateCmds: [],
|
|
338
|
+
email, events: 0, days: new Set(), rawCmds: new Set(), gateCmds: [],
|
|
441
339
|
});
|
|
442
340
|
|
|
443
341
|
// ── Main ───────────────────────────────────────────────────────────────────
|
|
@@ -471,7 +369,6 @@ const EMPTY_USER = (email) => ({
|
|
|
471
369
|
u.events += data.events;
|
|
472
370
|
data.days.forEach(d => u.days.add(d));
|
|
473
371
|
data.rawCmds.forEach(c => u.rawCmds.add(c));
|
|
474
|
-
data.versions.forEach(v => u.versions.add(v));
|
|
475
372
|
u.gateCmds.push(...data.gateCmds);
|
|
476
373
|
}
|
|
477
374
|
}
|
|
@@ -482,9 +379,5 @@ const EMPTY_USER = (email) => ({
|
|
|
482
379
|
return buildRow(email, u);
|
|
483
380
|
});
|
|
484
381
|
|
|
485
|
-
|
|
486
|
-
if (warnings.length) {
|
|
487
|
-
console.log(`\n⚠️ Version warnings (< ${CURRENT_VERSION} or unknown):`);
|
|
488
|
-
warnings.forEach(w => console.log(` - ${w.email}: ${w.versionStatus.latest || 'unknown'} (${w.versionStatus.status})`));
|
|
489
|
-
}
|
|
382
|
+
await writeExcelFile(rows, outputFile, sheetLabel, fromLabel, toLabel);
|
|
490
383
|
})();
|
package/scripts/init.js
CHANGED
|
@@ -2,7 +2,7 @@ const fs = require('fs-extra');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const os = require('os');
|
|
4
4
|
const chalk = require('chalk');
|
|
5
|
-
const { input, checkbox, confirm } = require('@inquirer/prompts');
|
|
5
|
+
const { input, checkbox, confirm, select } = require('@inquirer/prompts');
|
|
6
6
|
const { syncDocsRepos } = require('./docs-repo');
|
|
7
7
|
const memoryStore = require('./memory-store');
|
|
8
8
|
|
|
@@ -937,6 +937,29 @@ async function setupAdapter(projectDir, adapter) {
|
|
|
937
937
|
console.log(chalk.green(`✓ Credentials verified successfully`));
|
|
938
938
|
}
|
|
939
939
|
|
|
940
|
+
// Backlog: let the user pick a default project for tasks created manually (no ticket to
|
|
941
|
+
// derive one from otherwise) — kept for later use, e.g. syncing a local task up to Backlog.
|
|
942
|
+
if (adapter === 'backlog' && verifyResult.ok) {
|
|
943
|
+
const spaceKey = credentials.BACKLOG_SPACE_KEY;
|
|
944
|
+
const domain = spaceKey.includes('.') ? spaceKey : `${spaceKey}.backlog.com`;
|
|
945
|
+
try {
|
|
946
|
+
const { fetchBacklogProjects } = require('./use');
|
|
947
|
+
const projects = await fetchBacklogProjects(domain, credentials.BACKLOG_API_KEY);
|
|
948
|
+
if (projects.length > 0) {
|
|
949
|
+
const projectId = await select({
|
|
950
|
+
message: 'Default Backlog project for tasks created manually (no ticket):',
|
|
951
|
+
choices: projects.map((p) => ({ name: `${p.projectKey} ${p.name}`, value: p.id })),
|
|
952
|
+
});
|
|
953
|
+
const picked = projects.find((p) => p.id === projectId);
|
|
954
|
+
credentials.BACKLOG_DEFAULT_PROJECT_ID = picked.id;
|
|
955
|
+
credentials.BACKLOG_DEFAULT_PROJECT_KEY = picked.projectKey;
|
|
956
|
+
console.log(chalk.green(`✓ Default project set to ${picked.projectKey}.`));
|
|
957
|
+
}
|
|
958
|
+
} catch (err) {
|
|
959
|
+
console.log(chalk.gray(` (Could not fetch Backlog projects to pick a default: ${err.message} — skipping.)`));
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
|
|
940
963
|
const claudeConfigDir = path.join(
|
|
941
964
|
process.env.APPDATA ||
|
|
942
965
|
(process.platform === 'darwin'
|