@shanyucoder/flowgrid 0.1.9 → 0.1.10

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 (69) hide show
  1. package/bin/flowgrid.mjs +43 -9
  2. package/bin/lib/audit-run.mjs +5 -1
  3. package/bin/lib/docs-hub-locale.mjs +9 -0
  4. package/bin/lib/init-scaffold.mjs +30 -2
  5. package/dist/docs/mcp/tools.js +4 -4
  6. package/dist/docs/mcp/tools.js.map +1 -1
  7. package/dist/docs/scan/ids.d.ts +1 -1
  8. package/dist/docs/scan/ids.js +6 -6
  9. package/dist/docs/scan/ids.js.map +1 -1
  10. package/dist/docs/scan/route.js +2 -2
  11. package/dist/docs/scan/route.js.map +1 -1
  12. package/engines/cases/render-cases.mjs +33 -25
  13. package/engines/docs/lib/audit-hub-prd.mjs +136 -0
  14. package/engines/docs/lib/audit-risks-catalog.mjs +142 -0
  15. package/engines/docs/lib/docs-hub-locale.mjs +100 -0
  16. package/engines/docs/lib/render-bundle-markdown.mjs +8 -1
  17. package/engines/docs/vitepress/config.ts +4 -4
  18. package/engines/spec/lib/audit-bundle-gaps.mjs +38 -7
  19. package/engines/spec/lib/audit-flow-gaps.mjs +2 -2
  20. package/engines/spec/lib/bundle-schema.mjs +3 -1
  21. package/engines/testcase/runners/lib/resolve-hub-id.mjs +3 -3
  22. package/harness/common/skills/legacy/SKILL.md +2 -2
  23. package/harness/docs/extracts/common-scope.md +8 -8
  24. package/harness/docs/extracts/spec-core.md +1 -1
  25. package/harness/docs/extracts/spec-prd-lite.md +11 -13
  26. package/harness/docs/extracts/tpl-module.md +9 -40
  27. package/harness/docs/extracts/tpl-overview-prd.md +11 -0
  28. package/harness/docs/extracts/tpl-risk-register.md +28 -0
  29. package/harness/docs/extracts/tpl-surface-prd.md +7 -0
  30. package/harness/docs/rules/docs-hub.mdc +1 -1
  31. package/harness/docs/rules/flowgrid-process.mdc +1 -1
  32. package/harness/docs/skills/adopt/SKILL.md +1 -1
  33. package/harness/docs/skills/background-logic/SKILL.md +1 -1
  34. package/harness/docs/skills/common-spec/SKILL.md +1 -1
  35. package/harness/docs/skills/cross-service/SKILL.md +1 -1
  36. package/harness/docs/skills/db-erd/SKILL.md +1 -1
  37. package/harness/docs/skills/grill/SKILL.md +2 -0
  38. package/harness/docs/skills/grill-hub-prd/SKILL.md +38 -0
  39. package/harness/docs/skills/module/SKILL.md +8 -5
  40. package/harness/docs/skills/overview/SKILL.md +7 -6
  41. package/harness/docs/skills/risk-register/SKILL.md +29 -0
  42. package/harness/docs/skills/spec/SKILL.md +3 -3
  43. package/harness/docs/skills/surfaces/SKILL.md +3 -1
  44. package/harness/docs/skills/{business-process → user-flow}/SKILL.md +8 -8
  45. package/harness/fe/skills/gen-common/SKILL.md +1 -1
  46. package/harness/tests/extracts/grill-scenario-flow.md +2 -2
  47. package/harness/tests/skills/grill-testcase/SKILL.md +1 -1
  48. package/harness/tests/skills/scenario/SKILL.md +11 -11
  49. package/harness/tests/skills/testcase/SKILL.md +1 -1
  50. package/harness/tests/templates/SC.example.md +16 -16
  51. package/lexicon/registry-tags.en.txt +2 -2
  52. package/package.json +1 -1
  53. package/templates/project-skeleton/architecture/03-business-process/index.md +3 -0
  54. package/templates/project-skeleton/architecture/{03-business-process → 03-user-flows}/FLOW-login.md +6 -6
  55. package/templates/project-skeleton/architecture/{03-business-process → 03-user-flows}/FLOW-template.md +6 -7
  56. package/templates/project-skeleton/architecture/11-risks/index.md +7 -17
  57. package/templates/project-skeleton/architecture/11-risks/risk-register.md +34 -0
  58. package/templates/project-skeleton/architecture/12-glossary/index.md +2 -1
  59. package/templates/project-skeleton/overview/index.md +21 -43
  60. package/templates/project-skeleton/overview/operational-areas/_template.md +15 -22
  61. package/templates/project-skeleton/surfaces/_module-index.template.md +40 -0
  62. package/templates/project-skeleton/surfaces/_surface-index.template.md +43 -0
  63. package/templates/shared/bundle-authoring.md +5 -2
  64. package/templates/shared/default-layout.ejs +108 -62
  65. package/templates/shared/feature.bundle.yaml +15 -6
  66. package/templates/shared/ir/generated/spec.md +24 -24
  67. package/templates/shared/tpl-api-contract.md +5 -5
  68. package/templates/tests-skeleton/catalog/locale.yaml +16 -15
  69. package/templates/tests-skeleton/tpl-testcase-plan.md +6 -6
