@educa-corp/sdd-framework 0.9.2 → 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 (76) 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 +85 -75
  6. package/core/commands/qc-design-test.md +144 -25
  7. package/core/commands/qc-plan.md +40 -7
  8. package/core/commands/qc-review.md +74 -7
  9. package/core/commands/qc-run-test.md +21 -1
  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/modules/qc-playwright/stack-profile.yaml +3 -3
  14. package/core/rules/workflow.md +1 -1
  15. package/core/skills/qc/qa-analyst/DOC_GAP.template.md +47 -17
  16. package/core/skills/qc/qa-analyst/acceptance-criteria.md +1 -1
  17. package/core/skills/qc/qa-analyst/business-rules.md +2 -2
  18. package/core/skills/qc/qa-analyst/data-flow.md +2 -2
  19. package/core/skills/qc/qa-analyst/spec-breakdown.md +4 -4
  20. package/core/skills/qc/qa-analyst/spec-issue-reporter.md +14 -2
  21. package/core/skills/qc/qa-designer/api/auth-chain.md +155 -0
  22. package/core/skills/qc/qa-designer/api/auth-sequence.md +75 -0
  23. package/core/skills/qc/qa-designer/api/common-headers.md +61 -0
  24. package/core/skills/qc/qa-designer/api/crud-sequence.md +122 -0
  25. package/core/skills/qc/qa-designer/api/endpoint.md +231 -0
  26. package/core/skills/qc/qa-designer/api/http-status-codes.md +102 -0
  27. package/core/skills/qc/qa-designer/e2e/journey.md +13 -8
  28. package/core/skills/qc/qa-designer/exploratory/charter.md +2 -0
  29. package/core/skills/qc/qa-designer/exploratory/explore-to-functional.md +7 -4
  30. package/core/skills/qc/qa-designer/functional/api.md +87 -18
  31. package/core/skills/qc/qa-designer/functional/gui-feature.md +12 -9
  32. package/core/skills/qc/qa-designer/functional/gui-screen.md +12 -10
  33. package/core/skills/qc/qa-designer/integration/api.md +12 -5
  34. package/core/skills/qc/qa-designer/integration/db.md +12 -6
  35. package/core/skills/qc/qa-designer/integration/gui.md +12 -5
  36. package/core/skills/qc/qa-designer/integration/kafka.md +12 -5
  37. package/core/skills/qc/qa-designer/non-functional.md +12 -5
  38. package/core/skills/qc/qa-designer/shared/action-keywords-glossary.md +91 -0
  39. package/core/skills/qc/qa-designer/shared/duplicate-check-procedure.md +105 -0
  40. package/core/skills/qc/qa-designer/shared/implicit-scenarios.md +22 -0
  41. package/core/skills/qc/qa-designer/shared/precision-rules.md +198 -0
  42. package/core/skills/qc/qa-designer/shared/read-doc-gap-inputs.md +25 -0
  43. package/core/skills/qc/qa-designer/shared/skill-decision-tree.md +93 -0
  44. package/core/skills/qc/qa-designer/shared/tc-metadata-format.md +243 -0
  45. package/core/skills/qc/qa-planner/risk-model.md +1 -1
  46. package/core/skills/qc/qa-planner/test-plan.md +24 -13
  47. package/core/skills/qc/qa-reviewer/script/e2e.md +9 -1
  48. package/core/skills/qc/qa-reviewer/script/exploratory.md +9 -1
  49. package/core/skills/qc/qa-reviewer/script/functional.md +9 -1
  50. package/core/skills/qc/qa-reviewer/script/integration.md +9 -1
  51. package/core/skills/qc/qa-reviewer/script/non-functional.md +9 -1
  52. package/core/skills/qc/qa-reviewer/shared/read-doc-gap-inputs.md +26 -0
  53. package/core/skills/qc/qa-reviewer/shared/review-check-groups.md +207 -0
  54. package/core/skills/qc/qa-reviewer/shared/review-file-template.md +228 -0
  55. package/core/skills/qc/qa-reviewer/test-case/e2e.md +71 -13
  56. package/core/skills/qc/qa-reviewer/test-case/exploratory.md +53 -4
  57. package/core/skills/qc/qa-reviewer/test-case/functional.md +63 -15
  58. package/core/skills/qc/qa-reviewer/test-case/integration.md +64 -12
  59. package/core/skills/qc/qa-reviewer/test-case/non-functional.md +72 -13
  60. package/core/skills/qc/qa-runner/e2e.md +1 -1
  61. package/core/skills/qc/qa-runner/exploratory/session.md +1 -1
  62. package/core/steps/context-loader.md +1 -1
  63. package/core/steps/qc-scope.md +119 -0
  64. package/core/templates/project-context.yaml +3 -1
  65. package/docs/02-concepts/pipeline-steps/08-qc-automation.md +1 -1
  66. package/docs/02-concepts/pipeline-steps/09-validate-traces.md +1 -1
  67. package/docs/04-reference/configuration.md +146 -146
  68. package/docs/04-reference/trace-schema.md +1 -1
  69. package/docs/explain/00-setup-ai-first.md +1 -1
  70. package/docs/explain/15-qc-analyze.md +1 -1
  71. package/docs/explain/16-qc-plan.md +1 -1
  72. package/docs/explain/17-qc-design-test.md +1 -1
  73. package/docs/plans/qc-implementation-log.md +288 -5
  74. package/docs/plans/qc-sync-command.md +2 -1
  75. package/package.json +1 -1
  76. package/scripts/migrate-qc-docs.js +261 -0
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.2
1
+ 0.9.4
@@ -165,7 +165,7 @@ Mỗi dòng ⚠️/🔴 phải ứng với một trạng thái **context-loader
165
165
  🔴/⚠️ (không chặn ≠ không báo — người đọc log sau này vẫn cần thấy).
