@shanyucoder/flowgrid 0.1.5 → 0.1.9

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 (169) hide show
  1. package/README.md +1 -1
  2. package/adapters/laravel/registries/codegen.registry.json +9 -9
  3. package/bin/flowgrid.mjs +326 -115
  4. package/bin/lib/agent-mcp.mjs +4 -0
  5. package/bin/lib/agent-profiles.mjs +30 -6
  6. package/bin/lib/audit-run.mjs +1 -1
  7. package/bin/lib/cli-update.mjs +48 -8
  8. package/bin/lib/doctor.mjs +90 -3
  9. package/bin/lib/harness-overlay.mjs +12 -5
  10. package/bin/lib/harness-sync.mjs +17 -3
  11. package/bin/lib/init-adapters.mjs +75 -0
  12. package/bin/lib/init-scaffold.mjs +9 -0
  13. package/bin/lib/inject-consumer-scripts.mjs +115 -0
  14. package/bin/lib/merge-stack-config.mjs +89 -0
  15. package/bin/lib/project-gitignore.mjs +1 -0
  16. package/bin/lib/repo-maps-align.mjs +203 -0
  17. package/dist/graph/config/load-config.js +7 -2
  18. package/dist/graph/config/load-config.js.map +1 -1
  19. package/dist/graph/mcp/tools.js +1 -1
  20. package/dist/graph/mcp/tools.js.map +1 -1
  21. package/dist/graph/registry/load-registries.d.ts +1 -0
  22. package/dist/graph/registry/load-registries.js +14 -1
  23. package/dist/graph/registry/load-registries.js.map +1 -1
  24. package/engines/cases/render-cases.mjs +67 -5
  25. package/engines/docs/lib/qa-item.mjs +91 -0
  26. package/engines/docs/lib/render-api-summary-markdown.mjs +28 -0
  27. package/engines/docs/lib/render-bundle-markdown.mjs +77 -2
  28. package/engines/docs/lib/render-data-model-markdown.mjs +171 -0
  29. package/engines/docs/lib/render-design-tables.mjs +89 -7
  30. package/engines/docs/lib/render-qa-list.mjs +123 -25
  31. package/engines/docs/lib/render-template.mjs +6 -0
  32. package/engines/docs/render-docs.mjs +6 -2
  33. package/engines/docs/vitepress/config.ts +7 -7
  34. package/engines/docs/vitepress/surfaces-nav.mjs +45 -14
  35. package/engines/openapi/check-backend-spec.mjs +2 -2
  36. package/engines/openapi/lib/markdown-table.mjs +8 -0
  37. package/engines/openapi/lib/render-backend-spec-markdown.mjs +78 -0
  38. package/engines/registry-sync/be-capabilities-sync.mjs +118 -0
  39. package/engines/registry-sync/fe-design-sync.mjs +258 -0
  40. package/engines/registry-sync/run-registry-sync.mjs +107 -0
  41. package/engines/shared/e2e-output-layout.mjs +68 -0
  42. package/engines/shared/flowgrid-e2e-root.mjs +19 -0
  43. package/engines/shared/resolve-flowgrid-context.mjs +176 -0
  44. package/engines/spec/lib/audit-api-gaps.mjs +1 -1
  45. package/engines/spec/lib/audit-bundle-gaps.mjs +92 -3
  46. package/engines/spec/lib/audit-db-tables.mjs +529 -0
  47. package/engines/spec/lib/audit-e2e-coverage.mjs +88 -30
  48. package/engines/spec/lib/bundle-schema.mjs +4 -1
  49. package/engines/spec/lib/open-qa.mjs +71 -21
  50. package/engines/spec/split-bundle.mjs +11 -1
  51. package/engines/testcase/runners/generate-api.mjs +23 -23
  52. package/engines/testcase/runners/generate.mjs +19 -17
  53. package/engines/testcase/runners/lib/bootstrap-context.mjs +44 -0
  54. package/engines/testcase/runners/lib/write-files.mjs +37 -9
  55. package/harness/agents/antigravity/rules/antigravity-mcp.mdc +12 -0
  56. package/harness/agents/gemini/rules/gemini-mcp.mdc +11 -0
  57. package/harness/agents/gemini_antigravity/rules/antigravity-mcp.mdc +8 -6
  58. package/harness/be/skills/{grill-api → audit-api}/SKILL.md +8 -7
  59. package/harness/common/extracts/artifact-graph.md +2 -2
  60. package/harness/common/extracts/artifactgraph-phase-hooks.md +2 -2
  61. package/harness/common/extracts/docs-mark-detect.md +2 -2
  62. package/harness/common/extracts/entity-relationship.md +23 -0
  63. package/harness/common/rules/flowgrid-ux-common.mdc +3 -3
  64. package/harness/common/skills/configure-repo-maps/SKILL.md +4 -2
  65. package/harness/docs/extracts/agent-execution-protocol.md +3 -3
  66. package/harness/docs/extracts/api-codegen-readiness.md +34 -0
  67. package/harness/docs/extracts/api-codegen-tags.md +30 -0
  68. package/harness/docs/extracts/api-contract.md +43 -0
  69. package/harness/docs/extracts/api-spec-sync.md +35 -0
  70. package/harness/docs/extracts/artifactgraph-hooks-docs.md +1 -1
  71. package/harness/docs/extracts/call-external.md +16 -0
  72. package/harness/docs/extracts/common-scope.md +9 -10
  73. package/harness/docs/extracts/db-audit-wizard.md +45 -0
  74. package/harness/docs/extracts/derived-data.md +18 -0
  75. package/harness/docs/extracts/design-leaf-signoff.md +16 -0
  76. package/harness/docs/extracts/extract-registry.docs.json +11 -2
  77. package/harness/docs/extracts/qa-inbox.md +19 -10
  78. package/harness/docs/extracts/qa-team.md +32 -0
  79. package/harness/docs/extracts/spec-core.md +7 -3
  80. package/harness/docs/extracts/spec-evolution.md +21 -0
  81. package/harness/docs/extracts/spec-prd-lite.md +19 -0
  82. package/harness/docs/extracts/spec-requirement.md +6 -2
  83. package/harness/docs/extracts/spec-ssot-prep.md +25 -0
  84. package/harness/docs/extracts/tpl-module.md +12 -0
  85. package/harness/docs/extracts/verify-gate.md +33 -0
  86. package/harness/docs/extracts/wire-spec-feedback.md +31 -0
  87. package/harness/docs/rules/agent-compliance.mdc +1 -1
  88. package/harness/docs/rules/team-flow-spec.mdc +3 -4
  89. package/harness/docs/schemas/flowgrid-docs/qa-item.schema.json +65 -0
  90. package/harness/docs/skills/adopt/SKILL.md +2 -0
  91. package/harness/docs/skills/api/SKILL.md +4 -5
  92. package/harness/docs/skills/api-spec/SKILL.md +19 -6
  93. package/harness/docs/skills/api-update/SKILL.md +4 -4
  94. package/harness/docs/skills/architecture/SKILL.md +1 -1
  95. package/harness/docs/skills/business-process/SKILL.md +2 -0
  96. package/harness/docs/skills/common/SKILL.md +2 -2
  97. package/harness/docs/skills/common-spec/SKILL.md +10 -47
  98. package/harness/docs/skills/db-erd/SKILL.md +26 -0
  99. package/harness/docs/skills/grill/SKILL.md +28 -22
  100. package/harness/docs/skills/grill-api/SKILL.md +4 -6
  101. package/harness/docs/skills/grill-api-spec/SKILL.md +44 -24
  102. package/harness/docs/skills/grill-bqa/SKILL.md +28 -13
  103. package/harness/docs/skills/grill-common-spec/SKILL.md +10 -35
  104. package/harness/docs/skills/grill-dev/SKILL.md +8 -7
  105. package/harness/docs/skills/grill-docs/SKILL.md +11 -4
  106. package/harness/docs/skills/module/SKILL.md +3 -1
  107. package/harness/docs/skills/openapi/SKILL.md +2 -1
  108. package/harness/docs/skills/overview/SKILL.md +7 -1
  109. package/harness/docs/skills/qa-resolve/SKILL.md +13 -12
  110. package/harness/docs/skills/qa-review/SKILL.md +45 -0
  111. package/harness/docs/skills/spec/SKILL.md +43 -10
  112. package/harness/docs/skills/update-spec/SKILL.md +5 -2
  113. package/harness/fe/extracts/wire-audit-loop.md +72 -0
  114. package/harness/fe/extracts/wire-phase.md +45 -0
  115. package/harness/fe/rules/platform-design-vocabulary.mdc +1 -1
  116. package/harness/fe/rules/team-flow-prototype.mdc +8 -4
  117. package/harness/fe/skills/gen-common/SKILL.md +11 -84
  118. package/harness/fe/skills/grill-prototype/SKILL.md +51 -25
  119. package/harness/fe/skills/grill-test/SKILL.md +78 -20
  120. package/harness/fe/skills/grill-wire/SKILL.md +81 -0
  121. package/harness/fe/skills/prototype/SKILL.md +3 -2
  122. package/harness/fe/skills/wire/SKILL.md +8 -3
  123. package/harness/shared/AGENTS.md +3 -3
  124. package/harness/shared/SSOT_AGENT_PROTOCOL.md +3 -3
  125. package/harness/tests/extracts/grill-api-hook.md +69 -0
  126. package/harness/tests/extracts/grill-scenario-flow.md +39 -0
  127. package/harness/tests/extracts/grill-screen-tc.md +40 -0
  128. package/harness/tests/extracts/testcase-gen-cli.md +57 -0
  129. package/harness/tests/extracts/testcase-plan.md +29 -0
  130. package/harness/tests/extracts/tests-verify-gate.md +29 -0
  131. package/harness/tests/extracts/wire-test-handoff.md +37 -0
  132. package/harness/tests/skills/grill-testcase/SKILL.md +1 -0
  133. package/harness/tests/skills/test-api/SKILL.md +14 -6
  134. package/harness/tests/skills/testcase/SKILL.md +3 -1
  135. package/harness/tests/templates/TC.example-api.yaml +7 -1
  136. package/harness/tests/templates/TC.example.yaml +4 -1
  137. package/harness/tests/templates/tpl-testcase-plan.md +75 -0
  138. package/package.json +1 -1
  139. package/stacks/fastapi.json +1 -0
  140. package/stacks/laravel.json +1 -0
  141. package/stacks/nestjs.json +72 -0
  142. package/stacks/nextjs-nest.json +1 -0
  143. package/stacks/nuxt4-nest.json +1 -0
  144. package/templates/project-skeleton/architecture/03-business-process/FLOW-template.md +2 -0
  145. package/templates/project-skeleton/overview/index.md +68 -2
  146. package/templates/project-skeleton/overview/operational-areas/_template.md +37 -0
  147. package/templates/project-skeleton/qa/README.md +4 -8
  148. package/templates/project-skeleton/surfaces/common/data-model/index.md +10 -2
  149. package/templates/schemas/qa-item.schema.json +98 -0
  150. package/templates/shared/api-03-mock.stub.yaml +14 -0
  151. package/templates/shared/backend-api.bundle.yaml +3 -0
  152. package/templates/shared/backend-api.yaml +4 -0
  153. package/templates/shared/be-capabilities.registry.base.json +8 -0
  154. package/templates/shared/bundle-authoring.md +44 -7
  155. package/templates/shared/default-layout.ejs +131 -18
  156. package/templates/shared/design-spec.yaml +2 -3
  157. package/templates/shared/design.registry.base.json +38 -0
  158. package/templates/shared/feature.bundle.yaml +16 -9
  159. package/templates/shared/ir/generated/spec.md +281 -0
  160. package/templates/shared/ir-spec.yaml +1 -1
  161. package/templates/shared/qa-authoring.md +78 -0
  162. package/templates/shared/qa-item.yaml +35 -14
  163. package/templates/shared/tpl-api-contract.md +133 -0
  164. package/templates/shared/tpl-screen-data-model.md +76 -0
  165. package/templates/tests-skeleton/cases/README.md +4 -0
  166. package/templates/tests-skeleton/catalog/locale.yaml +9 -0
  167. package/templates/tests-skeleton/tpl-testcase-plan.md +9 -0
  168. package/harness/docs/skills/api-integration/SKILL.md +0 -110
  169. package/harness/docs/skills/grill-integration-spec/SKILL.md +0 -51