@@ -2,9 +2,13 @@
2
2
  const pageId = spec['page-id'] || spec.id || '';
3
3
  const docStatus = spec.status || 'draft';
4
4
  const docOwner = spec.owner || '';
5
- const hasMetrics = spec.successMetrics && String(spec.successMetrics).trim();
5
+ const hasScopeIn = spec.scopeIn && String(spec.scopeIn).trim();
6
6
  const hasNonGoals = spec.nonGoals && String(spec.nonGoals).trim();
7
7
  const hasStories = !!spec.userStories;
8
+ const hasNfr = spec.nfr && String(spec.nfr).trim();
9
+ const hasUserFlows = spec.userFlows && String(spec.userFlows).trim();
10
+ const qaOpen = spec['Q&A'] && String(spec['Q&A']).trim();
11
+ const hasEntities = spec.entities && (Array.isArray(spec.entities) ? spec.entities.length : true);
8
12
  const pageSections = spec.sections || spec.design?.sections || [];
9
13
  const pageActions = spec.actions || spec.design?.actions || [];
10
14
  const pageStateMatrix = spec.stateMatrix || spec.design?.stateMatrix;
@@ -21,23 +25,28 @@
21
25
  const hasLegacy = spec.legacy && (spec.legacy.behaviors?.length || spec.legacy.fields?.length || spec.legacy.evidence?.length);
22
26
  const hasDebt = spec.pendingTechDebt && spec.pendingTechDebt.length;
23
27
 
28
+ const contentLocale = context.docsContentLocale || 'vi';
24
29
  const toc = [];
25
- toc.push('1. [Tổng quan](#overview)');
26
- if (hasMetrics) toc.push('2. [Chỉ số thành công](#success-metrics)');
27
- if (hasNonGoals) toc.push('3. [Phạm vi không làm](#non-goals)');
28
- if (hasStories) toc.push('4. [User stories và hành trình màn hình](#user-stories--screen-journey)');
29
- if (hasState) toc.push('5. [Ma trận trạng thái và phân quyền](#state--permission-matrix)');
30
- if (hasListColumns) toc.push('6. [Bảng cột danh sách](#list-columns)');
31
- if (hasListExtras) toc.push('6b. [Bộ lọc & phân trang](#list-filters-pagination)');
32
- if (hasValidation) toc.push(`${hasListColumns || hasListExtras ? '7' : '6'}. [Từ điển dữ liệu và validation](#data-dictionary--validation)`);
33
- if (hasCustomWidget) toc.push(`${hasListColumns ? '8' : '7'}. [Widget tùy biến](#custom-widgets)`);
34
- const actionNum = hasListColumns ? (hasCustomWidget ? '9' : '8') : (hasCustomWidget ? '8' : '7');
35
- if (hasActions) toc.push(`${actionNum}. [Luồng hành động](#action-flows)`);
36
- else if (pageSections.length) toc.push(`${actionNum}. [Bố cục màn hình](#page-composition)`);
37
- if (hasReview) toc.push('9. [Ghi chú review](#review-notes)');
38
- if (hasLegacy) toc.push('10. [Legacy evidence](#legacy-evidence)');
39
- if (hasDebt) toc.push('11. [Tech debt](#pending-tech-debt)');
40
- toc.push('- [API SSOT](#api-ssot)');
30
+ let n = 1;
31
+ const addToc = (label, anchor) => { toc.push(`${n}. [${label}](#${anchor})`); n += 1; };
32
+ addToc('Overview', 'overview');
33
+ if (hasScopeIn) addToc('In scope', 'scope-in');
34
+ if (hasNonGoals) addToc('Out of scope', 'scope-out');
35
+ if (hasStories) addToc('User stories & requirements', 'user-stories--screen-journey');
36
+ addToc('Data & integrations / API', 'data-integrations');
37
+ if (hasUserFlows) addToc('User flows', 'user-flows');
38
+ if (hasNfr) addToc('Non-functional (NFR)', 'nfr');
39
+ if (hasState) addToc('State & permissions matrix', 'state--permission-matrix');
40
+ if (hasListColumns) addToc('List columns', 'list-columns');
41
+ if (hasListExtras) addToc('Filters & pagination', 'list-filters-pagination');
42
+ if (hasValidation) addToc('Data dictionary & validation', 'data-dictionary--validation');
43
+ if (hasCustomWidget) addToc('Custom widgets', 'custom-widgets');
44
+ if (hasActions) addToc('Action flows', 'action-flows');
45
+ else if (pageSections.length) addToc('Page composition', 'page-composition');
46
+ if (qaOpen) addToc('Appendix — open questions', 'appendix-open-questions');
47
+ if (hasReview) addToc('Review notes', 'review-notes');
48
+ if (hasLegacy) addToc('Legacy evidence', 'legacy-evidence');
49
+ if (hasDebt) addToc('Tech debt', 'pending-tech-debt');
41
50
 