166
166
 
167
167
 
168
- *Lưu ý: Với lệnh này, target ở Bước 1 là một UC-ID hoặc file feature/PRD. Đọc spec chính thức của UC đó — file `.feature` (mang `@trace.id={UC-ID}` và mỗi scenario `@trace.scenario={UC-ID}-SC{N}`), PRD, và design-spec — từ feature package `{paths.specs_dir}/{domain}/{prd-slug}/` (file `.feature` dưới `bdd/`, file PRD `{TICKET-ID}-{prd-slug}.md` ở gốc folder, và design-spec dưới `design-spec/`). Spec của framework CHÍNH LÀ source of truth; đừng suy lại các requirement đã có ở đó. **Ngoài ra đọc tech-doc gộp** `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md` làm **nguồn thứ hai** — xem §Đối chiếu tài liệu kỹ thuật.*
168
+ *Lưu ý: Với lệnh này, target ở Bước 1 là một **TICKET-ID** (mã PRD), hoặc một UC-ID / file feature / file PRD — cả ba đều quy về TICKET-ID ở §Phạm vi QC. Trạm này chạy cho **cả PRD × một nền**. Đọc spec chính thức của **mọi UC trong phạm vi** — file `.feature` (mang `@trace.id={UC-ID}` và mỗi scenario `@trace.scenario={UC-ID}-SC{N}`), PRD, và design-spec — từ feature package `{paths.specs_dir}/{domain}/{prd-slug}/` (file `.feature` dưới `bdd/`, file PRD `{TICKET-ID}-{prd-slug}.md` ở gốc folder, và design-spec dưới `design-spec/`). Spec của framework CHÍNH LÀ source of truth; đừng suy lại các requirement đã có ở đó. **Ngoài ra đọc tech-doc gộp** `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md` làm **nguồn thứ hai** — xem §Đối chiếu tài liệu kỹ thuật.*
169
169
 
170
170
  ## Context
171
171
  **BẮT BUỘC — đọc `.agent/steps/context-loader.md` và thực thi TOÀN BỘ quy trình trong đó**,
@@ -177,60 +177,42 @@ placeholder bên dưới sẽ rỗng và lệnh sẽ đọc/ghi sai chỗ.
177
177
 
178
178
  ---
179
179
 
180
- ## Platform Resolution *(thiết lập cho cả QC pass mọi stage sau kế thừa)*
180
+ ## Phạm vi QC PRD nào, nền nào, những UC nào
181
181
 
