cabloy 5.1.162 → 5.1.164

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 (96) hide show
  1. package/.cabloy-version +1 -1
  2. package/CHANGELOG.md +16 -0
  3. package/README.md +2 -0
  4. package/package.json +1 -1
  5. package/repo-docs/backend/shared-rbac-architecture.md +4 -0
  6. package/repo-docs/blogs/ai-spec-driven-development/cover-en-v1.png +0 -0
  7. package/repo-docs/blogs/ai-spec-driven-development/implementation-burndown.png +0 -0
  8. package/repo-docs/blogs/ai-spec-driven-development/implementation-gantt.png +0 -0
  9. package/repo-docs/blogs/ai-spec-driven-development/index.md +217 -0
  10. package/repo-docs/blogs/index.md +15 -0
  11. package/repo-docs/editions/overview.md +2 -0
  12. package/repo-docs/frontend/environment-config-guide.md +43 -10
  13. package/repo-docs/frontend/resource-list-page-deep-dive.md +14 -1
  14. package/repo-docs/frontend/table-action-visibility-permission-flow-guide.md +20 -0
  15. package/repo-docs/frontend/table-guide.md +42 -0
  16. package/repo-docs/frontend/table-resource-crud-cookbook.md +25 -1
  17. package/repo-e2e/docs/specs/blogs-index.spec.ts +45 -9
  18. package/repo-e2e/specs/cabloy-basic.spec.ts +249 -2
  19. package/vona/packages-cli/cli/package.json +1 -1
  20. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/controller/{{resourceName}}.ts_ +17 -6
  21. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/dto/{{resourceName}}DeleteBulk.tsx_ +14 -0
  22. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/dto/{{resourceName}}SelectResItem.tsx_ +7 -1
  23. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/boilerplate/src/service/{{resourceName}}.ts_ +6 -0
  24. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/snippets/3-en-us.ts +12 -1
  25. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudBasic/snippets/4-zh-cn.ts +12 -1
  26. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/controller/{{resourceName}}.ts_ +8 -0
  27. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/dto/{{resourceName}}DeleteBulk.tsx_ +14 -0
  28. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/dto/{{resourceName}}SelectResItem.tsx_ +7 -1
  29. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/boilerplate/src/service/{{resourceName}}.ts_ +6 -0
  30. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/snippets/3-en-us.ts +1 -0
  31. package/vona/packages-cli/cli-set-api/cli/templates/tools/crudStart/snippets/4-zh-cn.ts +1 -0
  32. package/vona/packages-cli/cli-set-api/package.json +1 -1
  33. package/vona/packages-vona/vona/package.json +1 -1
  34. package/vona/src/suite/a-training/modules/training-student/src/.metadata/index.ts +9 -0
  35. package/vona/src/suite/a-training/modules/training-student/src/config/locale/en-us.ts +1 -0
  36. package/vona/src/suite/a-training/modules/training-student/src/config/locale/zh-cn.ts +1 -0
  37. package/vona/src/suite/a-training/modules/training-student/src/controller/student.ts +9 -0
  38. package/vona/src/suite/a-training/modules/training-student/src/dto/studentDeleteBulk.tsx +14 -0
  39. package/vona/src/suite/a-training/modules/training-student/src/dto/studentSelectResItem.tsx +7 -1
  40. package/vona/src/suite/a-training/modules/training-student/src/service/student.ts +6 -0
  41. package/vona/src/suite/a-training/modules/training-student/test/student.test.ts +114 -2
  42. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
  43. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/src/.metadata/index.ts +24 -3
  44. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/src/.metadata/locales.ts +18 -0
  45. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/src/bean/bean.rbacResourceBulk.ts +50 -0
  46. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/src/bean/index.ts +1 -0
  47. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/src/config/errors.ts +18 -0
  48. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/src/config/locale/en-us.ts +6 -0
  49. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/src/config/locale/zh-cn.ts +6 -0
  50. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/src/index.ts +1 -0
  51. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/test/resourceBulk.test.ts +128 -0
  52. package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
  53. package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
  54. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  55. package/zova/packages-cli/cli/package.json +2 -2
  56. package/zova/packages-cli/cli-set-front/cli/templates/create/component/boilerplateTableActionBulk/controller.tsx_ +2 -2
  57. package/zova/packages-cli/cli-set-front/package.json +1 -1
  58. package/zova/packages-zova/zova/package.json +2 -2
  59. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/schemas.ts +16 -12
  60. package/zova/src/suite/a-training/modules/training-student/src/api/openapi/types.ts +66 -7
  61. package/zova/src/suite/a-training/modules/training-student/src/api/trainingStudent.ts +18 -0
  62. package/zova/src/suite/a-training/modules/training-student/src/apiSchema/trainingStudent.ts +5 -0
  63. package/zova/src/suite/cabloy-basic/modules/basic-commands/src/.metadata/index.ts +16 -0
  64. package/zova/src/suite/cabloy-basic/modules/basic-commands/src/bean/command.deleteBulk.tsx +35 -0
  65. package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockPage/controller.tsx +143 -7
  66. package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockTable/controller.tsx +3 -2
  67. package/zova/src/suite/cabloy-basic/modules/basic-page/src/component/blockToolbarBulk/controller.tsx +76 -4
  68. package/zova/src/suite/cabloy-basic/modules/basic-page/src/config/locale/en-us.ts +7 -0
  69. package/zova/src/suite/cabloy-basic/modules/basic-page/src/config/locale/zh-cn.ts +6 -0
  70. package/zova/src/suite/cabloy-basic/modules/basic-page/src/lib/selection.ts +84 -0
  71. package/zova/src/suite/cabloy-basic/modules/basic-page/test/lib/selection.test.ts +99 -0
  72. package/zova/src/suite/cabloy-basic/modules/basic-table/src/.metadata/component/actionDeleteBulk.ts +31 -0
  73. package/zova/src/suite/cabloy-basic/modules/basic-table/src/.metadata/index.ts +13 -0
  74. package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/actionCreate/controller.tsx +5 -2
  75. package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/actionDeleteBulk/controller.tsx +57 -0
  76. package/zova/src/suite/cabloy-basic/modules/basic-table/src/component/table/render.tsx +35 -7
  77. package/zova/src/suite/cabloy-basic/modules/basic-table/src/config/locale/en-us.ts +4 -0
  78. package/zova/src/suite/cabloy-basic/modules/basic-table/src/config/locale/zh-cn.ts +4 -0
  79. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
  80. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/lib/selectedIds.ts +15 -0
  81. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +57 -0
  82. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/test/lib/selectedIds.test.ts +20 -0
  83. package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
  84. package/zova/src/suite-vendor/a-zova/modules/a-command/package.json +1 -1
  85. package/zova/src/suite-vendor/a-zova/modules/a-command/src/lib/beanCommandSelectedBase.ts +33 -0
  86. package/zova/src/suite-vendor/a-zova/modules/a-command/src/lib/index.ts +1 -0
  87. package/zova/src/suite-vendor/a-zova/modules/a-command/src/types/command.ts +5 -0
  88. package/zova/src/suite-vendor/a-zova/modules/a-command/test/lib/beanCommandSelectedBase.test.ts +47 -0
  89. package/zova/src/suite-vendor/a-zova/modules/a-openapi/package.json +1 -1
  90. package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/action.ts +3 -0
  91. package/zova/src/suite-vendor/a-zova/modules/a-openapi/src/types/resource/tableActionBulk.ts +19 -0
  92. package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +1 -1
  93. package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/controller.tsx +86 -26
  94. package/zova/src/suite-vendor/a-zova/modules/a-table/src/component/table/render.tsx +30 -6
  95. package/zova/src/suite-vendor/a-zova/modules/a-table/src/types/table.ts +2 -0
  96. package/zova/src/suite-vendor/a-zova/package.json +4 -4