@@ -8,8 +8,10 @@ Hub: `docs/templates/feature.bundle.yaml` · split: `pnpm spec:split`
8
8
  |-----|---------|
9
9
  | `page-id` | Screen identity (`cmp-adm-000-01`). Split copies this onto `ir/spec.yaml` as `page-id` (not `id`, so it does not collide with requirement/section ids). Legacy bundles may still use `id`. |
10
10
  | `summary` | Phải trình bày dạng bullet. Bắt buộc có các tiêu đề (chuẩn Arc42 business): **mục tiêu nghiệp vụ** (business_goals), **các bên liên quan** (stakeholders), **kịch bản người dùng** (user_journey), **bối cảnh** (description, input liên kết cross-page/module, output) và **cách giải quyết** (tùy chọn). Mục đích để 100% Non-tech Stakeholder hiểu và duyệt. |
11
+ | `successMetrics` | **Tùy chọn** khi có thông tin — bullet (string multiline hoặc list). Chỉ số đo được / qualitative metric cấp **màn/feature**. Product-level → `overview/`. Split → `ir/spec.yaml`; render → section riêng trong `ir/generated/spec.md`. |
12
+ | `nonGoals` | **Tùy chọn** — phạm vi **không làm** trên màn/phase (PRD non-goals). Tránh nhét vào `requirements` prose. |
11
13
  | `userStories` | **Khối User Stories chuyên sâu cho màn hình:** `primary`, `contextAndHandoff` (+ `screenAccess`), `scenarios` (5 kịch bản chuẩn + **scenario thứ 6 “Affordances UX”** khi màn có delete/filter/breadcrumb/disabled/import — xem `feature.bundle.yaml`), `acceptanceCriteria` (kèm checkbox UX khi áp dụng). **Split:** `pnpm spec:split` copy nguyên khối sang `ir/spec.yaml` (business prose); **không** tự sinh từ `design` — Agent phải cập nhật `userStories` khi bổ sung DSL/`#needs-component`/audit `UX_*`. Render → `## User Stories & Screen Journey` trong Markdown. |