182
- > **PHẢI chạy TRƯỚC Guard bên dưới.** `{UC-ID}-SC{N}` chỉ độc nhất trong (UC × platform), nên
183
- > một UC đa nền **nhiều file `.feature`** — `bdd/web/`, `bdd/app/`, `bdd/system/` — và mỗi
184
- > file mang `@trace.status` **riêng**: bản web có thể `approved` trong khi bản app còn `draft`.
185
- > Guard đọc "file `.feature` của UC" khi chưa biết platform là đọc một file **bất kỳ trong ba**:
186
- > báo `approved` trong khi bản đang dùng còn nháp, hoặc chặn oan một bản đã duyệt.
187
- > Chốt platform trước thì Guard mới có đúng một file để đọc.
188
-
189
- `{UC-ID}-SC{N}` chỉ độc nhất trong (UC × platform) — `web SC3` và `app SC3` là hai scenario khác nhau, và sổ trace tách theo `{UC-ID}-{platform}.tsv`. Nên **một QC pass khoá đúng MỘT platform**, và mọi artifact QC nằm dưới `{paths.qc_dir}/{UC-ID}/{active_platform}/`.
190
-
191
- Phân giải `active_platform` — theo thứ tự, dừng ở cái đầu tiên khớp:
192
- 1. `$ARGUMENTS` nêu platform (`web`/`app`/`system`) → dùng.
193
- 2. Target ở Bước 1 là một file `.feature` → đọc `# @trace.platform` của nó.
194
- 3. Glob `{paths.specs_dir}/{domain}/{prd-slug}/bdd/*/` — **đúng một** thư mục platform tồn tại → dùng nó.
195
- 4. Nhiều platform mà không suy được → hỏi *"QC pass này cho platform nào? (web/app/system)"*.
196
- **Có `--yes`:** không hỏi — dừng với lỗi rõ ràng, vì đoán bừa platform là ghi artifact vào sai
197
- thư mục và ghi `qc_status` vào sai sổ trace:
198
- ```
199
- ❌ {UC-ID} có {n} platform ({list}) — không suy được platform nào cho QC pass này.
200
- Chạy headless thì phải nêu tường minh: /qc-analyze {UC-ID} web --yes
201
- ```
202
-
203
- Lưu `active_platform`. Đọc **đúng file `.feature` của platform đó** (`{paths.specs_dir}/{domain}/{prd-slug}/bdd/{active_platform}/{UC-ID}*.feature`) làm nguồn SC — không trộn SC chéo platform.
204
-
205
- ---
206
-
207
- ## Guard — BDD đã duyệt chưa
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.
208
184
 
209
- *Chạy SAU Platform Resolution xem do khối trên.*
185
+ chốt bốn thứ 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.
210
189
 
211
- Đọc `# @trace.status:` từ header file `.feature` **của `{active_platform}` đã phân giải**:
212
- - `approved` tiếp tục bình thường.
213
- - `draft` (hoặc khác `approved`) **CHECKPOINT cảnh báo mềm** (không chặn cứng cho phép QC sớm/prototype):
214
- ```
215
- ⚠️ BDD của {UC-ID} ({active_platform}) đang ở @trace.status: {status} (chưa duyệt). QC chạy trên BDD chưa chốt có thể phải làm lại.
216
- Khuyến nghị: review-context (BDD) sạch + người duyệt đặt `# @trace.status: approved` rồi mới chạy QC.
217
- Vẫn chạy QC bây giờ? (Y/N)
218
- ```
219
- Chỉ tiếp khi chọn Y — **trừ khi `$ARGUMENTS` có `--yes`**: coi như Y, **nhưng vẫn IN khối cảnh
220
- báo** (không chặn ≠ không báo — người đọc log sau này vẫn cần thấy QC đã chạy trên BDD nháp).
190
+ > **QC chạy trên BDD chưa chốt thể phải làm lại.** `qc-scope` mặc định chỉ lấy UC có
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`
192
+ > với dấu `⏸ Chưa xét` **không im lặng bỏ khỏi bảng**, "chưa xét" khác "đã xét, sạch".
193
+ > Cố ý QC sớm thì thêm `--include-draft`, và artifact phải ghi rõ nó dựa trên BDD nháp.
221
194
 
