@sun-asterisk/sungen 3.2.34-beta.1 → 3.2.35

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 (177) hide show
  1. package/dist/cli/commands/audit.d.ts.map +1 -1
  2. package/dist/cli/commands/audit.js +3 -29
  3. package/dist/cli/commands/audit.js.map +1 -1
  4. package/dist/cli/index.js +0 -2
  5. package/dist/cli/index.js.map +1 -1
  6. package/dist/exporters/test-data-resolver.d.ts.map +1 -1
  7. package/dist/exporters/test-data-resolver.js +12 -2
  8. package/dist/exporters/test-data-resolver.js.map +1 -1
  9. package/dist/generators/gherkin-parser/index.d.ts.map +1 -1
  10. package/dist/generators/gherkin-parser/index.js +1 -0
  11. package/dist/generators/gherkin-parser/index.js.map +1 -1
  12. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/contain-text-assertion.hbs +2 -1
  13. package/dist/generators/test-generator/adapters/appium/templates/steps/assertions/expanded-assertion.hbs +2 -0
  14. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/date-picker-unsupported.hbs +5 -0
  15. package/dist/generators/test-generator/adapters/playwright/templates/steps/actions/fill-action.hbs +13 -1
  16. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +9 -1
  17. package/dist/generators/test-generator/adapters/playwright/templates/steps/assertions/expanded-assertion.hbs +12 -0
  18. package/dist/generators/test-generator/code-generator.js +4 -5
  19. package/dist/generators/test-generator/code-generator.js.map +1 -1
  20. package/dist/generators/test-generator/diagnostics.d.ts +9 -3
  21. package/dist/generators/test-generator/diagnostics.d.ts.map +1 -1
  22. package/dist/generators/test-generator/diagnostics.js +9 -3
  23. package/dist/generators/test-generator/diagnostics.js.map +1 -1
  24. package/dist/generators/test-generator/patterns/types.d.ts +1 -1
  25. package/dist/generators/test-generator/patterns/types.d.ts.map +1 -1
  26. package/dist/generators/test-generator/step-mapper.d.ts +8 -0
  27. package/dist/generators/test-generator/step-mapper.d.ts.map +1 -1
  28. package/dist/generators/test-generator/step-mapper.js +52 -8
  29. package/dist/generators/test-generator/step-mapper.js.map +1 -1
  30. package/dist/generators/test-generator/utils/data-resolver.d.ts +16 -0
  31. package/dist/generators/test-generator/utils/data-resolver.d.ts.map +1 -1
  32. package/dist/generators/test-generator/utils/data-resolver.js +59 -0
  33. package/dist/generators/test-generator/utils/data-resolver.js.map +1 -1
  34. package/dist/generators/test-generator/utils/selector-resolver.d.ts.map +1 -1
  35. package/dist/generators/test-generator/utils/selector-resolver.js +10 -2
  36. package/dist/generators/test-generator/utils/selector-resolver.js.map +1 -1
  37. package/dist/harness/audit.d.ts.map +1 -1
  38. package/dist/harness/audit.js +2 -13
  39. package/dist/harness/audit.js.map +1 -1
  40. package/dist/harness/next-step.d.ts +0 -2
  41. package/dist/harness/next-step.d.ts.map +1 -1
  42. package/dist/harness/next-step.js +1 -15
  43. package/dist/harness/next-step.js.map +1 -1
  44. package/dist/harness/parse.d.ts +3 -6
  45. package/dist/harness/parse.d.ts.map +1 -1
  46. package/dist/harness/parse.js +4 -9
  47. package/dist/harness/parse.js.map +1 -1
  48. package/dist/harness/sensors.d.ts.map +1 -1
  49. package/dist/harness/sensors.js +9 -11
  50. package/dist/harness/sensors.js.map +1 -1
  51. package/dist/harness/viewpoint-baseline.d.ts.map +1 -1
  52. package/dist/harness/viewpoint-baseline.js +1 -7
  53. package/dist/harness/viewpoint-baseline.js.map +1 -1
  54. package/dist/harness/viewpoint-ledger.d.ts +3 -13
  55. package/dist/harness/viewpoint-ledger.d.ts.map +1 -1
  56. package/dist/harness/viewpoint-ledger.js +8 -38
  57. package/dist/harness/viewpoint-ledger.js.map +1 -1
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +2 -1
  60. package/dist/index.js.map +1 -1
  61. package/dist/orchestrator/ai-skills/commands.d.ts.map +1 -1
  62. package/dist/orchestrator/ai-skills/commands.js +5 -3
  63. package/dist/orchestrator/ai-skills/commands.js.map +1 -1
  64. package/dist/orchestrator/ai-skills/golden-skills.test.js +29 -0
  65. package/dist/orchestrator/ai-skills/golden-skills.test.js.map +1 -1
  66. package/dist/orchestrator/flow-manager.d.ts +1 -0
  67. package/dist/orchestrator/flow-manager.d.ts.map +1 -1
  68. package/dist/orchestrator/flow-manager.js +52 -3
  69. package/dist/orchestrator/flow-manager.js.map +1 -1
  70. package/dist/orchestrator/screen-manager.d.ts.map +1 -1
  71. package/dist/orchestrator/screen-manager.js +52 -4
  72. package/dist/orchestrator/screen-manager.js.map +1 -1
  73. package/dist/orchestrator/templates/ai-src/commands/add-flow.md +26 -4
  74. package/dist/orchestrator/templates/ai-src/commands/create-test.md +22 -30
  75. package/dist/orchestrator/templates/ai-src/config/claude.md +20 -7
  76. package/dist/orchestrator/templates/ai-src/config/copilot.md +20 -7
  77. package/dist/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +1 -1
  78. package/dist/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md +3 -3
  79. package/dist/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +9 -0
  80. package/dist/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +2 -1
  81. package/dist/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +5 -4
  82. package/dist/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +24 -8
  83. package/dist/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +1 -3
  84. package/dist/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +5 -4
  85. package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/SKILL.md +0 -18
  86. package/dist/orchestrator/templates/specs-test-data.ts +22 -4
  87. package/dist/orchestrator/viewpoint-pattern-catalog.d.ts +1 -4
  88. package/dist/orchestrator/viewpoint-pattern-catalog.d.ts.map +1 -1
  89. package/dist/orchestrator/viewpoint-pattern-catalog.js +2 -5
  90. package/dist/orchestrator/viewpoint-pattern-catalog.js.map +1 -1
  91. package/package.json +3 -3
  92. package/src/cli/commands/audit.ts +3 -26
  93. package/src/cli/index.ts +0 -2
  94. package/src/exporters/test-data-resolver.ts +10 -2
  95. package/src/generators/gherkin-parser/index.ts +1 -0
  96. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/contain-text-assertion.hbs +2 -1
  97. package/src/generators/test-generator/adapters/appium/templates/steps/assertions/expanded-assertion.hbs +2 -0
  98. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/date-picker-unsupported.hbs +5 -0
  99. package/src/generators/test-generator/adapters/playwright/templates/steps/actions/fill-action.hbs +13 -1
  100. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/contain-text-assertion.hbs +9 -1
  101. package/src/generators/test-generator/adapters/playwright/templates/steps/assertions/expanded-assertion.hbs +12 -0
  102. package/src/generators/test-generator/code-generator.ts +4 -4
  103. package/src/generators/test-generator/diagnostics.ts +9 -3
  104. package/src/generators/test-generator/patterns/types.ts +2 -0
  105. package/src/generators/test-generator/step-mapper.ts +56 -9
  106. package/src/generators/test-generator/utils/data-resolver.ts +53 -0
  107. package/src/generators/test-generator/utils/selector-resolver.ts +10 -2
  108. package/src/harness/audit.ts +2 -13
  109. package/src/harness/next-step.ts +1 -17
  110. package/src/harness/parse.ts +5 -13
  111. package/src/harness/sensors.ts +8 -9
  112. package/src/harness/viewpoint-baseline.ts +1 -7
  113. package/src/harness/viewpoint-ledger.ts +11 -52
  114. package/src/index.ts +2 -1
  115. package/src/orchestrator/ai-skills/commands.ts +5 -3
  116. package/src/orchestrator/ai-skills/golden-skills.test.ts +23 -0
  117. package/src/orchestrator/flow-manager.ts +53 -3
  118. package/src/orchestrator/screen-manager.ts +52 -4
  119. package/src/orchestrator/templates/ai-src/commands/add-flow.md +26 -4
  120. package/src/orchestrator/templates/ai-src/commands/create-test.md +22 -30
  121. package/src/orchestrator/templates/ai-src/config/claude.md +20 -7
  122. package/src/orchestrator/templates/ai-src/config/copilot.md +20 -7
  123. package/src/orchestrator/templates/ai-src/skills/sungen-api-coverage-model/SKILL.md +1 -1
  124. package/src/orchestrator/templates/ai-src/skills/sungen-capture-mobile/SKILL.md +3 -3
  125. package/src/orchestrator/templates/ai-src/skills/sungen-data-factory/SKILL.md +9 -0
  126. package/src/orchestrator/templates/ai-src/skills/sungen-delivery/SKILL.md +2 -1
  127. package/src/orchestrator/templates/ai-src/skills/sungen-error-mapping/SKILL.md +5 -4
  128. package/src/orchestrator/templates/ai-src/skills/sungen-gherkin-syntax/SKILL.md +24 -8
  129. package/src/orchestrator/templates/ai-src/skills/sungen-harness-audit/SKILL.md +1 -3
  130. package/src/orchestrator/templates/ai-src/skills/sungen-tc-generation/SKILL.md +5 -4
  131. package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/SKILL.md +0 -18
  132. package/src/orchestrator/templates/specs-test-data.ts +22 -4
  133. package/src/orchestrator/viewpoint-pattern-catalog.ts +2 -5
  134. package/dist/cli/commands/viewpoint.d.ts +0 -7
  135. package/dist/cli/commands/viewpoint.d.ts.map +0 -1
  136. package/dist/cli/commands/viewpoint.js +0 -70
  137. package/dist/cli/commands/viewpoint.js.map +0 -1
  138. package/dist/harness/viewpoint-draft-header.d.ts +0 -7
  139. package/dist/harness/viewpoint-draft-header.d.ts.map +0 -1
  140. package/dist/harness/viewpoint-draft-header.js +0 -100
  141. package/dist/harness/viewpoint-draft-header.js.map +0 -1
  142. package/dist/harness/viewpoint-mode.d.ts +0 -9
  143. package/dist/harness/viewpoint-mode.d.ts.map +0 -1
  144. package/dist/harness/viewpoint-mode.js +0 -52
  145. package/dist/harness/viewpoint-mode.js.map +0 -1
  146. package/dist/harness/viewpoint-priority.d.ts +0 -19
  147. package/dist/harness/viewpoint-priority.d.ts.map +0 -1
  148. package/dist/harness/viewpoint-priority.js +0 -36
  149. package/dist/harness/viewpoint-priority.js.map +0 -1
  150. package/dist/harness/viewpoint-review-open.d.ts +0 -6
  151. package/dist/harness/viewpoint-review-open.d.ts.map +0 -1
  152. package/dist/harness/viewpoint-review-open.js +0 -90
  153. package/dist/harness/viewpoint-review-open.js.map +0 -1
  154. package/dist/harness/viewpoint-scan-refs.d.ts +0 -50
  155. package/dist/harness/viewpoint-scan-refs.d.ts.map +0 -1
  156. package/dist/harness/viewpoint-scan-refs.js +0 -200
  157. package/dist/harness/viewpoint-scan-refs.js.map +0 -1
  158. package/dist/harness/viewpoint-scan.d.ts +0 -41
  159. package/dist/harness/viewpoint-scan.d.ts.map +0 -1
  160. package/dist/harness/viewpoint-scan.js +0 -118
  161. package/dist/harness/viewpoint-scan.js.map +0 -1
  162. package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/review-checklist.md +0 -63
  163. package/dist/orchestrator/templates/ai-src/skills/sungen-viewpoint/viewpoint-template.md +0 -158
  164. package/dist/orchestrator/viewpoint-scaffold.d.ts +0 -3
  165. package/dist/orchestrator/viewpoint-scaffold.d.ts.map +0 -1
  166. package/dist/orchestrator/viewpoint-scaffold.js +0 -70
  167. package/dist/orchestrator/viewpoint-scaffold.js.map +0 -1
  168. package/src/cli/commands/viewpoint.ts +0 -58
  169. package/src/harness/viewpoint-draft-header.ts +0 -55
  170. package/src/harness/viewpoint-mode.ts +0 -49
  171. package/src/harness/viewpoint-priority.ts +0 -38
  172. package/src/harness/viewpoint-review-open.ts +0 -51
  173. package/src/harness/viewpoint-scan-refs.ts +0 -154
  174. package/src/harness/viewpoint-scan.ts +0 -111
  175. package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/review-checklist.md +0 -63
  176. package/src/orchestrator/templates/ai-src/skills/sungen-viewpoint/viewpoint-template.md +0 -158
  177. package/src/orchestrator/viewpoint-scaffold.ts +0 -36
