@educa-corp/sdd-framework 0.9.3 → 0.9.4

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 (59) hide show
  1. package/bin/build.js +11 -0
  2. package/bin/qc-base-map.json +119 -49
  3. package/bin/self-check.js +30 -0
  4. package/core/FRAMEWORK_VERSION +1 -1
  5. package/core/commands/qc-analyze.md +6 -119
  6. package/core/commands/qc-design-test.md +123 -143
  7. package/core/commands/qc-plan.md +6 -119
  8. package/core/commands/qc-review.md +59 -125
  9. package/core/commands/qc-run-test.md +6 -119
  10. package/core/commands/setup-ai-first.md +5 -5
  11. package/core/commands/update-framework.md +1 -1
  12. package/core/commands/validate-traces.md +1 -1
  13. package/core/rules/workflow.md +1 -1
  14. package/core/skills/qc/qa-analyst/DOC_GAP.template.md +1 -1
  15. package/core/skills/qc/qa-analyst/spec-breakdown.md +2 -2
  16. package/core/skills/qc/qa-designer/api/auth-chain.md +155 -0
  17. package/core/skills/qc/qa-designer/api/auth-sequence.md +75 -0
  18. package/core/skills/qc/qa-designer/api/common-headers.md +61 -0
  19. package/core/skills/qc/qa-designer/api/crud-sequence.md +122 -0
  20. package/core/skills/qc/qa-designer/api/endpoint.md +231 -0
  21. package/core/skills/qc/qa-designer/api/http-status-codes.md +102 -0
  22. package/core/skills/qc/qa-designer/e2e/journey.md +13 -8
  23. package/core/skills/qc/qa-designer/exploratory/charter.md +2 -0
  24. package/core/skills/qc/qa-designer/exploratory/explore-to-functional.md +7 -4
  25. package/core/skills/qc/qa-designer/functional/api.md +87 -18
  26. package/core/skills/qc/qa-designer/functional/gui-feature.md +12 -9
  27. package/core/skills/qc/qa-designer/functional/gui-screen.md +12 -10
  28. package/core/skills/qc/qa-designer/integration/api.md +12 -5
  29. package/core/skills/qc/qa-designer/integration/db.md +12 -6
  30. package/core/skills/qc/qa-designer/integration/gui.md +12 -5
  31. package/core/skills/qc/qa-designer/integration/kafka.md +12 -5
  32. package/core/skills/qc/qa-designer/non-functional.md +12 -5
  33. package/core/skills/qc/qa-designer/shared/action-keywords-glossary.md +91 -0
  34. package/core/skills/qc/qa-designer/shared/duplicate-check-procedure.md +105 -0
  35. package/core/skills/qc/qa-designer/shared/implicit-scenarios.md +22 -0
  36. package/core/skills/qc/qa-designer/shared/precision-rules.md +198 -0
  37. package/core/skills/qc/qa-designer/shared/read-doc-gap-inputs.md +25 -0
  38. package/core/skills/qc/qa-designer/shared/skill-decision-tree.md +93 -0
  39. package/core/skills/qc/qa-designer/shared/tc-metadata-format.md +243 -0
  40. package/core/skills/qc/qa-planner/risk-model.md +1 -1
  41. package/core/skills/qc/qa-reviewer/script/e2e.md +9 -1
  42. package/core/skills/qc/qa-reviewer/script/exploratory.md +9 -1
  43. package/core/skills/qc/qa-reviewer/script/functional.md +9 -1
  44. package/core/skills/qc/qa-reviewer/script/integration.md +9 -1
  45. package/core/skills/qc/qa-reviewer/script/non-functional.md +9 -1
  46. package/core/skills/qc/qa-reviewer/shared/read-doc-gap-inputs.md +26 -0
  47. package/core/skills/qc/qa-reviewer/shared/review-check-groups.md +207 -0
  48. package/core/skills/qc/qa-reviewer/shared/review-file-template.md +228 -0
  49. package/core/skills/qc/qa-reviewer/test-case/e2e.md +71 -13
  50. package/core/skills/qc/qa-reviewer/test-case/exploratory.md +53 -4
  51. package/core/skills/qc/qa-reviewer/test-case/functional.md +63 -15
  52. package/core/skills/qc/qa-reviewer/test-case/integration.md +64 -12
  53. package/core/skills/qc/qa-reviewer/test-case/non-functional.md +72 -13
  54. package/core/skills/qc/qa-runner/e2e.md +1 -1
  55. package/docs/02-concepts/pipeline-steps/09-validate-traces.md +1 -1
  56. package/docs/04-reference/trace-schema.md +1 -1
  57. package/docs/explain/00-setup-ai-first.md +1 -1
  58. package/docs/plans/qc-implementation-log.md +145 -3
  59. package/package.json +1 -1