222
- > **Vì sao `--yes` phải phủ cả guard mềm này.** `steps/gate.md` mở đường chạy headless
223
- > (`claude -p "… --yes"`), nhưng `--yes` chỉ khai là bỏ qua CHECKPOINT của gate. Guard mềm ở đây
224
- > một `(Y/N)` thứ hai nên lệnh vẫn treo hạn chế độ không người trả lời, và đường
225
- > headless gate hứa bị bít cho đúng lệnh này. Một cổng chỉ chặn được khi có người ngồi đó
226
- > thì chế độ headless không phải cổng, treo.
195
+ > **Vì sao trạm này chạy CẢ PRD chứ không từng UC** *(B11)*. Ba lý do, theo thứ tự quan trọng:
196
+ >
197
+ > 1. **Mâu thuẫn chéo UC chỉ lộ ra khi đọc cùng lúc.** UC1 nói một kiểu, UC3 nói kiểu khác
198
+ > chạy tách từng UC thì về **cấu trúc** không thể thấy, không phải "khó thấy".
199
+ > 2. **Rẻ hơn.** PRD, bản thiết kế, tài liệu kỹ thuật nguồn **dùng chung**; chạy per-UC là
200
+ > đọc lại chúng mỗi UC một lượt. Phần dùng chung chiếm đa số đầu vào.
201
+ > 3. **Một tài liệu cho một tính năng** là cách PO và QC vốn làm việc — file gốc của đội QC
202
+ > (`DOC_GAP_FEAT-02-3.md`) không có hậu tố UC, và `qa-planner/test-plan.md` vốn viết
203
+ > *"Test Plan cho một feature"*.
227
204
 
228
205
  ---
229
206
 
230
207
  ## Đối chiếu tài liệu kỹ thuật *(nguồn thứ hai — bắt lệch nghiệp vụ ↔ kỹ thuật)*
231
208
 
232
209
  Định vị tech-doc gộp cấp PRD: `{paths.tech_docs_dir}/{domain}/{prd-slug}/tech-docs/{TICKET-ID}-tech-design.md`.
233
- Nó phủ **nhiều UC** — lọc theo `@trace.ucs` header, chỉ đọc phần chạm `{UC-ID}` đang xét.
210
+ Nó phủ **nhiều UC** — trạm này cũng phủ nhiều UC, nên đọc **mọi phần chạm `uc_list`**
211
+ (đối chiếu `@trace.ucs` ở header với `uc_list`). Phần thuộc UC ngoài phạm vi (`⏸ Chưa xét`) thì bỏ qua.
212
+
213
+ > **Đây là chỗ layout cấp PRD trả lãi rõ nhất.** Tech-doc gộp là **một** tài liệu phủ cả PRD.
214
+ > Chạy per-UC thì nó bị đọc lại N lần, mỗi lần lọc bỏ gần hết — và mâu thuẫn giữa hai UC trong
215
+ > **cùng** tài liệu đó không lần nào lộ ra, vì không lần nào thấy cả hai.
234
216
 
235
217
  **Không tìm thấy → cảnh báo mềm, KHÔNG chặn** (dự án có thể chưa dựng tech-doc):