12
- | `spec` | Design v1 — actors, requirements, `ui.routes`, **`ui.list` / `ui.form` / `ui.detail`**, `acceptance`. **Không** author `spec.api` — API SSOT là `api/<seq>/01-backend-spec.yaml`. |
14
+ | `spec` | Design v1 — **`entities`**, **`relationships`**, actors, requirements, `ui.routes`, **`ui.list` / `ui.form` / `ui.detail`**, `acceptance`. **Không** author `spec.api` — API SSOT là `api/<seq>/01-backend-spec.yaml`. |
13
15
  | `gen` | **Bắt buộc trước flowgrid gen:** `codegen.profile` (`auth` login/forgot/reset; `change-password`; `public`; `not-found`/`error`; `list`/`create`/`admin-crud`) + entity/module, `tags`, derived `ui.*`. `/grill-dev` ghi. Endpoint `action` ghi trên **01**, không trên bundle. |
14
16
  | `legacy` | Legacy facts + evidence pointers |
15
17
  | `design` | Nested **`sections[]`** (card/container/form + `meaning` + `purpose` + `visual` + `interaction` + `validation` + `messages` + `states` + `bind`/`db`) · **`nav`** (`screenAccess` & `sidebar.hierarchy`) · `zones[]` fallback · `behavior` · **`actions[]`** (onSuccess, onSpecificError, backgroundTrigger) |