package/bin/build.js CHANGED
@@ -56,6 +56,17 @@ const RUNTIME_READ = {
56
56
  'steps/report-footer.md':
57
57
  '**Đọc `.agent/steps/report-footer.md`** và áp đúng khuôn footer trong đó (Status Badge ·\n' +
58
58
  'Output Artifacts · Next) cho report cuối, kèm khối bên dưới.',
59
+
60
+ // Vào đây ở B12: nó được 5 lệnh qc-* include, nên inline là nhân 5 bản của cùng một
61
+ // quy trình — đúng loại phình mà test "mức slim" tồn tại để canh (nó bắt được thật).
62
+ 'steps/qc-scope.md':
63
+ '**BẮT BUỘC — đọc `.agent/steps/qc-scope.md` và thực thi TOÀN BỘ quy trình trong đó**,\n' +
64
+ 'rồi mới tiếp tục phần bên dưới.\n' +
65
+ '\n' +
66
+ 'Nó chốt bốn thứ mà mọi trạm QC đều cần: `TICKET-ID` · `active_platform` ·\n' +
67
+ '`qc_artifact_dir` · `uc_list` (kèm trạng thái BDD từng UC, và cờ `--include-draft`).\n' +
68
+ 'Bỏ qua thì artifact QC ghi vào **sai thư mục** và `qc_status` ghi vào **sai sổ trace** —\n' +
69
+ 'cả hai đều xảy ra trong im lặng, không có bước nào phía sau bắt được.',
59
70
  };
60
71
 