236
218
  ```
@@ -259,9 +241,9 @@ Nó phủ **nhiều UC** — lọc theo `@trace.ucs` ở header, chỉ đọc ph
259
241
  Tech-doc có sổ ẩn số thiết kế riêng (`§12`), với vòng đời và người chịu trách nhiệm riêng, và
260
242
  `/generate-code` đã canh nó. **Trạm này chỉ đọc, tuyệt đối không ghi vào.**
261
243
 
262
- Với mỗi mục `open` trong §12 chạm `{UC-ID}`:
244
+ Với mỗi mục `open` trong §12 chạm **bất kỳ UC trong `uc_list`**:
263
245
  - **KHÔNG mở gap mới** trong `DOC_GAP.md` về cùng chuyện đó.
264
- - Ghi vào `REQUIREMENT_ANALYSIS.md` mục *"Đang chờ chốt (từ §12 tech-doc)"*: `{id}` · điều chưa biết · owner · severity.
246
+ - Ghi vào `REQUIREMENT_ANALYSIS.md` mục *"Đang chờ chốt (từ §12 tech-doc)"*: `{id}` · **UC** · điều chưa biết · owner · severity.
265
247
  - Test case chạm nó về sau sẽ bị chặn — nhưng bị chặn bởi **một mục đã có người xử lý**, không phải bởi một câu hỏi mới gửi PO.
266
248
 
267
249
  > **Vì sao không ghi vào.** Một ẩn số đã nằm trong §12 nghĩa là **đã có người đang lo**: có
@@ -272,8 +254,8 @@ Với mỗi mục `open` trong §12 chạm `{UC-ID}`:
272
254
 
273
255
  **Ngoại lệ — mục `spec-defect` là việc của PO.** §12 phân ba loại: `nội tại` (backend tự quyết) ·
274
256
  `cross-service` (đội khác) · `spec-defect` (PRD/BDD sai hoặc thiếu). Hai loại đầu → ghi "đang chờ".
275
- Loại thứ ba **đúng là gap tài liệu** → vẫn ghi vào `DOC_GAP.md`, trỏ ngược về `{id}` của §12 để
276
- không đếm hai lần.
257
+ Loại thứ ba **đúng là gap tài liệu** → vẫn ghi vào `DOC_GAP.md` (cột `UC` = UC bị chạm), trỏ
258
+ ngược về `{id}` của §12 để không đếm hai lần.
277
259
 
278
260
  ---
279
261
 
@@ -301,7 +283,8 @@ Thứ tự điển hình: spec-breakdown → business-rules / data-flow → acce
301
283
 
302
284
  File `.feature` chính thức đã định nghĩa scenario là `@trace.scenario={UC-ID}-SC{N}` với
303
285
  `@trace.business_rules`. Map mọi `BR-xx` / `AC-xx` bạn tạo ra tới `{UC-ID}-SC{N}` sở hữu nó
304
- và ghi lại mapping — qc-design-testqc-run-test cần để gắn tag
286
+ và ghi lại mapping — **làm cho từng UC trong `uc_list`**, `BR`/`AC` phải mang UC của nó
287
+ (một file phân tích giờ phủ nhiều UC, nên `BR-01` không còn tự phân biệt được là của UC nào) — qc-design-test và qc-run-test cần nó để gắn tag
305
288
  `@trace.verifies` cho test và ghi `qc_status` theo từng scenario.
306
289
 
307
290
  ## Quét gap — hai nguồn, gộp rồi mới thẩm định
@@ -367,13 +350,23 @@ Gộp gap từ **cả hai nguồn** vào một tập trước khi sang bước t
367
350
 
368
351
  ## DOC_GAP (bắt buộc)
369
352
 
370
- Luôn tạo một file gaps theo `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md`:
371
- - Mỗi gap `GAP-xx`, phân loại MISSING / AMBIGUOUS / CONTRADICTORY / ASSUMPTION / OPEN QUESTION, với severity (🔴 Blocker → 🟢 Low) và function/BR/AC bị ảnh hưởng.
353
+ Luôn tạo **đúng MỘT** file gap cho cả (PRD × nền) theo
354
+ `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md` các UC các hàng bên trong, phân
355
+ biệt bằng cột `UC`:
356
+ - **Bảng 11 cột**, cột 2 là `UC`. ID gap `GAP-UC{N}-{nnn}` (vd `GAP-UC1-001`); gap thuộc cả
357
+ PRD → `GAP-GEN-{nnn}`. Đánh số **độc lập trong từng UC** — phân tích lại UC1 KHÔNG được làm
358
+ đổi số gap của UC2, vì test case đã có đang trỏ `🚫 Block: [GAP-UC2-003]`.
359
+ - **Section `Phạm vi phân tích`** — mỗi UC một hàng kèm `@trace.status`, đã phân tích chưa, số
360
+ gap. UC ngoài phạm vi ghi `⏸ Chưa xét`, **không bỏ khỏi bảng**.
361
+ - Mỗi gap phân loại MISSING / AMBIGUOUS / CONTRADICTORY / ASSUMPTION / OPEN QUESTION, với severity (🔴 Blocker → ⚪ Low) và function/BR/AC bị ảnh hưởng.
372
362
  - Không bao giờ bịa câu trả lời; đánh dấu giả định là `ASSUMPTION` để PO/dev confirm.
373
- - Bất kỳ `🔴 Blocker` nào còn `Open` ⇒ UC chưa sẵn sàng cho qc-design-test bàn giao cho qc-plan.
363
+ - Bất kỳ `🔴 Blocker` nào còn `Open` ⇒ **UC cột `UC` của hàng đó** chưa sẵn sàng cho
364
+ qc-design-test — bàn giao cho qc-plan. *Chặn theo từng UC, KHÔNG chặn cả PRD:* một blocker ở
365
+ UC3 không có lý do gì dừng việc thiết kế test cho UC1. Ghi rõ UC nào bị chặn ở report.
374
366
  - **Đẩy các defect spec thực sự lên PO (không chỉ giữ local).** Một blocker là lỗi thật
375
367
  trong spec chính thức — `AMBIGUOUS` / `CONTRADICTORY` / `MISSING` trong PRD/BDD — phải tới
376
368
  PO qua feedback flow, không chỉ nằm trong `DOC_GAP.md`: tạo `/report-bug {UC-ID} {desc}`
369
+ (`{UC-ID}` lấy từ cột `UC` của hàng gap — bug đi theo UC, không theo PRD)
377
370
  (BUG_FLOW của nó phân loại PRD vs BDD), hoặc `/propose-scenario {UC-ID}` nếu gap là thiếu test
378
371
  coverage. Gap `ASSUMPTION` / `OPEN QUESTION` được confirm qua questions-for-dev của qc-plan — không file thành bug.
379
372
 
@@ -399,18 +392,28 @@ nó bị loại. Cập nhật `Tổng số gap` + bảng ưu tiên sau khi áp v
399
392
 
400
393
  ## Output
401
394
 
402
- Ghi **hai file** dưới `{paths.qc_dir}/{UC-ID}/{active_platform}/` + **một** dưới `{paths.refinement_dir}/` — **đừng** tách phân tích
403
- thành một-file-mỗi-bước (không có file spec-breakdown / business-rules / data-flow / AC riêng):
404
-
405
- 1. **`{paths.qc_dir}/{UC-ID}/{active_platform}/REQUIREMENT_ANALYSIS.md`**bản phân tích hợp nhất duy nhất.
406
- Section theo thứ tự: phân requirement → bảng business-rule (`BR-xx`) data-flow
407
- acceptance-criteria (`AC-xx`), mỗi `BR`/`AC` map tới `{UC-ID}-SC{N}` (của `.feature` platform này) sở hữu nó.
408
- Cuối file thêm mục **"Đang chờ chốt (từ §12 tech-doc)"** các ẩn số thiết kế `open` chạm UC này
409
- (`{id}` · điều chưa biết · owner · severity). Rỗng thì ghi "Không có"; **đừng bỏ mục**.
410
- 2. **`{paths.qc_dir}/{UC-ID}/{active_platform}/DOC_GAP.md`** file gap, theo
395
+ Ghi **hai file** dưới `{qc_artifact_dir}` (= `{paths.qc_dir}/{TICKET-ID}/{active_platform}/`)
396
+ + **một** dưới `{paths.refinement_dir}/`.
397
+
398
+ **Mỗi loại đúng MỘT file cho cả PRD** đừng tách một-file-mỗi-UC, cũng đừng tách
399
+ một-file-mỗi-bước (không file spec-breakdown / business-rules / data-flow / AC riêng):
400
+
401
+ 1. **`{qc_artifact_dir}REQUIREMENT_ANALYSIS.md`** bản phân tích hợp nhất duy nhất cho cả PRD.
402
+ Mở đầu bằng **bảng `Phạm vi phân tích`** (cùng nội dung với bảng trong `DOC_GAP.md`), rồi
403
+ **một mục cho mỗi UC trong phạm vi**, mỗi mục theo thứ tự: phân rã requirement → bảng
404
+ business-rule (`BR-xx`) → data-flow → acceptance-criteria (`AC-xx`), mỗi `BR`/`AC` map tới
405
+ `{UC-ID}-SC{N}` (của `.feature` nền này) sở hữu nó.
406
+ Thêm một mục **"Mâu thuẫn chéo UC"** — chỗ hai UC của cùng PRD nói khác nhau. Rỗng thì ghi
407
+ "Không có". *Đây là thứ chỉ trạm cấp PRD nhìn thấy được; đừng bỏ mục.*
408
+ Cuối file thêm mục **"Đang chờ chốt (từ §12 tech-doc)"** — các ẩn số thiết kế `open` chạm
409
+ các UC trong phạm vi (`{id}` · UC · điều chưa biết · owner · severity). Rỗng thì ghi
410
+ "Không có"; **đừng bỏ mục**.
411
+ 2. **`{qc_artifact_dir}DOC_GAP.md`** — file gap, theo
411
412
  `{paths.qc_skills_dir}/qa-analyst/DOC_GAP.template.md` + luật viết ở
412
413
  `{paths.qc_skills_dir}/qa-analyst/spec-issue-reporter.md`. Bắt buộc:
413
- - **Bảng 10 cột** đúng thứ tự, có cột **Giao cho đội** (Dev / PO / BA / Design / Kiến trúc / Dữ liệu).
414
+ - **Bảng 11 cột** đúng thứ tự, có cột **`UC`** (cột 2) và cột **Giao cho đội** (Dev / PO / BA / Design / Kiến trúc / Dữ liệu).
415
+ - Section **"Phạm vi phân tích"** ngay sau metadata — mỗi UC một hàng kèm `@trace.status`,
416
+ đã phân tích chưa, số gap. UC ngoài phạm vi ghi `⏸ Chưa xét`.
414
417
  - Ô câu hỏi đủ **bốn phần** tách bằng `<br/>`: **Bối cảnh → Vấn đề → Tại sao quan trọng → Gợi ý**.
415
418
  *Ba phần đầu cho PO xếp ưu tiên; phần cuối cho PO trả lời nhanh mà không phải nghĩ lại từ đầu.*
416
419
  - Section **"Tài liệu đầu vào đã đọc để phân tích"** đặt ngay sau metadata — liệt kê **đủ** mọi
@@ -418,7 +421,7 @@ thành một-file-mỗi-bước (không có file spec-breakdown / business-rules
418
421
  với *"chưa đọc"*.
419
422
  - Mức nặng nhất dùng từ **`🔴 Blocker`** (không phải `Critical`) — `/qc-run-test` đọc đúng từ này
420
423
  để đặt *"scenario đang chờ PO"* vào sổ trace.
421
- 3. **`{paths.refinement_dir}/{UC-ID}-qa-findings.yaml`** — **cùng dữ liệu gap**, ở định dạng Review Board đọc được. Xem §Bản findings dưới đây.
424
+ 3. **`{paths.refinement_dir}/{TICKET-ID}-qa-findings.yaml`** — **cùng dữ liệu gap**, ở định dạng Review Board đọc được. Xem §Bản findings dưới đây.
422
425
 
423
426
  `{paths.qc_dir}` là folder top-level NHÌN THẤY trong QC repo (mặc định `docs/`, **không** phải
424
427
  `.agent/review/` ẩn) để team QC mở và xử lý output dễ dàng. Spec chính thức ở lại
@@ -433,8 +436,10 @@ Dùng **đúng schema của `/refine-prd`** để Review Board đọc được c
433
436
 
434
437
  ```yaml