@@ -36,6 +38,39 @@ Hub: `docs/templates/feature.bundle.yaml` · split: `pnpm spec:split`
36
38
  - Missing hard facts → `#missing_info` on that field. Do not wait for grill to invent inventory.
37
39
  - `/grill-bqa`, `/grill-dev`, `/grill-docs` only re-check, fill gaps, or fix conflicts.
38
40
 
41
+ ## Data model — Phase 0 ERD vs screen detail {#data-model--phase-0-erd-vs-screen-detail}
42
+
43
+ **Phase 0 (không viết trong `/spec` session đầu nếu chưa có):** `/db-erd` → `<LCA>/common/db-erd.md` (Mermaid ER, ownership). Workflow: [architecture-data.md](../../docs/workflows/architecture-data.md).
44
+
45
+ **Phase 1 — trên leaf bundle (bắt buộc khi field persisted):**
46
+
47
+ | Chỗ ghi | Nội dung |
48
+ |---------|----------|
49
+ | `spec.entities[]` | Entity **của màn** (name, mô tả ngắn, link entity ER) — mirror subset ERD, không copy full diagram |
50
+ | `spec.relationships[]` | Quan hệ màn này đọc/ghi (optional) |
51
+ | `design.sections[].items[].db` | `schema` (table hoặc tên khớp ERD), `field` (column), `enumMapping` cho select/chip |
52
+ | `design.sections[].items[].bind` | `field` — tên payload form/API (có thể camelCase khác column DB) |
53
+ | `spec.ui.list.columns[]` | `key` khớp `bind.field` hoặc cột DB; cột chỉ tính toán → `#derived-data`, không `db` |
54
+ | `spec.ui.form` / `detail` fields | Cùng quy tắc `bind` + `db` trên từng control |
55
+
56
+ **Quy tắc:**
57
+
58
+ - Có `validation` trên control persisted → **phải** có `db.field` (hoặc QA defer) trừ khi field pure UI (`#derived-data`).
59
+ - `meaning` = nghiệp vụ; `db` = vật lý — không gộp một dòng chung trong `requirements`.
60
+ - Split: `entities` + `db` → **`ir/design.yaml`**; BA MD generated **không** render `db` (dev đọc IR / `01`).
61
+ - Grill-dev: `01.modules[].entities[].fields` phải **khớp** tập `db.field` màn (review tay; `#update:*` khi đổi).
62
+
63
+ **Không:** dump SQL migration; thay `db-erd.md` bằng `entities: []` rỗng khi form có >3 field persisted.
64
+
65
+ **Review (multi-table):**
66
+
67
+ | Artifact | Ai đọc |
68
+ |----------|--------|
69
+ | `design.dataModel` | BA/lead — vai trò từng bảng (`tables[].roleOnScreen`, `summary`) — mẫu [tpl-screen-data-model.md](./tpl-screen-data-model.md) |
70
+ | `ir/generated/data-model.md` | Sau `flowgrid split` — bảng/cột gom theo `schema`; link từ `spec.md` |
71
+ | `api/.../01-backend-spec.yaml` | BE codegen — `modules[].entities[]` + `fields[]` khớp `db` trên design |
72
+ | `flowgrid audit spec` | `confirms[]` `CONFIRM_DB_*` → AskQuestion (extract `db-audit-wizard.md`); không chặn split |
73
+
39
74
  ## spec (design v1) — có
40
75
 
41
76
  - `actors`, `entities`, `relationships`
@@ -76,8 +111,9 @@ Authoring (`/spec`, grill-*) still **writes** `*.bundle.yaml`, then split.
76
111
  | `*.bundle.yaml` | **Ghi** `/spec`, grill-*; **đọc cả file** `/testcase`, `/grill-testcase` | Đầy đủ spec+gen+design+`userStories`; tests hub read-only |
77
112
  | `ir/design.yaml` | **Đọc cả file** — grill-*, FE `/prototype`, codegen | Tech sau split; `api` chiếu từ 01. |
