@educa-corp/sdd-framework 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/gate-trace.js +25 -2
- package/bin/index.js +32 -5
- package/bin/lint-trace.js +41 -0
- package/bin/self-check.js +430 -3
- package/bin/trace-schema.json +391 -30
- package/core/FRAMEWORK_VERSION +1 -1
- package/{commands/extend-prd.md → core/commands/amend-prd.md} +205 -173
- package/core/commands/dev-run-test.md +47 -9
- package/core/commands/extend-prd.md +39 -12
- package/core/commands/generate-bdd.md +43 -4
- package/core/commands/generate-code.md +33 -0
- package/core/commands/generate-tech-docs.md +34 -2
- package/core/commands/qc-run-test.md +29 -3
- package/core/commands/refine-prd.md +13 -2
- package/core/commands/review-context.md +43 -8
- package/core/commands/sync.md +105 -1
- package/core/commands/validate-traces.md +284 -11
- package/core/rules/workflow.md +34 -0
- package/core/steps/context-loader.md +26 -5
- package/core/templates/feature.template +1 -1
- package/docs/02-concepts/architecture.md +36 -0
- package/docs/04-reference/commands.md +148 -134
- package/docs/04-reference/trace-schema.md +39 -0
- package/docs/explain/02b-extend-prd.md +1 -1
- package/docs/explain/02c-amend-prd.md +152 -0
- package/docs/explain/28-sync.md +25 -0
- package/docs/explain/README.md +136 -135
- package/package.json +1 -8
- package/commands/debug.md +0 -529
- package/commands/debug.tmpl +0 -260
- package/commands/define-product.md +0 -438
- package/commands/define-product.tmpl +0 -225
- package/commands/dev-gen-test.md +0 -700
- package/commands/dev-gen-test.tmpl +0 -490
- package/commands/dev-run-test.md +0 -435
- package/commands/dev-run-test.tmpl +0 -225
- package/commands/dev-smoke-test.md +0 -374
- package/commands/dev-smoke-test.tmpl +0 -217
- package/commands/extend-prd.tmpl +0 -273
- package/commands/fix-bug.md +0 -519
- package/commands/fix-bug.tmpl +0 -197
- package/commands/generate-architecture.md +0 -354
- package/commands/generate-architecture.tmpl +0 -197
- package/commands/generate-bdd.md +0 -923
- package/commands/generate-bdd.tmpl +0 -590
- package/commands/generate-code.md +0 -859
- package/commands/generate-code.tmpl +0 -649
- package/commands/generate-design-spec.md +0 -737
- package/commands/generate-design-spec.tmpl +0 -524
- package/commands/generate-prd.md +0 -722
- package/commands/generate-prd.tmpl +0 -226
- package/commands/generate-spec-manifest.md +0 -321
- package/commands/generate-spec-manifest.tmpl +0 -164
- package/commands/generate-tech-docs.md +0 -920
- package/commands/generate-tech-docs.tmpl +0 -273
- package/commands/learn.md +0 -399
- package/commands/learn.tmpl +0 -130
- package/commands/map-testids.md +0 -238
- package/commands/map-testids.tmpl +0 -81
- package/commands/propose-scenario.md +0 -359
- package/commands/propose-scenario.tmpl +0 -202
- package/commands/qc-analyze.md +0 -269
- package/commands/qc-analyze.tmpl +0 -112
- package/commands/qc-design-test.md +0 -226
- package/commands/qc-design-test.tmpl +0 -69
- package/commands/qc-plan.md +0 -206
- package/commands/qc-plan.tmpl +0 -49
- package/commands/qc-report.md +0 -217
- package/commands/qc-report.tmpl +0 -60
- package/commands/qc-review.md +0 -210
- package/commands/qc-review.tmpl +0 -53
- package/commands/qc-run-test.md +0 -326
- package/commands/qc-run-test.tmpl +0 -116
- package/commands/refine-prd.md +0 -653
- package/commands/refine-prd.tmpl +0 -281
- package/commands/report-bug.md +0 -305
- package/commands/report-bug.tmpl +0 -148
- package/commands/review-code.md +0 -415
- package/commands/review-code.tmpl +0 -146
- package/commands/review-context.md +0 -902
- package/commands/review-context.tmpl +0 -530
- package/commands/review-tech-docs.md +0 -561
- package/commands/review-tech-docs.tmpl +0 -404
- package/commands/setup-ai-first.md +0 -602
- package/commands/setup-ai-first.tmpl +0 -450
- package/commands/sync.md +0 -430
- package/commands/sync.tmpl +0 -429
- package/commands/update-framework.md +0 -203
- package/commands/update-framework.tmpl +0 -202
- package/commands/validate-traces.md +0 -1077
- package/commands/validate-traces.tmpl +0 -920
- package/hooks/data-guard.js +0 -232
- package/hooks/settings.json +0 -19
- package/modules/android-compose/module.yaml +0 -13
- package/modules/android-compose/stack-profile.yaml +0 -57
- package/modules/angular/architecture-snippets/component-patterns.md +0 -187
- package/modules/angular/module.yaml +0 -6
- package/modules/angular/stack-profile.yaml +0 -38
- package/modules/context-engineering/architecture-snippets/context-design.md +0 -119
- package/modules/context-engineering/module.yaml +0 -9
- package/modules/context-engineering/stack-profile.yaml +0 -61
- package/modules/dotnet/architecture-snippets/clean-arch.md +0 -160
- package/modules/dotnet/module.yaml +0 -6
- package/modules/dotnet/stack-profile.yaml +0 -50
- package/modules/flutter/module.yaml +0 -14
- package/modules/flutter/stack-profile.yaml +0 -59
- package/modules/golang/architecture-snippets/domain-layout.md +0 -283
- package/modules/golang/module.yaml +0 -6
- package/modules/golang/stack-profile.yaml +0 -40
- package/modules/ios-swiftui/module.yaml +0 -13
- package/modules/ios-swiftui/stack-profile.yaml +0 -55
- package/modules/java-spring/architecture-snippets/layered-arch.md +0 -201
- package/modules/java-spring/module.yaml +0 -15
- package/modules/java-spring/stack-profile.yaml +0 -28
- package/modules/nextjs/architecture-snippets/app-router-patterns.md +0 -269
- package/modules/nextjs/module.yaml +0 -14
- package/modules/nextjs/stack-profile.yaml +0 -74
- package/modules/nuxt/module.yaml +0 -14
- package/modules/nuxt/stack-profile.yaml +0 -58
- package/modules/phaser-game/architecture-snippets/phaser-scene-patterns.md +0 -646
- package/modules/phaser-game/module.yaml +0 -15
- package/modules/phaser-game/stack-profile.yaml +0 -90
- package/modules/php-laravel/architecture-snippets/service-repository.md +0 -302
- package/modules/php-laravel/module.yaml +0 -15
- package/modules/php-laravel/stack-profile.yaml +0 -56
- package/modules/qc-playwright/stack-profile.yaml +0 -66
- package/modules/react/architecture-snippets/hooks-query-patterns.md +0 -254
- package/modules/react/module.yaml +0 -14
- package/modules/react/stack-profile.yaml +0 -63
- package/modules/react-native/module.yaml +0 -14
- package/modules/react-native/stack-profile.yaml +0 -56
- package/modules/vue/module.yaml +0 -14
- package/modules/vue/stack-profile.yaml +0 -65
- package/rules/data-protection.md +0 -80
- package/rules/workflow.md +0 -99
- package/skills/code/SKILL.md +0 -19
- package/skills/code/SKILL.tmpl +0 -19
- package/skills/debug/SKILL.md +0 -19
- package/skills/debug/SKILL.tmpl +0 -19
- package/skills/design-spec/SKILL.md +0 -11
- package/skills/design-spec/SKILL.tmpl +0 -11
- package/skills/discovery/SKILL.md +0 -14
- package/skills/discovery/SKILL.tmpl +0 -14
- package/skills/prd/SKILL.md +0 -19
- package/skills/prd/SKILL.tmpl +0 -19
- package/skills/qc/qa-analyst/DOC_GAPS.template.md +0 -63
- package/skills/qc/qa-analyst/acceptance-criteria.md +0 -60
- package/skills/qc/qa-analyst/business-rules.md +0 -59
- package/skills/qc/qa-analyst/data-flow.md +0 -64
- package/skills/qc/qa-analyst/spec-breakdown.md +0 -61
- package/skills/qc/qa-designer/e2e/journey.md +0 -41
- package/skills/qc/qa-designer/exploratory/charter.md +0 -68
- package/skills/qc/qa-designer/exploratory/explore-to-functional.md +0 -43
- package/skills/qc/qa-designer/functional/api.md +0 -45
- package/skills/qc/qa-designer/functional/gui-feature.md +0 -46
- package/skills/qc/qa-designer/functional/gui-screen.md +0 -52
- package/skills/qc/qa-designer/integration/api.md +0 -42
- package/skills/qc/qa-designer/integration/db.md +0 -39
- package/skills/qc/qa-designer/integration/gui.md +0 -40
- package/skills/qc/qa-designer/integration/kafka.md +0 -40
- package/skills/qc/qa-designer/non-functional.md +0 -40
- package/skills/qc/qa-planner/test-plan.md +0 -120
- package/skills/qc/qa-reviewer/script/e2e.md +0 -87
- package/skills/qc/qa-reviewer/script/exploratory.md +0 -45
- package/skills/qc/qa-reviewer/script/functional.md +0 -101
- package/skills/qc/qa-reviewer/script/integration.md +0 -91
- package/skills/qc/qa-reviewer/script/non-functional.md +0 -126
- package/skills/qc/qa-reviewer/test-case/e2e.md +0 -73
- package/skills/qc/qa-reviewer/test-case/exploratory.md +0 -43
- package/skills/qc/qa-reviewer/test-case/functional.md +0 -76
- package/skills/qc/qa-reviewer/test-case/integration.md +0 -69
- package/skills/qc/qa-reviewer/test-case/non-functional.md +0 -73
- package/skills/qc/qa-runner/e2e.md +0 -49
- package/skills/qc/qa-runner/exploratory/session.md +0 -36
- package/skills/qc/qa-runner/functional/api.md +0 -35
- package/skills/qc/qa-runner/functional/gui-feature.md +0 -51
- package/skills/qc/qa-runner/functional/gui-screen.md +0 -55
- package/skills/qc/qa-runner/integration.md +0 -47
- package/skills/qc/qa-runner/non-functional.md +0 -49
- package/skills/qc/qa-runner/report/report.md +0 -37
- package/skills/setup-ai-first/SKILL.md +0 -19
- package/skills/setup-ai-first/SKILL.tmpl +0 -19
- package/skills/spec/SKILL.md +0 -19
- package/skills/spec/SKILL.tmpl +0 -19
- package/skills/test/SKILL.md +0 -18
- package/skills/test/SKILL.tmpl +0 -18
- package/steps/business-language.md +0 -56
- package/steps/capture-lesson.md +0 -112
- package/steps/context-loader.md +0 -406
- package/steps/gate.md +0 -151
- package/steps/report-footer.md +0 -125
- package/steps/review-fanout.md +0 -159
- package/steps/spawn-agent.md +0 -129
- package/steps/trace-mirror.md +0 -53
- package/templates/README.md +0 -70
- package/templates/architecture.template.md +0 -394
- package/templates/ci/trace-gate.yml +0 -146
- package/templates/design-spec.template.md +0 -217
- package/templates/feature.template +0 -123
- package/templates/hooks/pre-push +0 -61
- package/templates/platform-guide.template.md +0 -145
- package/templates/prd.template.md +0 -283
- package/templates/product-definition.template.md +0 -188
- package/templates/project-context.yaml +0 -212
- package/templates/tech-design.template.md +0 -490
|
@@ -159,7 +159,7 @@ Mỗi dòng ⚠️/🔴 phải ứng với một trạng thái **context-loader
|
|
|
159
159
|
🔴/⚠️ (không chặn ≠ không báo — người đọc log sau này vẫn cần thấy).
|
|
160
160
|
|
|
161
161
|
|
|
162
|
-
*Lưu ý:
|
|
162
|
+
*Lưu ý: Lệnh này **không có target file đơn** — nó quét nhiều thư mục, nên gate Bước 1 không phân giải file. Phạm vi audit do **Step 0-A** phân giải từ `$ARGUMENTS` (`--domain` / `--prd` / `--uc`; không có cờ nào = toàn bộ).*
|
|
163
163
|
|
|
164
164
|
## Context
|
|
165
165
|
**BẮT BUỘC — đọc `.agent/steps/context-loader.md` và thực thi TOÀN BỘ quy trình trong đó**,
|
|
@@ -173,6 +173,60 @@ placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
|
|
|
173
173
|
|
|
174
174
|
## Process
|
|
175
175
|
|
|
176
|
+
### Step 0-A — Phân giải **phạm vi audit** *(chạy TRƯỚC Step 0)*
|
|
177
|
+
|
|
178
|
+
*Contract: `bin/trace-schema.json` → `gate.report_root_keys`. Đây là đầu PRODUCER của một sợi dây mà đầu CONSUMER (`gate-trace`) đã có sẵn từ trước.*
|
|
179
|
+
|
|
180
|
+
Parse `$ARGUMENTS` (đã tách các cờ khác ở gate Bước 1):
|
|
181
|
+
|
|
182
|
+
| Cờ | `scope.kind` | `scope.value` | Hẹp lại những gì |
|
|
183
|
+
|---|---|---|---|
|
|
184
|
+
| *(không có)* | `all` | `"all"` | Không hẹp — audit toàn bộ |
|
|
185
|
+
| `--domain {domain}` | `domain` | tên domain | Chỉ sổ + spec dưới `{domain}/` |
|
|
186
|
+
| `--prd {TICKET-ID}` | `prd` | TICKET-ID | Chỉ **một** feature-package (phân giải `{domain}/{prd-slug}` từ TICKET-ID) |
|
|
187
|
+
| `--uc {UC-ID}` | `uc` | UC-ID | Chỉ **một** UC — **mọi platform của nó** (`{UC-ID}-*.tsv`) |
|
|
188
|
+
|
|
189
|
+
Nhiều cờ scope cùng lúc → **DỪNG**, báo lỗi: chúng loại trừ nhau, và tự ý ưu tiên một cái là hẹp phạm vi mà người dùng không biết.
|
|
190
|
+
|
|
191
|
+
`--prd`/`--uc` không phân giải được về một package/UC có thật → **DỪNG** và liệt kê ứng viên. **KHÔNG** âm thầm rơi về `all` (chạy toàn bộ khi người ta xin một phần là đốt 30 phút không ai muốn) và **KHÔNG** âm thầm audit rỗng (báo cáo "sạch" trên 0 row).
|
|
192
|
+
|
|
193
|
+
Lưu `scope` — mọi step sau dùng nó:
|
|
194
|
+
|
|
195
|
+
| Step | Hẹp thế nào |
|
|
196
|
+
|---|---|
|
|
197
|
+
| Step 0 / Step 1 | `all_trace_dirs` giữ nguyên, nhưng chỉ đọc TSV **khớp scope**: `{trace_dir}/{domain}/**` · `{trace_dir}/{domain}/{prd-slug}/**` · `{trace_dir}/**/{UC-ID}-*.tsv` |
|
|
198
|
+
| Step 1.0 (lint) | truyền `--trace` như cũ — **lint luôn chạy toàn bộ**. Sổ hỏng ở domain khác vẫn là sổ hỏng, và lint rẻ (không LLM) |
|
|
199
|
+
| Step 2b · 3.9 · 4 · 5* · 7 | chỉ các PRD/UC trong scope |
|
|
200
|
+
| Step 6 · 6b | chỉ ghi lại TSV + mốc của phần trong scope |
|
|
201
|
+
| Step 8 | ghi `scope` **và** `domain` vào biên bản (xem dưới) |
|
|
202
|
+
|
|
203
|
+
**In phạm vi ngay đầu run**, trước khi làm gì:
|
|
204
|
+
```
|
|
205
|
+
Phạm vi: {all | domain={d} | prd={TICKET-ID} | uc={UC-ID}}
|
|
206
|
+
{n} PRD · {m} UC · {k} sổ → {ước lượng: toàn bộ repo | một phần}
|
|
207
|
+
```
|
|
208
|
+
*Không có dòng này thì người dùng không biết mình vừa gọi một lệnh cỡ nào — và đây là lệnh đắt nhất trong 33 lệnh (~33k token chỉ dẫn trước khi mở artifact nào).*
|
|
209
|
+
|
|
210
|
+
> **Vì sao step này là "nối dây", không phải "thêm tính năng" (GAPS-v4 G57).**
|
|
211
|
+
> Trước nó, **năm** chỗ trong lệnh này mô tả một *"domain argument"* / *"domain filter"* **không tồn
|
|
212
|
+
> tại**: ghi chú Gate (*"target là một tên domain hoặc UC-ID cụ thể từ `$ARGUMENTS`"*) · Step 1 đọc TSV
|
|
213
|
+
> *"khớp domain target"* · schema biên bản `"<domain argument, or 'all' if no filter>"` · Step 8
|
|
214
|
+
> *"nếu có domain filter, chỉ gồm các PRD đó"* · dòng `trace-history` mang field `domain`.
|
|
215
|
+
> Và `gate-trace` **đã** đọc `report.domain` rồi **chặn PR** nếu nó khác `all`.
|
|
216
|
+
>
|
|
217
|
+
> Nhưng Step 0 đặt `all_trace_dirs` = toàn bộ **vô điều kiện**, và chỗ duy nhất parse `$ARGUMENTS` là
|
|
218
|
+
> Step 5e cho hai cờ `--realign`. Nên ô đó **luôn** ghi `all`, và phần kiểm của gate **chưa bao giờ
|
|
219
|
+
> chạy một lần nào**.
|
|
220
|
+
>
|
|
221
|
+
> Đây đúng hình dạng **R1 fail build vì nó** — *"field có consumer mà không có producer"*, ca
|
|
222
|
+
> `@trace.sc_version` (3 consumer, 0 producer, sống qua nhiều version không ai bắt được). Nó sống
|
|
223
|
+
> được vì R1 chỉ canh field trong **sổ TSV** và tag trong **code**, không canh key ở cấp gốc **biên
|
|
224
|
+
> bản JSON**. `self-check` **R9(h)** giờ canh cả hai đầu.
|
|
225
|
+
>
|
|
226
|
+
> Và ghi chú Gate còn **tệ hơn im lặng** — nó gây nhầm: một agent đọc *"target là một tên domain hoặc
|
|
227
|
+
> UC-ID"* sẽ **tin rằng** scoping hoạt động.
|
|
228
|
+
|
|
229
|
+
---
|
|
176
230
|
### Step 0 — Umbrella Mode Detection
|
|
177
231
|
|
|
178
232
|
Kiểm tra mảng `services` có tồn tại trong `project-context.yaml` không.
|
|
@@ -257,6 +311,29 @@ Nếu version SC trong `.feature` khác `spec_ver` của `.tsv` → cập nhật
|
|
|
257
311
|
|
|
258
312
|
Cũng phát hiện SC có trong `.feature` (platform đó) nhưng thiếu trong `.tsv` → thêm row mới với `status: UNTRACKED`. *(sc_id trùng số giữa các platform là 2 scenario khác nhau → mỗi sổ platform giữ tập SC riêng, không dedupe chéo platform.)*
|
|
259
313
|
|
|
314
|
+
### Step 2c — Phân giải lại `service` từ config hiện tại *(G51)*
|
|
315
|
+
|
|
316
|
+
*Cùng tinh thần Step 2 với `spec_ver`: cột TSV là **cache**, `services:` trong `project-context.yaml`
|
|
317
|
+
là **nguồn**. Mỗi lần chạy, đối chiếu lại.*
|
|
318
|
+
|
|
319
|
+
Với mỗi row có `service` ∈ (`unrouted`, `unresolved`), tra lại `services:` theo
|
|
320
|
+
`domain` + `platform` (từ tên file sổ) + `prd_slug`:
|
|
321
|
+
|
|
322
|
+
| Kết quả tra | Hành động |
|
|
323
|
+
|---|---|
|
|
324
|
+
| Giờ **khớp** một entry | **Cập nhật `service` = path đó** (in memory, ghi lại ở Step 6). Không cần chạy lại `/generate-bdd`. |
|
|
325
|
+
| Vẫn không khớp | Giữ `unrouted` → gắn cờ 🟠 `SERVICE_UNROUTED` |
|
|
326
|
+
| Config vẫn sai cấu trúc | Giữ `unresolved` → cùng cờ, nhưng lý do khác (bug config, không phải chờ quyết) |
|
|
327
|
+
|
|
328
|
+
> **Vì sao lệnh này được ghi một cột do `/generate-bdd` sở hữu:** đây là **ngoại lệ có chủ ý** với luật
|
|
329
|
+
> *"mỗi cột một chủ"* (`rules/workflow.md` §Trace Contract), và nó **không vi phạm tinh thần** của luật:
|
|
330
|
+
> lệnh này **không ghi một giá trị mới** — nó chỉ **phân giải một placeholder mà `/generate-bdd` đã cố ý
|
|
331
|
+
> để lại**. Khai tường minh trong `bin/trace-schema.json`: `service.written_by = [generate-bdd, validate-traces]`.
|
|
332
|
+
>
|
|
333
|
+
> Đây là thứ làm sổ **tự lành**: architect thêm mapping → lần `/validate-traces` kế tiếp nâng
|
|
334
|
+
> `unrouted` → path. Không sửa tay, không sinh lại BDD. Không có bước này thì `unrouted` **đọng lại
|
|
335
|
+
> vĩnh viễn** — đúng bệnh `TBD` mà G1/G28 đã chỉ ra.
|
|
336
|
+
|
|
260
337
|
### Step 2b — Reverse audit (bắt tag mồ côi)
|
|
261
338
|
|
|
262
339
|
*Step 2 đi chiều **spec → code** (mỗi row TSV, SC đó implement tới đâu). Step này đi **chiều ngược** — bắt lớp lỗi mà Step 2 cấu trúc không thể thấy: code trỏ vào một scenario **không còn tồn tại**. Xảy ra khi gen lại BDD làm một SC biến mất (gộp / đổi số / xoá) trong khi code implement nó vẫn nằm đó, vẫn được caller gọi.*
|
|
@@ -291,6 +368,84 @@ Không tìm thấy tag mồ côi nào → bỏ qua im lặng.
|
|
|
291
368
|
|
|
292
369
|
> **Vì sao DRIFT xét trước GAP:** một scenario đã có code, chưa test, **và** spec vừa drift phải hiện `DRIFT` (không phải `GAP`) — vì `generate-code` xử `GAP` = "skip codegen, chạy /dev-gen-test" còn `DRIFT` = "regenerate". Nếu GAP thắng, code lỗi thời bị bỏ qua và test lại sinh trên code cũ. UNTRACKED vẫn phải là Rule 1 để scenario chưa code (gen_ver `—`) không lọt vào DRIFT.
|
|
293
370
|
|
|
371
|
+
### Step 3.9 — Phát hiện sửa spec ngoài đường chính thức *(chạy TRƯỚC Step 4)*
|
|
372
|
+
|
|
373
|
+
*Contract: `bin/trace-schema.json` → `spec_edit_detection`. Cờ: `PRD_UNTRACKED_EDIT`.*
|
|
374
|
+
|
|
375
|
+
**Vì sao bước này đứng TRƯỚC Step 4.** Step 4 và mọi bước sau nó đều dựa trên một giả định
|
|
376
|
+
**chưa được kiểm**: *nhãn `Version` của PRD phản ánh đúng nội dung hiện tại của nó.* Nếu ai sửa nội
|
|
377
|
+
dung mà không bump nhãn thì giả định đó sai, và Step 4 sẽ phán *"version khớp ⇒ sạch"* trên một tài
|
|
378
|
+
liệu đã đổi. Kiểm cấu trúc drift trên một nhãn không còn đúng thì cũng vô nghĩa như G1 kiểm cấu trúc
|
|
379
|
+
một quyển sổ sắp mất — nên hỏi trước.
|
|
380
|
+
|
|
381
|
+
**Điểm mù mà nó bịt (GAPS-v4 G54).** Toàn bộ lưới an toàn của framework so **nhãn version**, không so
|
|
382
|
+
**nội dung** — không có một content hash nào ở đâu. Nên một PRD bị sửa tay là điểm mù **tuyệt đối**:
|
|
383
|
+
Step 4 thấy `PRD Version == prd_version` ⇒ sạch · `gate-trace` thấy report khớp sổ ⇒ PASS ·
|
|
384
|
+
`require-fresh-audit` thấy PR không chạm tag ⇒ không đòi audit. **Cả ba tầng xanh, và cả ba đúng theo
|
|
385
|
+
định nghĩa của chính chúng.**
|
|
386
|
+
|
|
387
|
+
#### Đọc mốc của lần audit trước
|
|
388
|
+
|
|
389
|
+
Đọc khối **`spec_baseline`** trong `trace-report.json` của lần chạy trước (path:
|
|
390
|
+
`{living_docs_dir}/trace-report.json`). Mỗi entry: `prd_path` · `sha_at_audit` · `version_at_audit`.
|
|
391
|
+
|
|
392
|
+
- Khối **vắng** (lần audit đầu, hoặc report sinh bởi version cũ hơn) → **bỏ qua so sánh**, chỉ **ghi
|
|
393
|
+
mốc mới** ở Step 6b. In một dòng: `ⓘ spec_baseline: lần đầu ghi mốc — check sửa-ngoài-đường bắt đầu có hiệu lực từ lần chạy sau.`
|
|
394
|
+
|
|
395
|
+
#### So — hai nguồn bằng chứng, cần cả hai
|
|
396
|
+
|
|
397
|
+
Với mỗi file PRD trong phạm vi, ở repo chứa `{paths.specs_dir}`:
|
|
398
|
+
|
|
399
|
+
| Nguồn | Lệnh | Bắt ca nào |
|
|
400
|
+
|---|---|---|
|
|
401
|
+
| **git diff** | `git -C {specs repo} diff --name-only {sha_at_audit}..HEAD -- {prd_path}` | sửa **đã commit** |
|
|
402
|
+
| **git status** | `git -C {specs repo} status --porcelain -- {prd_path}` | sửa **CHƯA commit** — ca thường gặp nhất, vì PO đang gõ |
|
|
403
|
+
|
|
404
|
+
Thiếu nguồn thứ hai là mù với cả một lớp ca: PO sửa xong, chưa commit, chạy audit — và audit nói sạch.
|
|
405
|
+
|
|
406
|
+
#### Phán
|
|
407
|
+
|
|
408
|
+
| Nội dung đổi? | `Version` hiện tại vs `version_at_audit` | Kết luận |
|
|
409
|
+
|:---:|---|---|
|
|
410
|
+
| **có** | **BẰNG NHAU** | 🔴 **`PRD_UNTRACKED_EDIT`** — có người sửa ngoài `/generate-prd` · `/extend-prd` · `/amend-prd` · `/refine-prd` · `/review-context` |
|
|
411
|
+
| có | khác | ✅ không cờ — đã đi đường chính thức. Step 4 tiếp quản bình thường |
|
|
412
|
+
| không | bất kỳ | ✅ không cờ |
|
|
413
|
+
|
|
414
|
+
**Không có báo oan:** ca duy nhất bật cờ là *"đổi nội dung, giữ nguyên nhãn"*. Sửa **và** bump
|
|
415
|
+
version thì `version != version_at_audit` ⇒ im.
|
|
416
|
+
|
|
417
|
+
#### Ca không kiểm được → nói rõ là đang mù
|
|
418
|
+
|
|
419
|
+
Không phải git repo · `sha_at_audit` không còn (history bị rewrite) · `git` không khả dụng →
|
|
420
|
+
**bỏ qua** check này và in:
|
|
421
|
+
```
|
|
422
|
+
⚠️ Chưa kiểm được sửa-ngoài-đường cho {n} PRD ({lý do}) — điểm mù G54 đang MỞ ở các file này.
|
|
423
|
+
```
|
|
424
|
+
**KHÔNG** bịa cờ, và **KHÔNG** im lặng. Im lặng là lựa chọn rẻ nhất và tệ nhất trong ba.
|
|
425
|
+
|
|
426
|
+
#### Đường ra — tự lành, cố ý KHÔNG có lệnh escape
|
|
427
|
+
|
|
428
|
+
Cách sửa đúng là **bump version + ghi một row changelog nêu UC bị ảnh hưởng** — tức đúng việc
|
|
429
|
+
`/amend-prd` làm hộ. Làm xong thì `version != version_at_audit` ⇒ cờ tự tắt, và logic `PRD_DRIFT`
|
|
430
|
+
bình thường tiếp quản (đúng, vì nội dung có đổi thật).
|
|
431
|
+
|
|
432
|
+
> **Vì sao KHÔNG có `--accept-edit`:** thêm nó là thêm một đường **dán nhãn lên thay đổi chưa ai
|
|
433
|
+
> xem** — đúng cái sai mà ba rào của `--realign` (Step 5e) tồn tại để chặn.
|
|
434
|
+
|
|
435
|
+
#### Vì sao cờ này KHÔNG nằm trong `gate.blocking`
|
|
436
|
+
|
|
437
|
+
Quyết định có chủ ý, ghi ở `spec_edit_detection.$comment`:
|
|
438
|
+
|
|
439
|
+
| | |
|
|
440
|
+
|---|---|
|
|
441
|
+
| `gate.blocking` nghĩa hẹp là **code đang hỏng** | Cờ này nói về **spec**; code có thể đang hoàn toàn đúng. R9(e) tồn tại để giữ đúng ranh giới đó |
|
|
442
|
+
| Nợ tồn khi mới bật | Mọi project đang chạy đều đã có PRD sửa tay ⇒ cờ chặn mới sẽ đỏ khắp nơi ở lần đầu ⇒ **người ta tắt cổng** ⇒ mất luôn 4 cờ 🔴 thật. Đó đúng là thất bại R9(e) được viết ra để chặn, chỉ đến bằng một cửa khác |
|
|
443
|
+
| Nhưng nó vẫn 🔴 | In mỗi lần chạy, có khối riêng trong report + counter trong `summary` — đủ để thấy, không đủ để làm tắt cổng |
|
|
444
|
+
|
|
445
|
+
Team nào đã dọn sạch nợ tồn thì tự thêm `prd_untracked_edit_count` vào `gate.blocking` (kèm `why`) —
|
|
446
|
+
`self-check` R13(e) canh việc đó.
|
|
447
|
+
|
|
448
|
+
---
|
|
294
449
|
### Step 4 — PRD version drift check
|
|
295
450
|
|
|
296
451
|
Với mỗi UC, so:
|
|
@@ -304,13 +459,43 @@ Nếu layer nào sau version PRD hiện tại → trích các changelog entry k
|
|
|
304
459
|
|
|
305
460
|
*PRD là tài liệu **cấp feature** phủ nhiều UC, nhưng version của nó là **một scalar**. Nên thêm UC7 — không đụng một chữ nào của UC1–UC6 — vẫn làm cả 6 UC cũ lệch version. So version thuần thì cả 6 ăn cờ đỏ oan.*
|
|
306
461
|
|
|
307
|
-
Đọc các row `# Change Log` của PRD **từ version của layer cũ nhất tới hiện tại
|
|
462
|
+
Đọc các row `# Change Log` của PRD **từ version của layer cũ nhất tới hiện tại**. Mỗi row mang một `{changelog_scope}` — contract máy đọc, khai ở `bin/trace-schema.json` → `changelog_row_contract`; producer là `/refine-prd` Phase 3, `/extend-prd` Bước 6, `/review-context` Fix/Resume Phase 3, và **bắt buộc** ghi thông tin này chính vì mục đích ở đây. *(`/generate-bdd` Version Check đọc cùng dữ liệu.)*
|
|
463
|
+
|
|
464
|
+
**Dựng `affected_ucs` theo ba bước — KHÔNG bỏ bước 2:**
|
|
465
|
+
|
|
466
|
+
**1. Tách mệnh đề.** Mỗi row `{changelog_scope}` gồm các mệnh đề ngăn bằng `;`, mỗi mệnh đề mở đầu bằng đơn vị sở hữu: `{UC-ID}: {mô tả}` hoặc `PRD-global: {mô tả}`.
|
|
467
|
+
|
|
468
|
+
**2. Chuẩn hoá về UC — phép phân giải `BR/AC → UC sở hữu`.** Trong mỗi mệnh đề, ngoài UC-ID mở đầu còn có thể có BR/AC được nêu. Đưa **mọi** BR/AC về UC sở hữu **trước khi** so:
|
|
469
|
+
|
|
470
|
+
| Gặp | Phân giải thành |
|
|
471
|
+
|---|---|
|
|
472
|
+
| `BR{n}` | UC có `BR{n}` trong **bảng Business Rule** của nó (PRD §3) |
|
|
473
|
+
| `AC{n}` | UC có `AC{n}` ở dòng **`**AC liên quan:**`** của nó (PRD §3) |
|
|
474
|
+
| `PRD-global` | **không** thuộc UC nào — không thêm gì vào `affected_ucs` |
|
|
475
|
+
| BR/AC **không** phân giải được về UC nào *(ID đã bị xoá, hoặc PRD lệch cấu trúc)* | coi **cả row** là **mơ hồ** → hàng 3 dưới đây. **KHÔNG** bỏ qua im lặng mệnh đề đó |
|
|
476
|
+
|
|
477
|
+
*Không phát sinh I/O: Step 4 đã mở file PRD này ở đầu bước.*
|
|
478
|
+
|
|
479
|
+
**3. Phân loại từng UC** *(first-match-wins)*:
|
|
308
480
|
|
|
309
481
|
| Điều kiện | Cờ | Hành động |
|
|
310
482
|
|---|---|---|
|
|
311
|
-
|
|
|
312
|
-
| UC này **
|
|
313
|
-
|
|
|
483
|
+
| **Bất kỳ** row nào trong khoảng **mơ hồ** (không mệnh đề nào nêu được đơn vị sở hữu, hoặc bước 2 không phân giải được) | `PRD_DRIFT` 🟠 cho **MỌI** UC | Không suy đoán được thì quét rộng |
|
|
484
|
+
| UC này **có** trong `affected_ucs` | `PRD_DRIFT` 🟠 | Cần regen thật — theo bảng `drifted_layers` bên dưới |
|
|
485
|
+
| UC này có trong `affected_ucs` **CHỈ** qua (các) mệnh đề mang hậu tố **`[no-behavior]`** | `PRD_STALE_REF` ⓘ | Producer đã **chứng minh** thay đổi không đổi hành vi (chỉ thêm/bỏ vỏ cấu trúc). Xem `changelog_row_contract.neutral_checks` |
|
|
486
|
+
| UC này **không** có trong `affected_ucs`, **và** mọi row trong khoảng đều nêu rõ scope | `PRD_STALE_REF` ⓘ | Nội dung không đổi, chỉ con trỏ version cũ. **KHÔNG** route regen — dùng `--realign-prd-version` |
|
|
487
|
+
|
|
488
|
+
> **Vì sao bước 2 là bắt buộc (G53).** Bản cũ viết *"trích tập UC/AC/BR được nêu"* rồi so bằng phép thử *"**UC** này có trong tập?"*. Hai câu đó **không khớp nhau**: tập chứa lẫn UC, AC và BR, nhưng phép thử chỉ hỏi về UC. Nên một row như `thêm UC7: AC12-AC14; sửa BR8` cho tập `{UC7, AC12-14, BR8}` — và **UC3, chủ sở hữu BR8, không có trong đó**.
|
|
489
|
+
>
|
|
490
|
+
> Row đó nêu rất nhiều ID nên **không** rơi vào hàng "mơ hồ". Kết quả: UC3 → ⓘ *"không phải lỗi"*, trong khi BR mà nó sở hữu vừa đổi hành vi. Và ⓘ **mở cửa** cho `--realign-prd-version` (rào an toàn của nó chỉ **từ chối khi UC là 🟠**) ⇒ nhãn `prd_version` bị dán lại ở cả TSV lẫn tag code ⇒ **cờ sạch vĩnh viễn trên thay đổi chưa ai implement**.
|
|
491
|
+
>
|
|
492
|
+
> Đây là G1 đúng nghĩa — cờ **im lặng** — chỉ khác là lần này có thêm một lệnh tự động đóng dấu lên nó.
|
|
493
|
+
|
|
494
|
+
> **Vì sao hàng "mơ hồ" lên ĐẦU bảng.** Nó là điều kiện **cấp row**, không phải cấp UC: một row mơ hồ làm mọi phán đoán per-UC trong khoảng đó vô giá trị. Xét nó sau các hàng kia thì một UC có thể được xếp ⓘ **trước khi** ta biết là không suy đoán được gì — và ⓘ là hạng mở cửa cho `--realign`.
|
|
495
|
+
|
|
496
|
+
> **Hàng "mơ hồ" là lưới an toàn — hỏng theo hướng an toàn.** Changelog viết ẩu thì ta mất tính năng *lọc*, KHÔNG mất tính năng *cảnh báo*. Đồng bộ với `generate-bdd` Version Check: *"changelog row không nêu rõ scope (mơ hồ) → khuyến nghị F (gen lại toàn bộ)"*.
|
|
497
|
+
>
|
|
498
|
+
> ⚠️ **Nhưng lưới an toàn KHÔNG phải cái cớ để producer ghi bừa.** Nó đúng khi **thiếu** thông tin; nó sai khi producer **có** thông tin mà không ghi. Đó là G52: `/review-context --fix` từng ghi cứng `Auto-fix: applied {N} findings` — 0 scope — trong khi findings YAML của nó có `uc_id` bắt buộc cho từng finding. Một sửa từ trong UC5 làm **cả 8 UC** ăn 🟠. `self-check` R12 giờ fail build nếu producer nào không nhắc `{changelog_scope}`.
|
|
314
499
|
|
|
315
500
|
> **Hàng thứ ba là lưới an toàn — hỏng theo hướng an toàn.** Changelog viết ẩu thì ta mất tính năng *lọc*, KHÔNG mất tính năng *cảnh báo*. Đồng bộ với `generate-bdd` Version Check: *"changelog row không nêu rõ UC/AC/BR (mơ hồ) → khuyến nghị F (gen lại toàn bộ)"*.
|
|
316
501
|
>
|
|
@@ -340,13 +525,18 @@ Skip cột nào chưa có revision đã lưu (`—`), hoặc cả UC chưa có t
|
|
|
340
525
|
|
|
341
526
|
*Tech-doc có **đúng cùng hình dạng** với PRD: một doc **GỘP cấp PRD**, một `@trace.revision` chung cho nhiều UC. Chế độ **APPEND** (`generate-tech-docs` Bước 1) thêm UC mới vào doc đã có → revision bump → **mọi UC cũ lệch**, dù phần của chúng không đổi một dòng.*
|
|
342
527
|
|
|
343
|
-
Đọc **§10 UC Coverage** và **Changelog** của tech-doc,
|
|
528
|
+
Đọc **§10 UC Coverage** và **Changelog** của tech-doc. Mỗi row Changelog mang một `{changelog_scope}` cùng contract với PRD (`changelog_row_contract`); producer là `/generate-tech-docs` Bước 1b, đơn vị "global" ở đây tên là `doc-global` thay cho `PRD-global`.
|
|
529
|
+
|
|
530
|
+
Dựng `affected_ucs` **theo đúng ba bước của Step 4**, kể cả bước 2 (`BR/AC → UC sở hữu`) — tech-doc cũng nêu §/SC/BR trong mô tả, và một mệnh đề nêu `§5.9` mà bỏ UC thì UC đó rơi vào ⓘ y như ca BR8 ở Step 4. Rồi phân loại *(first-match-wins, cùng thứ tự)*:
|
|
344
531
|
|
|
345
532
|
| Điều kiện | Cờ |
|
|
346
533
|
|---|---|
|
|
347
|
-
|
|
|
348
|
-
| UC này **
|
|
349
|
-
|
|
|
534
|
+
| **Bất kỳ** row nào trong khoảng **mơ hồ** (không nêu được đơn vị sở hữu, hoặc bước 2 không phân giải được) | 🟠 cho **MỌI** UC (lưới an toàn) |
|
|
535
|
+
| UC này **có** trong `affected_ucs` | `TECHDOC_DRIFT` / `FE_TECHDOC_DRIFT` 🟠 |
|
|
536
|
+
| UC này có trong `affected_ucs` **CHỈ** qua mệnh đề mang hậu tố `[no-behavior]` | `TECHDOC_STALE_REF` ⓘ |
|
|
537
|
+
| UC này **không** có trong `affected_ucs`, mọi row nêu rõ scope | `TECHDOC_STALE_REF` ⓘ — dùng `--realign-techdoc-revision` |
|
|
538
|
+
|
|
539
|
+
> **`doc-global` là đường ra bình thường cho tech-doc, không phải ngoại lệ.** Sửa §11 Cross-cutting hay §2 kiến trúc chung thì không mệnh đề nào nêu UC ⇒ `affected_ucs` rỗng ⇒ mọi UC ở lại ⓘ. Nên `/generate-tech-docs` **không cần** dùng `[no-behavior]`; marker đó dành cho producer biết chính xác `check_id` của từng fix (`/review-context --fix`). Consumer vẫn phải **hiểu** marker vì cùng một hàng bảng phục vụ cả hai artifact.
|
|
350
540
|
|
|
351
541
|
### Step 5e — Realign mode *(chỉ chạy khi có flag)*
|
|
352
542
|
|
|
@@ -462,6 +652,22 @@ Với mỗi file `.tsv` đã xử lý: ghi `spec_ver`, `status`, `last_updated`
|
|
|
462
652
|
Và **đồng bộ `prd_status` ← `| **Status** |`** của PRD tương ứng (`{paths.specs_dir}/{domain}/{prd-slug}/{TICKET-ID}-{prd-slug}.md`) — đối xứng với `uc_status`: PRD Metadata là nguồn-sự-thật về duyệt PRD. Không có bước này thì `prd_status` là **write-once** (chỉ `/generate-bdd` ghi một lần) và sẽ giữ `approved` vĩnh viễn sau khi `/refine-prd` hay `/review-context --fix` reset PRD về `draft`. *(Step 4 đã đọc file PRD này rồi — không phát sinh I/O.)*
|
|
463
653
|
**Đừng** sửa `dev_selftest`/`dev_selftest_at` (do `/dev-run-test` sở hữu) hay `qc_status`/`qc_run_at`/`qc_owner`/`qc_blocked_by` (do `/qc-run-test` + `/report-bug` sở hữu); lệnh này chỉ đọc chúng cho report.
|
|
464
654
|
|
|
655
|
+
### Step 6b — Ghi mốc `spec_baseline` cho lần audit sau
|
|
656
|
+
|
|
657
|
+
*Đây là **nửa GHI** của check ở Step 3.9. Thiếu nó thì cờ `PRD_UNTRACKED_EDIT` **không bao giờ bật** — và mọi rule khác vẫn ✅, vì R6 chỉ canh "giá trị enum có xuất hiện" và R7 chỉ canh "cờ có counter". `self-check` R13 canh đúng nửa này.*
|
|
658
|
+
|
|
659
|
+
Với **mỗi** file PRD trong phạm vi lần chạy này, ghi một entry vào khối `spec_baseline` của `trace-report.json` (Step 8):
|
|
660
|
+
|
|
661
|
+
| Key | Giá trị |
|
|
662
|
+
|---|---|
|
|
663
|
+
| `prd_path` | path tương đối tới file PRD trong specs repo |
|
|
664
|
+
| `sha_at_audit` | `git -C {specs repo} rev-parse HEAD` — commit tại thời điểm audit |
|
|
665
|
+
| `version_at_audit` | `\| **Version** \|` hiện tại của PRD |
|
|
666
|
+
|
|
667
|
+
Không phải git repo / `git` không khả dụng → ghi `sha_at_audit: null` và **vẫn ghi** `version_at_audit` (lần sau Step 3.9 sẽ báo đang mù, không im lặng).
|
|
668
|
+
|
|
669
|
+
> **Ghi mốc là thao tác cuối, sau khi đã phán xong.** Ghi trước thì một lần chạy bị ngắt giữa đường sẽ để lại mốc mới mà chưa phán gì — và lần sau so với mốc đó thì thay đổi trong khoảng đó **biến mất khỏi tầm quan sát vĩnh viễn**.
|
|
670
|
+
|
|
465
671
|
### Step 7 — Tính aggregate cho dashboard
|
|
466
672
|
|
|
467
673
|
```
|
|
@@ -486,6 +692,9 @@ fe_integrated = rows where fe_phase == integrated # FE đã wire adapter th
|
|
|
486
692
|
# công việc CHƯA XONG dù status có thể đã là OK (có code + có test trên mock).
|
|
487
693
|
orphaned_count = rows where status == ORPHANED # code còn, scenario đã bị xoá khỏi .feature (Step 2b/Rule 0)
|
|
488
694
|
trace_orphan_count = số tag @trace.implements/@trace.verifies trỏ vào SC không tồn tại VÀ không có row TSV (Step 2b)
|
|
695
|
+
prd_untracked_edit_count = số file PRD có nội dung đổi kể từ `sha_at_audit` mà `Version` KHÔNG đổi
|
|
696
|
+
(Step 3.9). 🔴 — có người sửa ngoài đường chính thức, nên MỌI phán đoán version
|
|
697
|
+
của Step 4/5 trên file đó đang dựa vào một nhãn không còn đúng.
|
|
489
698
|
prd_drift_count = số UC bị cờ PRD_DRIFT (lệch version VÀ changelog nêu UC này — Step 4)
|
|
490
699
|
prd_stale_ref_count = số UC bị cờ PRD_STALE_REF (lệch version nhưng changelog KHÔNG nêu UC này —
|
|
491
700
|
nội dung không đổi, chỉ con trỏ cũ). ⓘ không phải lỗi; sạch bằng --realign-prd-version
|
|
@@ -517,6 +726,11 @@ seam_unwired_count = số seam bị cờ SEAM_UNWIRED (hàng thật đã có nh
|
|
|
517
726
|
seam_pending_count = số seam bị cờ SEAM_PENDING (owner UC chưa gen — ⓘ chưa phải lỗi; PM dùng để xếp thứ tự gen)
|
|
518
727
|
stub_unresolved_count = số stub bị cờ STUB_UNRESOLVED (method còn trắng dù owner đã gen / có hàm song song — Step 5b)
|
|
519
728
|
stub_pending_count = số stub bị cờ STUB_PENDING (owner BDD chưa gen — ⓘ chưa phải lỗi)
|
|
729
|
+
service_unrouted_count = số row có `service` ∈ (unrouted, unresolved) SAU khi đã phân giải lại ở
|
|
730
|
+
Step 2c. 🟠 KHÔNG chặn PR — "chưa ai quyết repo" là trạng thái hợp lệ ở feature
|
|
731
|
+
đầu tiên của một domain mới, không phải code hỏng. Nhưng phải NHÌN THẤY ĐƯỢC:
|
|
732
|
+
không có counter thì `unrouted` đọng lại vĩnh viễn và `by_service` có một ô rác
|
|
733
|
+
mà không ai để ý — đúng bệnh `TBD` (G51).
|
|
520
734
|
# LUẬT (rules/workflow.md §Trace Contract): MỌI giá trị trong vocabularies.audit_flags phải có
|
|
521
735
|
# một counter {flag_lowercase}_count ở đây VÀ trong summary của JSON. bin/self-check.js R7 ép
|
|
522
736
|
# điều này — thiếu counter = cờ không quan sát được ở tầng tổng hợp, dashboard không thấy.
|
|
@@ -560,7 +774,11 @@ Schema:
|
|
|
560
774
|
```json
|
|
561
775
|
{
|
|
562
776
|
"generated_at": "<ISO-8601 timestamp>",
|
|
563
|
-
"
|
|
777
|
+
"scope": {
|
|
778
|
+
"kind": "all | domain | prd | uc",
|
|
779
|
+
"value": "<giá trị scope, hoặc 'all'>"
|
|
780
|
+
},
|
|
781
|
+
"domain": "<domain trong scope, hoặc 'all'>",
|
|
564
782
|
"summary": {
|
|
565
783
|
"total_prds": 0,
|
|
566
784
|
"approved_prds": 0,
|
|
@@ -579,6 +797,7 @@ Schema:
|
|
|
579
797
|
"fe_integrated": 0,
|
|
580
798
|
"orphaned_count": 0,
|
|
581
799
|
"trace_orphan_count": 0,
|
|
800
|
+
"prd_untracked_edit_count": 0,
|
|
582
801
|
"prd_drift_count": 0,
|
|
583
802
|
"prd_stale_ref_count": 0,
|
|
584
803
|
"bdd_drift_count": 0,
|
|
@@ -592,6 +811,7 @@ Schema:
|
|
|
592
811
|
"seam_pending_count": 0,
|
|
593
812
|
"stub_unresolved_count": 0,
|
|
594
813
|
"stub_pending_count": 0,
|
|
814
|
+
"service_unrouted_count": 0,
|
|
595
815
|
"dev_selftest_passing": 0,
|
|
596
816
|
"dev_selftest_failing": 0,
|
|
597
817
|
"dev_selftest_not_run": 0,
|
|
@@ -658,7 +878,24 @@ Schema:
|
|
|
658
878
|
]
|
|
659
879
|
}
|
|
660
880
|
],
|
|
881
|
+
"spec_baseline": [
|
|
882
|
+
{
|
|
883
|
+
"prd_path": "<specs/{domain}/{prd-slug}/{TICKET-ID}-{prd-slug}.md>",
|
|
884
|
+
"sha_at_audit": "<git rev-parse HEAD của specs repo, hoặc null nếu không phải git repo>",
|
|
885
|
+
"version_at_audit": "<Version của PRD tại thời điểm audit>"
|
|
886
|
+
}
|
|
887
|
+
],
|
|
661
888
|
"issues": {
|
|
889
|
+
"prd_untracked_edit": [
|
|
890
|
+
{
|
|
891
|
+
"prd_path": "<specs/{domain}/{prd-slug}/{TICKET-ID}-{prd-slug}.md>",
|
|
892
|
+
"version": "<Version hiện tại — KHÔNG đổi kể từ lần audit>",
|
|
893
|
+
"sha_at_audit": "<commit lần audit trước>",
|
|
894
|
+
"evidence": "git diff | git status | cả hai",
|
|
895
|
+
"affected_ucs": ["<mọi UC của PRD này — không suy đoán được ai bị đụng>"],
|
|
896
|
+
"fix": "bump Version + ghi row changelog nêu UC bị ảnh hưởng (đó là việc /amend-prd làm hộ). KHÔNG có --accept-edit: dán nhãn lên thay đổi chưa ai xem là đúng cái rào của --realign tồn tại để chặn."
|
|
897
|
+
}
|
|
898
|
+
],
|
|
662
899
|
"drift": [
|
|
663
900
|
{
|
|
664
901
|
"sc_id": "<SC-ID>",
|
|
@@ -797,6 +1034,16 @@ Schema:
|
|
|
797
1034
|
"fix": "Trỏ binding của <consumer_uc> sang <RealClass> (xoá/thay stub), build lại"
|
|
798
1035
|
}
|
|
799
1036
|
],
|
|
1037
|
+
"service_unrouted": [
|
|
1038
|
+
{
|
|
1039
|
+
"sc_id": "<SC-ID>",
|
|
1040
|
+
"platform": "web | app | system",
|
|
1041
|
+
"domain": "<domain của PRD>",
|
|
1042
|
+
"value": "unrouted | unresolved",
|
|
1043
|
+
"reason": "<lý do context-loader đã ghi — vd: domain chưa có entry trong services:>",
|
|
1044
|
+
"fix": "thêm mapping cho domain <domain> vào services: của .agent/project-context.yaml, rồi chạy lại /validate-traces (nó tự nâng unrouted → path). KHÔNG cần sinh lại BDD."
|
|
1045
|
+
}
|
|
1046
|
+
],
|
|
800
1047
|
"stub_unresolved": [
|
|
801
1048
|
{
|
|
802
1049
|
"artifact": "<ClassName#method>",
|
|
@@ -826,7 +1073,11 @@ Schema:
|
|
|
826
1073
|
Row `ORPHANED` xuất ra JSON là: `"status": "DRIFT"` + `"orphaned": true`. Panel chưa hỗ trợ vẫn hiện nó như `DRIFT` — đủ đúng về nghĩa ("code không khớp spec, cần xử lý") và **không im lặng**; panel có đọc `orphaned` thì hiện nhãn riêng. Chi tiết đầy đủ luôn có ở `orphaned[]` và ở report terminal.
|
|
827
1074
|
**TSV giữ nguyên chữ `ORPHANED`** trong cột `status` — TSV là nguồn-sự-thật, JSON chỉ là bản xuất cho panel.
|
|
828
1075
|
- `orphaned` (boolean): `true` chỉ khi cột `status` của TSV là `ORPHANED`; mọi row khác ghi `false` (đừng bỏ trống — panel đọc field vắng dễ ra `undefined`).
|
|
829
|
-
- Luôn ghi vào `{paths.trace_dir}/trace-report.json` bất kể
|
|
1076
|
+
- Luôn ghi vào `{paths.trace_dir}/trace-report.json` bất kể phạm vi — nếu có scope, chỉ gồm các PRD/UC đó trong `prds[]`, và ghi **cả hai** field:
|
|
1077
|
+
- **`scope`** = `{kind, value}` từ Step 0-A. Đây là field `gate-trace` dùng để quyết chặn — xem dưới.
|
|
1078
|
+
- **`domain`** = domain trong scope (`--domain` → chính nó · `--prd`/`--uc` → domain phân giải được · không scope → `all`). Giữ cho **tương thích ngược** với `gate-trace` bản cũ.
|
|
1079
|
+
|
|
1080
|
+
> ⚠️ **Biên bản có scope KHÔNG BAO GIỜ được coi là biên bản đầy đủ.** `gate-trace` G2 fail nếu `scope.kind !== "all"`, **không ngoại lệ** — nó không nhìn xem trên đĩa có bao nhiêu domain. Vì sao tuyệt đối: bản cũ hỏi *"còn domain nào khác không"*, nên trong repo **một domain** thì `others` là **rỗng** ⇒ không fail ⇒ một biên bản hẹp-theo-PRD được nhận là *"toàn bộ"*. Đó đúng là *"cấp giấy xanh cho thứ chưa ai xem"* mà chú thích của chính gate cảnh báo. Thêm cờ scope mà không siết G2 là biến cổng thành **sân khấu** — đúng cái G39 dựng lên để chống.
|
|
830
1081
|
- **TSV `"—"` mapping**: khi đọc file TSV, map giá trị dash sang kiểu JSON: `implemented_by: "—"` → `null`; `test_count: "—"` → `0`; `test_classes: "—"` → `[]`; `tech_doc_revision: "—"` → `0`; `fe_tech_doc_revision: "—"` → `0`; `dev_selftest: "—"` → `"not_run"`; `dev_selftest_at: "—"` → `null`; `qc_status: "—"` → `"not_run"`; `qc_run_at: "—"` → `null`; `qc_owner: "—"` → `null`; `qc_blocked_by: "—"` → `null`; `service: "—"` → `null`; `design_spec_version: "—"` → `null`
|
|
831
1082
|
- **Backward-compat:** TSV cũ có thể thiếu cột mới hơn trong header — coi cột vắng nào là giá trị rỗng của nó (**đừng báo lỗi, đừng bỏ qua cả file**): `qc_owner`/`qc_blocked_by` (pre-19-col) → `null`; `fe_tech_doc_revision` (pre-22-col) → `0`; `service`/`design_spec_version` (pre-24-col) → `null`. Lần `/generate-bdd` gen lại tiếp theo nâng header lên layout **24 cột** hiện tại.
|
|
832
1083
|
> **Đọc theo TÊN CỘT ở header row, KHÔNG theo vị trí.** Header là dòng đầu mỗi `.tsv` — parse nó rồi tra theo tên. Đếm vị trí sẽ vỡ ở đúng file cũ mà luật này sinh ra để đỡ. Header thiếu hoàn toàn (file hỏng) → mới báo lỗi cho file đó và đi tiếp, không abort cả lệnh.
|
|
@@ -991,6 +1242,20 @@ Trace orphan (tag trỏ vào SC không tồn tại, KHÔNG có row .tsv nào):
|
|
|
991
1242
|
(Không lệnh nào khác bắt được cái này — row .tsv đã bị xoá bởi version cũ,
|
|
992
1243
|
hoặc tag ghi sai id ngay từ đầu.)
|
|
993
1244
|
|
|
1245
|
+
🔴 PRD_UNTRACKED_EDIT — nội dung PRD đổi mà nhãn Version KHÔNG đổi ({n} file):
|
|
1246
|
+
specs/payment/create-invoice/PAY01-create-invoice.md Version 1.3 (không đổi từ lần audit)
|
|
1247
|
+
Bằng chứng : git status — sửa CHƯA commit
|
|
1248
|
+
Mốc cũ : sha a1b2c3d · Version 1.3
|
|
1249
|
+
⚠️ MỌI phán đoán version bên dưới cho PRD này đang dựa vào một nhãn không còn đúng.
|
|
1250
|
+
Không suy đoán được UC nào bị đụng → phải coi cả {n} UC của nó là chưa rõ.
|
|
1251
|
+
Sửa: bump Version + ghi một row changelog NÊU UC bị ảnh hưởng.
|
|
1252
|
+
Đó đúng là việc /amend-prd làm hộ (kèm kiểm va chạm + guard sau-ghi).
|
|
1253
|
+
Không có --accept-edit: dán nhãn lên thay đổi chưa ai xem là đúng cái rào của
|
|
1254
|
+
--realign tồn tại để chặn.
|
|
1255
|
+
|
|
1256
|
+
(hoặc: ⚠️ Chưa kiểm được sửa-ngoài-đường cho {n} PRD ({lý do}) — điểm mù G54 đang MỞ)
|
|
1257
|
+
(hoặc: ⓘ spec_baseline: lần đầu ghi mốc — check có hiệu lực từ lần chạy sau)
|
|
1258
|
+
|
|
994
1259
|
PRD Version Drift (changelog CÓ nêu UC này — nội dung đổi thật):
|
|
995
1260
|
{UC}-UC2 — code ở PRD v1.0, PRD giờ ở v1.2 [lệch: tsv, code]
|
|
996
1261
|
Thay đổi kể từ v1.0:
|
|
@@ -1026,6 +1291,14 @@ Seam & Stub Audit (mồ côi khi ghép luồng):
|
|
|
1026
1291
|
→ /generate-code {owner_uc} lấp logic vào {ClassName#method} tại chỗ, xoá hàm song song, build lại
|
|
1027
1292
|
ⓘ STUB_PENDING — {ClassName#method} ({stub_for}): owner {owner_uc} chưa gen (chưa phải lỗi)
|
|
1028
1293
|
|
|
1294
|
+
{khối dưới CHỈ in khi service_unrouted_count > 0 — else bỏ cả khối}
|
|
1295
|
+
Routing chưa chốt ({service_unrouted_count} scenario) — 🟠 KHÔNG chặn PR:
|
|
1296
|
+
🟠 SERVICE_UNROUTED — {sc_id} ({platform}), domain "{domain}": {reason}
|
|
1297
|
+
→ thêm mapping cho domain "{domain}" vào `services:` của .agent/project-context.yaml,
|
|
1298
|
+
rồi chạy lại /validate-traces — nó tự nâng unrouted → path. KHÔNG cần sinh lại BDD.
|
|
1299
|
+
BDD của các scenario này KHÔNG sai. Đây là bước cấu hình của architect, và nó chỉ CHẶN
|
|
1300
|
+
ở /generate-code (lệnh đó buộc phải biết ghi file vào repo nào).
|
|
1301
|
+
|
|
1029
1302
|
{khối dưới CHỈ in khi Step 7b tìm thấy ≥1 request Status: Open — else bỏ cả khối}
|
|
1030
1303
|
📥 Yêu cầu đổi PRD đang chờ ({n} — chưa ai xử lý):
|
|
1031
1304
|
{UC-ID} — "{title}" chờ {days_waiting} ngày
|
package/core/rules/workflow.md
CHANGED
|
@@ -65,6 +65,40 @@ Ba mức, định nghĩa đầy đủ ở `steps/gate.md` Bước 3a — **đây
|
|
|
65
65
|
**Ngoại lệ có chủ ý:** `qc_owner`/`qc_blocked_by` (con trỏ tới bug — spec đổi không làm bug biến
|
|
66
66
|
mất) và `test_count`/`test_classes` (test vẫn tồn tại trên đĩa; số lượng không sai, chỉ nội dung
|
|
67
67
|
cũ → **cảnh báo**, không hạ số, để tỷ lệ coverage không nhảy loạn).
|
|
68
|
+
- **Sửa spec phải đi qua một lệnh.** Mọi drift detector so **nhãn version**, không so **nội dung**
|
|
69
|
+
(0 content hash trong toàn bộ codebase) — nên một PRD/tech-doc bị sửa tay mà không bump version là
|
|
70
|
+
điểm mù **tuyệt đối**: cả `/validate-traces`, `gate-trace`, và `require-fresh-audit` đều xanh, và
|
|
71
|
+
cả ba **đúng theo định nghĩa của chính chúng**. Bốn cửa chính: `/generate-prd` (mới) ·
|
|
72
|
+
`/extend-prd` (**thêm**) · **`/amend-prd`** (**đổi**) · `/refine-prd`/`/review-context --resume`
|
|
73
|
+
(áp finding). `/validate-traces` Step 3.9 canh cửa sau bằng cờ 🔴 `PRD_UNTRACKED_EDIT`
|
|
74
|
+
(`spec_edit_detection`: git diff **và** git status vs mốc `spec_baseline`).
|
|
75
|
+
*Đường ra cố ý **tự lành**, không có `--accept-edit`: bump version + ghi row changelog nêu UC là
|
|
76
|
+
hết cờ. Một cờ escape sẽ là một đường dán nhãn lên thay đổi chưa ai xem — đúng cái ba rào của
|
|
77
|
+
`--realign` tồn tại để chặn.*
|
|
78
|
+
- **Làm mất hiệu lực có MỆNH ĐỀ ĐỐI NGẪU: ai KHẲNG ĐỊNH một giá trị dương phải được phép khẳng
|
|
79
|
+
định.** Luật ngay trên nói *"ai làm giá trị hết đúng thì phải hạ nó"* — đúng, và được thực thi tốt.
|
|
80
|
+
Nhưng thiếu nửa này thì chuỗi thành **hạ xuống → dựng lại**: `/generate-bdd` hạ
|
|
81
|
+
`dev_selftest → not_run` khi spec đổi, rồi `/dev-run-test` (lệnh kế tiếp trong vòng lặp dev bình
|
|
82
|
+
thường) ghi lại `pass` kèm **ngày hôm nay** vì test cũ + code cũ vẫn xanh.
|
|
83
|
+
`pass` **không** mang nghĩa *"test đã chạy xanh"* — nó mang nghĩa *"scenario này đã được nghiệm thu
|
|
84
|
+
theo spec **hiện tại**"*. Trên row `DRIFT` nghĩa thứ nhất đúng và nghĩa thứ hai **sai**. Nên `status`
|
|
85
|
+
trực giao với **kết quả chạy**, **KHÔNG** trực giao với **quyền khẳng định**.
|
|
86
|
+
Contract: `bin/trace-schema.json` → `positive_assertion_guards`; `self-check` **R14** canh chủ cột
|
|
87
|
+
thực sự rẽ nhánh theo `status`, `lint-trace` **T12** bắt trạng thái ở sổ thật bất kể ai ghi.
|
|
88
|
+
**`fail` không bao giờ bị chặn** — đây là guard chống *báo cáo sai*, không phải guard *che tin xấu*.
|
|
89
|
+
- **Dòng changelog là contract máy đọc, không phải ghi chú cho người đọc.** PRD và tech-doc gộp
|
|
90
|
+
đều phủ nhiều UC nhưng chỉ có **một** nhãn version, nên `/validate-traces` Step 4/5 lọc 🟠 `*_DRIFT`
|
|
91
|
+
vs ⓘ `*_STALE_REF` **bằng chính dòng đó**. Grammar khai ở `bin/trace-schema.json` →
|
|
92
|
+
`changelog_row_contract`; `self-check` **R12** fail build nếu lệch. Ba luật:
|
|
93
|
+
**(1)** mỗi mệnh đề mở đầu bằng **đơn vị sở hữu** — `{UC-ID}:` hoặc `PRD-global:`/`doc-global:`;
|
|
94
|
+
**(2)** BR/AC **luôn đi kèm UC sở hữu** (`UC3: sửa BR8`), **không bao giờ đứng một mình** —
|
|
95
|
+
consumer khớp theo UC, nên `sửa BR8` trơ trọi làm UC3 bị xếp ⓘ trong khi BR8 vừa đổi hành vi, và
|
|
96
|
+
`--realign-prd-version` (chỉ chặn 🟠) sẽ dán nhãn version lại lên đó;
|
|
97
|
+
**(3)** hậu tố `[no-behavior]` **chỉ** cho thay đổi mà producer **chứng minh được** là không đổi
|
|
98
|
+
hành vi (`changelog_row_contract.neutral_checks`) — không dành cho người tự khai.
|
|
99
|
+
*Lưới an toàn "row mơ hồ → 🟠 cho MỌI UC" đúng khi **thiếu** thông tin, và sai khi producer **có**
|
|
100
|
+
thông tin mà không ghi: đó là G52 — `/review-context --fix` từng ghi cứng một dòng 0 scope trong
|
|
101
|
+
khi findings YAML của nó có `uc_id` bắt buộc cho từng finding.*
|
|
68
102
|
- **Mỗi audit flag phải quan sát được ở CẢ BA tầng.** Mọi giá trị trong
|
|
69
103
|
`vocabularies.audit_flags` bắt buộc có đủ: **(1)** một counter `{flag_lowercase}_count`
|
|
70
104
|
trong Step 7 + `summary` của `trace-report.json` · **(2)** một mảng trong `issues` ·
|
|
@@ -161,11 +161,25 @@ services:
|
|
|
161
161
|
|
|
162
162
|
*(Cả 2a/2b/2c: override `paths.specs_dir`/`paths.tech_docs_dir` per-service CHỈ khi `setup.spec_source` KHÔNG được đặt. Khi `spec_source` ĐƯỢC đặt, MỌI BDD/tech-doc là artifact liên team → để bước 4 route sang spec repo; KHÔNG pin per-service ở đây.)*
|
|
163
163
|
|
|
164
|
-
**3. Fallback
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
164
|
+
**3. Fallback** — **hai trạng thái khác nhau, đừng gộp** *(G51)*:
|
|
165
|
+
|
|
166
|
+
> `unrouted` = **chưa ai quyết** repo. Hợp lệ, bình thường ở feature đầu tiên của domain mới.
|
|
167
|
+
> `unresolved` = **config sai cấu trúc**. Là **bug** cần sửa file, không phải trạng thái chờ.
|
|
168
|
+
>
|
|
169
|
+
> Trước G51 cả hai dùng chung tên `unresolved` nên chịu chung hình phạt: `/generate-bdd` DỪNG HẲN.
|
|
170
|
+
> Nhưng PRD/BDD là artifact **nghiệp vụ** — PO biết `domain` và biết `platform`, **không** biết code
|
|
171
|
+
> sẽ nằm repo nào, và thường lúc đó chưa ai quyết. Cổng đặt sai phase.
|
|
172
|
+
|
|
173
|
+
**→ `unrouted`** (chưa có mapping — **không** phải lỗi):
|
|
174
|
+
- Không phát hiện được domain, hoặc domain **không khớp key nào** trong `services` → giữ path mặc định từ Bước 1, đặt `active_service = unrouted`.
|
|
175
|
+
- Domain khớp map-theo-platform (2b) nhưng thiếu sub-key cho `active_platform` → `active_service = unrouted`, ghi lý do rõ (không tự đoán platform).
|
|
176
|
+
- Entry là map-theo-prd_slug (2c) nhưng thiếu key cho `prd_slug` → `active_service = unrouted`, ghi lý do rõ (không tự đoán submodule).
|
|
177
|
+
|
|
178
|
+
**→ `unresolved`** (config **sai cấu trúc** — bug):
|
|
179
|
+
- Entry vừa có `path` vừa có `by_prd_slug`, hoặc `by_prd_slug` lồng nhau → `active_service = unresolved`, nêu đúng key sai để người dùng sửa `project-context.yaml`.
|
|
180
|
+
|
|
181
|
+
*Cả hai đều KHÔNG chặn việc nạp context. Lệnh nào chặn là quyết định của lệnh đó: `/generate-bdd`
|
|
182
|
+
đi tiếp với `unrouted` (Step 1.6) · `/generate-code` DỪNG ở cả hai (nó buộc phải biết ghi vào đâu).*
|
|
169
183
|
|
|
170
184
|
**4. Tự động override theo spec source** — nếu `setup.spec_source` được đặt VÀ path tương ứng chưa được set tường minh trong `paths:`:
|
|
171
185
|
- Override `paths.specs_dir` → `{spec_source}/specs` — **luôn khi `spec_source` được đặt.** Mọi spec artifact (PRD, BDD, tech-docs, design-spec) nằm dưới gốc spec thống nhất trong spec repo dùng chung theo bố cục feature-package: `{spec_source}/specs/{domain}/{prd-slug}/`. Mọi umbrella (FE/App/BE) đều đọc từ đây. *(`specs/` theo service chỉ khi không có `spec_source`.)*
|
|
@@ -209,6 +223,13 @@ Khi `active_service` đã được phân giải thành một path thật ở Bư
|
|
|
209
223
|
|
|
210
224
|
**4. Nếu không tìm thấy config của service** — giữ mặc định umbrella, vẫn set `service_root = {active_service}` (luôn cần mốc path kể cả khi không có config override).
|
|
211
225
|
|
|
226
|
+
> ⚠️ **`service_root` KHÔNG BAO GIỜ được là một chuỗi trạng thái** *(G51)*. Nếu `active_service` là
|
|
227
|
+
> `unrouted` / `unresolved` / `multi` / `—` thì đặt **`service_root = null`** và giữ path mặc định
|
|
228
|
+
> umbrella — **đừng** nội suy giá trị đó thành tên thư mục.
|
|
229
|
+
> Bản trước đặt `service_root = {active_service}` vô điều kiện, nên `/generate-code` (ghi file
|
|
230
|
+
> **tương đối với `service_root`**) sẽ ghi source vào một thư mục tên đúng chữ `unresolved/`.
|
|
231
|
+
> `service_root = null` là tín hiệu để `/generate-code` DỪNG thay vì ghi bừa.
|
|
232
|
+
|
|
212
233
|
---
|
|
213
234
|
|
|
214
235
|
## Bước 2 — [PROJECT-CONFIG] Nạp module stack profile (có điều kiện)
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
# @trace.revision: 1 ← field tĩnh; version theo dõi bằng @trace.bdd_version
|
|
5
5
|
# @trace.domain: <domain>
|
|
6
6
|
# @trace.platform: {active_platform — web | app | system} ← BẮT BUỘC mọi mode; phải khớp segment bdd/{platform}/ của path
|
|
7
|
-
# @trace.service: {
|
|
7
|
+
# @trace.service: {service của ĐÚNG platform file này — BẮT BUỘC mọi mode. Nguồn của cột TSV `service`; trace gộp không tách theo service nên đây là chỗ DUY NHẤT mang thông tin sở hữu ở cấp row. Bốn giá trị: {path} · "unrouted" (chưa ai quyết repo — HỢP LỆ, cờ 🟠, KHÔNG chặn) · "unresolved" (config sai cấu trúc — bug) · "—" (single-service). KHÔNG ghi "multi": file này đã có MỘT platform xác định nên service_candidates.{platform}.path đã biết — ghi path đó (G51)}
|
|
8
8
|
# @trace.module: {active_module trong umbrella mode; "unknown" trong spec repo mode}
|
|
9
9
|
# @trace.status: draft
|
|
10
10
|
# @trace.author: AI-generated
|
|
@@ -86,6 +86,42 @@ Kết quả: `/generate-code` 108 KB → **69 KB**, `/refine-prd` 85 KB → **45
|
|
|
86
86
|
> **G50 gỡ hẳn legacy mode**, nên giờ mọi bản cài đều có `.agent/steps/` và nhánh build thứ hai
|
|
87
87
|
> biến mất. Hai nhánh build gần giống nhau là nợ chờ lệch.
|
|
88
88
|
|
|
89
|
+
### Tập publish — chỉ ship MỘT bản *(GAPS-v4 G59)*
|
|
90
|
+
|
|
91
|
+
`npm pack` chỉ mang **`bin/` · `core/` · `scripts/` · `docs/`**.
|
|
92
|
+
|
|
93
|
+
Trước G59, `files` có **10 mục**, và **7 trong 10** là bản sao của thứ đã có trong `core/` —
|
|
94
|
+
`commands/` (33/33 file `.md` **byte-identical** với `core/commands/`), cộng
|
|
95
|
+
`hooks/ modules/ rules/ skills/ steps/ templates/` (`diff -rq` không khác gì). Installer đọc
|
|
96
|
+
**chỉ `core/`** (`installCore(coreDir, agentDir, …)`), nên bản thứ hai không bao giờ được dùng.
|
|
97
|
+
|
|
98
|
+
| | Trước | Sau |
|
|
99
|
+
|---|---:|---:|
|
|
100
|
+
| Tarball nén | 1.2 MB | **710 kB** |
|
|
101
|
+
| Giải nén | 4.5 MB | **2.3 MB** |
|
|
102
|
+
| Số file | 395 | **215** |
|
|
103
|
+
|
|
104
|
+
**Nhưng cái đáng sửa hơn là một phép phân biệt bị vô hiệu.** `bin/index.js` dùng
|
|
105
|
+
`hasSources = exists(commands/generate-code.tmpl)` để biết *"đây là dev checkout hay bản cài từ
|
|
106
|
+
npm"*, và chú thích của nó viết thẳng: *"Chỉ nói trong DEV CHECKOUT. **Consumer không cần biết bước
|
|
107
|
+
này tồn tại**"*. Nhưng `commands/` được ship ⇒ `.tmpl` có mặt ⇒ phép thử **luôn đúng** ⇒ **mọi**
|
|
108
|
+
người dùng `npx` thấy `"Vừa sửa commands/*.tmpl ? Chạy npm run build trước"` — một câu họ không thể
|
|
109
|
+
làm gì với nó. *(Kiểm bằng cách `npm pack` rồi chạy tarball thật, không phải suy đoán.)*
|
|
110
|
+
|
|
111
|
+
Bốn nhánh sau khi sửa, `hasSources` đặt tên **một lần**:
|
|
112
|
+
|
|
113
|
+
| `corePrebuilt` | `hasSources` | Nghĩa | Làm gì |
|
|
114
|
+
|:---:|:---:|---|---|
|
|
115
|
+
| ✗ | ✓ | dev checkout, `core/` vắng/lệch | build từ nguồn |
|
|
116
|
+
| ✗ | ✗ | **bản cài npm bị thiếu/hỏng** | **lỗi rõ ràng + `exit 1`** — không cố build (G43: build ghi vào npx cache / global `node_modules`, có thể read-only, và hai `--init` song song sẽ đua nhau) |
|
|
117
|
+
| ✓ | ✓ | dev checkout, đã khớp version | in lời nhắc *"sửa `.tmpl` thì build lại"* |
|
|
118
|
+
| ✓ | ✗ | **bản cài npm, mọi thứ đúng** | **im lặng** ← đường của consumer |
|
|
119
|
+
|
|
120
|
+
Bất biến được `test/run.js` canh, viết theo **hình dạng** chứ không theo danh sách tên nên tự khớp
|
|
121
|
+
với dir thêm sau này: *không mục nào trong `files` được có bản mirror dưới `core/`*.
|
|
122
|
+
|
|
123
|
+
`scripts/` **phải giữ** — `bin/index.js` `require('../scripts/migrate-specs.js')` cho `--migrate-*`.
|
|
124
|
+
|
|
89
125
|
- Cơ chế `{{include:steps/...}}` → single source of truth ở `.tmpl` + `steps/`.
|
|
90
126
|
- **Không sửa tay** `commands/*.md` / `.agent/` — sửa `.tmpl`/`steps` rồi `node bin/build.js`. *(Quy ước + memory bảo vệ, không phải hook.)*
|
|
91
127
|
- **Sửa `steps/context-loader.md` hay `report-footer.md` giờ có hiệu lực NGAY** ở project đã cài — chúng được đọc lúc chạy, không còn phải build + publish + `/update-framework`.
|