435
438
  prd_source: "{paths.specs_dir}/{domain}/{prd-slug}/{TICKET-ID}-{prd-slug}.md"
436
- uc_id: "{UC-ID}"
439
+ ticket_id: "{TICKET-ID}"
437
440
  platform: "{active_platform}"
441
+ ucs: ["{UC-ID}", "…"] # các UC TRONG phạm vi (approved) — theo thứ tự
442
+ ucs_skipped: ["{UC-ID}"] # UC chưa xét, kèm lý do ở DOC_GAP §Phạm vi phân tích
438
443
  generated_at: "{ISO datetime}"
439
444
  generated_by: "qc-analyze"
440
445
  status: "pending_review"
@@ -452,7 +457,7 @@ findings:
452
457
  auto_fixable: false # LUÔN false — xem cảnh báo dưới
453
458
  status: "pending"
454
459
  applied_via: ""
455
- gap_ref: "GAP-xx" # trỏ ngược về hàng trong DOC_GAP.md
460
+ gap_ref: "GAP-UC1-001" # trỏ ngược về hàng trong DOC_GAP.md (ID mang UC)
456
461
 
457
462
  summary:
458
463
  total_findings: {N}
@@ -473,8 +478,9 @@ summary:
473
478
  > Đường đúng vẫn là kênh đã có: `/report-bug` cho defect spec thật, `/propose-scenario` cho thiếu