78
113
  | `ir/spec.yaml` | VitePress + stakeholder | Business page + requirements/acceptance. Không id/tag/bind. `"Q&A"`. Không stub `legacy` rỗng. |
79
- | `…/api/<seq>/01-backend-spec.yaml` | BE `/api`, `openapi:gen`, **author API** | Tech BE — SSOT duy nhất cho endpoint |
80
- | `<slug>.md` | Người (BA/QA) | Render từ **`ir/spec.yaml`** (chưa split thì không có trang) |
114
+ | `…/api/<seq>/01-backend-spec.yaml` | BE `/api`, `openapi_gen`, **author API** | Tech BE — SSOT duy nhất cho endpoint |
115
+ | `ir/generated/data-model.md` | BA/lead/BE review | Bảng/cột theo `schema` (multi-table); sinh từ `ir/design.yaml` |
116
+ | `ir/generated/spec.md` · `ir/generated/api.md` | BA/QA/Dev review (VitePress) | Render từ `ir/spec.yaml` + `01`; hướng dẫn API: [tpl-api-contract.md](./tpl-api-contract.md) |
81
117
 
82
118
  Không còn `ir/legacy.yaml`. `legacy:` trên `ir/spec.yaml` chỉ khi có evidence thật.
83
119
 
@@ -267,13 +303,14 @@ actions:
267
303
 
268
304
  YAML only per schema. No explanation. No markdown.
269
305
 
270
- ## Questions: AskQuestion wizard or hub `qa/open/`
306
+ ## Questions: AskQuestion wizard or hub `qa/`
271
307
 
272
308
  Do **not** write `openQuestions` anywhere. Schema/render không còn field này.
273
309
 
274
310
  - Member trả lời **ngay:** AskQuestion (options + Recommended + **Other**) → **STOP** → ghi field thật.
275
- - Member chọn **Other** mà **chưa quyết:** `.cursor/extracts/qa-inbox.md` → `qa/open/QA-<bundle.id>-NNNN.yaml`. Đóng sau bằng **`/qa-resolve <id>` + giải pháp**.
276
- - `grillStatus` có thể `done` khi vẫn còn file QA.
311
+ - Member chọn **Log as Tech Debt:** copy `.flowgrid/templates/qa-item.yaml` — quy tắc field/timeline: **`qa-authoring.md`** · extract `qa-inbox.md`.
312
+ - Đóng sau bằng **`/qa-resolve <id>` + giải pháp** (append `kind: answer`, `status: closed`).
313
+ - `grillStatus` có thể `done` khi vẫn còn file QA `status: open`.
277
314
 
278
315
  ## YAML Syntax & Escaping Rules
279
316
 
@@ -282,7 +319,7 @@ Do **not** write `openQuestions` anywhere. Schema/render không còn field này.
282
319
 
283
320
  ## ir/spec.yaml vs ir/design.yaml
284
321
 
285
- `pnpm spec:split` ghi hai file. FlowGrid bộ docs grill **không** đọc `ir/*` khi author — chỉ bundle. Split ghi `"Q&A":` trên **`ir/spec.yaml`** (id `QA-<bundle.id>-NNNN` cách nhau bởi `, `) từ `qa/open/` — không nhét list vào bundle.
322
+ `pnpm spec:split` ghi hai file. FlowGrid bộ docs grill **không** đọc `ir/*` khi author — chỉ bundle. Split ghi `"Q&A":` trên **`ir/spec.yaml`** (id `QA-<bundle.id>-NNNN` cách nhau bởi `, `) từ `qa/` — không nhét list vào bundle.
286
323
 
287
324
  Downstream (UI gen, API gen, testcase) đọc **chỉ `ir/design.yaml`**. Thiếu file = split/spec chưa xong. Story/copy chưa đủ thì **bổ sung design** (bundle.gen + split), không đọc `ir/spec.yaml`. `ir/spec.yaml` chỉ văn mô tả + `legacy` + `qa` (id treo).
288
325
 