61
72
  /**
@@ -47,7 +47,7 @@
47
47
  {
48
48
  "upstream": "agents/4-qa-tc-designer.md",
49
49
  "targets": [],
50
- "state": "undecided",
50
+ "state": "skipped",
51
51
  "why": "dinh nghia agent cua upstream; framework dung command + gate _agent_mode thay cho agent file"
52
52
  },
53
53
  {
@@ -329,58 +329,83 @@
329
329
  },
330
330
  {
331
331
  "upstream": "skills/qa-tc-designer/api-testcase-designer.md",
332
- "targets": [],
333
- "state": "undecided"
332
+ "targets": [
333
+ "skills/qc/qa-designer/functional/api.md"
334
+ ],
335
+ "state": "ported",
336
+ "base_sha": "211c18728baf029a7572f0ffb161e92a933fa8fb"
334
337
  },
335
338
  {
336
339
  "upstream": "skills/qa-tc-designer/api-testcase-designer/api/flows/auth-sequence.md",
337
- "targets": [],
338
- "state": "undecided"
340
+ "targets": [
341
+ "skills/qc/qa-designer/api/auth-sequence.md"
342
+ ],
343
+ "state": "ported",
344
+ "base_sha": "69d60c12fe6d641638132581cce1dc6c955354a6"
339
345
  },
340
346
  {
341
347
  "upstream": "skills/qa-tc-designer/api-testcase-designer/api/flows/common-flows.md",
342
348
  "targets": [],
343
- "state": "undecided"
349
+ "state": "skipped",
350
+ "why": "B12: ban nhap cu, nam gon trong auth-sequence.md + crud-sequence.md (dai va chi tiet hon)."
344
351
  },
345
352
  {
346
353
  "upstream": "skills/qa-tc-designer/api-testcase-designer/api/flows/crud-sequence.md",
347
- "targets": [],
348
- "state": "undecided"
354
+ "targets": [
355
+ "skills/qc/qa-designer/api/crud-sequence.md"
356
+ ],
357
+ "state": "ported",
358
+ "base_sha": "65bf214d9f9beabe3c40b8697ba7c058adfd8bb5"
349
359
  },
350
360
  {
351
361
  "upstream": "skills/qa-tc-designer/api-testcase-designer/api/references/common-headers.md",
352
- "targets": [],
353
- "state": "undecided"
362
+ "targets": [
363
+ "skills/qc/qa-designer/api/common-headers.md"
364
+ ],
365
+ "state": "ported",
366
+ "base_sha": "74f10a36bec7b40f4d9de2d0928798cb6fad0c2c"
354
367
  },
355
368
  {
356
369
  "upstream": "skills/qa-tc-designer/api-testcase-designer/api/references/error-codes.md",
357
370
  "targets": [],
358
- "state": "undecided"
371
+ "state": "skipped",
372
+ "why": "B12: ban nhap cu, nam gon trong http-status-codes.md (95 dong, day du hon). Phan header cua no nam trong common-headers.md. Lay ca hai la co hai bang tra cung mot thu roi chung lech nhau."
359
373
  },
360
374
  {
361
375
  "upstream": "skills/qa-tc-designer/api-testcase-designer/api/references/http-status-codes.md",
362
- "targets": [],
363
- "state": "undecided"
376
+ "targets": [
377
+ "skills/qc/qa-designer/api/http-status-codes.md"
378
+ ],
379
+ "state": "ported",
380
+ "base_sha": "a82baa99350f63955245764a6aa57c9dca6d055f"
364
381
  },
365
382
  {
366
383
  "upstream": "skills/qa-tc-designer/api-testcase-designer/api/templates/auth-chain.md",
367
- "targets": [],
368
- "state": "undecided"
384
+ "targets": [
385
+ "skills/qc/qa-designer/api/auth-chain.md"
386
+ ],
387
+ "state": "ported",
388
+ "base_sha": "ef8f9761f1b53d0ccd752f8fe0c13a427fa15f45"
369
389
  },
370
390
  {
371
391
  "upstream": "skills/qa-tc-designer/api-testcase-designer/api/templates/endpoint.md",
372
- "targets": [],
373
- "state": "undecided"
392
+ "targets": [
393
+ "skills/qc/qa-designer/api/endpoint.md"
394
+ ],
395
+ "state": "ported",
396
+ "base_sha": "7b38edd94e5efd37ff561f53cff92adedabaff7f"
374
397
  },
375
398
  {
376
399
  "upstream": "skills/qa-tc-designer/e2e-testcase-designer.md",
377
400
  "targets": [],
378
- "state": "undecided"
401
+ "state": "skipped",
402
+ "why": "B12: tuong ung skills/qc/qa-designer/e2e/journey.md. Chua doi chieu noi dung — de danh cho lan ra tang E2E rieng."
379
403
  },
380
404
  {
381
405
  "upstream": "skills/qa-tc-designer/integration-testcase-designer.md",
382
406
  "targets": [],
383
- "state": "undecided"
407
+ "state": "skipped",
408
+ "why": "B12: tuong ung 4 skill integration/{api,db,gui,kafka}.md. Chua doi chieu noi dung — de danh."
384
409
  },
385
410
  {
386
411
  "upstream": "skills/qa-tc-designer/manual-excel-testcase.md",
@@ -391,47 +416,70 @@
391
416
  {
392
417
  "upstream": "skills/qa-tc-designer/nfr-testcase-designer.md",
393
418
  "targets": [],
394
- "state": "undecided"
419
+ "state": "skipped",
420
+ "why": "B12: tuong ung skills/qc/qa-designer/non-functional.md. Chua doi chieu noi dung — de danh."
395
421
  },
396
422
  {
397
423
  "upstream": "skills/qa-tc-designer/shared/action-keywords-glossary.md",
398
- "targets": [],
399
- "state": "undecided"
424
+ "targets": [
425
+ "skills/qc/qa-designer/shared/action-keywords-glossary.md"
426
+ ],
427
+ "state": "ported",
428
+ "base_sha": "df56df91eea42342537ccf67b5946df32d98dbf4"
400
429
  },
401
430
  {
402
431
  "upstream": "skills/qa-tc-designer/shared/duplicate-check-procedure.md",
403
- "targets": [],
404
- "state": "undecided"
432
+ "targets": [
433
+ "skills/qc/qa-designer/shared/duplicate-check-procedure.md"
434
+ ],
435
+ "state": "ported",
436
+ "base_sha": "c59357a56b289bb9fb7fae8490db37109209cb3a"
405
437
  },
406
438
  {
407
439
  "upstream": "skills/qa-tc-designer/shared/implicit-scenarios.md",
408
- "targets": [],
409
- "state": "undecided"
440
+ "targets": [
441
+ "skills/qc/qa-designer/shared/implicit-scenarios.md"
442
+ ],
443
+ "state": "ported",
444
+ "base_sha": "6eb801a137ef4dfda97870c1eb36db62f3c782bc"
410
445
  },
411
446
  {
412
447
  "upstream": "skills/qa-tc-designer/shared/precision-rules.md",
413
- "targets": [],
414
- "state": "undecided"
448
+ "targets": [
449
+ "skills/qc/qa-designer/shared/precision-rules.md"
450
+ ],
451
+ "state": "ported",
452
+ "base_sha": "1c2005b55bd94c3e8fa25dfa12a29c8b23f7c01e"
415
453
  },
416
454
  {
417
455
  "upstream": "skills/qa-tc-designer/shared/read-doc-gap-inputs.md",
418
- "targets": [],
419
- "state": "undecided"
456
+ "targets": [
457
+ "skills/qc/qa-designer/shared/read-doc-gap-inputs.md"
458
+ ],
459
+ "state": "ported",
460
+ "base_sha": "a7e113b19f35aa47acf91e55cdfb3f630539806c"
420
461
  },
421
462
  {
422
463
  "upstream": "skills/qa-tc-designer/shared/skill-decision-tree.md",
423
- "targets": [],
424
- "state": "undecided"
464
+ "targets": [
465
+ "skills/qc/qa-designer/shared/skill-decision-tree.md"
466
+ ],
467
+ "state": "ported",
468
+ "base_sha": "57125f0c21512f2abcc55d00420e92c84c95fc8f"
425
469
  },
426
470
  {
427
471
  "upstream": "skills/qa-tc-designer/shared/tc-metadata-format.md",
428
- "targets": [],
429
- "state": "undecided"
472
+ "targets": [
473
+ "skills/qc/qa-designer/shared/tc-metadata-format.md"
474
+ ],
475
+ "state": "ported",
476
+ "base_sha": "a31d66a7a8dab19cf821c9102a88282b09e16cb9"
430
477
  },
431
478
  {
432
479
  "upstream": "skills/qa-tc-designer/ui-testcase-designer.md",
433
480
  "targets": [],
434
- "state": "undecided"
481
+ "state": "skipped",
482
+ "why": "B12: framework tach tang UI thanh 3 skill rieng (functional/gui-screen · gui-feature · integration/gui) theo cay quyet dinh, thay vi mot file 341 dong. Ky thuat EP/BVA/Decision-Table/State-Transition da co trong 3 skill do; khuon TC + do chinh xac da tach ra shared/. Con lai la phan khai agent + orchestrator — framework khong co."
435
483
  },
436
484
  {
437
485
  "upstream": "skills/qa-tc-reviewer/api-test-reviewer.md",
@@ -440,23 +488,35 @@
440
488
  },
441
489
  {
442
490
  "upstream": "skills/qa-tc-reviewer/e2e-test-reviewer.md",
443
- "targets": [],
444
- "state": "undecided"
491
+ "targets": [
492
+ "skills/qc/qa-reviewer/test-case/e2e.md"
493
+ ],
494
+ "state": "ported",
495
+ "base_sha": "276d0516f95421c726c8c55dc84c3c9244cf70e1"
445
496
  },
446
497
  {
447
498
  "upstream": "skills/qa-tc-reviewer/exploratory-test-reviewer.md",
448
- "targets": [],
449
- "state": "undecided"
499
+ "targets": [
500
+ "skills/qc/qa-reviewer/test-case/exploratory.md"
501
+ ],
502
+ "state": "ported",
503
+ "base_sha": "4e068bb896739b977a6a5b1f638010d498c46aef"
450
504
  },
451
505
  {
452
506
  "upstream": "skills/qa-tc-reviewer/integration-test-reviewer.md",
453
- "targets": [],
454
- "state": "undecided"
507
+ "targets": [
508
+ "skills/qc/qa-reviewer/test-case/integration.md"
509
+ ],
510
+ "state": "ported",
511
+ "base_sha": "639b54afb68df83b22168581c5c57b0f91d9cf6a"
455
512
  },
456
513
  {
457
514
  "upstream": "skills/qa-tc-reviewer/nfr-test-reviewer.md",
458
- "targets": [],
459
- "state": "undecided"
515
+ "targets": [
516
+ "skills/qc/qa-reviewer/test-case/non-functional.md"
517
+ ],
518
+ "state": "ported",
519
+ "base_sha": "5747c7a6bdfaaea1ff3e958688b38b3aeaf9b15a"
460
520
  },
461
521
  {
462
522
  "upstream": "skills/qa-tc-reviewer/script-e2e-reviewer.md",
@@ -485,18 +545,28 @@
485
545
  },
486
546
  {
487
547
  "upstream": "skills/qa-tc-reviewer/shared/read-doc-gap-inputs.md",
488
- "targets": [],
489
- "state": "undecided"
548
+ "targets": [
549
+ "skills/qc/qa-reviewer/shared/read-doc-gap-inputs.md"
550
+ ],
551
+ "state": "ported",
552
+ "base_sha": "3caa1562399ef1167ac2dc620add2c6c729e55df"
490
553
  },
491
554
  {
492
555
  "upstream": "skills/qa-tc-reviewer/shared/review-file-template.md",
493
- "targets": [],
494
- "state": "undecided"
556
+ "targets": [
557
+ "skills/qc/qa-reviewer/shared/review-file-template.md"
558
+ ],
559
+ "state": "ported",
560
+ "base_sha": "bd596393ecc016cbea106835681a4979c0a9ca0e"
495
561
  },
496
562
  {
497
563
  "upstream": "skills/qa-tc-reviewer/ui-test-reviewer.md",
498
- "targets": [],
499
- "state": "undecided"
564
+ "targets": [
565
+ "skills/qc/qa-reviewer/test-case/functional.md",
566
+ "skills/qc/qa-reviewer/shared/review-check-groups.md"
567
+ ],
568
+ "state": "ported",
569
+ "base_sha": "54f81eb6a132d3fb331f18ce0f1a7eb3a9cf19df"
500
570
  }
501
571
  ],
502
572
  "$decisions": {
package/bin/self-check.js CHANGED
@@ -1198,6 +1198,12 @@ for (const [vocab, def] of Object.entries(schema.vocabularies)) {
1198
1198
  // sau đều đoán. Đây đúng loại việc con người không làm đáng tin bằng sự cẩn thận.
1199
1199
  // → R16(b).
1200
1200
  //
1201
+ // (3) Một file NHẬN nội dung port mà KHÔNG đóng dấu nguồn gốc. Bản đồ khai đầy đủ, sha
1202
+ // đúng, self-check xanh — nhưng người mở file không biết đây là bản port, nên lần
1203
+ // đồng bộ sau bản đồ biết BASE mà người sửa file thì không. Ca thật: B12 gộp nội dung
1204
+ // `api-testcase-designer.md` vào `functional/api.md`. (b) và (e) đều mù vì chúng chỉ
1205
+ // so KHI frontmatter có khai. → R16(f).
1206
+ //
1201
1207
  // (a) là cái quan trọng nhất về lâu dài: file upstream KHÔNG có entry trong bản đồ =
1202
1208
  // bị bỏ quên trong im lặng. `state: skipped` (có lý do) là câu trả lời hợp lệ; VẮNG MẶT
1203
1209
  // thì không. Cùng khuôn với `checkpoint_levels` và `strict_use_check`: khai tường minh
@@ -1324,6 +1330,30 @@ for (const [vocab, def] of Object.entries(schema.vocabularies)) {
1324
1330
  }
1325
1331
  }
1326
1332
  }
1333
+
1334
+ // ── (f) target `ported` phải KHAI dấu nguồn gốc, không chỉ "khai đúng nếu có khai" ──
1335
+ //
1336
+ // Vì sao cần nhánh riêng: (b) và (e) đều chạy trong `if (m)` / `if (mp)` — chúng so
1337
+ // frontmatter với bản đồ KHI frontmatter có khai. File không khai gì thì cả hai im lặng.
1338
+ // Ca thật (B12): `functional/api.md` nhận nội dung port từ `api-testcase-designer.md`,
1339
+ // vào bản đồ đầy đủ với base_sha đúng, self-check XANH — mà đọc file thì không ai biết
1340
+ // nó là bản port. Mất `upstream_path` + `upstream_sha` ở file đích là mất BASE của merge
1341
+ // ba chiều lần sau: bản đồ biết, còn người sửa file thì không.
1342
+ for (const e of entries.filter(x => x.state === 'ported')) {
1343
+ for (const t of (e.targets || [])) {
1344
+ const tp = path.join(ROOT, t);
1345
+ if (!fs.existsSync(tp)) continue; // đã báo ở (c)
1346
+ const txt = fs.readFileSync(tp, 'utf8');
1347
+ const lack = ['upstream_path', 'upstream_sha']
1348
+ .filter(f => !new RegExp(`^${f}: .+$`, 'm').test(txt));
1349
+ if (lack.length) {
1350
+ err('R16', `${t} là target \`ported\` nhưng frontmatter THIẾU ${lack.join(' + ')}`,
1351
+ `Bản đồ khai nó port từ \`${e.upstream}\`, còn chính file thì không đóng dấu.
1352
+ ` +
1353
+ ' Người mở file không biết đây là bản port, và lần đồng bộ sau mất một chiều BASE.');
1354
+ }
1355
+ }
1356
+ }
1327
1357
  }
1328
1358
  }
1329
1359
  }
@@ -1 +1 @@
1
- 0.9.3
1
+ 0.9.4
@@ -179,126 +179,13 @@ placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
179
179
 
180
180
  ## Phạm vi QC — PRD nào, nền nào, những UC nào
181
181
 
182
- # QC Scope phân giải phạm vi cho mọi lệnh `qc-*`
183
-
184
- **Chạy TRƯỚC phần logic riêng của lệnh, và SAU `steps/gate.md`.** Bước này chốt bốn thứ mà
185
- cả 6 trạm QC đều cần, để chúng không tự suy mỗi trạm một kiểu:
186
-
187
- | Biến | Là gì |
188
- |---|---|
189
- | `TICKET-ID` | mã PRD — **thư mục artifact QC mang tên này** |
190
- | `active_platform` | `web` \| `app` \| `system` \| … — một QC pass khoá đúng MỘT nền |
191
- | `qc_artifact_dir` | `{paths.qc_dir}/{TICKET-ID}/{active_platform}/` |
192
- | `uc_list` | các UC của (PRD × nền) này, kèm trạng thái BDD từng UC |
193
-
194
- > **Vì sao gom về một chỗ.** Luật phân giải nền từng được copy-paste ở 5 lệnh và câu chữ đã
195
- > lệch nhau. Năm bản của một luật là nơi drift sống: sửa bốn, quên một, và trạm bị quên ghi
196
- > artifact vào sai thư mục **trong im lặng**.
197
-
198
- ---
199
-
200
- ## 1 — `TICKET-ID`
201
-
202
- Artifact QC gom theo **PRD**, không theo UC. Nên mọi trạm phải quy được về `TICKET-ID`:
203
-
204
- | `$ARGUMENTS` là | Cách lấy |
205
- |---|---|
206
- | **UC-ID** (`{TICKET-ID}-UC{N}`) | phần **trước** `-UC` — đúng luật `steps/gate.md` Bước 1 dùng để tìm tech-doc gộp |
207
- | **TICKET-ID** | dùng trực tiếp |
208
- | một **path file** (`.feature` / PRD / design-spec) | phân giải `{domain}` + `{prd-slug}` theo luật `context-loader` Bước 1, rồi lấy `TICKET-ID` từ tên file PRD `{TICKET-ID}-{prd-slug}.md` — file `.md` duy nhất ở gốc feature folder |
209
-
210
- Đối chiếu: `TICKET-ID` suy ra phải khớp tên file PRD thật. Lệch → **DỪNG**, in cả hai giá
211
- trị. (Suy sai `TICKET-ID` là ghi cả một PRD vào sai thư mục — không có bước nào phía sau bắt được.)
212
-
213
- ---
214
-
215
- ## 2 — `active_platform`
216
-
217
- > **PHẢI phân giải TRƯỚC mọi phép đọc `.feature`.** `{UC-ID}-SC{N}` chỉ độc nhất trong
218
- > (UC × nền), nên một UC đa nền có **nhiều file `.feature`** — `bdd/web/`, `bdd/app/`,
219
- > `bdd/system/` — và mỗi file mang `@trace.status` **riêng**: bản web có thể `approved`
220
- > trong khi bản app còn `draft`. Đọc "file `.feature` của UC" khi chưa biết nền là đọc một
221
- > file **bất kỳ trong ba**: báo `approved` trong khi bản đang dùng còn nháp, hoặc chặn oan
222
- > một bản đã duyệt.
223
-
224
- Theo thứ tự, dừng ở cái đầu tiên khớp:
225
-
226
- 1. `$ARGUMENTS` nêu nền (`web`/`app`/`system`/…) → dùng.
227
- 2. Target là một file `.feature` → đọc `# @trace.platform` của nó.
228
- 3. Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/*/` — **đúng một** thư mục nền → dùng nó.
229
- 4. Glob `{paths.qc_dir}/{TICKET-ID}/*/` — **đúng một** thư mục nền đã có artifact → dùng nó.
230
- *(chỉ dùng cho trạm 2–6; trạm `/qc-analyze` là trạm tạo ra thư mục đó nên không có gì để soi.)*
231
- 5. Nhiều nền mà không suy được → hỏi *"QC pass này cho nền nào? (web/app/system)"*.
232
- **Có `--yes`:** không hỏi — DỪNG với lỗi rõ ràng, vì đoán bừa nền là ghi artifact vào sai
233
- thư mục và ghi `qc_status` vào sai sổ trace:
234
- ```
235
- ❌ {TICKET-ID} có {n} nền ({list}) — không suy được nền nào cho QC pass này.
236
- Chạy headless thì phải nêu tường minh: /{lệnh} {TICKET-ID} web --yes
237
- ```
238
-
239
- Lưu `active_platform`. Từ đây, **mọi** phép đọc `.feature` chỉ đọc thư mục
240
- `bdd/{active_platform}/` — không trộn SC chéo nền.
241
-
242
- ---
243
-
244
- ## 3 — `qc_artifact_dir`
245
-
246
- ```
247
- qc_artifact_dir = {paths.qc_dir}/{TICKET-ID}/{active_platform}/
248
- ```
249
-
250
- Chứa: `REQUIREMENT_ANALYSIS.md` · `DOC_GAP.md` · `TEST_PLAN.md` · `test-cases/*.Test.md`
251
- — **mỗi loại đúng MỘT file cho cả PRD**, các UC là mục/hàng bên trong.
252
-
253
- `{paths.qc_dir}` là folder top-level **nhìn thấy** trong repo QC (mặc định `docs/`, **không**
254
- phải `.agent/` ẩn) để đội QC mở và xử lý output dễ dàng. Spec chính thức ở lại spec submodule
255
- của PO — đừng ghi artifact QC vào đó.
256
-
257
- > **Sổ trace KHÔNG theo layout này.** Nó vẫn là `{paths.trace_dir}/{domain}/{prd-slug}/{UC-ID}-{active_platform}.tsv`
258
- > — một sổ cho mỗi (UC × nền), vì mỗi hàng là một scenario. Liên kết giữa hai bên đi qua
259
- > **cột `UC`** của bảng gap, không qua đường dẫn file.
260
-
261
- ---
262
-
263
- ## 4 — `uc_list`
264
-
265
- Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/*.feature`. Mỗi file → một
266
- UC: đọc `# @trace.id` (mã UC) và `# @trace.status` từ header.
267
-
268
- Chia hai nhóm:
269
-
270
- | Nhóm | Điều kiện | Xử lý |
271
- |---|---|---|
272
- | **Trong phạm vi** | `@trace.status: approved` | phân tích / thiết kế / chạy bình thường |
273
- | **Chưa xét** | khác `approved` | **KHÔNG** phân tích; vẫn ghi một hàng vào bảng phạm vi kèm trạng thái thật |
274
-
275
- In bảng phạm vi ra trước khi làm gì:
276
- ```
277
- Phạm vi QC — {TICKET-ID} / {active_platform}
278
- ✅ {UC-ID} {tên UC} approved
279
- ⏸ {UC-ID} {tên UC} draft → chưa xét
280
- → {n} UC trong phạm vi · {m} chưa xét
281
- ```
282
-
283
- **Cờ `--include-draft`:** phân tích cả UC chưa duyệt, nhưng **vẫn in bảng trên** và đánh dấu
284
- trong artifact là dựa trên BDD nháp.
285
-
286
- **Không UC nào `approved` và không có `--include-draft` → DỪNG:**
287
- ```
288
- ❌ {TICKET-ID} ({active_platform}): 0/{n} UC có BDD approved — không có gì để chạy.
289
- Cách đúng: người duyệt đặt `# @trace.status: approved` rồi chạy lại.
290
- Muốn chạy sớm trên BDD nháp (prototype): thêm --include-draft
291
- ```
292
-
293
- > **Vì sao có `--include-draft` chứ không chặn cứng.** QC sớm trên BDD nháp là một cách dùng
294
- > **cố ý được cho phép** từ trước (guard cũ là cảnh báo mềm, không phải chặn). Bỏ hẳn nó là
295
- > lấy đi một năng lực đang có mà không ai khai. Còn để mặc định `approved`-only thì cái
296
- > thường gặp là cái an toàn, và cái sớm phải nói ra.
297
-
298
- > **Vì sao `--yes` không thay được `--include-draft`.** `--yes` nghĩa *"tôi không ngồi đây để
299
- > trả lời"*; `--include-draft` nghĩa *"tôi biết BDD còn nháp và vẫn muốn chạy"*. Gộp hai cái
300
- > là để một lần chạy headless âm thầm phân tích spec chưa chốt rồi bàn giao như thể đã chốt.
182
+ **BẮT BUỘCđọc `.agent/steps/qc-scope.md` thực thi TOÀN BỘ quy trình trong đó**,
183
+ rồi mới tiếp tục phần bên dưới.
301
184
 
185
+ Nó chốt bốn thứ mà mọi trạm QC đều cần: `TICKET-ID` · `active_platform` ·
186
+ `qc_artifact_dir` · `uc_list` (kèm trạng thái BDD từng UC, và cờ `--include-draft`).
187
+ Bỏ qua thì artifact QC ghi vào **sai thư mục** và `qc_status` ghi vào **sai sổ trace** —
188
+ cả hai đều xảy ra trong im lặng, không có bước nào phía sau bắt được.
302
189
 
303
190
  > **QC chạy trên BDD chưa chốt có thể phải làm lại.** `qc-scope` mặc định chỉ lấy UC có
304
191
  > `@trace.status: approved`; UC còn nháp vẫn vào bảng *Phạm vi phân tích* của `DOC_GAP.md`