474
479
  > độ phủ. File `.yaml` này để **PO đọc và quyết trong một chỗ quen**, không phải để máy tự áp.
475
480
 
476
- **File riêng, không ghi chung với `/refine-prd`.** Tên `{UC-ID}` nên không đụng
477
- `{prd-slug}-findings.yaml`. Ghi chung sẽ phá trường `applied_to_version` mà `/refine-prd` dùng để
481
+ **File riêng, không ghi chung với `/refine-prd`.** Cả hai giờ đều ở cấp PRD, nên khác biệt nằm
482
+ ở **hậu tố**: `{TICKET-ID}-qa-findings.yaml` (trạm này) vs `{prd-slug}-findings.yaml`
483
+ (`/refine-prd`). Đừng gộp. Ghi chung sẽ phá trường `applied_to_version` mà `/refine-prd` dùng để
478
484
  phân biệt *"PRD đổi do chính tôi áp fix"* với *"có người lạ sửa"* — và nó sẽ mãi mãi tưởng có
479
485
  người sửa sau lưng, mỗi lần chạy đều quét lại toàn bộ kèm cảnh báo giả.
480
486
 
@@ -486,17 +492,21 @@ người sửa sau lưng, mỗi lần chạy đều quét lại toàn bộ kèm
486
492
  Output Artifacts · Next) cho report cuối, kèm khối bên dưới.