@@ -1,5 +1,64 @@
1
+ <%
2
+ const pageId = spec['page-id'] || spec.id || '';
3
+ const docStatus = spec.status || 'draft';
4
+ const docOwner = spec.owner || '';
5
+ const hasMetrics = spec.successMetrics && String(spec.successMetrics).trim();
6
+ const hasNonGoals = spec.nonGoals && String(spec.nonGoals).trim();
7
+ const hasStories = !!spec.userStories;
8
+ const pageSections = spec.sections || spec.design?.sections || [];
9
+ const pageActions = spec.actions || spec.design?.actions || [];
10
+ const pageStateMatrix = spec.stateMatrix || spec.design?.stateMatrix;
11
+ const hasState = !!pageStateMatrix;
12
+ const listUi = spec.ui?.list || {};
13
+ const hasListColumns = !!(context.renderListColumnsTable && context.renderListColumnsTable(listUi));
14
+ const hasListFilters = !!(context.renderListFiltersTable && context.renderListFiltersTable(listUi));
15
+ const hasListPagination = !!(listUi.pagination && typeof listUi.pagination === 'object');
16
+ const hasListExtras = hasListFilters || hasListPagination;
17
+ const hasValidation = !!(context.renderValidationDictionaryTable && context.renderValidationDictionaryTable(pageSections));
18
+ const hasCustomWidget = !!(context.renderCustomWidgetSpecs && context.renderCustomWidgetSpecs(pageSections));
19
+ const hasActions = pageActions && pageActions.length;
20
+ const hasReview = spec.review && (spec.review.layoutNotes || spec.review.summary);
21
+ const hasLegacy = spec.legacy && (spec.legacy.behaviors?.length || spec.legacy.fields?.length || spec.legacy.evidence?.length);
22
+ const hasDebt = spec.pendingTechDebt && spec.pendingTechDebt.length;
23
+
24
+ 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)');
41
+
42
+ const route = spec.ui?.routes?.[0];
43
+ const routePath = route?.path || '';
44
+ const testId = route?.pageTestId || '';
45
+ %>
1
46
  # <%= spec.title || spec.id %>
2
47
 
