@educa-corp/sdd-framework 0.4.2 → 0.5.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/self-check.js +124 -6
- package/bin/trace-schema.json +1199 -692
- package/commands/debug.md +3 -2
- package/commands/define-product.md +3 -2
- package/commands/dev-gen-test.md +37 -9
- package/commands/dev-run-test.md +37 -9
- package/commands/dev-smoke-test.md +3 -2
- package/commands/extend-prd.md +907 -0
- package/commands/extend-prd.tmpl +270 -0
- package/commands/fix-bug.md +37 -9
- package/commands/generate-architecture.md +3 -2
- package/commands/generate-bdd.md +56 -13
- package/commands/generate-bdd.tmpl +18 -3
- package/commands/generate-code.md +73 -16
- package/commands/generate-code.tmpl +36 -7
- package/commands/generate-design-spec.md +3 -2
- package/commands/generate-prd.md +28 -2
- package/commands/generate-prd.tmpl +25 -0
- package/commands/generate-spec-manifest.md +3 -2
- package/commands/generate-tech-docs.md +3 -2
- package/commands/learn.md +3 -2
- package/commands/map-testids.md +3 -2
- package/commands/propose-scenario.md +55 -3
- package/commands/propose-scenario.tmpl +52 -1
- package/commands/qc-analyze.md +3 -2
- package/commands/qc-design-test.md +4 -2
- package/commands/qc-design-test.tmpl +1 -0
- package/commands/qc-plan.md +3 -2
- package/commands/qc-report.md +3 -2
- package/commands/qc-review.md +3 -2
- package/commands/qc-run-test.md +50 -10
- package/commands/qc-run-test.tmpl +13 -1
- package/commands/refine-prd.md +3 -2
- package/commands/report-bug.md +3 -2
- package/commands/review-code.md +7 -5
- package/commands/review-code.tmpl +4 -3
- package/commands/review-context.md +6 -4
- package/commands/review-context.tmpl +3 -2
- package/commands/review-tech-docs.md +3 -2
- package/commands/setup-ai-first.md +3 -2
- package/commands/sync.md +40 -16
- package/commands/sync.tmpl +37 -14
- package/commands/update-framework.md +3 -2
- package/commands/validate-traces.md +318 -33
- package/commands/validate-traces.tmpl +315 -31
- package/core/FRAMEWORK_VERSION +1 -1
- package/core/commands/debug.md +3 -2
- package/core/commands/define-product.md +3 -2
- package/core/commands/dev-gen-test.md +37 -9
- package/core/commands/dev-run-test.md +37 -9
- package/core/commands/dev-smoke-test.md +3 -2
- package/core/commands/extend-prd.md +907 -0
- package/core/commands/fix-bug.md +37 -9
- package/core/commands/generate-architecture.md +3 -2
- package/core/commands/generate-bdd.md +56 -13
- package/core/commands/generate-code.md +73 -16
- package/core/commands/generate-design-spec.md +3 -2
- package/core/commands/generate-prd.md +28 -2
- package/core/commands/generate-spec-manifest.md +3 -2
- package/core/commands/generate-tech-docs.md +3 -2
- package/core/commands/learn.md +3 -2
- package/core/commands/map-testids.md +3 -2
- package/core/commands/propose-scenario.md +55 -3
- package/core/commands/qc-analyze.md +3 -2
- package/core/commands/qc-design-test.md +4 -2
- package/core/commands/qc-plan.md +3 -2
- package/core/commands/qc-report.md +3 -2
- package/core/commands/qc-review.md +3 -2
- package/core/commands/qc-run-test.md +50 -10
- package/core/commands/refine-prd.md +3 -2
- package/core/commands/report-bug.md +3 -2
- package/core/commands/review-code.md +7 -5
- package/core/commands/review-context.md +6 -4
- package/core/commands/review-tech-docs.md +3 -2
- package/core/commands/setup-ai-first.md +3 -2
- package/core/commands/sync.md +40 -16
- package/core/commands/update-framework.md +3 -2
- package/core/commands/validate-traces.md +318 -33
- package/core/rules/workflow.md +18 -0
- package/core/steps/report-footer.md +3 -2
- package/core/steps/trace-mirror.md +34 -7
- package/core/templates/feature.template +1 -1
- package/docs/01-getting-started/installation.md +18 -1
- package/docs/01-getting-started/what-is-sdd.md +4 -2
- package/docs/02-concepts/architecture.md +27 -3
- package/docs/02-concepts/pipeline-steps/02-specification.md +39 -3
- package/docs/02-concepts/pipeline-steps/04-bdd.md +24 -2
- package/docs/02-concepts/pipeline-steps/05-tech-docs.md +18 -1
- package/docs/02-concepts/pipeline-steps/06-code.md +35 -4
- package/docs/02-concepts/pipeline-steps/09-validate-traces.md +137 -12
- package/docs/02-concepts/pipeline-steps/10-feedback-loop.md +59 -3
- package/docs/02-concepts/roles-and-hitl.md +1 -1
- package/docs/02-concepts/traceability.md +126 -117
- package/docs/03-guides/developer.md +20 -4
- package/docs/03-guides/product-owner.md +72 -68
- package/docs/03-guides/tester-qa.md +81 -70
- package/docs/04-reference/commands.md +134 -105
- package/docs/04-reference/configuration.md +146 -94
- package/docs/04-reference/trace-schema.md +26 -9
- package/docs/explain/02-generate-prd.md +80 -78
- package/docs/explain/02b-extend-prd.md +125 -0
- package/docs/explain/03-refine-prd.md +86 -86
- package/docs/explain/04-review-context.md +18 -1
- package/docs/explain/06-generate-bdd.md +23 -0
- package/docs/explain/08-review-tech-docs.md +20 -5
- package/docs/explain/10-review-code.md +36 -2
- package/docs/explain/19-qc-run-test.md +87 -67
- package/docs/explain/21-validate-traces.md +74 -68
- package/docs/explain/23-fix-bug.md +19 -3
- package/docs/explain/26-propose-scenario.md +70 -63
- package/docs/explain/README.md +135 -134
- package/package.json +50 -50
- package/rules/workflow.md +18 -0
- package/steps/report-footer.md +3 -2
- package/steps/trace-mirror.md +34 -7
- package/templates/feature.template +1 -1
package/bin/self-check.js
CHANGED
|
@@ -8,16 +8,23 @@
|
|
|
8
8
|
* 3 consumer, 0 producer, sống qua nhiều version mà không lệnh nào, không cổng
|
|
9
9
|
* nào, không test nào bắt được. Đây là cái bắt lớp lỗi đó một cách mechanical.
|
|
10
10
|
*
|
|
11
|
-
*
|
|
12
|
-
* R1 field có read_by nhưng KHÔNG có producer
|
|
13
|
-
* R2 field có producer nhưng KHÔNG ai đọc → WARN (bắt được G15, fe_phase)
|
|
11
|
+
* 7 rule — áp cho tags · tsv_columns · aux_tables (_seams.tsv) · queues (feedback/*):
|
|
12
|
+
* R1 field/hàng đợi có read_by nhưng KHÔNG có producer → ERROR (bắt được G1; queue: G26)
|
|
13
|
+
* R2 field có producer nhưng KHÔNG ai đọc → WARN (bắt được G15, fe_phase, test_type)
|
|
14
14
|
* R3 actor được khai là producer/consumer mà file của
|
|
15
|
-
* nó KHÔNG nhắc tên field
|
|
15
|
+
* nó KHÔNG nhắc tên field/cột/path_key → ERROR (bắt được G5, G12, G13, G31, G32)
|
|
16
16
|
* R4 {paths.X} dùng trong lệnh mà X không có trong
|
|
17
17
|
* project-context.yaml, và ngược lại key chết → ERROR / WARN (bắt được G17)
|
|
18
|
-
* R5 pattern bị cấm xuất hiện trở lại → ERROR (bắt được G3, G8, G10, G18, G21)
|
|
18
|
+
* R5 pattern bị cấm xuất hiện trở lại → ERROR (bắt được G3, G8, G10, G18, G21, G11)
|
|
19
19
|
* R6 giá trị enum khai trong vocabulary mà không actor
|
|
20
20
|
* nào dùng → WARN (bắt được G19: integration vs integrated)
|
|
21
|
+
* R7 audit flag thiếu counter {flag}_count trong CẢ
|
|
22
|
+
* Step 7 LẪN khối `summary` của validate-traces → ERROR (bắt được G33)
|
|
23
|
+
*
|
|
24
|
+
* PHẠM VI (G30): trước đó schema chỉ phủ 16/44 tag — R1/R3 mù với 28 cái còn lại, và ĐÓ LÀ
|
|
25
|
+
* cách G31 (@trace.service: 2 consumer khai, 0 nơi chứa) cùng G32 (design-spec: 0 cột/0 tag/
|
|
26
|
+
* 0 cờ) sống qua nhiều version với dấu ✅. Dấu ✅ chỉ có nghĩa "phần ĐÃ KHAI thì đúng".
|
|
27
|
+
* Giữ phạm vi đầy đủ: thêm tag/cột/bảng/hàng đợi mới thì khai NGAY, đừng để dồn.
|
|
21
28
|
*
|
|
22
29
|
* Chạy: node bin/self-check.js (exit 1 nếu có ERROR)
|
|
23
30
|
* node bin/self-check.js --warn-only (luôn exit 0)
|
|
@@ -126,6 +133,59 @@ function checkField(kind, name, def) {
|
|
|
126
133
|
for (const t of schema.tags) checkField('tag', t.name, t);
|
|
127
134
|
for (const c of schema.tsv_columns) checkField('cột TSV', c.name, c);
|
|
128
135
|
|
|
136
|
+
// Bảng phụ (_seams.tsv) — nguồn của 2 cờ 🔴 chặn PR nhưng nằm ngoài SoT tới G30.
|
|
137
|
+
// Chạy CÙNG checkField, không cần rule mới: mỗi cột là một field có producer/consumer.
|
|
138
|
+
for (const t of schema.aux_tables || []) {
|
|
139
|
+
checkField(`bảng phụ \`${t.name}\``, t.name, t); // file phải được cả 2 đầu nhắc tên
|
|
140
|
+
// `column_actors` = actor thao tác ở MỨC CỘT (ghi/parse từng field). Actor chỉ dùng bảng ở
|
|
141
|
+
// MỨC BẢNG (vd review-code chỉ kiểm "có dòng PENDING không") KHÔNG phải nhắc đủ tên cột —
|
|
142
|
+
// đòi thế là báo oan. Không khai `column_actors` → mặc định mọi actor.
|
|
143
|
+
const colActors = t.column_actors || [...(t.written_by || []), ...(t.read_by || [])];
|
|
144
|
+
for (const col of t.columns || []) {
|
|
145
|
+
for (const actor of new Set(colActors)) {
|
|
146
|
+
const text = actorText[actor];
|
|
147
|
+
if (text === undefined) continue;
|
|
148
|
+
if (!mentions(text, col)) {
|
|
149
|
+
err('R3', `cột \`${t.name}:${col}\`: schema khai \`${actor}\` đụng bảng này nhưng ${schema.actors[actor]} KHÔNG nhắc cột \`${col}\``,
|
|
150
|
+
`Hoặc lệnh thiếu implement, hoặc cột đã đổi tên. Bảng này là NGUỒN của SEAM_UNWIRED/STUB_UNRESOLVED —\n`
|
|
151
|
+
+ ` đổi tên cột mà không ai bắt được thì hai cờ 🔴 chặn PR sẽ mù trong im lặng.`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Hàng đợi feedback — bắt ca "hàng đợi có producer nhưng KHÔNG ai drain" (hình dạng của G26).
|
|
158
|
+
//
|
|
159
|
+
// KHÔNG dùng chung mentions() với tag/cột: path key luôn xuất hiện dưới dạng `{paths.X}`,
|
|
160
|
+
// tức LUÔN có dấu `.` đứng trước — mà mentions() cố ý chặn ký tự đó (để `@trace.XXsc_version`
|
|
161
|
+
// không bị coi là đã nhắc `sc_version`). Dùng thẳng thì 100% queue báo lỗi oan.
|
|
162
|
+
// Ở đây chỉ cần chặn hậu tố: `bug_reports_dir` không được khớp `bug_reports_dirX`.
|
|
163
|
+
const mentionsKey = (text, key) =>
|
|
164
|
+
new RegExp(`\\b${key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?![A-Za-z0-9_])`).test(text);
|
|
165
|
+
|
|
166
|
+
for (const q of schema.queues || []) {
|
|
167
|
+
const producers = q.written_by || [];
|
|
168
|
+
const consumers = q.read_by || [];
|
|
169
|
+
const label = `hàng đợi \`${q.name}\``;
|
|
170
|
+
|
|
171
|
+
if (producers.length && !consumers.length) {
|
|
172
|
+
err('R1', `${label}: có producer nhưng KHÔNG ai đọc — file vào đó rồi nằm lại vĩnh viễn`,
|
|
173
|
+
`Đây là hình dạng của G26 (prd-change-requests: có producer, có storage, có commit,\n`
|
|
174
|
+
+ ` có mặt trong /sync — nhưng 0 consumer, và không gì báo).`);
|
|
175
|
+
}
|
|
176
|
+
if (consumers.length && !producers.length) {
|
|
177
|
+
err('R1', `${label}: có consumer nhưng KHÔNG ai ghi vào — hàng đợi chết?`);
|
|
178
|
+
}
|
|
179
|
+
for (const actor of new Set([...producers, ...consumers])) {
|
|
180
|
+
const text = actorText[actor];
|
|
181
|
+
if (text === undefined) continue;
|
|
182
|
+
if (!mentionsKey(text, q.path_key)) {
|
|
183
|
+
err('R3', `${label}: schema khai \`${actor}\` đụng hàng đợi này nhưng ${schema.actors[actor]} KHÔNG nhắc \`${q.path_key}\``,
|
|
184
|
+
`Hoặc lệnh thiếu implement, hoặc schema khai sai.`);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
129
189
|
// ── R4 — {paths.X} vs project-context.yaml ────────────────────────────────────
|
|
130
190
|
const cfgPath = path.join(ROOT, schema.path_keys.config_file);
|
|
131
191
|
if (!fs.existsSync(cfgPath)) {
|
|
@@ -202,9 +262,67 @@ for (const [vocab, def] of Object.entries(schema.vocabularies)) {
|
|
|
202
262
|
}
|
|
203
263
|
}
|
|
204
264
|
|
|
265
|
+
// ── R7 — mỗi audit flag phải có counter trong summary ─────────────────────────
|
|
266
|
+
// Vì sao tồn tại (G33): PRD_DRIFT / TECHDOC_DRIFT / FE_TECHDOC_DRIFT được TÍNH ở Step 4-5
|
|
267
|
+
// và CÓ mảng chi tiết trong `issues`, nhưng KHÔNG có counter trong `summary`. Dashboard đọc
|
|
268
|
+
// `summary` → ba cờ đó vô hình ở tầng tổng hợp. Bất đối xứng 7/10 còn tệ hơn thiếu cả 10:
|
|
269
|
+
// người viết dashboard thấy có counter cho các cờ khác nên tưởng `summary` là đủ.
|
|
270
|
+
//
|
|
271
|
+
// CỐ Ý KHÔNG CÓ ESCAPE HATCH (không `allow_no_counter`). Một luật có cửa thoát dùng được
|
|
272
|
+
// ngay là luật yếu — đó chính là cách G30 xảy ra (schema phủ 16/43 tag rồi dừng). Cờ nào
|
|
273
|
+
// "chỉ để nhắc, chưa phải lỗi" (SEAM_PENDING/STUB_PENDING) thì counter của nó vẫn có giá
|
|
274
|
+
// trị: PM đọc để xếp thứ tự gen UC. Thêm 2 dòng rẻ hơn nhiều so với một ngoại lệ vĩnh viễn.
|
|
275
|
+
// SIẾT (G30 lô 8): bản đầu chỉ hỏi "tên counter có xuất hiện Ở ĐÂU ĐÓ trong file không".
|
|
276
|
+
// Test G27 lộ ra lỗ: gỡ counter khỏi ĐÚNG hai chỗ luật đòi (Step 7 + `summary`) mà R7 vẫn PASS,
|
|
277
|
+
// vì tên còn sót trong khối report terminal. Tức nó bắt được ca "quên hoàn toàn" (ca thật của
|
|
278
|
+
// G33) nhưng KHÔNG bắt ca "có ở report mà quên ở summary" — mà đó CHÍNH LÀ ca làm dashboard mù.
|
|
279
|
+
// Giờ kiểm từng khối riêng.
|
|
280
|
+
{
|
|
281
|
+
const flagVocab = schema.vocabularies.audit_flags;
|
|
282
|
+
const vtName = 'validate-traces';
|
|
283
|
+
const vtText = actorText[vtName];
|
|
284
|
+
if (flagVocab && vtText !== undefined) {
|
|
285
|
+
/** Cắt lát văn bản giữa hai mốc; null nếu không tìm thấy mốc (đừng im lặng bỏ qua). */
|
|
286
|
+
const slice = (from, to) => {
|
|
287
|
+
const a = vtText.indexOf(from);
|
|
288
|
+
if (a < 0) return null;
|
|
289
|
+
const b = vtText.indexOf(to, a + from.length);
|
|
290
|
+
return vtText.slice(a, b < 0 ? undefined : b);
|
|
291
|
+
};
|
|
292
|
+
const zones = [
|
|
293
|
+
{ label: 'Step 7 (aggregate)', text: slice('### Step 7 —', '### Step 7b') },
|
|
294
|
+
{ label: '`summary` của trace-report.json', text: slice('"summary": {', '"prds"') },
|
|
295
|
+
];
|
|
296
|
+
|
|
297
|
+
for (const z of zones) {
|
|
298
|
+
if (z.text === null) {
|
|
299
|
+
warn('R7', `Không định vị được khối "${z.label}" trong ${schema.actors[vtName]} — R7 bỏ qua khối này`,
|
|
300
|
+
'Mốc cắt đã đổi? Cập nhật `slice()` trong self-check.js, đừng để R7 mù trong im lặng.');
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const missing = [];
|
|
305
|
+
for (const flag of flagVocab.values || []) {
|
|
306
|
+
const counter = `${flag.toLowerCase()}_count`;
|
|
307
|
+
const gone = zones.filter(z => z.text !== null && !mentions(z.text, counter));
|
|
308
|
+
if (gone.length) missing.push({ flag, counter, zones: gone.map(z => z.label) });
|
|
309
|
+
}
|
|
310
|
+
if (missing.length) {
|
|
311
|
+
err('R7', `${missing.length} audit flag thiếu counter trong ${schema.actors[vtName]}`,
|
|
312
|
+
missing.map(m => `${m.flag} → thiếu \`${m.counter}\` ở: ${m.zones.join(' + ')}`).join('\n ')
|
|
313
|
+
+ `\n Counter PHẢI có ở CẢ HAI khối. Chỉ có ở report terminal là KHÔNG đủ —`
|
|
314
|
+
+ `\n dashboard đọc \`summary\`, nên cờ sẽ vô hình ở tầng tổng hợp (G33).`);
|
|
315
|
+
}
|
|
316
|
+
} else if (!flagVocab) {
|
|
317
|
+
warn('R7', 'Không tìm thấy vocabulary `audit_flags` — R7 không chạy được');
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
|
|
205
321
|
// ── report ────────────────────────────────────────────────────────────────────
|
|
206
322
|
const tagCount = schema.tags.length;
|
|
207
323
|
const colCount = schema.tsv_columns.length;
|
|
324
|
+
const auxCount = (schema.aux_tables || []).length;
|
|
325
|
+
const queueCount = (schema.queues || []).length;
|
|
208
326
|
console.log('');
|
|
209
327
|
console.log('Self-check (bin/trace-schema.json) ...');
|
|
210
328
|
console.log('');
|
|
@@ -219,7 +337,7 @@ show(errors, '❌');
|
|
|
219
337
|
show(warns, '⚠️ ');
|
|
220
338
|
|
|
221
339
|
if (!errors.length && !warns.length) {
|
|
222
|
-
console.log(` ✅ ${tagCount} tag + ${colCount} cột TSV + ${Object.keys(schema.vocabularies).length - 1} vocabulary — contract khớp mọi lệnh`);
|
|
340
|
+
console.log(` ✅ ${tagCount} tag + ${colCount} cột TSV + ${auxCount} bảng phụ + ${queueCount} hàng đợi + ${Object.keys(schema.vocabularies).length - 1} vocabulary — contract khớp mọi lệnh`);
|
|
223
341
|
}
|
|
224
342
|
console.log('');
|
|
225
343
|
|