package/.cabloy-version CHANGED
@@ -1 +1 @@
1
- 5.1.162
1
+ 5.1.164
package/CHANGELOG.md CHANGED
@@ -1,5 +1,21 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.1.164
4
+
5
+ ### Features
6
+
7
+ - Enhance resource selection and CRUD CLI tools.
8
+
9
+ ### Improvements
10
+
11
+ - Include demonstration suites in the documentation.
12
+
13
+ ## 5.1.163
14
+
15
+ ### Improvements
16
+
17
+ - Update the Basic homepage end-to-end assertion.
18
+
3
19
  ## 5.1.162
4
20
 
5
21
  ### Features
package/README.md CHANGED
@@ -74,6 +74,8 @@ For fuller guidance on choosing an edition and working in an existing checkout,
74
74
  | E-commerce | A complete Admin site, Web site, personal center, payment flows, Markdown editing, and related capabilities. | ✅ | — |
75
75
  | System Management | User management, role management, department management, RBAC authorization, and menu authorization. | — | ✅ |
76
76
 
77
+ Included demonstration suites give AI vibe coding agents high-quality, project-native code examples. They improve development efficiency while reducing token use. For production deployments, set `PROJECT_DISABLED_SUITES` to a comma-separated list of unneeded suite names to disable them; see the [Environment and Config Guide](https://cabloy.com/frontend/environment-config-guide#built-in-env-variables).
78
+
77
79
  > “Not included” means that the suite is not part of the default edition baseline. It does not limit what can be built with Cabloy.
78
80
 
79
81
  ## Highlights
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cabloy",
3
- "version": "5.1.162",
3
+ "version": "5.1.164",
4
4
  "gitHead": "2c5c19284bab738e492856189acb6fad74b8a7b7",
5
5
  "description": "A Node.js fullstack framework",
6
6
  "keywords": [
@@ -129,6 +129,10 @@ After guard admission, a controller must consume the current typed scope. The co
129
129
  | Bulk mutation | `checkEntries(entries)` | Ensure every intended target is inside scope |
130
130
  | Create | `ownerValues()` | Derive trusted owner/scope fields instead of accepting widening input |
131
131
 
132
+ For an atomic multi-ID Resource command, have the command DTO validate its request-list maximum and use `a-rbac` to validate that the list is nonempty and has no duplicate normalized identities. Then load every requested record through the ordinary instance-scoped model boundary. Require complete one-for-one resolution before calling `checkEntries(entries)` and before any mutation. This treats missing, soft-deleted, and other-instance records uniformly as absent; do not use an unscoped query merely to distinguish them. Run the full preflight and mutation inside the domain transaction.
133
+
134
+ `a-rbac` exposes `this.bean.rbacResourceBulk.entries(...)` for the reusable integrity and authorization sequence. Pass the facade a fresh, ordinary instance-scoped loader (rather than a cache-backed lookup) from the domain transaction. It preserves the requested order, invokes `checkEntries(...)` only after complete normal-scoped loading, and emits localized `a-rbac` scoped errors for invalid ID lists and absent entries.
135
+
132
136
  A representative list shape is:
133
137
 
134
138
  ```typescript
@@ -0,0 +1,217 @@
1
+ ---
2
+ title: CabloyJS AI Spec-Driven Development Automatically Generates Gantt and Burndown Charts
3
+ titleZh: AI Spec 驱动开发:用 CabloyJS 的两个 Skills 把 Spec 变成可核验交付
4
+ titleEn: CabloyJS AI Spec-Driven Development Automatically Generates Gantt and Burndown Charts
5
+ subtitle: From specification generation to bounded execution, see how CabloyJS helps AI work within explicit business boundaries, technical contracts, and acceptance evidence.
6
+ summary: CabloyJS provides two complementary AI Skills—cabloy-spec-generation and cabloy-spec-execution. The first establishes a suite’s specification and traceability authority; the second delivers one confirmed WBS increment and records verification evidence. Using a-commerce as an example, this article explains the generated Spec files, the two derived charts, and how they connect.
7
+ tags:
8
+ - AI Spec-Driven Development
9
+ - AI Coding
10
+ - Spec-Driven Development
11
+ - Cabloy
12
+ - Vona
13
+ - Zova
14
+ - Software Architecture
15
+ - Fullstack
16
+ slug: ai-spec-driven-development-en
17
+ cover: ./cover-en-v1.png
18
+ date: 2026-09-11
19
+ ---
20
+
21
+ # CabloyJS AI Spec-Driven Development Automatically Generates Gantt and Burndown Charts
22
+
23
+ The easiest way to start AI coding is to describe a page or an API in a chat window. But once a project has multiple modules, multiple audiences, and an evolving frontend-backend contract, the genuinely difficult questions become: where does this change belong? Which boundaries have already been confirmed? How can we prove that it is actually complete?
24
+
25
+ CabloyJS includes two complementary Claude Code Skills that divide the work into two phases: first establish specification authority inside the repository, then execute a bounded delivery increment. This article introduces the two Skills, explains the Spec files they produce and consume, and uses real charts from the built-in `a-commerce` suite to show the result.
26
+
27
+ ## Meet the two Skills first
28
+
29
+ The two Skills are invoked as follows:
30
+
31
+ - `cabloy-spec-generation`: creates or maintains the suite planning records under `repo-specs/<suite>/`, establishing traceability among product, technical, delivery, and acceptance concerns.
32
+ - `cabloy-spec-execution`: works from an existing specification to execute one explicit `WBS-*` item—or a finite, approved phase with a closed boundary—and records the actual verification results.
33
+
34
+ The following table summarizes the difference:
35
+
36
+ | Dimension | `cabloy-spec-generation` | `cabloy-spec-execution` |
37
+ | ------------------- | ----------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
38
+ | Problem addressed | What should be built, and how should boundaries and acceptance be recorded? | How should one approved item be implemented and proven complete? |
39
+ | Input | A new suite description or a request to maintain an existing specification | An explicit `WBS-*` item, or a finite and approved phase |
40
+ | Primary outputs | PRD, SRS, PDP/WBS, ATP, ADRs, derived status, and charts | Source changes, test results, Evidence, derived progress, and charts |
41
+ | Confirmation gate | Confirms identity, scope, topology, site strategy, and more before planning records are written | Confirms the execution dossier, dependencies, scope, commands, and evidence requirements before implementation |
42
+ | What it does not do | Does not write business source code or treat planning as implementation | Does not invent requirements, replace specification generation, or automatically expand into adjacent tasks |
43
+
44
+ ### 1. Establish a specification baseline with generation
45
+
46
+ In Claude Code, begin with a business description:
47
+
48
+ ```text
49
+ /cabloy-spec-generation Plan an equipment-maintenance suite for field technicians and operations managers: include equipment records, work orders, asset history, role-based access control, and an Admin dashboard.
50
+ ```
51
+
52
+ The Skill first performs read-only discovery and asks for missing inputs. Its typical steps are:
53
+
54
+ 1. inspect the repository root, `package.json`, `CLAUDE.md`, and the edition marker;
55
+ 2. distinguish a new suite from an extension of an existing suite, and check whether `repo-specs/<suite>/` already exists;
56
+ 3. independently evaluate whether Web and Admin should reuse an existing site or use a dedicated site;
57
+ 4. collect product goals, roles, scope, module topology, tenancy and authorization, states, transactions, verification, and release constraints;
58
+ 5. show a generation confirmation gate before writing planning records;
59
+ 6. generate files in authority order, run exact-ID traceability checks, and produce two derived charts.
60
+
61
+ ### 2. Execute a bounded increment with execution
62
+
63
+ Once the specification is ready and a task’s dependencies are satisfied, invoke execution:
64
+
65
+ ```text
66
+ /cabloy-spec-execution WBS-40-03
67
+ ```
68
+
69
+ `WBS-40-03` is only an example. A real project should use an ID that actually exists in the target suite. The AI then follows this process:
70
+
71
+ 1. read `README.md`, the PRD, SRS, applicable ADRs, the complete WBS, ATP/test plan, progress, Evidence, and derived charts;
72
+ 2. check the edition, current revision, workspace state, dependencies, blockers, unresolved `TODO`s, and whether previous evidence is still valid;
73
+ 3. prepare an execution dossier listing the objective, scope, exclusions, authority IDs, source ownership, specialist-Skill routing, verification commands, and evidence format;
74
+ 4. obtain explicit confirmation before routing into backend, frontend, or Contract Loop specialist workflows;
75
+ 5. run the narrowest meaningful check first, followed by the ATP-required verification;
76
+ 6. save Evidence containing the revision, environment, steps, results, and locations of sanitized artifacts;
77
+ 7. update derived `progress.md`, regenerate and check the charts, and leave one unambiguous next action.
78
+
79
+ ```text
80
+ /cabloy-spec-execution Execute the next task in the equipment-maintenance suite
81
+ ```
82
+
83
+ If the next incremental WBS ID is unknown, ask the AI to first read the suite’s `progress.md` and combine it with the WBS, ATP, and existing Evidence to list executable `WBS-*` IDs whose dependencies are satisfied. The user can then confirm the specific item to execute next.
84
+
85
+ ## What generation produces: a connected set of Spec files
86
+
87
+ For a long-lived business suite, the default core output of `cabloy-spec-generation` is located at:
88
+
89
+ ```text
90
+ repo-specs/<suite>/
91
+ ├── README.md
92
+ ├── prd.md
93
+ ├── srs.md
94
+ ├── pdp-wbs.md
95
+ ├── test-plan.md
96
+ ├── progress.md
97
+ ├── implementation-gantt.svg
98
+ ├── implementation-burndown.svg
99
+ └── decisions/
100
+ └── 0001-<suite-boundary-slug>.md
101
+ ```
102
+
103
+ This is not simply one large document split into several files. It is a model of **separated authority**: each record owns one kind of fact, while the others refer to it through stable IDs and links.
104
+
105
+ | File | Facts it owns | What it should not replace |
106
+ | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
107
+ | `README.md` | Index, reading order, suite baseline, topology summary, and authority map | Detailed PRD, SRS, or WBS content |
108
+ | `prd.md` | Product goals, roles, scope, journeys, business rules, product requirements, and product acceptance | Table names, DTO syntax, routes, or test commands |
109
+ | `srs.md` | Technical contracts; data and capability ownership; tenancy; identity; authorization; states; transactions; concurrency; APIs/DTOs; SSR; and non-functional boundaries | API authority or authorization determined by a UI layout |
110
+ | `pdp-wbs.md` | Delivery phases, WBS tasks, dependencies, completion checks, and delivery traceability | Upstream product requirements or technical contracts |
111
+ | `test-plan.md` | ATP acceptance scenarios, test levels, fixtures, verification procedures, Evidence format, and release gates | Planned commands presented as results that have already passed |
112
+ | `progress.md` | WBS state, blockers, Evidence pointers, decisions, and the next proof | Requirements, contracts, or acceptance rules |
113
+ | `decisions/*.md` | Long-lived scope, architecture, security, ownership, or integration decisions | A duplicate SRS or status log |
114
+ | `implementation-gantt.svg` | A WBS-derived view of phases, task order, dependencies, and status | Planning authority, date commitments, or duration commitments |
115
+ | `implementation-burndown.svg` | A scope-and-status-derived count of remaining items | A velocity trend or forecast when no historical snapshots exist |
116
+
117
+ ## From business intent to evidence: how the files connect
118
+
119
+ The core traceability chain is:
120
+
121
+ ```text
122
+ PRD requirement
123
+
124
+ SRS contract
125
+
126
+ PDP/WBS task
127
+
128
+ ATP scenario
129
+
130
+ observed Evidence
131
+ ```
132
+
133
+ It can also be written more compactly:
134
+
135
+ ```text
136
+ PRD → SRS → WBS → ATP → Evidence
137
+ ```
138
+
139
+ For example, a product requirement that “an authorized operator can view orders in the current tenant” must be made concrete in the SRS as identity, tenancy, authorization, data ownership, and API contracts. The WBS then defines the implementation boundary and dependencies; the ATP provides executable happy-path, unauthorized, and cross-tenant scenarios; finally, the actual runtime observations are saved as sanitized evidence.
140
+
141
+ When a task is executed, the document-state update order is:
142
+
143
+ ```text
144
+ Upstream authority changes
145
+ → traceability matrix
146
+ → WBS dependencies and completion checks
147
+ → ATP procedures and expected Evidence
148
+ → progress / Evidence
149
+ → two derived charts
150
+ ```
151
+
152
+ `progress.md` and both SVGs are downstream derived outputs. They help people and AI understand the current state quickly.
153
+
154
+ ## How to read the two charts
155
+
156
+ The charts are automatically generated by built-in scripts from `pdp-wbs.md`, `test-plan.md`, and `progress.md`.
157
+
158
+ ### Gantt: inspect delivery order, not a calendar schedule
159
+
160
+ The Gantt chart reads the formal phases, WBS tasks, dependencies, and progress statuses. It is useful for answering: what delivery packages exist? What is their approximate order? Which tasks depend on preceding phases?
161
+
162
+ The current chart for the built-in `a-commerce` suite contains 23 formal WBS tasks, all shown as 23/23 `verified`.
163
+
164
+ ![A-Commerce implementation roadmap: WBS phases, dependencies, and derived status](./implementation-gantt.png)
165
+
166
+ ### Burndown: inspect scope counts, not velocity forecasts
167
+
168
+ In the current `a-commerce` snapshot, the Burndown chart shows 23 active WBS items, 23 `verified`, 0 remaining, and 0 deferred.
169
+
170
+ ![A-Commerce WBS scope-count burndown: approved scope and remaining-item counts](./implementation-burndown.png)
171
+
172
+ ## When this approach is worthwhile
173
+
174
+ This approach is best suited to enterprise systems that evolve over time, span modules, involve multiple collaborators, and include both Vona backend and Zova Admin/Web/SSR work—especially where tenancy, authorization, transactions, concurrency, auditability, or external integrations are important.
175
+
176
+ For a one-off page, a simple website, or a very small utility, a lightweight prompt-to-code workflow may be more economical.
177
+
178
+ ## Start with a small increment
179
+
180
+ Choose one clearly bounded capability rather than trying to plan an entire system at once:
181
+
182
+ 1. Use generation to plan the requirement.
183
+ - Record goals, scope, and exclusions.
184
+ - Confirm technical contracts, ownership, dependencies, and ATP.
185
+ 2. Use execution to deliver one WBS ID.
186
+ - Run applicable verification and save sanitized Evidence.
187
+ - Update progress and the derived charts.
188
+ 3. Proceed to the next increment.
189
+
190
+ Create a Cabloy project from the public scaffolding:
191
+
192
+ ```bash
193
+ npm create cabloy
194
+ npm run dev
195
+ npm run dev:zova:admin
196
+ npm run dev:zova:web
197
+ ```
198
+
199
+ ## Conclusion
200
+
201
+ AI Spec-Driven Development in CabloyJS is not another way of saying “prompts become code.” It is a more concrete delivery path:
202
+
203
+ ```text
204
+ Business intent → specification authority → bounded WBS → acceptance procedure → observed Evidence
205
+ ```
206
+
207
+ `cabloy-spec-generation` organizes intent into a traceable planning chain. `cabloy-spec-execution` advances one increment within confirmed boundaries. Together, they reduce the AI’s need to guess about business scope, technical ownership, and the definition of done.
208
+
209
+ ## Further reading
210
+
211
+ - [AI Spec-Driven Development](https://cabloy.com/ai/ai-spec-driven-development)
212
+ - [Generate a Cabloy Suite Specification](https://cabloy.com/ai/playbook-spec-generation)
213
+ - [Execute an Approved Cabloy Specification Increment](https://cabloy.com/ai/playbook-spec-execution)
214
+ - [Verification](https://cabloy.com/ai/verification)
215
+ - [Contract Loop Playbook](https://cabloy.com/fullstack/contract-loop-playbook)
216
+ - [Vona + Zova Integration](https://cabloy.com/fullstack/vona-zova-integration)
217
+ - [Cabloy Fullstack Quick Start Tutorials](https://cabloy.com/fullstack/tutorials-overview)
@@ -9,6 +9,21 @@ aside: false
9
9
  Selected articles on Cabloy, Vona, Zova, and the architectural questions that shape full-stack applications.
10
10
 
11
11
  <div class="cabloy-blog-grid">
12
+ <article class="cabloy-blog-card">
13
+ <a class="cabloy-blog-card__cover" href="/blogs/ai-spec-driven-development/" aria-label="Read CabloyJS AI Spec-Driven Development Automatically Generates Gantt and Burndown Charts">
14
+ <img src="/blogs/ai-spec-driven-development/cover-en-v1.png" alt="CabloyJS AI Spec-Driven Development Automatically Generates Gantt and Burndown Charts" />
15
+ </a>
16
+ <div class="cabloy-blog-card__content">
17
+ <p class="cabloy-blog-card__date">September 11, 2026</p>
18
+ <h2><a href="/blogs/ai-spec-driven-development/">CabloyJS AI Spec-Driven Development Automatically Generates Gantt and Burndown Charts</a></h2>
19
+ <p>Learn how two CabloyJS Skills establish specification authority, execute bounded WBS increments, and derive Gantt and Burndown status views from the same traceability chain.</p>
20
+ <div class="cabloy-blog-card__tags" aria-label="Article topics">
21
+ <span>AI Spec-Driven Development</span><span>AI Coding</span><span>Specification Workflow</span><span>Fullstack</span>
22
+ </div>
23
+ <a class="cabloy-blog-card__read" href="/blogs/ai-spec-driven-development/">Read article <span aria-hidden="true">→</span></a>
24
+ </div>
25
+ </article>
26
+
12
27
  <article class="cabloy-blog-card">
13
28
  <a class="cabloy-blog-card__cover" href="/blogs/nextjs-integrated-fullstack-cabloy-contract-loop/" aria-label="Read Next.js Integrated Full Stack and Cabloy’s Bidirectional Contract Loop">
14
29
  <img src="/blogs/nextjs-integrated-fullstack-cabloy-contract-loop/cover-en-v1.png" alt="Next.js Integrated Full Stack and Cabloy’s Bidirectional Contract Loop" />
@@ -33,6 +33,8 @@ Both editions share Vona + Zova, suite-based modular delivery, CLI-first workflo
33
33
  | E-commerce | A complete Admin site, Web site, personal center, payment flows, Markdown editing, and related capabilities. | ✅ | — |
34
34
  | System Management | User management, role management, department management, RBAC authorization, and menu authorization. | — | ✅ |
35
35
 
36
+ Included demonstration suites give AI vibe coding agents high-quality, project-native code examples. They improve development efficiency while reducing token use. For production deployments, set `PROJECT_DISABLED_SUITES` to a comma-separated list of unneeded suite names to disable them; see the [Environment and Config Guide](https://cabloy.com/frontend/environment-config-guide#built-in-env-variables).
37
+
36
38
  > “Not included” means that the suite is not part of the default edition baseline. It does not limit what can be built with Cabloy.
37
39
 
38
40
  ## Choosing an edition
@@ -196,16 +196,49 @@ This distinction is central to writing Zova code that behaves correctly across b
196
196
 
197
197
  ## Built-in env variables
198
198
 
199
- Zova exposes built-in variables covering areas such as:
200
-
201
- - app identity
202
- - router mode
203
- - dev-server settings
204
- - project-disabled suites/modules
205
- - build output settings
206
- - API/proxy configuration
207
- - SSR-specific values
208
- - mock configuration
199
+ Zova provides the following built-in environment variables. Configure them in `zova/env/.env` or in an applicable meta-specific env file.
200
+
201
+ ### App identity and presentation
202
+
203
+ `SITE_ID`, `APP_NAME`, `APP_TITLE`, `APP_DESCRIPTION`, `APP_VERSION`, `APP_META_VIEWPORT`, `APP_PUBLIC_PATH`, `APP_LOCALE_DEFAULT`, `APP_LOCALE_HEADER_KEY`, `APP_TZ_HEADER_KEY`
204
+
205
+ ### Router settings
206
+
207
+ `ROUTER_MODE`, `ROUTER_PAGE_HOME`, `ROUTER_PAGE_LOGIN`, `ROUTER_KEY_RETURNTO`
208
+
209
+ ### Development-server settings
210
+
211
+ `DEV_SERVER_HOSTNAME`, `DEV_SERVER_PORT`, `DEV_SERVER_HMR_PORT`
212
+
213
+ ### Project-disabled suites and modules
214
+
215
+ `PROJECT_DISABLED_SUITES`, `PROJECT_DISABLED_MODULES`
216
+
217
+ `PROJECT_DISABLED_SUITES` disables entire suites, including every module in each disabled suite. `PROJECT_DISABLED_MODULES` disables individual modules without disabling their containing suite. Both accept comma-separated relative names.
218
+
219
+ For example, a production deployment that does not need the e-commerce demonstration suite can set:
220
+
221
+ ```dotenv
222
+ PROJECT_DISABLED_SUITES=a-commerce
223
+ ```
224
+
225
+ ### Build output settings
226
+
227
+ `LOGGER_CLIENT_DEFAULT`, `BUILD_OUTDIR`, `BUILD_MINIFY`, `BUILD_SOURCEMAP`, `BUILD_TARGET_BROWSER`, `BUILD_TARGET_NODE`, `BUILD_ANALYZE`, `BUILD_COPY_DIST`, `BUILD_COPY_RELEASE`, `BUILD_REST_COPY_DIST`
228
+
229
+ ### API and proxy configuration
230
+
231
+ `API_BASE_URL`, `API_PREFIX`, `API_JWT`, `OPENAPI_BASE_URL_DEFAULT`, `OPENAPI_BASE_URL_<MODULE>`, `PROXY_API_ENABLED`, `PROXY_API_BASE_URL`, `PROXY_API_PREFIX`
232
+
233
+ Use `OPENAPI_BASE_URL_<MODULE>` to override the OpenAPI base URL for a specific module. Replace `<MODULE>` with the module's uppercase underscore-separated name, such as `OPENAPI_BASE_URL_HOME_API`.
234
+
235
+ ### SSR-specific values
236
+
237
+ `SSR_API_BASE_URL`, `SSR_PROD_PORT`, `SSR_PROD_PROTOCOL`, `SSR_PROD_HOST`, `SSR_WITH_VONA`, `SSR_PROFILE`, `SSR_PROFILE_PUBLIC_RESPONSE_CACHE_EXPIRES`, `SSR_PROFILE_SESSION_RESPONSE_CACHE_EXPIRES`, `SSR_COOKIE_THEMEDARK_DEFAULT`
238
+
239
+ ### Mock-server configuration
240
+
241
+ `MOCK_ENABLED`, `MOCK_LOGGER`, `MOCK_BASE_NAME`, `MOCK_BUILD`, `MOCK_BUILD_PORT`, `MOCK_BUILD_OUTPUT`, `MOCK_BUILD_CORS`
209
242
 
210
243
  That means many common project-level knobs already exist and should be reused before inventing project-specific patterns.
211
244
 
@@ -151,6 +151,15 @@ This controller becomes the deeper owner for:
151
151
  - `schemaFilter`, `schemaRow`, and `permissions`
152
152
  - the shared page JSX/render context (`$$page`)
153
153
  - permission-driven table-meta refresh through `tableRef.refreshMeta()`
154
+ - transient selected IDs and snapshots of selected loaded rows
155
+
156
+ ### Page-owned row selection
157
+
158
+ Selection is page-local interaction state, not `ModelResource` cache state. `blockPage` keeps canonical selected IDs and loaded-row snapshots, while `ZTable` receives only a controlled TanStack selection map for the current page.
159
+
160
+ Consequently, normal paging and same-query refetches preserve selected IDs and refresh a snapshot when its row reappears. The header checkbox selects or clears only **currently loaded** rows; it never selects all rows matching a server-side filter. Selection clears when the result universe materially changes: fixed query, submitted/reset filter, sort, page size, resource identity, or a successful selected-row mutation. Page-size changes also return to page 1, and **Done** clears selection before hiding the selection column.
161
+
162
+ Snapshots exist for browser UX only. A selected-row command sends IDs; the backend reloads and authorizes every persisted target through the normal instance-scoped model boundary.
154
163
 
155
164
  This is the second key architectural boundary:
156
165
 
@@ -184,8 +193,12 @@ This controller consumes `$$page` and passes the canonical page-owned values int
184
193
  - `data`
185
194
  - `schemaRow`
186
195
  - `tableScope`
196
+ - controlled sorting
197
+ - controlled row selection
198
+
199
+ It captures the table controller ref back onto `$$page.tableRef`; it does not configure selection policy.
187
200
 
188
- It also captures the table controller ref back onto `$$page.tableRef`.
201
+ `blockPage.selectionPolicy` is the page-owned configuration: `undefined` is automatic, `'always'` shows checkboxes immediately, `'onDemand'` exposes **Select / Done** even without a selected action, and `false` prevents selection. With the automatic policy, a sibling bulk-toolbar action that declares `requiresSelection: true` makes **Select / Done** available. The toolbar's action requirement remains independent from page policy.
189
202
 
190
203
  This is the clearest proof that `blockTable` is a bridge into table runtime, not the main owner of list state.
191
204
 
@@ -169,6 +169,24 @@ This is the clearest source-confirmed bulk-action rule:
169
169
  - bulk action visibility is permission-driven
170
170
  - it is resolved in the bulk-toolbar block
171
171
 
172
+ ### Selected-action enablement is stricter than discovery
173
+
174
+ A bulk action with `requiresSelection: true` remains visible when its coarse permission check succeeds, but it is disabled unless all of these conditions hold:
175
+
176
+ 1. at least one ID is selected;
177
+ 2. every selected ID still has a loaded row snapshot; and
178
+ 3. `$passport.checkPermission(...)` permits **every** selected snapshot with the action's permission hint.
179
+
180
+ The final condition is intentionally an all-record check. A constrained RBAC projection may allow an action for some rows while denying it for others; a mixed selection must not appear enabled.
181
+
182
+ The bulk action contract separates authored metadata from toolbar runtime props:
183
+
184
+ - DTO authors configure static `requiresSelection`, `selectedMaxIds`, and optional unconditional `disabled` in action `options`.
185
+ - `blockToolbarBulk` supplies current-page runtime values as `dynamicSelection`, `dynamicDisabled`, and `dynamicDisabledReason`.
186
+ - A component disables itself when either static `disabled` or toolbar-owned `dynamicDisabled` is true. Custom action components consume the `dynamic*` values as UI state; command implementations should not depend on them as mutation policy.
187
+
188
+ This is still only a browser projection. Direct selected-row API requests must reload the targets and enforce authorization on the server.
189
+
172
190
  ## Row action hints vs bulk action hints
173
191
 
174
192
  The permission hint contracts live in:
@@ -240,6 +258,8 @@ If a list-page action is missing unexpectedly, ask:
240
258
  5. for row actions, is the operations-row cell bean actually resolving a visible renderer set?
241
259
  6. for bulk actions, is the bulk toolbar reading the expected action config?
242
260
  7. are you accidentally expecting entry-page `formScene` behavior in a list-page action surface?
261
+ 8. for a selected action, is selection nonempty, complete, and permitted for every selected snapshot?
262
+ 9. does the backend endpoint independently reload and authorize all submitted IDs?
243
263
 
244
264
  ## Where to read next
245
265
 
@@ -119,6 +119,48 @@ A practical reading takeaway is:
119
119
  - **the visible wrapper component is thin**
120
120
  - **the controller bean is the real runtime owner**
121
121
 
122
+ ## Row selection and selected actions
123
+
124
+ Row selection is opt-in. Direct `ZTable` use remains unchanged unless it receives the controlled TanStack selection surface:
125
+
126
+ ```tsx
127
+ <ZTable
128
+ data={this.students}
129
+ schema={this.schemaRow}
130
+ enableRowSelection={true}
131
+ rowSelection={this.rowSelection}
132
+ onRowSelectionChange={updater => {
133
+ this.rowSelection = functionalUpdate(updater, this.rowSelection);
134
+ }}
135
+ />
136
+ ```
137
+
138
+ The framework-owned selection column is not a schema field: it is left-pinned, non-sortable, and uses the stable `getRowId(...)` identity. The header checkbox selects only rows currently loaded in the table; under server pagination it never means every row matching the filter.
139
+
140
+ For standard resource list pages, `basic-page:blockPage` owns selected IDs, loaded row snapshots, and the `selectionPolicy` configuration. Its default `undefined` policy is automatic: a `tableActionBulk` action that opts in with `requiresSelection: true` makes the localized **Select** control available:
141
+
142
+ ```ts
143
+ ZovaRender.block('basic-page:blockPage', {
144
+ blocks: [
145
+ ZovaRender.block('basic-page:blockToolbarBulk', {
146
+ actions: [
147
+ ZovaRender.tableActionBulk('basic-table:actionCreate'),
148
+ ZovaRender.tableActionBulk('basic-table:actionDeleteBulk', {
149
+ requiresSelection: true,
150
+ }),
151
+ ],
152
+ }),
153
+ ZovaRender.block('basic-page:blockTable'),
154
+ ],
155
+ });
156
+ ```
157
+
158
+ Use `selectionPolicy: 'onDemand'` to expose **Select / Done** without a selected action, `'always'` to show row selection continuously, or `false` to disable row selection for the page. In automatic and on-demand modes, **Select** keeps the checkbox column hidden until the user enters selection mode; **Done** clears the selection and hides the column. Required actions remain discoverable but disabled until every selected row has a current snapshot and passes the projected row-permission check.
159
+
160
+ Schema metadata remains declarative: author `requiresSelection`, `selectedMaxIds`, and an unconditional `disabled` in action options. A custom bulk-action component receives current toolbar state through `dynamicSelection`, `dynamicDisabled`, and `dynamicDisabledReason`; it should disable when either `disabled` or `dynamicDisabled` is true. Frontend snapshots provide UX only—server actions must reload and authorize all submitted IDs.
161
+
162
+ Selection persists while paging the same result universe and during ordinary refetches. It clears on a material fixed-query change, filter submit/reset, sorting, page-size change, resource change, or a successful selection-consuming mutation. Page-size changes also return to page 1.
163
+
122
164
  ## Step 3: Let schema metadata drive the default columns
123
165
 
124
166
  In the default path, `ZTable` reads table-scene metadata from the row schema.
@@ -165,6 +165,7 @@ Its controller is responsible for:
165
165
  - creating query state
166
166
  - loading schema and paged data
167
167
  - exposing `data`, `schemaRow`, `schemaFilter`, `permissions`, and `paged`
168
+ - owning selected IDs, loaded-row snapshots, and the page-level `selectionPolicy`
168
169
  - refreshing table metadata when permissions change
169
170
 
170
171
  This is one of the most important architecture facts about CRUD pages.
@@ -258,6 +259,26 @@ A practical rule is:
258
259
  - if the page only needs standard create or other bulk actions, keep the existing block and adjust the metadata
259
260
  - if an action is reusable, prefer a reusable action render resource rather than page-local ad hoc code
260
261
 
262
+ ### Declare selected-row actions explicitly
263
+
264
+ `tableActionBulk` describes toolbar placement, not necessarily a batch mutation. Keep Create selection-independent. An action that consumes selected rows must declare `requiresSelection: true`:
265
+
266
+ ```ts
267
+ ZovaRender.block('basic-page:blockToolbarBulk', {
268
+ actions: [
269
+ ZovaRender.tableActionBulk('basic-table:actionCreate'),
270
+ ZovaRender.tableActionBulk('basic-table:actionDeleteBulk', {
271
+ requiresSelection: true,
272
+ selectedMaxIds: 100,
273
+ }),
274
+ ],
275
+ });
276
+ ```
277
+
278
+ With `blockPage`'s default automatic `selectionPolicy`, this exposes an on-demand **Select** control. It reveals the checkbox column; **Done** clears selection before hiding it. Coarse permission controls action discoverability. A selected action is disabled until the selection is nonempty, all selected IDs have loaded snapshots, and every snapshot passes the browser permission matcher. `selectedMaxIds` is optional action policy: when declared, the toolbar disables only that action after the configured count is exceeded; when omitted for a selection-requiring action, the frontend fallback applies.
279
+
280
+ The DTO declares static policy only. Custom bulk-action components receive the current selection and eligibility as `dynamicSelection`, `dynamicDisabled`, and `dynamicDisabledReason`. Those snapshots are only UX evidence: the command must reload, authorize, and mutate every submitted ID atomically on the backend.
281
+
261
282
  ## Step 6: Let `blockTable` bridge page state into `ZTable`
262
283
 
263
284
  The standard table block is:
@@ -271,10 +292,13 @@ Its bridge role is very clear:
271
292
  - pass row schema into `schema`
272
293
  - pass the resource order schema into `schemaOrder`
273
294
  - pass controlled `sorting` and `onSortingChange`
295
+ - pass controlled `rowSelection` and `onRowSelectionChange`
274
296
  - pass page CEL scope into `tableScope`
275
297
  - capture the table controller through `controllerRef`
276
298
 
277
- That means `blockTable` does not own list fetching or pagination logic.
299
+ `blockPage.selectionPolicy` controls row-selection behavior: `undefined` uses automatic behavior, `'always'` shows selection immediately, `'onDemand'` exposes Select/Done without a selected action, and `false` disables selection. `blockTable` only consumes the resulting page state. The header checkbox operates only on the loaded page under server pagination.
300
+
301
+ That means `blockTable` does not own list fetching, pagination, or selection policy.
278
302
 
279
303
  It owns the handoff from page resource state to the reusable table runtime.
280
304
 
@@ -25,6 +25,7 @@ interface IArticleGeometry {
25
25
  }
26
26
 
27
27
  const blogArticlePaths = [
28
+ '/blogs/ai-spec-driven-development/',
28
29
  '/blogs/ai-react-nextjs-enterprise-architecture-cabloy/',
29
30
  '/blogs/cabloy-fullstack-resource-addressing/',
30
31
  '/blogs/nextjs-integrated-fullstack-cabloy-contract-loop/',
@@ -118,29 +119,46 @@ test(
118
119
  await expect(page.locator('.VPDoc.has-aside')).toHaveCount(0);
119
120
 
120
121
  const cards = page.locator('.cabloy-blog-card');
121
- await expect(cards).toHaveCount(4);
122
- for (let index = 0; index < 4; index++) {
122
+ await expect(cards).toHaveCount(5);
123
+ for (let index = 0; index < 5; index++) {
123
124
  await expect(cards.nth(index)).toBeVisible();
124
125
  await expect(cards.nth(index).locator('.cabloy-blog-card__cover img')).toBeVisible();
125
126
  }
127
+ await expect(
128
+ cards.nth(0).getByRole('link', {
129
+ name: 'CabloyJS AI Spec-Driven Development Automatically Generates Gantt and Burndown Charts',
130
+ exact: true,
131
+ }),
132
+ ).toBeVisible();
133
+ await expect(cards.nth(0).locator('.cabloy-blog-card__date')).toHaveText('September 11, 2026');
134
+ await expect(cards.nth(0).locator('.cabloy-blog-card__cover')).toHaveAttribute(
135
+ 'href',
136
+ '/blogs/ai-spec-driven-development/',
137
+ );
126
138
 
127
139
  const geometry = await getBlogsGeometry(page);
128
140
  expect(geometry.grid.display).toBe('grid');
129
- expect(geometry.cards).toHaveLength(4);
141
+ expect(geometry.cards).toHaveLength(5);
130
142
 
131
- const [firstCard, ...remainingCards] = geometry.cards;
143
+ const [firstCard, secondCard, thirdCard, fourthCard, fifthCard] = geometry.cards;
144
+ const firstRow = [firstCard, secondCard, thirdCard, fourthCard];
132
145
  expect(firstCard.width).toBeGreaterThanOrEqual(279);
133
- for (const card of remainingCards) {
146
+ for (const card of firstRow) {
134
147
  expect(Math.abs(card.top - firstCard.top)).toBeLessThanOrEqual(1);
135
148
  expect(Math.abs(card.width - firstCard.width)).toBeLessThanOrEqual(1);
136
- expect(card.left).toBeGreaterThan(firstCard.left);
137
149
  expect(card.left).toBeGreaterThanOrEqual(geometry.grid.left - 1);
138
150
  expect(card.right).toBeLessThanOrEqual(geometry.grid.right + 1);
139
151
  expect(card.right).toBeLessThanOrEqual(1441);
140
152
  }
141
- expect(firstCard.left).toBeGreaterThanOrEqual(geometry.grid.left - 1);
142
- expect(firstCard.right).toBeLessThanOrEqual(geometry.grid.right + 1);
143
- expect(firstCard.right).toBeLessThanOrEqual(1441);
153
+ expect(secondCard.left).toBeGreaterThan(firstCard.left);
154
+ expect(thirdCard.left).toBeGreaterThan(secondCard.left);
155
+ expect(fourthCard.left).toBeGreaterThan(thirdCard.left);
156
+ expect(fifthCard.top).toBeGreaterThan(firstCard.bottom);
157
+ expect(fifthCard.left).toBeCloseTo(firstCard.left, 0);
158
+ expect(fifthCard.width).toBeCloseTo(firstCard.width, 0);
159
+ expect(fifthCard.left).toBeGreaterThanOrEqual(geometry.grid.left - 1);
160
+ expect(fifthCard.right).toBeLessThanOrEqual(geometry.grid.right + 1);
161
+ expect(fifthCard.right).toBeLessThanOrEqual(1441);
144
162
 
145
163
  await expect.poll(() => getDocumentHorizontalOverflow(page)).toBeLessThanOrEqual(1);
146
164
  expect(pageErrors).toEqual([]);
@@ -163,6 +181,24 @@ test(
163
181
  await expect(page.locator('.VPDoc.has-sidebar')).toHaveCount(0);
164
182
  await expect(page.locator('.VPDoc.has-aside')).toHaveCount(1);
165
183
  await expect(page.locator('.aside')).toBeVisible();
184
+ if (path === '/blogs/ai-spec-driven-development/') {
185
+ await expect(
186
+ page.getByRole('heading', {
187
+ name: 'CabloyJS AI Spec-Driven Development Automatically Generates Gantt and Burndown Charts',
188
+ level: 1,
189
+ }),
190
+ ).toBeVisible();
191
+ await expect(
192
+ page.getByAltText(
193
+ 'A-Commerce implementation roadmap: WBS phases, dependencies, and derived status',
194
+ ),
195
+ ).toBeVisible();
196
+ await expect(
197
+ page.getByAltText(
198
+ 'A-Commerce WBS scope-count burndown: approved scope and remaining-item counts',
199
+ ),
200
+ ).toBeVisible();
201
+ }
166
202
 
167
203
  const geometry = await getArticleGeometry(page);
168
204
  expect(geometry.container.width).toBeCloseTo(1216, 0);