@@ -1,41 +0,0 @@
1
- import { ItemSource } from './viewpoint-scan-refs';
2
- import { ViewpointMode } from './viewpoint-mode';
3
- export interface VpRow {
4
- id: string;
5
- line: number;
6
- /** Item numbers the row cites (normalised: `009` → `9`), and spec-declared ids it cites (`FR-BC-03`). */
7
- refs: string[];
8
- /** `# MANUAL/ENV` or `@manual` on the row or an enclosing heading. */
9
- manual: boolean;
10
- /** The Kỳ vọng / Expected cell has text (best effort when the table has no such header). */
11
- hasExpected: boolean;
12
- }
13
- export interface ViewpointScan {
14
- /** The create-test mode this file calls for (viewpoint-mode.ts): 2 = AI drafts, 1 = review only. */
15
- mode: ViewpointMode;
16
- modeReason: string;
17
- vpRows: VpRow[];
18
- /** Distinct VP ids among `vpRows` — a reused id is several rows but one id. */
19
- distinctIds: number;
20
- rowsWithoutExpected: string[];
21
- manualRows: string[];
22
- itemSource: ItemSource;
23
- /** Spec/inventory items no VP row cites — a hint for the review, not a verdict. */
24
- uncitedItems: {
25
- ref: string;
26
- name: string;
27
- }[];
28
- /** VP ids no feature file mentions; null when the unit has no feature file yet. */
29
- withoutScenario: string[] | null;
30
- /** Why `vpRows` is empty, else null. */
31
- note: string | null;
32
- }
33
- export declare const NO_VP_IDS_NOTE = "file has no VP ids; review falls back to AI-only";
34
- /**
35
- * VP rows of one viewpoint text (already comment-stripped, as `readViewpointText` returns it).
36
- * `specIds`: the requirement ids spec.md declares — only those count as spec citations.
37
- */
38
- export declare function scanViewpointText(text: string, specIds?: ReadonlySet<string>): VpRow[];
39
- /** Scan one unit directory (`qa/screens/<n>`, `qa/flows/<n>`, …); `unit` names its feature files. */
40
- export declare function scanViewpointUnit(unitDir: string, unit: string): ViewpointScan;
41
- //# sourceMappingURL=viewpoint-scan.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"viewpoint-scan.d.ts","sourceRoot":"","sources":["../../src/harness/viewpoint-scan.ts"],"names":[],"mappings":"AAeA,OAAO,EAAwD,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACzG,OAAO,EAAiB,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEhE,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,yGAAyG;IACzG,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,sEAAsE;IACtE,MAAM,EAAE,OAAO,CAAC;IAChB,4FAA4F;IAC5F,WAAW,EAAE,OAAO,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,oGAAoG;IACpG,IAAI,EAAE,aAAa,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,+EAA+E;IAC/E,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,UAAU,CAAC;IACvB,mFAAmF;IACnF,YAAY,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC9C,mFAAmF;IACnF,eAAe,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACjC,wCAAwC;IACxC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAUD,eAAO,MAAM,cAAc,qDAAqD,CAAC;AAEjF;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,WAAW,CAAC,MAAM,CAAa,GAAG,KAAK,EAAE,CAoBjG;AAED,qGAAqG;AACrG,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,aAAa,CA0B9E"}
@@ -1,118 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.NO_VP_IDS_NOTE = void 0;
37
- exports.scanViewpointText = scanViewpointText;
38
- exports.scanViewpointUnit = scanViewpointUnit;
39
- /**
40
- * `sungen viewpoint scan` — the code half of reviewing a QA-written test-viewpoint.md.
41
- *
42
- * It does set arithmetic on ids and nothing else: which VP rows exist, what each cites, which spec
43
- * items nobody cites, which ids no scenario carries. It never judges meaning, never edits the file,
44
- * and feeds no audit finding, score or baseline.
45
- *
46
- * A VP row is a markdown table row whose FIRST cell is a full id (`VP-UI-001`, `FL-LOGIN-3`).
47
- * Priority-table rows (`VP-LOGIN (Auth)`), bullets and appendix text are not rows. This is
48
- * deliberately NOT `parseViewpointItems` (viewpoint-ledger.ts), which also reads id-less rows.
49
- */
50
- const fs = __importStar(require("fs"));
51
- const path = __importStar(require("path"));
52
- const read_text_1 = require("./read-text");
53
- const unit_paths_1 = require("./unit-paths");
54
- const viewpoint_scan_refs_1 = require("./viewpoint-scan-refs");
55
- const viewpoint_mode_1 = require("./viewpoint-mode");
56
- const VP_ID = /^(VP|FL)-[A-Z][A-Z0-9]*(-[A-Z0-9]+)*-\d+$/;
57
- const MANUAL = /\bMANUAL\/ENV\b|@manual\b/i;
58
- const EXPECTED_COL = /kỳ vọng|kết quả mong đợi|expected|期待/i;
59
- /** Whole-word match (Unicode-aware: `\b` fails on `ồ`) so `Resource` / `Datasource` are not a source column. */
60
- const SOURCE_COL = /(?<![\p{L}\p{N}])(?:nguồn|source|tham chiếu|ref|出典|根拠)(?![\p{L}\p{N}])/iu;
61
- const PRIORITY = /^(critical|high|med(?:ium)?|low|cao|trung bình|thấp)$/i;
62
- const BLANK = /^[\s\-–—…]*$/;
63
- const SPEC_ID = /(?<![\w-])[A-Z][A-Z0-9]*(?:-[A-Z0-9]+)*-\d+[A-Z]?(?![\w-])/gi;
64
- exports.NO_VP_IDS_NOTE = 'file has no VP ids; review falls back to AI-only';
65
- /**
66
- * VP rows of one viewpoint text (already comment-stripped, as `readViewpointText` returns it).
67
- * `specIds`: the requirement ids spec.md declares — only those count as spec citations.
68
- */
69
- function scanViewpointText(text, specIds = new Set()) {
70
- const rows = [];
71
- for (const r of (0, viewpoint_scan_refs_1.tableRows)(text)) {
72
- const id = r.cells[0];
73
- if (!VP_ID.test(id))
74
- continue;
75
- const exp = r.header?.findIndex((c) => EXPECTED_COL.test(c)) ?? -1;
76
- const src = r.header?.findIndex((c) => SOURCE_COL.test(c)) ?? -1;
77
- const cites = src > 0 ? r.cells[src] ?? '' : r.cells.slice(1).join(' | ');
78
- rows.push({
79
- id,
80
- line: r.line,
81
- refs: [...(0, viewpoint_scan_refs_1.extractRefs)(cites), ...[...cites.matchAll(SPEC_ID)].map((m) => m[0].toUpperCase()).filter((id) => specIds.has(id))],
82
- manual: MANUAL.test(r.raw) || MANUAL.test(r.section),
83
- // Without a Kỳ vọng header: any later cell with text that is neither a priority word nor a ref.
84
- hasExpected: exp > 0
85
- ? !BLANK.test(r.cells[exp] ?? '')
86
- : r.cells.slice(2).some((c) => !BLANK.test(c) && !PRIORITY.test(c) && (0, viewpoint_scan_refs_1.extractRefs)(c).length === 0),
87
- });
88
- }
89
- return rows;
90
- }
91
- /** Scan one unit directory (`qa/screens/<n>`, `qa/flows/<n>`, …); `unit` names its feature files. */
92
- function scanViewpointUnit(unitDir, unit) {
93
- const reqDir = path.join(unitDir, 'requirements');
94
- const vpPath = path.join(reqDir, 'test-viewpoint.md');
95
- const vpText = (0, read_text_1.readViewpointText)(vpPath);
96
- const universes = (0, viewpoint_scan_refs_1.itemUniverses)(reqDir);
97
- const vpRows = scanViewpointText(vpText, new Set(universes.specIds.map((i) => i.ref)));
98
- const { mode, reason } = (0, viewpoint_mode_1.viewpointMode)(fs.existsSync(vpPath) ? (0, read_text_1.readTextFile)(vpPath) : null, vpRows);
99
- const cited = new Set(vpRows.flatMap((r) => r.refs));
100
- const { source, items } = (0, viewpoint_scan_refs_1.citedUniverse)(universes, cited, /(?<![\w-])UI-\d{1,3}\b/i.test(vpText));
101
- const features = (0, unit_paths_1.featureFilesFor)(unitDir, unit).filter((f) => fs.existsSync(f));
102
- // Same rule as the ledger (viewpoint-ledger.ts): an id is carried when the feature text contains it.
103
- const featureText = features.map((f) => (0, read_text_1.readTextFile)(f).toLowerCase()).join('\n');
104
- return {
105
- mode,
106
- modeReason: reason,
107
- vpRows,
108
- distinctIds: new Set(vpRows.map((r) => r.id)).size,
109
- rowsWithoutExpected: vpRows.filter((r) => !r.hasExpected).map((r) => r.id),
110
- manualRows: vpRows.filter((r) => r.manual).map((r) => r.id),
111
- itemSource: source,
112
- uncitedItems: items.filter((i) => !cited.has(i.ref)),
113
- withoutScenario: features.length === 0 ? null : vpRows.filter((r) => !featureText.includes(r.id.toLowerCase())).map((r) => r.id),
114
- note: vpRows.length > 0 ? null
115
- : fs.existsSync(vpPath) ? exports.NO_VP_IDS_NOTE : 'requirements/test-viewpoint.md is missing',
116
- };
117
- }
118
- //# sourceMappingURL=viewpoint-scan.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"viewpoint-scan.js","sourceRoot":"","sources":["../../src/harness/viewpoint-scan.ts"],"names":[],"mappings":"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"}
@@ -1,63 +0,0 @@
1
- # Viewpoint review checklist
2
-
3
- create-test runs this review of `requirements/test-viewpoint.md` inline, in both modes, and asks
4
- no question. Inputs: the viewpoint; the scan JSON (`sungen viewpoint scan --screen <name> --json`,
5
- a flow takes `--flow <name>`); `spec.md`, `ui-inventory.md` and the message table when present.
6
- Output: `requirements/viewpoint-review.md` (format below).
7
-
8
- ## Rules
9
- - **Mode 1 — QA file (no `drafted-by: ai`): read only.** Never edit `test-viewpoint.md`. Every gap
10
- goes into the review file; nothing is filled in. Look for what is **missing or contradicts the
11
- spec**, not for how the QA writes: run only the checks the `Mode 1` column allows, as narrowed
12
- there. A value the spec does not mention is not "invented". Wording the QA checks by eye ("Hiển thị đúng", "Khớp design"), their question style and
13
- their environment marks are the QA's convention, never a finding.
14
- - **Mode 2 — `drafted-by: ai`: review your own draft.** Run the checks, fix the draft, run them
15
- again — checks 4 and 9 field by field, before the review is written. The review file keeps only
16
- what needs a QA decision: questions, inferred expectations, scope calls.
17
- - **Cite or drop.** Every finding names at least one VP id from the scan's `vpRows`, or an item ref
18
- (`item N`, a spec id). A gap with no VP yet cites the item or spec line it comes from. A finding
19
- you cannot anchor is not written.
20
- - **Distinct ids.** The scan lists a row per table occurrence: an id repeated in the questions table
21
- (mục 5) is a reference, not a second VP. Count and cite each id once.
22
- - **No VP ids** (scan `note`: file has no VP ids): cite the file's own row labels (`DT-2 case 3`, a
23
- field name) with the line number, plus item refs.
24
- - **Meaning, not format.** Headings, column order, wording style and language are never findings.
25
- - The scan's `withoutScenario` is generation work (a scenario per VP id), not a review finding.
26
- - **Audit findings** (create-test may route one here, check `audit`) enter only when they cite a VP id
27
- from the scan's `vpRows` and the fix is an edit the QA can make in this file. A finding about
28
- sungen itself — the page type, an id it read in a message code or a section-8 table — stays in the
29
- hand-back, never in the review.
30
-
31
- ## Checks
32
- One check, one fix hint. **Output order: 2, 9, 3, 6, 1, 4, 5, 7, 8** — highest impact first.
33
-
34
- | # | Check | Mode 1 | Read | Suggestion to write |
35
- |---|-------|--------|------|---------------------|
36
- | 1 | Each VP row says what to test and what to expect: `Kỳ vọng` is not empty and not vague ("OK", "đúng", "hiển thị đúng", "như spec"). | empty `Kỳ vọng` only | scan `rowsWithoutExpected`, the rows | Name the observable: message code, target screen, control state, value. |
37
- | 2 | Every expectation is traceable to the spec, the UI, the message table, a Design Decision (mục 6) or a Shared Check of this skill. One found in none of them is invented, unless mục 5 holds its question (`Nguồn: Suy luận → Q-0x`). | only an expectation that contradicts what the spec states; a value the spec does not mention (a URL, a screen id, a rule of thumb) is the QA's knowledge, not a finding | spec, UI, message table, mục 6 | Cite the source, or turn the expectation into a closed question in mục 5. |
38
- | 3 | A rule the spec applies to several fields names every field (`Ô` column, or one row per field). | ✓ | spec rules | Name the missing fields. |
39
- | 4 | Each accepted-input class in the spec (whitelist, value list, allowed characters, conversion such as trim, case, full→half width) has a positive VP — also a whitelist or list the spec names but does not reproduce. | ✓ | spec rules | Add an "accepted, no error" VP for the class, citing the list by name; not a question in mục 5. |
40
- | 5 | A VP that needs a real device, account, external service or elapsed time is marked `# MANUAL/ENV`. | — | scan `manualRows`, the rows | Mark the row or its group heading. |
41
- | 6 | Each entry of the scan's `uncitedItems`: deliberately out of scope (mục 6 or the scope note says so), or a gap? | ✓ | scan `uncitedItems`, mục 6, mục 7 | Add a VP citing `item N`, or a Design Decision saying why it is out. |
42
- | 7 | Questions are closed (yes/no, or pick one of the listed options) and carry `Major` / `Minor`. | — | mục 5 | Rewrite as a closed question; add the level. |
43
- | 8 | An edge case (mục 4) or decision (mục 6) states testable behaviour that no VP id carries — alone or together: an edge case split across several rows is carried. | ✓ | mục 4, mục 6 | Add a VP row. If a row already covers it, name that id in this finding — not in mục 4, where an id counts as a second declaration (`VIEWPOINT-ID-REUSED`). |
44
- | 9 | A row's `Kỳ vọng` is only a question pointer (`→ Q-0x`) although the spec states the expected result for that condition. | ✓ | spec, mục 5 | Write the spec's expectation in the row; keep the question for the part the spec leaves open. |
45
-
46
- ## Output — `requirements/viewpoint-review.md`
47
- Overwrite it on every run, never append. Write Problem and Suggestion in the viewpoint's language;
48
- one row per finding, or one row listing every id that shares the same fix.
49
-
50
- ```markdown
51
- # Viewpoint review — <unit>
52
- > Date: <YYYY-MM-DD> · test-viewpoint.md sha256: <hash> · Mode: <1 — QA file, review only | 2 — AI draft>
53
- > QA: tick `[x]` once handled, or delete the row. `sungen audit` reports the rows still open.
54
-
55
- | # | VP / item | Check | Problem | Suggestion | [ ] |
56
- |---|-----------|-------|---------|------------|-----|
57
- | 1 | VP-…-NNN · item N | 2 | <what is wrong, in one sentence> | <the fix> | [ ] |
58
- ```
59
-
60
- - `sha256` of `test-viewpoint.md` as reviewed (`shasum -a 256` / `sha256sum`); mode 2: after your fixes.
61
- - At most 20 rows, in check order. Past 20, one line under the table: `+N more (checks …)`.
62
- - Nothing to report: keep the header, write `No open points.` instead of the table.
63
- - The hand-back counts the rows: `Viewpoint review: N điểm cần QA xem`.
@@ -1,158 +0,0 @@
1
- # Test Viewpoint — <MÃ MÀN> <Tên màn>
2
- <!--
3
- Template Test Viewpoint (skill sungen-viewpoint). Dòng mẫu và hướng dẫn nằm trong comment, nên khung chưa điền khai báo 0 viewpoint. Điền: chép dòng mẫu ra ngoài comment rồi sửa; nhóm không áp dụng thì xoá, ghi lý do ở bảng Coverage (mục 7).
4
- Nháp do AI soạn (unit chưa có viewpoint QA): dòng ĐẦU TIÊN của file là `drafted-by: ai`, đặt ngoài comment. Nháp không bao giờ được lấy làm chuẩn đo; QA duyệt xong thì accept (`sungen audit … --accept-viewpoint`), accept xoá dòng đó và từ đó file thuộc QA. `sungen add` / `sungen add-flow` không ghi dòng này.
5
- -->
6
- > **Tài liệu đầu vào:** <đặc tả / design / bảng message của màn>
7
- > **Loại tài liệu:** bảng quan điểm kiểm thử — KHÔNG phải test case chi tiết. Mỗi quan điểm gắn mã `VP-…` để truy vết sang test case.
8
- > **Nguyên tắc:** mỗi dòng trả lời câu hỏi "test CÁI GÌ" và "kỳ vọng ra sao"; điểm spec chưa rõ thì ghi ở mục 5, **không tự bịa kỳ vọng** — spec đã nói thì ghi kỳ vọng theo spec, chỉ phần spec bỏ ngỏ mới thành câu hỏi. Quan điểm cần môi trường/thiết bị/thời gian thật: đánh dấu `# MANUAL/ENV`.
9
-
10
- ## 0. Metadata
11
- | Trường | Giá trị |
12
- |--------|---------|
13
- | Màn hình | <mã + tên gốc / tên tiếng Việt, route> |
14
- | Vị trí trong flow | <vào từ đâu, đi tiếp tới đâu> |
15
- | Primary UI pattern | <form / list / detail / create / login / search…> |
16
- | Nền tảng | <trình duyệt / thiết bị / độ phân giải cần test> |
17
- | Đặc thù cần nhấn | <rủi ro hoặc đặc điểm quan trọng nhất của màn> |
18
- | Tiền điều kiện | <đăng nhập? role? dữ liệu cần chuẩn bị?> |
19
- | Ngày tạo viewpoint | <YYYY-MM-DD> |
20
-
21
- ## 1. UI Patterns Identified
22
- *(Nhận diện màn dùng pattern nào → quyết định nhóm viewpoint nào cần áp dụng.)*
23
-
24
- | # | Pattern | Áp dụng? | Ghi chú |
25
- |---|---------|----------|---------|
26
- <!-- Áp dụng? = ✅ / ❌ (❌ thì ghi lý do). Một dòng cho mỗi pattern, số + tên theo catalog sungen-viewpoint (web 1–17, mobile thêm 18–32):
27
- | 1 | Form & Inputs | ✅ | <các ô nhập, validate lúc nào> |
28
- | 12 | Modal / Dialog | ❌ | <lý do không áp dụng> |
29
- -->
30
-
31
- ## 2. Priority Viewpoints
32
- ### Viewpoint grouping — ưu tiên theo nhóm VP
33
- | Nhóm VP | Priority | Lý do |
34
- |---------|----------|-------|
35
- <!-- Priority = High / Med / Low; một dòng cho mỗi nhóm có ở mục 3: VP-VAL (Validation) · VP-LOGIC (Nghiệp vụ) · VP-UI-BTN (Nút & trạng thái) · VP-NAV (Điều hướng) · VP-AUTH (Quyền / tiền điều kiện) · VP-SEC (Bảo mật) · VP-UI (Hiển thị). Ví dụ:
36
- | VP-VAL (Validation) | High | <lý do> | -->
37
-
38
- ## 3. Viewpoints chi tiết
39
- *(Pri = priority. Nguồn = số item trong spec/UI. Mỗi dòng = "test cái gì".)*
40
- <!--
41
- - Mọi bảng mục 3 dùng cùng cột. Mã VP-<NHÓM>[-<FIELD>]-NNN ở cột đầu, mỗi mã khai báo một lần. Flow: mã FL-…, thêm nhóm liên màn (dữ liệu / quyền giữ qua các màn).
42
- - Kỳ vọng = thứ nhìn thấy được: mã message, màn đích, trạng thái control. Kỳ vọng trống thì dòng KHÔNG tính là viewpoint.
43
- - Ô: để trống khi cả nhóm là một ô. Một luật áp cho nhiều ô: liệt kê đủ từng ô, không lấy 1 ô làm đại diện.
44
- - Pri = High / Med / Low. Nguồn = `item N` · `SPEC <id>` · `Suy luận → Q-0x` (suy luận bắt buộc có câu hỏi ở mục 5). Cần thiết bị / tài khoản / thời gian thật: `# MANUAL/ENV` trong ô Quan điểm hoặc tiêu đề nhóm.
45
- -->
46
-
47
- ### A. VP-UI — Hiển thị tĩnh & trạng thái ban đầu
48
- | VP | Quan điểm kiểm thử | Ô | Kỳ vọng | Pri | Nguồn |
49
- |----|--------------------|---|---------|-----|-------|
50
- <!-- | VP-UI-001 | Trạng thái lúc mở màn | | <nhãn, placeholder, giá trị mặc định, nút chính disabled / enabled> | | item N | -->
51
-
52
- ### B. VP-VAL-<FIELD> — <Tên field> (item x)
53
- | VP | Quan điểm kiểm thử | Ô | Kỳ vọng | Pri | Nguồn |
54
- |----|--------------------|---|---------|-----|-------|
55
- <!--
56
- | VP-VAL-…-001 | Rỗng → blur / submit | | → <mã message> | High | |
57
- | VP-VAL-…-002 | Chỉ khoảng trắng / khoảng trắng đầu-cuối | | | | |
58
- | VP-VAL-…-003 | Biên độ dài: min−1 / min / max / max+1 | | | High | |
59
- | VP-VAL-…-004 | Sai định dạng / ký tự không cho phép | | | | |
60
- | VP-VAL-…-008 | Mọi ký tự / giá trị spec cho phép (whitelist, danh sách, chuyển đổi) được chấp nhận | | Không lỗi, giá trị giữ nguyên / đã chuyển đổi | High | |
61
- | VP-VAL-…-005 | Full-width / half-width | | | | |
62
- | VP-VAL-…-006 | Paste / autofill | | | | |
63
- | VP-VAL-…-007 | Đang lỗi → sửa đúng → message biến mất | | | | |
64
- -->
65
- *(Lặp nhóm B cho từng field.)*
66
-
67
- ### C. VP-LOGIC — Nghiệp vụ chính / submit
68
- | VP | Quan điểm kiểm thử | Ô | Kỳ vọng | Pri | Nguồn |
69
- |----|--------------------|---|---------|-----|-------|
70
- <!--
71
- | VP-LOGIC-001 | Happy path | | | High | |
72
- | VP-LOGIC-002 | Server trả lỗi nghiệp vụ | | | | |
73
- | VP-LOGIC-003 | Lỗi mạng / server `# MANUAL/ENV` | | | | |
74
- -->
75
-
76
- ### D. VP-UI-BTN — Nút & trạng thái
77
- | VP | Quan điểm kiểm thử | Ô | Kỳ vọng | Pri | Nguồn |
78
- |----|--------------------|---|---------|-----|-------|
79
- <!--
80
- | VP-UI-BTN-001 | Chưa đủ điều kiện → disabled | | | High | |
81
- | VP-UI-BTN-002 | Đủ điều kiện → enabled | | | High | |
82
- | VP-UI-BTN-003 | Loading sau khi bấm | | | | |
83
- | VP-UI-BTN-004 | Bấm kép → chỉ 1 request `# MANUAL/ENV` | | | High | |
84
- -->
85
-
86
- ### E. VP-NAV — Điều hướng
87
- | VP | Quan điểm kiểm thử | Ô | Kỳ vọng | Pri | Nguồn |
88
- |----|--------------------|---|---------|-----|-------|
89
- <!-- | VP-NAV-001 | | | → <màn đích>, cùng tab / tab mới | | | -->
90
-
91
- ### F. VP-AUTH — Quyền / tiền điều kiện truy cập (nếu có)
92
- ### G. VP-SEC — Bảo mật (nếu có)
93
- ### H. VP-UI-PLTF — Nền tảng / thiết bị (nếu có)
94
-
95
- ### DT-1 — Bảng quyết định: <luật phụ thuộc ≥ 2 điều kiện> (nếu có)
96
- <!-- Mỗi Case = 1 dòng có mã VP. Cột giữa là điều kiện (✓ / ✗ / giá trị), rồi Hành động và Kỳ vọng. Ví dụ:
97
- | VP | <Điều kiện 1> | <Điều kiện 2> | Hành động | Kỳ vọng | Pri | Nguồn |
98
- |----|---------------|---------------|-----------|---------|-----|-------|
99
- | VP-LOGIC-010 | ✓ | ✗ | Tick nốt ô con còn lại | "Chọn tất cả" tự → ✓ | High | item N |
100
- -->
101
-
102
- ## 4. Edge Cases
103
- *(Ca dễ bị sót khi viết test case.)*
104
- <!--
105
- - <biên nối tiếp: max rồi max+1>
106
- - <bấm nút đúng lúc field vừa thành không hợp lệ>
107
- - <mất mạng trước khi bấm vs trong lúc chờ phản hồi>
108
- - <phiên hết hạn giữa chừng>
109
- -->
110
-
111
- ## 5. Open questions / Điểm CHƯA CHỐT
112
- *(Spec chưa nêu → KHÔNG tự bịa kỳ vọng; ghi thành câu hỏi đóng để hỏi BA/dev.)*
113
-
114
- | Mã | Mức | VP liên quan | Câu hỏi đóng cần BA/dev xác nhận |
115
- |-----|-----|--------------|----------------------------------|
116
- <!-- Mức: Major = test case của VP liên quan chờ câu trả lời; Minor = không chặn. -->
117
- <!-- | Q-01 | Major / Minor | VP-… | <câu hỏi cụ thể, trả lời được bằng có/không hoặc chọn 1 phương án> | -->
118
-
119
- ## Known issues — lỗi đã biết (KHÔNG phải câu hỏi)
120
- *(Lỗi đã xác nhận, còn tồn tại: scenario của VP đó gắn `@known-defect:DEF-NNN` (bản ghi ở `qa/defects/`), đỏ có chủ đích. Spec chưa rõ thì ghi ở mục 5.)*
121
-
122
- | KI | VP liên quan | Lỗi | Hiện tượng |
123
- |----|--------------|-----|------------|
124
- <!-- | KI-1 | VP-… | DEF-001 | <thấy gì, khác Kỳ vọng ở đâu> | -->
125
-
126
- ## 6. Design Decisions
127
- *(Quyết định có chủ đích của spec — KHÔNG báo "thiếu" hoặc "bug" cho các mục này.)*
128
- <!--
129
- - D1: <vd: nút disabled tới khi đủ điều kiện> (SPEC <id>)
130
- - D2: <vd: lỗi đăng nhập không nói rõ sai email hay sai mật khẩu>
131
- -->
132
-
133
- ## 7. Traceability & tự nhận giới hạn
134
- - **Tổng: N viewpoint** — liệt kê số lượng theo từng nhóm. Mọi item trong spec có ít nhất 1 VP trỏ về.
135
- - ⚠ **Giới hạn:** bảng chứng minh "mọi VP đã liệt kê có kỳ vọng" — KHÔNG chứng minh "đã liệt kê ĐỦ VP". Đối chiếu lại với đặc tả gốc + bảng message trước khi viết test case.
136
- - **Scope note:** <VP nào cần môi trường/thiết bị thật, VP nào test ở tầng API/E2E>.
137
- - Còn **K điểm chưa chốt** (Q-…) — test case cho VP liên quan chờ câu trả lời.
138
-
139
- ### Coverage — độ phủ tự kiểm
140
- <!-- Mỗi nhóm một dòng. Trạng thái: `có VP` (✓) · `để sau → <đích>` (# MANUAL/ENV, unit/flow khác, Q-0x) · `Không áp dụng` (➖) kèm lý do.
141
- Thêm dòng nền tảng theo catalog sungen-viewpoint khi màn có: web — XSS / SQL injection / URL (Shared Checks); mobile — Bàn phím ảo (30) · Vòng đời app & xoay màn (32) · Offline (27) · Quyền hệ thống (22) · Deep-link (26). -->
142
- | Nhóm kiểm tra | Trạng thái | VP / lý do |
143
- |---------------|------------|------------|
144
- | Happy path | | |
145
- | Negative | | |
146
- | Biên | | |
147
- | Charset | | |
148
- | Trạng thái nút | | |
149
- | Điều hướng | | |
150
- | Quyền | | |
151
- | Bảo mật | | |
152
- | Nền tảng | | |
153
- | Trạng thái tải / rỗng / lỗi | | |
154
-
155
- ## 8. References · Phụ lục — Mã thông báo dùng trong tài liệu
156
- | Mã | Nghĩa | Kiểu hiển thị |
157
- |----|-------|---------------|
158
- <!-- | M01 | <nội dung message> | <dưới ô nhập / trên form / toast / mail> | -->
@@ -1,3 +0,0 @@
1
- export declare const VIEWPOINT_TEMPLATE_PATH: string;
2
- export declare function viewpointScaffold(title: string, isMobile: boolean): string;
3
- //# sourceMappingURL=viewpoint-scaffold.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"viewpoint-scaffold.d.ts","sourceRoot":"","sources":["../../src/orchestrator/viewpoint-scaffold.ts"],"names":[],"mappings":"AAeA,eAAO,MAAM,uBAAuB,QAEnC,CAAC;AAKF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,GAAG,MAAM,CAa1E"}
@@ -1,70 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.VIEWPOINT_TEMPLATE_PATH = void 0;
37
- exports.viewpointScaffold = viewpointScaffold;
38
- /**
39
- * The `requirements/test-viewpoint.md` that `sungen add` and `sungen add-flow` write.
40
- *
41
- * It IS the QA template the AI drafts from (`skills/sungen-viewpoint/viewpoint-template.md`), read
42
- * at run time so QA and AI always start from the same file. Two substitutions only: the title, and
43
- * the template's sample UI-pattern rows → the full catalog for the project's platform (#842).
44
- *
45
- * Every sample row sits inside `<!-- -->`, so an untouched scaffold declares no viewpoint and the
46
- * audit reports VIEWPOINT-MISSING instead of measuring the suite against samples (#841). The
47
- * scaffold never carries an active `drafted-by: ai` line: that marks an AI draft, and this is not one.
48
- */
49
- const fs = __importStar(require("fs"));
50
- const path = __importStar(require("path"));
51
- const viewpoint_pattern_catalog_1 = require("./viewpoint-pattern-catalog");
52
- exports.VIEWPOINT_TEMPLATE_PATH = path.join(__dirname, 'templates', 'ai-src', 'skills', 'sungen-viewpoint', 'viewpoint-template.md');
53
- /** A UI-pattern row: `| 12 | Modal / Dialog | … |`. Only section 1 of the template has them. */
54
- const PATTERN_ROW = /^\|\s*\d+\s*\|/;
55
- function viewpointScaffold(title, isMobile) {
56
- const lines = fs.readFileSync(exports.VIEWPOINT_TEMPLATE_PATH, 'utf-8').replace(/\r\n?/g, '\n').split('\n');
57
- const titleAt = lines.findIndex((l) => l.startsWith('# '));
58
- const rowsAt = lines.findIndex((l) => PATTERN_ROW.test(l));
59
- // A template edit that drops either anchor must fail here, not ship a scaffold without them.
60
- if (titleAt < 0 || rowsAt < 0) {
61
- throw new Error(`viewpoint-template.md has no title or no UI-pattern sample rows: ${exports.VIEWPOINT_TEMPLATE_PATH}`);
62
- }
63
- let rowsEnd = rowsAt;
64
- while (rowsEnd < lines.length && PATTERN_ROW.test(lines[rowsEnd]))
65
- rowsEnd++;
66
- lines[titleAt] = `# Test Viewpoint — ${title}`;
67
- lines.splice(rowsAt, rowsEnd - rowsAt, ...(0, viewpoint_pattern_catalog_1.uiPatternRows)(isMobile));
68
- return lines.join('\n');
69
- }
70
- //# sourceMappingURL=viewpoint-scaffold.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"viewpoint-scaffold.js","sourceRoot":"","sources":["../../src/orchestrator/viewpoint-scaffold.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsBA,8CAaC;AAnCD;;;;;;;;;;GAUG;AACH,uCAAyB;AACzB,2CAA6B;AAC7B,2EAA4D;AAE/C,QAAA,uBAAuB,GAAG,IAAI,CAAC,IAAI,CAC9C,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,kBAAkB,EAAE,uBAAuB,CACxF,CAAC;AAEF,gGAAgG;AAChG,MAAM,WAAW,GAAG,gBAAgB,CAAC;AAErC,SAAgB,iBAAiB,CAAC,KAAa,EAAE,QAAiB;IAChE,MAAM,KAAK,GAAG,EAAE,CAAC,YAAY,CAAC,+BAAuB,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACpG,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;IAC3D,MAAM,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,6FAA6F;IAC7F,IAAI,OAAO,GAAG,CAAC,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,MAAM,IAAI,KAAK,CAAC,oEAAoE,+BAAuB,EAAE,CAAC,CAAC;IACjH,CAAC;IACD,IAAI,OAAO,GAAG,MAAM,CAAC;IACrB,OAAO,OAAO,GAAG,KAAK,CAAC,MAAM,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAC7E,KAAK,CAAC,OAAO,CAAC,GAAG,sBAAsB,KAAK,EAAE,CAAC;IAC/C,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM,EAAE,GAAG,IAAA,yCAAa,EAAC,QAAQ,CAAC,CAAC,CAAC;IACnE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC"}
@@ -1,58 +0,0 @@
1
- import { Command } from 'commander';
2
- import { resolveUnit } from '../resolve-unit';
3
- import { scanViewpointUnit, ViewpointScan } from '../../harness/viewpoint-scan';
4
-
5
- const SHOWN = 10;
6
-
7
- /** `a, b, c … +N more` — a list long enough to scroll past is a count plus a sample. */
8
- const sample = (xs: string[]): string => (xs.length <= SHOWN ? xs.join(', ') : `${xs.slice(0, SHOWN).join(', ')} … +${xs.length - SHOWN} more`);
9
-
10
- function printScan(name: string, s: ViewpointScan): void {
11
- const L = console.log;
12
- L(`\n━━━ Viewpoint scan: ${name} ━━━`);
13
- L(` mode ${s.mode} (${s.modeReason}) — ${s.mode === 2 ? 'the AI drafts / revises its own draft' : 'review only, the file is never edited'}`);
14
- L(` ${s.vpRows.length} rows (${s.distinctIds} distinct ids) without Kỳ vọng: ${s.rowsWithoutExpected.length} manual: ${s.manualRows.length}`);
15
- if (s.note) L(` ⚠ ${s.note}`);
16
- if (s.rowsWithoutExpected.length) L(` no Kỳ vọng: ${sample(s.rowsWithoutExpected)}`);
17
- if (s.manualRows.length) L(` manual: ${sample(s.manualRows)}`);
18
- if (s.itemSource === 'none') L(' item source: none (the viewpoint cites no spec id, spec item or UI-NNN)');
19
- else L(` item source: ${s.itemSource} uncited items: ${s.uncitedItems.length}`);
20
- if (s.uncitedItems.length) L(` ${sample(s.uncitedItems.map((i) => (i.name ? `${i.ref} (${i.name})` : i.ref)))}`);
21
- if (s.withoutScenario === null) L(' scenarios: no feature file yet');
22
- else L(` VP ids with no scenario: ${s.withoutScenario.length}${s.withoutScenario.length ? `\n ${sample(s.withoutScenario)}` : ''}`);
23
- L('');
24
- }
25
-
26
- /**
27
- * `sungen viewpoint scan` — ids and item refs only. Read-only: no audit finding, no score, no
28
- * baseline effect; the file is never edited. What the rows MEAN is the review skill's job.
29
- */
30
- export function registerViewpointCommand(program: Command): void {
31
- const viewpoint = program
32
- .command('viewpoint')
33
- .description('Read a unit\'s requirements/test-viewpoint.md');
34
-
35
- viewpoint
36
- .command('scan')
37
- .description('List VP rows (full-id first cell), the item numbers they cite, uncited spec items and VP ids no scenario carries — read-only')
38
- .option('-s, --screen <name>', 'Screen (or flow) name')
39
- .option('--flow <name>', 'Flow name (qa/flows/<name>)')
40
- .option('--json', 'Output the scan as JSON')
41
- .action((o: { screen?: string; flow?: string; json?: boolean }) => {
42
- try {
43
- if (o.screen && o.flow) throw new Error('Provide --screen <name> or --flow <name>, not both');
44
- const name = o.screen || o.flow;
45
- if (!name) throw new Error('Provide --screen <name> or --flow <name>');
46
- const unit = resolveUnit(name);
47
- if (!unit) throw new Error(`Not found: qa/screens/${name} or qa/flows/${name}`);
48
- if (o.flow && unit.kind !== 'flow') throw new Error(`Not a flow: ${name} resolves to qa/${unit.unitId} (use --screen)`);
49
-
50
- const scan = scanViewpointUnit(unit.dir, name);
51
- if (o.json) console.log(JSON.stringify(scan, null, 2));
52
- else printScan(name, scan);
53
- } catch (e) {
54
- console.error('Error:', e instanceof Error ? e.message : e);
55
- process.exit(1);
56
- }
57
- });
58
- }
@@ -1,55 +0,0 @@
1
- import * as fs from 'fs';
2
- import { stripHtmlComments } from './read-text';
3
- import { writeFileAtomic } from './write-atomic';
4
-
5
- /**
6
- * `drafted-by: ai` — provenance, not acceptance. The AI writes it as the first header line of a
7
- * viewpoint it drafted; while it is there the file is an unreviewed draft: the audit records no
8
- * baseline for it and holds the two axes measured against it. `--accept-viewpoint` removes it.
9
- *
10
- * Read by a line scan, not by the viewpoint parser: the key must be found in a file the parser
11
- * judges nothing about, and in a QA-written file it simply is not there.
12
- */
13
- const DRAFT_KEY = /^\s*drafted-by:\s*ai\b/i;
14
- /** The key is a header line: looking further down would read prose that merely mentions it. */
15
- const HEADER_LINES = 15;
16
-
17
- /** A line with its own terminator, so a removal keeps CRLF and a lone `\r` exactly as written. */
18
- const withTerminator = (raw: string): string[] => raw.match(/[^\r\n]*(?:\r\n|\r|\n)|[^\r\n]+$/g) ?? [];
19
-
20
- /** Index of the draft line in `raw`, or -1. Comments are blanked line-for-line, so indexes agree with the raw file. */
21
- function draftLineIndex(raw: string): number {
22
- const visible = stripHtmlComments(raw.replace(/\r\n?/g, '\n')).split('\n');
23
- let seen = 0;
24
- for (let i = 0; i < visible.length && seen < HEADER_LINES; i++) {
25
- if (visible[i].trim() === '') continue;
26
- if (DRAFT_KEY.test(visible[i])) return i;
27
- seen++;
28
- }
29
- return -1;
30
- }
31
-
32
- export function isAiDraft(text: string): boolean {
33
- return draftLineIndex(text) >= 0;
34
- }
35
-
36
- /** `raw` without the one draft line; every other byte is untouched. A file with no draft line comes back as is. */
37
- export function removeDraftLine(raw: string): string {
38
- const at = draftLineIndex(raw);
39
- if (at < 0) return raw;
40
- return withTerminator(raw).filter((_, i) => i !== at).join('');
41
- }
42
-
43
- export function viewpointFileIsAiDraft(viewpointPath: string): boolean {
44
- try { return isAiDraft(fs.readFileSync(viewpointPath, 'utf-8')); } catch { return false; }
45
- }
46
-
47
- /** Remove the draft line from the file, atomically. True when a line was removed; throws a clear error when the write fails. */
48
- export function removeDraftLineFromFile(viewpointPath: string): boolean {
49
- const raw = fs.readFileSync(viewpointPath, 'utf-8');
50
- if (!isAiDraft(raw)) return false;
51
- try { writeFileAtomic(viewpointPath, removeDraftLine(raw)); } catch (err) {
52
- throw new Error(`could not remove the \`drafted-by: ai\` line from ${viewpointPath} (${err instanceof Error ? err.message : err}) — the file is unchanged; remove the line by hand`);
53
- }
54
- return true;
55
- }