@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.
Files changed (116) hide show
  1. package/bin/self-check.js +124 -6
  2. package/bin/trace-schema.json +1199 -692
  3. package/commands/debug.md +3 -2
  4. package/commands/define-product.md +3 -2
  5. package/commands/dev-gen-test.md +37 -9
  6. package/commands/dev-run-test.md +37 -9
  7. package/commands/dev-smoke-test.md +3 -2
  8. package/commands/extend-prd.md +907 -0
  9. package/commands/extend-prd.tmpl +270 -0
  10. package/commands/fix-bug.md +37 -9
  11. package/commands/generate-architecture.md +3 -2
  12. package/commands/generate-bdd.md +56 -13
  13. package/commands/generate-bdd.tmpl +18 -3
  14. package/commands/generate-code.md +73 -16
  15. package/commands/generate-code.tmpl +36 -7
  16. package/commands/generate-design-spec.md +3 -2
  17. package/commands/generate-prd.md +28 -2
  18. package/commands/generate-prd.tmpl +25 -0
  19. package/commands/generate-spec-manifest.md +3 -2
  20. package/commands/generate-tech-docs.md +3 -2
  21. package/commands/learn.md +3 -2
  22. package/commands/map-testids.md +3 -2
  23. package/commands/propose-scenario.md +55 -3
  24. package/commands/propose-scenario.tmpl +52 -1
  25. package/commands/qc-analyze.md +3 -2
  26. package/commands/qc-design-test.md +4 -2
  27. package/commands/qc-design-test.tmpl +1 -0
  28. package/commands/qc-plan.md +3 -2
  29. package/commands/qc-report.md +3 -2
  30. package/commands/qc-review.md +3 -2
  31. package/commands/qc-run-test.md +50 -10
  32. package/commands/qc-run-test.tmpl +13 -1
  33. package/commands/refine-prd.md +3 -2
  34. package/commands/report-bug.md +3 -2
  35. package/commands/review-code.md +7 -5
  36. package/commands/review-code.tmpl +4 -3
  37. package/commands/review-context.md +6 -4
  38. package/commands/review-context.tmpl +3 -2
  39. package/commands/review-tech-docs.md +3 -2
  40. package/commands/setup-ai-first.md +3 -2
  41. package/commands/sync.md +40 -16
  42. package/commands/sync.tmpl +37 -14
  43. package/commands/update-framework.md +3 -2
  44. package/commands/validate-traces.md +318 -33
  45. package/commands/validate-traces.tmpl +315 -31
  46. package/core/FRAMEWORK_VERSION +1 -1
  47. package/core/commands/debug.md +3 -2
  48. package/core/commands/define-product.md +3 -2
  49. package/core/commands/dev-gen-test.md +37 -9
  50. package/core/commands/dev-run-test.md +37 -9
  51. package/core/commands/dev-smoke-test.md +3 -2
  52. package/core/commands/extend-prd.md +907 -0
  53. package/core/commands/fix-bug.md +37 -9
  54. package/core/commands/generate-architecture.md +3 -2
  55. package/core/commands/generate-bdd.md +56 -13
  56. package/core/commands/generate-code.md +73 -16
  57. package/core/commands/generate-design-spec.md +3 -2
  58. package/core/commands/generate-prd.md +28 -2
  59. package/core/commands/generate-spec-manifest.md +3 -2
  60. package/core/commands/generate-tech-docs.md +3 -2
  61. package/core/commands/learn.md +3 -2
  62. package/core/commands/map-testids.md +3 -2
  63. package/core/commands/propose-scenario.md +55 -3
  64. package/core/commands/qc-analyze.md +3 -2
  65. package/core/commands/qc-design-test.md +4 -2
  66. package/core/commands/qc-plan.md +3 -2
  67. package/core/commands/qc-report.md +3 -2
  68. package/core/commands/qc-review.md +3 -2
  69. package/core/commands/qc-run-test.md +50 -10
  70. package/core/commands/refine-prd.md +3 -2
  71. package/core/commands/report-bug.md +3 -2
  72. package/core/commands/review-code.md +7 -5
  73. package/core/commands/review-context.md +6 -4
  74. package/core/commands/review-tech-docs.md +3 -2
  75. package/core/commands/setup-ai-first.md +3 -2
  76. package/core/commands/sync.md +40 -16
  77. package/core/commands/update-framework.md +3 -2
  78. package/core/commands/validate-traces.md +318 -33
  79. package/core/rules/workflow.md +18 -0
  80. package/core/steps/report-footer.md +3 -2
  81. package/core/steps/trace-mirror.md +34 -7
  82. package/core/templates/feature.template +1 -1
  83. package/docs/01-getting-started/installation.md +18 -1
  84. package/docs/01-getting-started/what-is-sdd.md +4 -2
  85. package/docs/02-concepts/architecture.md +27 -3
  86. package/docs/02-concepts/pipeline-steps/02-specification.md +39 -3
  87. package/docs/02-concepts/pipeline-steps/04-bdd.md +24 -2
  88. package/docs/02-concepts/pipeline-steps/05-tech-docs.md +18 -1
  89. package/docs/02-concepts/pipeline-steps/06-code.md +35 -4
  90. package/docs/02-concepts/pipeline-steps/09-validate-traces.md +137 -12
  91. package/docs/02-concepts/pipeline-steps/10-feedback-loop.md +59 -3
  92. package/docs/02-concepts/roles-and-hitl.md +1 -1
  93. package/docs/02-concepts/traceability.md +126 -117
  94. package/docs/03-guides/developer.md +20 -4
  95. package/docs/03-guides/product-owner.md +72 -68
  96. package/docs/03-guides/tester-qa.md +81 -70
  97. package/docs/04-reference/commands.md +134 -105
  98. package/docs/04-reference/configuration.md +146 -94
  99. package/docs/04-reference/trace-schema.md +26 -9
  100. package/docs/explain/02-generate-prd.md +80 -78
  101. package/docs/explain/02b-extend-prd.md +125 -0
  102. package/docs/explain/03-refine-prd.md +86 -86
  103. package/docs/explain/04-review-context.md +18 -1
  104. package/docs/explain/06-generate-bdd.md +23 -0
  105. package/docs/explain/08-review-tech-docs.md +20 -5
  106. package/docs/explain/10-review-code.md +36 -2
  107. package/docs/explain/19-qc-run-test.md +87 -67
  108. package/docs/explain/21-validate-traces.md +74 -68
  109. package/docs/explain/23-fix-bug.md +19 -3
  110. package/docs/explain/26-propose-scenario.md +70 -63
  111. package/docs/explain/README.md +135 -134
  112. package/package.json +50 -50
  113. package/rules/workflow.md +18 -0
  114. package/steps/report-footer.md +3 -2
  115. package/steps/trace-mirror.md +34 -7
  116. 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
- * 6 rule:
12
- * R1 field có read_by nhưng KHÔNG có producer → ERROR (bắt được G1)
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 → ERROR (bắt được G5, G12, G13)
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