487
493
 
488
494
  ```
489
- /qc-analyze Hoàn tất — {UC-ID} ({active_platform})
490
- Files : {paths.qc_dir}/{UC-ID}/{active_platform}/REQUIREMENT_ANALYSIS.md + DOC_GAP.md (10 cột)
491
- {paths.refinement_dir}/{UC-ID}-qa-findings.yaml ← mở bằng Review Board (chuột phải)
492
- Nguồn : PRD · BDD({active_platform}) · design-spec · tech-doc{nếu thiếu tech-doc: " (THIẾU không đối chiếu được nghiệp vụ ↔ kỹ thuật)"}
495
+ /qc-analyze Hoàn tất — {TICKET-ID} ({active_platform})
496
+ Phạm vi: {n}/{N} UC phân tích{nếu có UC chưa xét: " · ⏸ {m} chưa xét: {danh sách UC-ID} (BDD chưa approved)"}
497
+ Files : {paths.qc_dir}/{TICKET-ID}/{active_platform}/REQUIREMENT_ANALYSIS.md + DOC_GAP.md (11 cột)
498
+ {paths.refinement_dir}/{TICKET-ID}-qa-findings.yaml ← mở bằng Review Board (chuột phải)
499
+ Nguồn : PRD · BDD({active_platform}, {n} UC) · design-spec · tech-doc{nếu thiếu tech-doc: " (THIẾU — không đối chiếu được nghiệp vụ ↔ kỹ thuật)"}
493
500
  Quét : 4 kỹ năng phân tích + 4 lăng kính (xử lý lỗi · giao diện · dữ liệu & cấu hình
494
501
  · đối chiếu chéo: PRD↔design-spec, bdd↔design-spec)
495
502
  Verify: {raw} gap thô → {N} còn Open (❌ {invalid} bịa/đã-trả-lời · ⚠️ {reclass} không phải gap nghiệp vụ · 🔁 {merge} trùng)
496
- Gaps : {N} ({blockers} blocker) blocker spec-defect? /report-bug {UC-ID} | coverage gap /propose-scenario {UC-ID}
497
- Chờ chốt: {G} ẩn số §12 tech-doc đang open chạm UC này (đã ghi vào REQUIREMENT_ANALYSIS, KHÔNG hỏi lại PO)
503
+ Gaps : {N} ({blockers} blocker) theo UC: {UC1: n · UC2: n · …}{nếu có: " · toàn PRD: {n}"}
504
+ blocker spec-defect? /report-bug {UC-ID của hàng đó} | coverage gap /propose-scenario {UC-ID}
505
+ Chéo UC: {X} mâu thuẫn giữa các UC của cùng PRD (đã ghi vào REQUIREMENT_ANALYSIS §Mâu thuẫn chéo UC)
506
+ Chặn : {danh sách UC có 🔴 Blocker còn Open} — các UC còn lại vẫn thiết kế test được bình thường
507
+ Chờ chốt: {G} ẩn số §12 tech-doc đang open chạm các UC này (đã ghi vào REQUIREMENT_ANALYSIS, KHÔNG hỏi lại PO)
498
508
  SC map: {M} BR/AC map tới {K} scenario
499
- Next : /qc-plan {UC-ID} {active_platform} ← rủi ro / what-if / câu hỏi cho dev
509
+ Next : /qc-plan {TICKET-ID} {active_platform} ← rủi ro / what-if / câu hỏi cho dev
500
510
  (giải quyết các gap 🔴 Blocker với PO/Dev trước)
501
511
  ```
502
512