42
51
  const route = spec.ui?.routes?.[0];
43
52
  const routePath = route?.path || '';
@@ -45,13 +54,13 @@
45
54
  %>
46
55
  # <%= spec.title || spec.id %>
47
56
 
48
- ## Mục lục (Contents)
57
+ ## Table of contents
49
58
 
50
59
  <%= toc.join('\n') %>
51
60
 
52
61
  ---
53
62
 
54
- ## Tổng quan {#overview}
63
+ ## Overview {#overview}
55
64
 
56
65
  | | |
57
66
  | --- | --- |
@@ -75,36 +84,29 @@
75
84
  %>
76
85
  <%= screenLine %>
77
86
 
78
- - **QA inbox:** [Danh sách câu hỏi treo](../../qa/index.md) (cùng function leaf)
87
+ - **QA inbox:** [Open questions](../../qa/index.md) (same function leaf)
79
88
  - **Generated (VitePress):** [spec](./spec.md) · [data-model](./data-model.md) · [api](./api.md) _(khi có `01`)_
80
89
 
81
90
  <% if (spec.summary) { %>
82
91
  <%= spec.summary %>
83
92
  <% } %>
84
93
 
85
- <% if (hasMetrics) { %>
86
- ## Chỉ số thành công {#success-metrics}
94
+ <% if (hasScopeIn) { %>
95
+ ## In scope {#scope-in}
87
96
 
88
- <%= spec.successMetrics %>
97
+ <%= spec.scopeIn %>
89
98
 
90
99
  <% } %>
91
100
 
92
101
  <% if (hasNonGoals) { %>
93
- ## Phạm vi không làm {#non-goals}
102
+ ## Out of scope {#scope-out}
94
103
 
95
104
  <%= spec.nonGoals %>
96
105
 
97
106
  <% } %>
98
107
 
99
- ## API SSOT {#api-ssot}
100
-
101
- Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function leaf (không gộp vào bundle). Sau split: `ir/design.yaml` chiếu endpoint; OpenAPI/mock theo stack.
102
-
103
- - **Data model (review, multi-table):** [data-model.md](./data-model.md) — bảng/cột; SSOT codegen = `01` + `ir/design.yaml` `db`.
104
- - **API (đọc trên site):** [api.md](./api.md) — tóm tắt endpoint từ `01-backend-spec.yaml` (khi có file).
105
-
106
108
  <% if (spec.userStories) { %>
107
- ## User Stories & Screen Journey {#user-stories--screen-journey}
109
+ ## User stories & requirements {#user-stories--screen-journey}
108
110
 
109
111
  <% if (typeof spec.userStories === 'string') { %>
110
112
  <%= spec.userStories %>
@@ -112,29 +114,29 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
112
114
  <% spec.userStories.forEach(story => { %>
113
115
  ### <%= story.title || story.name || 'User Story' %>
114
116
  <% if (story.asA || story.role) { %>
115
- > **Là một** <%= story.asA || story.role %>,
116
- > **Tôi muốn** <%= story.iWant || story.action %>,
117
- > **Để** <%= story.soThat || story.benefit %>.
117
+ > **As a** <%= story.asA || story.role %>,
118
+ > **I want** <%= story.iWant || story.action %>,
119
+ > **so that** <%= story.soThat || story.benefit %>.
118
120
  <% } else if (story.story) { %>
119
121
  > <%= story.story %>
120
122
  <% } %>
121
123
 
122
124
  <% if (story.preConditions && story.preConditions.length) { %>
123
- **Điều kiện tiên quyết (Pre-conditions):**
125
+ **Pre-conditions:**
124
126
  <% (Array.isArray(story.preConditions) ? story.preConditions : [story.preConditions]).forEach(p => { %>
125
127
  - <%= p %>
126
128
  <% }); %>
127
129
  <% } %>
128
130
 
129
131
  <% if (story.scenarios && story.scenarios.length) { %>
130
- **Kịch bản chi tiết trên màn hình (Scenarios):**
132
+ **Scenarios:**
131
133
  <% story.scenarios.forEach(sc => { %>
132
134
  - **<%= sc.name || sc.title %>:** <%= sc.description || sc.steps || sc %>
133
135
  <% }); %>
134
136
  <% } %>
135
137
 
136
138
  <% if (story.acceptanceCriteria && story.acceptanceCriteria.length) { %>
137
- **Tiêu chí nghiệm thu (Acceptance Criteria):**
139
+ **Acceptance criteria:**
138
140
  <% (Array.isArray(story.acceptanceCriteria) ? story.acceptanceCriteria : [story.acceptanceCriteria]).forEach(ac => { %>
139
141
  - [ ] <%= ac %>
140
142
  <% }); %>
@@ -143,34 +145,34 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
143
145
  <% }); %>
144
146
  <% } else if (typeof spec.userStories === 'object') { %>
145
147
  <% if (spec.userStories.primary) { %>
146
- ### User Story Chính
147
- > **Là một** <%= spec.userStories.primary.asA || spec.userStories.primary.role %>,
148
- > **Tôi muốn** <%= spec.userStories.primary.iWant || spec.userStories.primary.action %>,
149
- > **Để** <%= spec.userStories.primary.soThat || spec.userStories.primary.benefit %>.
148
+ ### Primary user story
149
+ > **As a** <%= spec.userStories.primary.asA || spec.userStories.primary.role %>,
150
+ > **I want** <%= spec.userStories.primary.iWant || spec.userStories.primary.action %>,
151
+ > **so that** <%= spec.userStories.primary.soThat || spec.userStories.primary.benefit %>.
150
152
  <% } %>
151
153
 
152
154
  <% if (spec.userStories.contextAndHandoff) { %>
153
- ### Cách thức Truy cập & Chuyển giao Màn hình (Screen Access & Handoff)
155
+ ### Screen access & handoff
154
156
  <% if (spec.userStories.contextAndHandoff.screenAccess) { %>
155
157
  <% const sa = spec.userStories.contextAndHandoff.screenAccess; %>
156
- - **Loại truy cập (Access Type):** `<%= sa.accessType || 'N/A' %>`
158
+ - **Access type:** `<%= sa.accessType || 'N/A' %>`
157
159
  <% if (sa.accessType === 'directRoute' && sa.directRoute) { %>
158
- - **Đường dẫn URL (Path Router):** `<%= sa.directRoute.routePath || '' %>` <%= sa.directRoute.isPublic ? '(Công khai / Public)' : '(Cần đăng nhập / Authenticated)' %>
160
+ - **Path:** `<%= sa.directRoute.routePath || '' %>` <%= sa.directRoute.isPublic ? '(public)' : '(authenticated)' %>
159
161
  <% } else if (sa.accessType === 'sidebarMenu' && sa.sidebarMenu) { %>
160
- - **Menu Sidebar:** <%= sa.sidebarMenu.menuHierarchy ? sa.sidebarMenu.menuHierarchy.join(' > ') : '' %> > **<%= sa.sidebarMenu.menuLabel || '' %>**
162
+ - **Sidebar menu:** <%= sa.sidebarMenu.menuHierarchy ? sa.sidebarMenu.menuHierarchy.join(' > ') : '' %> > **<%= sa.sidebarMenu.menuLabel || '' %>**
161
163
  <% } else if (sa.accessType === 'contextualAction' && sa.contextualAction) { %>
162
- - **Kích hoạt từ (Triggered By):** <%= sa.contextualAction.triggerControl || '' %> từ màn hình `<%= sa.contextualAction.sourceScreen || '' %>` (Loại: `<%= sa.contextualAction.navigationType || 'pageRedirect' %>`)
164
+ - **Triggered by:** <%= sa.contextualAction.triggerControl || '' %> on screen `<%= sa.contextualAction.sourceScreen || '' %>` (navigation: `<%= sa.contextualAction.navigationType || 'pageRedirect' %>`)
163
165
  <% } %>
164
166
  <% } %>
165
- - **Màn hình nguồn (Source Screen):** `<%= spec.userStories.contextAndHandoff.sourceScreen || 'Direct' %>`
167
+ - **Source screen:** `<%= spec.userStories.contextAndHandoff.sourceScreen || 'Direct' %>`
166
168
  <% if (spec.userStories.contextAndHandoff.inputsReceived && spec.userStories.contextAndHandoff.inputsReceived.length) { %>
167
- - **Dữ liệu tiếp nhận (Inputs):** <%= Array.isArray(spec.userStories.contextAndHandoff.inputsReceived) ? spec.userStories.contextAndHandoff.inputsReceived.join(' · ') : spec.userStories.contextAndHandoff.inputsReceived %>
169
+ - **Inputs received:** <%= Array.isArray(spec.userStories.contextAndHandoff.inputsReceived) ? spec.userStories.contextAndHandoff.inputsReceived.join(' · ') : spec.userStories.contextAndHandoff.inputsReceived %>
168
170
  <% } %>
169
- - **Điều hướng tiếp theo (Next Screen):** `<%= spec.userStories.contextAndHandoff.nextScreenOnSuccess || 'None' %>`
171
+ - **Next screen on success:** `<%= spec.userStories.contextAndHandoff.nextScreenOnSuccess || 'None' %>`
170
172
  <% } %>
171
173
 
172
174
  <% if (spec.userStories.scenarios && spec.userStories.scenarios.length) { %>
173
- ### Kịch bản Chi tiết trên Màn hình (Scenarios)
175
+ ### Scenarios
174
176
  <% spec.userStories.scenarios.forEach(sc => { %>
175
177
  #### <%= sc.name || sc.title %>
176
178
  <% if (sc.steps && sc.steps.length) { %>
@@ -184,7 +186,7 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
184
186
  <% } %>
185
187
 
186
188
  <% if (spec.userStories.acceptanceCriteria && spec.userStories.acceptanceCriteria.length) { %>
187
- ### Tiêu chí Nghiệm thu (Acceptance Criteria)
189
+ ### Acceptance criteria
188
190
  <% spec.userStories.acceptanceCriteria.forEach(ac => { %>
189
191
  - [ ] <%= ac %>
190
192
  <% }); %>
@@ -193,6 +195,37 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
193
195
  <% } %>
194
196
  <% } %>
195
197
 
198
+ ## Data & integrations / API {#data-integrations}
199
+
200
+ Backend contract: `api/<seq>/01-backend-spec.yaml` on the same function leaf (not merged into bundle).
201
+
202
+ - **Data model (read):** [data-model.md](./data-model.md)
203
+ - **API (read):** [api.md](./api.md) — summary from `01-backend-spec.yaml` when present
204
+ - **Codegen SSOT:** `ir/design.yaml` + `01` + bundle `db` fields
205
+
206
+ <% if (hasEntities) { %>
207
+ ### Entities on this screen (summary)
208
+
209
+ ```yaml
210
+ <%= context.stringify(spec.entities).trim() %>
211
+ ```
212
+
213
+ <% } %>
214
+
215
+ <% if (hasUserFlows) { %>
216
+ ## User flows {#user-flows}
217
+
218
+ <%= spec.userFlows %>
219
+
220
+ <% } %>
221
+
222
+ <% if (hasNfr) { %>
223
+ ## Non-functional (NFR) {#nfr}
224
+
225
+ <%= spec.nfr %>
226
+
227
+ <% } %>
228
+
196
229
  <%
197
230
  const page = {
198
231
  nav: spec.nav || spec.design?.nav,
@@ -204,21 +237,25 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
204
237
  %>
205
238
 
206
239
  <% if (pageStateMatrix) { %>
207
- ## Ma Trận Trạng Thái Giao Diện & Phân Quyền (State & Permission Matrix) {#state--permission-matrix}
240
+ ## State & permissions matrix {#state--permission-matrix}
208
241
 
209
242
  <%- context.renderStateMatrixTable(pageStateMatrix) %>
210
243
 
211
244
  <% } %>
212
245
 
246
+ <% if (hasListColumns || hasListExtras || hasValidation || hasCustomWidget || hasActions || pageSections.length) { %>
247
+ > **Note (business reviewers):** Sections below are technical screen detail (dev/QA). Skip when reviewing scope and user stories only.
248
+
249
+ <% } %>
213
250
  <%
214
251
  const listColumnsMd = context.renderListColumnsTable && context.renderListColumnsTable(listUi);
215
252
  if (listColumnsMd) {
216
253
  %>
217
- ## Bảng cột danh sách (List columns) {#list-columns}
254
+ ## List columns {#list-columns}
218
255
 
219
256
  <%- listColumnsMd %>
220
257
 
221
- <p><strong>DB chi tiết / multi-table:</strong> <a href="./data-model.md">data-model.md</a> · SSOT ghi: <code>*.bundle.yaml</code> + <code>ir/design.yaml</code>.</p>
258
+ <p><strong>Multi-table DB detail:</strong> <a href="./data-model.md">data-model.md</a> · SSOT: <code>*.bundle.yaml</code> + <code>ir/design.yaml</code>.</p>
222
259
 
223
260
  <% } %>
224
261
 
@@ -227,16 +264,16 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
227
264
  const paginationMd = context.renderListPaginationBlock && context.renderListPaginationBlock(listUi);
228
265
  if (filtersMd || paginationMd) {
229
266
  %>
230
- ## Bộ lọc & phân trang (List filters & pagination) {#list-filters-pagination}
267
+ ## List filters & pagination {#list-filters-pagination}
231
268
 
232
269
  <% if (filtersMd) { %>
233
- ### Bộ lọc
270
+ ### Filters
234
271
 
235
272
  <%- filtersMd %>
236
273
 
237
274
  <% } %>
238
275
  <% if (paginationMd) { %>
239
- ### Phân trang
276
+ ### Pagination
240
277
 
241
278
  <%- paginationMd %>
242
279
 
@@ -248,7 +285,7 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
248
285
  const validationTableMd = context.renderValidationDictionaryTable(pageSections);
249
286
  if (validationTableMd) {
250
287
  %>
251
- ## Danh Mục Trường Nhập Liệu & Quy Tắc Kiểm Tra Hợp Lệ (Data Dictionary & Validation) {#data-dictionary--validation}
288
+ ## Data dictionary & validation {#data-dictionary--validation}
252
289
 
253
290
  <%- validationTableMd %>
254
291
 
@@ -268,19 +305,28 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
268
305
  const actionFlowsMd = context.renderActionFlowsDetailed(pageActions);
269
306
  if (actionFlowsMd) {
270
307
  %>
271
- ## Đặc Tả Quy Trình Hành Động (Action Flows) {#action-flows}
308
+ ## Action flows {#action-flows}
272
309
 
273
310
  <%- actionFlowsMd %>
274
311
 
275
312
  <% } else { %>
276
- ## Bố cục màn hình {#page-composition}
313
+ ## Page composition {#page-composition}
277
314
 
278
315
  <%- context.renderPageComposition(page, { businessOnly: true }) %>
279
316
  <% } %>
280
317
 
318
+ <% if (qaOpen) { %>
319
+ ## Appendix — open questions {#appendix-open-questions}
320
+
321
+ Câu hỏi treo (SSOT): [qa/index.md](../../qa/index.md)
322
+
323
+ **ID trên leaf:** `<%= spec['Q&A'] %>`
324
+
325
+ <% } %>
326
+
281
327
  <%
282
328
  const skipKeys = new Set([
283
- 'id', 'page-id', 'title', 'summary', 'successMetrics', 'nonGoals', 'userStories', 'acceptance', 'ui', 'design', 'legacy', 'review', 'template',
329
+ 'id', 'page-id', 'title', 'summary', 'successMetrics', 'scopeIn', 'nonGoals', 'userFlows', 'nfr', 'userStories', 'acceptance', 'ui', 'design', 'legacy', 'review', 'template', 'entities', 'relationships',
284
330
  'nav', 'sections', 'zones', 'behavior', 'actions', 'stateMatrix', 'grillStatus', 'specOrigin',
285
331
  'status', 'owner', 'schema', 'qa', 'Q&A', 'pendingTechDebt', 'i18n', 'gen', '_meta'
286
332
  ]);
@@ -6,7 +6,7 @@ title: Feature title
6
6
  status: draft
7
7
  owner: portal-team
8
8
 
9
- # 1. BẢN TÓM TẮT TỔNG QUAN (ARC42 BUSINESS SUMMARY)
9
+ # 1. TỔNG QUAN NGHIỆP VỤ (PRD — mặt đọc BA)
10
10
  summary: |
11
11
  - mục tiêu nghiệp vụ (business_goals): [Nêu rõ vấn đề đang giải quyết và giá trị kinh tế/nghiệp vụ mang lại. Viết sâu sắc để Stakeholder hiểu rõ vì sao phải làm.]
12
12
  - các bên liên quan (stakeholders): [Ai dùng, ai hưởng lợi, ai quản lý?]
@@ -17,13 +17,22 @@ summary: |
17
17
  - output: [Kết quả đầu ra]
18
18
  - cách giải quyết (solution): [Tuỳ chọn. Ghi kỹ thuật phức tạp nếu có.]
19
19
 
20
- successMetrics: |
21
- - [Chỉ số đo được khi màn/feature đạt mục tiêu — VD: thời gian hoàn tất thao tác, tỷ lệ lỗi validation]
22
- - [Bỏ bullet nếu chưa có số — ghi qualitative metric]
20
+ scopeIn: |
21
+ - [Trong phạm vi màn/phase này — bullet rõ việc màn làm]
22
+ - [VD: xem danh sách, lọc, mở chi tiết từ dòng bảng]
23
23
 
24
24
  nonGoals: |
25
- - [Phạm vi KHÔNG làm trên màn/phase này — tránh scope creep]
26
- - [VD: không xử lý export Excel tại màn list — defer QA/debt]
25
+ - [Ngoài phạm vi — tránh scope creep]
26
+ - [VD: không export Excel tại màn list — defer QA/debt]
27
+
28
+ userFlows: |
29
+ - [Luồng người dùng — link FLOW-* trong architecture/03-user-flows hoặc common/user-flows]
30
+
31
+ nfr: |
32
+ - **Hiệu năng:** [VD: danh sách < 2s với 10k bản ghi — phân trang server]
33
+ - **Bảo mật:** [VD: RBAC theo role; audit log thao tác nhạy cảm]
34
+
35
+ # Rủi ro: KHÔNG khai báo trên bundle — chỉ architecture/11-risks/risk-register.md
27
36
 
28
37
  userStories:
29
38
  primary:
@@ -1,22 +1,22 @@
1
1
 
2
2
  # Feature title
3
3
 
4
- ## Mục lục (Contents)
5
-
6
- 1. [Tổng quan](#overview)
7
- 2. [Chỉ số thành công](#success-metrics)
8
- 3. [Phạm vi không làm](#non-goals)
9
- 4. [User stories và hành trình màn hình](#user-stories--screen-journey)
10
- 5. [Ma trận trạng thái và phân quyền](#state--permission-matrix)
11
- 6. [Bảng cột danh sách](#list-columns)
12
- 7. [Từ điển dữ liệu và validation](#data-dictionary--validation)
13
- 8. [Widget tùy biến](#custom-widgets)
14
- 9. [Luồng hành động](#action-flows)
4
+ ## Table of contents
5
+
6
+ 1. [Overview](#overview)
7
+ 2. [Success metrics](#success-metrics)
8
+ 3. [Out of scope](#non-goals)
9
+ 4. [User stories & requirements](#user-stories--screen-journey)
10
+ 5. [State & permissions matrix](#state--permission-matrix)
11
+ 6. [List columns](#list-columns)
12
+ 7. [Data dictionary & validation](#data-dictionary--validation)
13
+ 8. [Custom widgets](#custom-widgets)
14
+ 9. [Action flows](#action-flows)
15
15
  - [API SSOT](#api-ssot)
16
16
 
17
17
  ---
18
18
 
19
- ## Tổng quan {#overview}
19
+ ## Overview {#overview}
20
20
 
21
21
  | | |
22
22
  | --- | --- |
@@ -44,7 +44,7 @@
44
44
 
45
45
 
46
46
 
47
- ## Chỉ số thành công {#success-metrics}
47
+ ## Success metrics {#success-metrics}
48
48
 
49
49
  - [Chỉ số đo được khi màn/feature đạt mục tiêu — VD: thời gian hoàn tất thao tác, tỷ lệ lỗi validation]
50
50
  - [Bỏ bullet nếu chưa có số — ghi qualitative metric]
@@ -53,7 +53,7 @@
53
53
 
54
54
 
55
55
 
56
- ## Phạm vi không làm {#non-goals}
56
+ ## Out of scope {#scope-out}
57
57
 
58
58
  - [Phạm vi KHÔNG làm trên màn/phase này — tránh scope creep]
59
59
  - [VD: không xử lý export Excel tại màn list — defer QA/debt]
@@ -68,14 +68,14 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
68
68
  - **Data model (review, multi-table):** [data-model.md](./data-model.md) — bảng/cột tách khỏi spec BA; SSOT codegen = `01` + `ir/design.yaml` `db`.
69
69
 
70
70
 
71
- ## User Stories & Screen Journey {#user-stories--screen-journey}
71
+ ## User stories & requirements {#user-stories--screen-journey}
72
72
 
73
73
 
74
74
 
75
- ### User Story Chính
76
- > **Là một** [Persona / Role - vd: Nhân viên Vận hành / Khách hàng / Quản trị viên],
77
- > **Tôi muốn** [Hành động chính trên màn hình: xem danh sách, lọc, tạo mới, cập nhật, phê duyệt...],
78
- > **Để** [Mục đích kinh doanh và giá trị thực tế đạt được].
75
+ ### Primary user story
76
+ > **As a** [persona / role],
77
+ > **I want** [primary action on this screen],
78
+ > **so that** [business outcome].
79
79
 
80
80
 
81
81
 
@@ -192,7 +192,7 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
192
192
 
193
193
 
194
194
 
195
- ## Ma Trận Trạng Thái Giao Diện & Phân Quyền (State & Permission Matrix) {#state--permission-matrix}
195
+ ## State & permissions matrix {#state--permission-matrix}
196
196
 
197
197
  | Trạng Thái Bản Ghi (Record Status) | Trạng Thái Trường Form (Fields State) | Nút Hành Động Khả Dụng (Visible Buttons) | Ghi Chú Phân Quyền RBAC (Role Overrides) |
198
198
  | --- | --- | --- | --- |
@@ -204,7 +204,7 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
204
204
 
205
205
 
206
206
 
207
- ## Bảng cột danh sách (List columns) {#list-columns}
207
+ ## List columns {#list-columns}
208
208
 
209
209
  | Nhãn cột | Key | Ý nghĩa nghiệp vụ | Mục đích UI | Widget / render | Sort | DB (schema.field) |
210
210
  | --- | --- | --- | --- | --- | --- | --- |
@@ -215,7 +215,7 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
215
215
 
216
216
 
217
217
 
218
- ## Danh Mục Trường Nhập Liệu & Quy Tắc Kiểm Tra Hợp Lệ (Data Dictionary & Validation) {#data-dictionary--validation}
218
+ ## Data dictionary & validation {#data-dictionary--validation}
219
219
 
220
220
  | Tên Trường (Label) | Mã Kỹ Thuật (Key) | Kiểu (Type) | Bắt Buộc? | Ràng Buộc & Quy Tắc Hợp Lệ (Rules) | Thông Báo Lỗi Inline (Messages) |
221
221
  | --- | --- | --- | --- | --- | --- |
@@ -229,7 +229,7 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
229
229
 
230
230
  <div id="custom-widgets"></div>
231
231
 
232
- ## Đặc Tả Khối Giao Diện Tùy Biến (Custom UI Blocks)
232
+ ## Custom widgets {#custom-widgets}
233
233
 
234
234
  ### Khối Tùy Biến (`custom`)
235
235
  - **Mục đích thao tác:** Theo dõi tiến độ xử lý và xem chi tiết phản hồi từng bước duyệt
@@ -238,7 +238,7 @@ Hợp đồng backend: `api/<seq>/01-backend-spec.yaml` trong cùng function lea
238
238
 
239
239
 
240
240
 
241
- ## Đặc Tả Quy Trình Hành Động (Action Flows) {#action-flows}
241
+ ## Action flows {#action-flows}
242
242
 
243
243
  ### Lưu & Xác Nhận
244
244
  - **Mục đích thao tác:** Thẩm định toàn bộ form và gửi dữ liệu lên máy chủ
@@ -6,7 +6,7 @@
6
6
 
7
7
  ---
8
8
 
9
- ## Đọc trước khi viết (global)
9
+ ## Read before writing (global)
10
10
 
11
11
  | Ai | Đọc gì trên VitePress / docs hub | Khi nào mở YAML `01` |
12
12
  |----|-----------------------------------|----------------------|
@@ -19,7 +19,7 @@ Sau `flowgrid split` + `flowgrid render`: sidebar leaf **Spec · W-*** · **Data
19
19
 
20
20
  ---
21
21
 
22
- ## Bộ trio trên function leaf
22
+ ## Function leaf trio
23
23
 
24
24
  ```text
25
25
  surfaces/<surface>/CMP-*/<NN…>/<slug>/
@@ -42,7 +42,7 @@ surfaces/<surface>/CMP-*/<NN…>/<slug>/
42
42
 
43
43
  ---
44
44
 
45
- ## Cấu trúc `01` (tóm tắt field)
45
+ ## `01` structure (field summary)
46
46
 
47
47
  Mẫu đầy đủ: [`backend-api.yaml`](./backend-api.yaml) (sau `flowgrid init` → `.flowgrid/templates/backend-api.yaml`).
48
48
 
@@ -108,7 +108,7 @@ Skill: `/openapi` · Redoc/Swagger UI: `openapi_build_ui` (tùy hub).
108
108
 
109
109
  ---
110
110
 
111
- ## YAML an toàn
111
+ ## Safe YAML
112
112
 
113
113
  - Chuỗi có `:` → bọc `"..."`.
114
114
  - Chạy `flowgrid api:check` trước handoff.
@@ -116,7 +116,7 @@ Skill: `/openapi` · Redoc/Swagger UI: `openapi_build_ui` (tùy hub).
116
116
 
117
117
  ---
118
118
 
119
- ## Liên kết bundle FE
119
+ ## FE bundle link
120
120
 
121
121
  Trên bundle chỉ khai báo **hành vi UI** và `apiRefs`; chi tiết endpoint nằm trên `01`:
122
122
 
@@ -1,16 +1,17 @@
1
- memberLocale: vi
1
+ # Overwritten on scaffold — English headings; prose in TC YAML uses contentLocale.
2
+ structureLocale: en
3
+ contentLocale: vi
2
4
  headings:
3
- vi:
4
- home: Trang chủ
5
- cases: Testcase
6
- scenarios: Scenario
7
- plans: Kế hoạch kiểm thử
8
- preconditions: Điều kiện tiên quyết
9
- steps: Các bước thực hiện
10
- expected: Kết quả mong đợi
11
- traceability: Liên kết docs (traceability)
12
- testMatrix: Ma trận kiểm thử
13
- crossRefDocs: Đối chiếu docs hub
14
- technical: Chi tiết kỹ thuật
15
- testData: Dữ liệu kiểm thử
16
- coverage: Phạm vi coverage
5
+ home: Home
6
+ cases: Test cases
7
+ scenarios: Scenarios
8
+ plans: Test plans
9
+ preconditions: Preconditions
10
+ steps: Steps
11
+ expected: Expected results
12
+ traceability: Traceability (docs SSOT)
13
+ testMatrix: Test matrix (equivalence)
14
+ crossRefDocs: Docs hub cross-reference
15
+ technical: Technical details
16
+ testData: Test data
17
+ coverage: Coverage
@@ -1,9 +1,9 @@
1
- # Testcase plan — hướng dẫn hub
1
+ # Testcase plan — hub guide
2
2
 
3
- Bản đầy đủ (toolkit): sau `flowgrid init` copy từ package `harness/tests/templates/tpl-testcase-plan.md` hoặc xem repo FlowGrid `templates/tests-skeleton/tpl-testcase-plan.md` (sync với harness).
3
+ Full toolkit copy: `harness/tests/templates/tpl-testcase-plan.md` (synced with this skeleton).
4
4
 
5
- - SSOT ghi: `cases/**/TC-*.yaml`
6
- - SSOT đọc team: `cases:render` → `TC-*.md` trên VitePress (`flowgrid dev` port 5174)
7
- - Đối chiếu nghiệp vụ: docs hub `FLOWGRID_DOCS_ROOT` — bundle + `ir/generated/spec.md`
5
+ - **SSOT write:** `cases/**/TC-*.yaml` — field names **English**; narrative in `contentLocale` (`catalog/locale.yaml`).
6
+ - **SSOT read:** `cases:render` → `TC-*.md` on VitePress (`flowgrid dev`, port 5174).
7
+ - **Business alignment:** docs hub — bundle + `ir/generated/spec.md`. Product copy values live in bundle `i18n:`, not in TC prose.
8
8
 
9
- Mẫu vàng: `TC.example.yaml` (init / harness templates).
9
+ Golden example: `TC.example.yaml` (init / harness templates).