48
+ ## Mục lục (Contents)
49
+
50
+ <%= toc.join('\n') %>
51
+
52
+ ---
53
+
54
+ ## Tổng quan {#overview}
55
+
56
+ | | |
57
+ | --- | --- |
58
+ | **Page ID** | `<%= pageId || '—' %>` |
59
+ | **Status** | <%= docStatus %> |
60
+ | **Owner** | <%= docOwner || '—' %> |
61
+
3
62
  <% if (context.testcases && context.testcases.length) { %>
4
63
  - **Testcase plans:** <%= context.testcases.map(tc => '`' + (tc.data.title || tc.data.id || tc.file) + '`').join(' · ') %> (hub: [base_test](https://github.com/raintr91/base_test))
5
64
  <% } else { %>
@@ -7,24 +66,45 @@
7
66
  <% } %>
8
67
 
9
68
  <%
10
- const route = spec.ui?.routes?.[0];
11
- const path = route?.path || '';
12
- const testId = route?.pageTestId || '';
13
69
  let screenLine = '- **Screen:** None';
14
- if (path) {
15
- const screen = context.renderScreenLink ? context.renderScreenLink(path) : '`' + path + '`';
70
+ if (routePath) {
71
+ const screen = context.renderScreenLink ? context.renderScreenLink(routePath) : '`' + routePath + '`';
16
72
  const suffix = testId ? ` · \`${testId}\`` : '';
17
73
  screenLine = `- **Screen:** ${screen}${suffix}`;
18
74
  }
19
75
  %>
20
76
  <%= screenLine %>
21
77
 
78
+ - **QA inbox:** [Danh sách câu hỏi treo](../../qa/index.md) (cùng function leaf)
79
+ - **Generated (VitePress):** [spec](./spec.md) · [data-model](./data-model.md) · [api](./api.md) _(khi có `01`)_
80
+
22
81
  <% if (spec.summary) { %>
23
82
  <%= spec.summary %>
24
83
  <% } %>
25
84
 
85
+ <% if (hasMetrics) { %>
86
+ ## Chỉ số thành công {#success-metrics}
87
+
88
+ <%= spec.successMetrics %>
89
+
90
+ <% } %>
91
+
92
+ <% if (hasNonGoals) { %>
93
+ ## Phạm vi không làm {#non-goals}
94
+
95
+ <%= spec.nonGoals %>
96
+
97
+ <% } %>
98
+
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
+
26
106
  <% if (spec.userStories) { %>
27
- ## User Stories & Screen Journey
107
+ ## User Stories & Screen Journey {#user-stories--screen-journey}
28
108
 
29
109
  <% if (typeof spec.userStories === 'string') { %>
30
110
  <%= spec.userStories %>
@@ -114,11 +194,6 @@
114
194
  <% } %>
115
195
 
116
196
  <%
117
- // 1. Executive Summary & Screen Journey
118
- const pageSections = spec.sections || spec.design?.sections || [];
119
- const pageActions = spec.actions || spec.design?.actions || [];
120
- const pageStateMatrix = spec.stateMatrix || spec.design?.stateMatrix;
121
-
122
197
  const page = {
123
198
  nav: spec.nav || spec.design?.nav,
124
199
  sections: pageSections,
@@ -129,17 +204,51 @@
129
204
  %>
130
205
 
131
206
  <% if (pageStateMatrix) { %>
132
- ## Ma Trận Trạng Thái Giao Diện & Phân Quyền (State & Permission Matrix)
207
+ ## Ma Trận Trạng Thái Giao Diện & Phân Quyền (State & Permission Matrix) {#state--permission-matrix}
133
208
 
134
209
  <%- context.renderStateMatrixTable(pageStateMatrix) %>
135
210
 
136
211
  <% } %>
137
212
 
213
+ <%
214
+ const listColumnsMd = context.renderListColumnsTable && context.renderListColumnsTable(listUi);
215
+ if (listColumnsMd) {
216
+ %>
217
+ ## Bảng cột danh sách (List columns) {#list-columns}
218
+
219
+ <%- listColumnsMd %>
220
+
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>
222
+
223
+ <% } %>
224
+
225
+ <%
226
+ const filtersMd = context.renderListFiltersTable && context.renderListFiltersTable(listUi);
227
+ const paginationMd = context.renderListPaginationBlock && context.renderListPaginationBlock(listUi);
228
+ if (filtersMd || paginationMd) {
229
+ %>
230
+ ## Bộ lọc & phân trang (List filters & pagination) {#list-filters-pagination}
231
+
232
+ <% if (filtersMd) { %>
233
+ ### Bộ lọc
234
+
235
+ <%- filtersMd %>
236
+
237
+ <% } %>
238
+ <% if (paginationMd) { %>
239
+ ### Phân trang
240
+
241
+ <%- paginationMd %>
242
+
243
+ <% } %>
244
+
245
+ <% } %>
246
+
138
247
  <%
139
248
  const validationTableMd = context.renderValidationDictionaryTable(pageSections);
140
249
  if (validationTableMd) {
141
250
  %>
142
- ## Danh Mục Trường Nhập Liệu & Quy Tắc Kiểm Tra Hợp Lệ (Data Dictionary & Validation)
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}
143
252
 
144
253
  <%- validationTableMd %>
145
254
 
@@ -149,6 +258,8 @@
149
258
  const customWidgetMd = context.renderCustomWidgetSpecs(pageSections);
150
259
  if (customWidgetMd) {
151
260
  %>
261
+ <div id="custom-widgets"></div>
262
+
152
263
  <%- customWidgetMd %>
153
264
 
154
265
  <% } %>
@@ -157,17 +268,19 @@
157
268
  const actionFlowsMd = context.renderActionFlowsDetailed(pageActions);
158
269
  if (actionFlowsMd) {
159
270
  %>
160
- ## Đặc Tả Quy Trình Hành Động (Action Flows)
271
+ ## Đặc Tả Quy Trình Hành Động (Action Flows) {#action-flows}
161
272
 
162
273
  <%- actionFlowsMd %>
163
274
 
164
275
  <% } else { %>
276
+ ## Bố cục màn hình {#page-composition}
277
+
165
278
  <%- context.renderPageComposition(page, { businessOnly: true }) %>
166
279
  <% } %>
167
280
 
168
281
  <%
169
282
  const skipKeys = new Set([
170
- 'id', 'page-id', 'title', 'summary', 'userStories', 'acceptance', 'ui', 'design', 'legacy', 'review', 'template',
283
+ 'id', 'page-id', 'title', 'summary', 'successMetrics', 'nonGoals', 'userStories', 'acceptance', 'ui', 'design', 'legacy', 'review', 'template',
171
284
  'nav', 'sections', 'zones', 'behavior', 'actions', 'stateMatrix', 'grillStatus', 'specOrigin',
172
285
  'status', 'owner', 'schema', 'qa', 'Q&A', 'pendingTechDebt', 'i18n', 'gen', '_meta'
173
286
  ]);
@@ -187,7 +300,7 @@
187
300
  <% }); %>
188
301
 
189
302
  <% if (spec.pendingTechDebt && spec.pendingTechDebt.length) { %>
190
- ## Pending tech debt
303
+ ## Pending tech debt {#pending-tech-debt}
191
304
 
192
305
  | ID | Status | Summary |
193
306
  | --- | --- | --- |
@@ -198,7 +311,7 @@
198
311
  <% } %>
199
312
 
200
313
  <% if (spec.review && (spec.review.layoutNotes || spec.review.summary)) { %>
201
- ## Review — layout notes
314
+ ## Review — layout notes {#review-notes}
202
315
 
203
316
  <% if (spec.review.layoutNotes) { %>
204
317
  <% if (Array.isArray(spec.review.layoutNotes)) { %>
@@ -210,7 +323,7 @@
210
323
  <% } %>
211
324
 
212
325
  <% if (spec.legacy && (spec.legacy.behaviors?.length || spec.legacy.fields?.length || spec.legacy.evidence?.length)) { %>
213
- ## Legacy evidence
326
+ ## Legacy evidence {#legacy-evidence}
214
327
 
215
328
  <% if (spec.legacy.behaviors && spec.legacy.behaviors.length) { %>
216
329
  ### Behaviors
@@ -1,6 +1,5 @@
1
- # Design spec v1 — /spec (round 1)
2
- # Do NOT add codegen, ui.filters, ui.columns, or portal-gen tags here.
3
- # After /grill-docs → see docs/templates/spec.yaml (portal-gen-ready).
1
+ # DEPRECATED — không author leaf mới. SSOT: feature.bundle.yaml + bundle-authoring.md
2
+ # File giữ cho repo cũ / tham chiếu shape spec (design v1). /spec skill → chỉ bundle.
4
3
 
5
4
  id: role-page-function
6
5
  title: Feature title
@@ -0,0 +1,38 @@
1
+ {
2
+ "version": 1,
3
+ "canonicalSystem": "flowgrid-fe",
4
+ "description": "Design DSL registry (UI shells, #ui, Mo*). Refresh with flowgrid registry:sync after base changes.",
5
+ "defaults": {
6
+ "shellByProfile": {
7
+ "list": "DataListPage",
8
+ "create": "DataFormPage",
9
+ "detail": "DataDetailPage",
10
+ "admin-crud": "DataListPage",
11
+ "auth": "AuthShell"
12
+ }
13
+ },
14
+ "shells": {
15
+ "DataListPage": {
16
+ "tag": "#shell: DataListPage",
17
+ "portal": { "status": "planned", "path": "components/organisms/DataListPage.vue" },
18
+ "portalGen": {
19
+ "templateStandard": "list/page-standard",
20
+ "templateCustom": "list/page-custom"
21
+ }
22
+ },
23
+ "DataFormPage": {
24
+ "tag": "#shell: DataFormPage",
25
+ "portal": { "status": "planned", "path": "components/organisms/DataFormPage.vue" }
26
+ },
27
+ "DataDetailPage": {
28
+ "tag": "#shell: DataDetailPage",
29
+ "portal": { "status": "planned", "path": "components/organisms/DataDetailPage.vue" }
30
+ }
31
+ },
32
+ "fieldWidgets": {},
33
+ "componentAliases": {},
34
+ "aliasIndex": {},
35
+ "components": {},
36
+ "composables": {},
37
+ "helpers": {}
38
+ }
@@ -1,6 +1,4 @@
1
- # SSOT — portal-feature-bundle/v1. Split: pnpm spec:split -- <path>
2
- # Authoring: docs/templates/bundle-authoring.md
3
- # /spec MUST fill inventory when information exists. grill-* only re-checks gaps/conflicts.
1
+ # portal-feature-bundle/v1 — Authoring: bundle-authoring.md · Split: flowgrid split <path>
4
2
 
5
3
  schema: portal-feature-bundle/v1
6
4
  page-id: role-domain-function
@@ -19,7 +17,14 @@ summary: |
19
17
  - output: [Kết quả đầu ra]
20
18
  - cách giải quyết (solution): [Tuỳ chọn. Ghi kỹ thuật phức tạp nếu có.]
21
19
 
22
- # 2. KHỐI USER STORIES CHI TIẾT CHO MÀN HÌNH (SCREEN USER STORIES & SCENARIOS)
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]
23
+
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]
27
+
23
28
  userStories:
24
29
  primary:
25
30
  asA: "[Persona / Role - vd: Nhân viên Vận hành / Khách hàng / Quản trị viên]"
@@ -27,10 +32,7 @@ userStories:
27
32
  soThat: "[Mục đích kinh doanh và giá trị thực tế đạt được]"
28
33
 
29
34
  contextAndHandoff:
30
- # 3 LOẠI CÁCH THỨC VÀO MÀN HÌNH (SCREEN ACCESS ENTRY MODE):
31
- # - directRoute: Dành cho Login, Register, Public/Guest Page (mô tả path router)
32
- # - sidebarMenu: Dành cho Dashboard, CRUD List, Admin Pages (mô tả menu đa cấp dropdown + label text)
33
- # - contextualAction: Dành cho Detail, Edit Modal, Settings (mô tả nút bấm xxx từ màn hình A)
35
+ # screenAccess: directRoute | sidebarMenu | contextualAction — xem bundle-authoring.md
34
36
  screenAccess:
35
37
  accessType: sidebarMenu # directRoute | sidebarMenu | contextualAction
36
38
  directRoute:
@@ -74,7 +76,6 @@ userStories:
74
76
  - "Hệ thống khóa nút và hiển thị loading để chống bấm đúp (double submit prevention)."
75
77
  - "Lưu dữ liệu thành công (không phải xóa): Toast hoặc inline alert; điều hướng theo Handoff. Thao tác xóa — xem scenario UX affordances bên dưới."
76
78
 
77
- # Thêm scenario này khi màn có delete/breadcrumb/filter/disabled reason / import CSV (sau audit UX_* hoặc DSL gap).
78
79
  - name: "Affordances UX chuẩn portal (UX — khi áp dụng)"
79
80
  steps:
80
81
  - "Breadcrumb / ngữ cảnh: Người dùng luôn biết đang ở module và bản ghi nào (trail hoặc back + title rõ)."
@@ -194,6 +195,12 @@ gen:
194
195
  testIds: null
195
196
 
196
197
  design:
198
+ # dataModel: # optional — multi-table review; xem tpl-screen-data-model.md
199
+ # erdRef: "<LCA>/common/db-erd.md"
200
+ # tables:
201
+ # - schema: records
202
+ # roleOnScreen: read-write
203
+ # summary: "Hồ sơ chính trên form"
197
204
  inherits: null
198
205
  shell:
199
206
  tag: "#shell: DataListPage" # AuthPage | AppShell