cabloy 5.1.203 → 5.1.205

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 (127) hide show
  1. package/.cabloy-version +1 -1
  2. package/.gitignore +2 -0
  3. package/CHANGELOG.md +21 -0
  4. package/lint-staged.config.mjs +2 -0
  5. package/oxfmt.config.ts +2 -0
  6. package/oxlint.config.ts +2 -0
  7. package/package.json +6 -5
  8. package/repo-agent-governance/managed-assets.json +9 -9
  9. package/repo-agent-governance/policies/repository.md +5 -2
  10. package/repo-agent-governance/skills/cabloy-backend-scaffold/references/follow-up-checklist.md +2 -2
  11. package/repo-agent-governance/skills/cabloy-workflow/SKILL.md +2 -0
  12. package/repo-docs/backend/migration-and-changes.md +13 -12
  13. package/repo-docs/backend/orm-select-guide.md +6 -0
  14. package/repo-docs/backend/runtime-and-flavors.md +2 -1
  15. package/repo-docs/backend/transaction-guide.md +12 -0
  16. package/repo-docs/backend/unit-testing.md +12 -8
  17. package/repo-docs/blogs/ai-coding-one-prompt-crud-ssr-multitasking/cover-en-v1.png +0 -0
  18. package/repo-docs/blogs/ai-coding-one-prompt-crud-ssr-multitasking/images/completion-summary.jpg +0 -0
  19. package/repo-docs/blogs/ai-coding-one-prompt-crud-ssr-multitasking/images/multitasking-draft.jpg +0 -0
  20. package/repo-docs/blogs/ai-coding-one-prompt-crud-ssr-multitasking/images/ssr-refresh.jpg +0 -0
  21. package/repo-docs/blogs/ai-coding-one-prompt-crud-ssr-multitasking/index.md +216 -0
  22. package/repo-docs/blogs/index.md +15 -0
  23. package/repo-docs/frontend/scripts.md +1 -1
  24. package/repo-docs/fullstack/parallel-worktree-environment.md +6 -6
  25. package/repo-docs/fullstack/ssr-entry-modes.md +19 -0
  26. package/repo-docs/index.md +2 -0
  27. package/repo-docs/reference/repo-scripts.md +22 -1
  28. package/repo-e2e/config/playwright.config.ts +32 -14
  29. package/repo-e2e/docs/specs/blogs-index.spec.ts +23 -6
  30. package/repo-e2e/scripts/e2e.test.ts +39 -0
  31. package/repo-e2e/scripts/e2e.ts +19 -1
  32. package/repo-e2e/scripts/managedTest.test.ts +389 -0
  33. package/repo-e2e/scripts/managedTest.ts +210 -0
  34. package/repo-e2e/scripts/managedTestImports.test.ts +74 -0
  35. package/repo-e2e/scripts/managedTestImports.ts +110 -0
  36. package/repo-e2e/scripts/runE2e.ts +12 -1
  37. package/repo-e2e/scripts/startE2eVona.ts +37 -5
  38. package/repo-e2e/specs/a-commerce.spec.ts +1 -1
  39. package/repo-e2e/specs/cabloy-basic.spec.ts +1 -1
  40. package/repo-e2e/specs/controller-boundary.spec.ts +1 -1
  41. package/repo-e2e/specs/home-user-account.spec.ts +2 -1
  42. package/repo-e2e/specs/markdown.spec.ts +1 -1
  43. package/repo-e2e/specs/routed-dialog.spec.ts +1 -1
  44. package/scripts/runIsolatedTest.test.ts +426 -0
  45. package/scripts/runIsolatedTest.ts +454 -0
  46. package/scripts/testClaimCleanup.test.ts +346 -0
  47. package/scripts/testClaimCleanup.ts +375 -0
  48. package/scripts/testClaimProcesses.test.ts +106 -0
  49. package/scripts/testClaimProcesses.ts +129 -0
  50. package/scripts/testProcessGuardian.ts +163 -0
  51. package/scripts/upgrade.ts +1 -16
  52. package/vona/oxfmt.config.ts +2 -0
  53. package/vona/oxlint.config.ts +2 -0
  54. package/vona/package.original.json +2 -2
  55. package/vona/packages-cli/cli/package.json +1 -1
  56. package/vona/packages-cli/cli/src/play.test.ts +37 -0
  57. package/vona/packages-cli/cli/src/play.ts +23 -1
  58. package/vona/packages-cli/cli-set-api/package.json +1 -1
  59. package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.dbReset.ts +16 -7
  60. package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.dev.ts +13 -7
  61. package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.play.ts +5 -1
  62. package/vona/packages-cli/cli-set-api/src/lib/bean/cli.bin.test.ts +12 -7
  63. package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/configUtils.ts +21 -2
  64. package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/generateVonaMeta.ts +10 -0
  65. package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/resolveTestCleanupConfig.test.ts +153 -0
  66. package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/resolveTestCleanupConfig.ts +141 -0
  67. package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/testCleanupAdapters.test.ts +148 -0
  68. package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/testCleanupAdapters.ts +127 -0
  69. package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/testCleanupProcess.test.ts +47 -0
  70. package/vona/packages-cli/cli-set-api/src/lib/bean/toolsBin/testCleanupProcess.ts +139 -0
  71. package/vona/packages-cli/cli-set-api/src/lib/utils.ts +2 -3
  72. package/vona/packages-cli/cli-set-api/templates/config/_tsconfig_project.json +1 -0
  73. package/vona/packages-cli/cli-set-api/templates/config/_tsconfig_source.json +1 -0
  74. package/vona/packages-cli/cli-set-api/test/binDevMode.test.ts +18 -0
  75. package/vona/packages-cli/cli-set-api/toolsIsolate/dbReset.ts +15 -2
  76. package/vona/packages-cli/cli-set-api/tsconfig.build.json +2 -1
  77. package/vona/packages-utils/babel-plugin-vona-bean-module/package.json +1 -1
  78. package/vona/packages-utils/babel-plugin-vona-bean-module/src/index.test.ts +16 -0
  79. package/vona/packages-utils/babel-plugin-vona-bean-module/src/index.ts +3 -2
  80. package/vona/packages-vona/vona/package.json +1 -1
  81. package/vona/packages-vona/vona-core/package.json +1 -1
  82. package/vona/packages-vona/vona-core/src/lib/addon/sqlite3.ts +1 -1
  83. package/vona/packages-vona/vona-core/src/lib/core/config.ts +44 -6
  84. package/vona/packages-vona/vona-core/src/lib/core/configCleanup.test.ts +40 -0
  85. package/vona/packages-vona/vona-core/src/lib/core/index.ts +3 -0
  86. package/vona/packages-vona/vona-core/src/lib/core/isolationGuard.test.ts +301 -0
  87. package/vona/packages-vona/vona-core/src/lib/core/isolationGuard.ts +312 -0
  88. package/vona/packages-vona/vona-core/src/lib/core/testClaim.test.ts +577 -0
  89. package/vona/packages-vona/vona-core/src/lib/core/testClaim.ts +985 -0
  90. package/vona/packages-vona/vona-core/src/lib/core/testIsolation.ts +129 -0
  91. package/vona/packages-vona/vona-core/src/lib/framework/createApp.ts +2 -0
  92. package/vona/packages-vona/vona-core/src/lib/framework/start.ts +4 -0
  93. package/vona/packages-vona/vona-core/src/lib/framework/useApp.ts +5 -1
  94. package/vona/packages-vona/vona-core/src/types/application/app.ts +2 -0
  95. package/vona/packages-vona/vona-core/src/types/utils/env.ts +1 -0
  96. package/vona/packages-vona/vona-mock/package.json +1 -1
  97. package/vona/pnpm-lock.yaml +40 -40
  98. package/vona/src/suite/a-commerce/modules/commerce-payment/test/paymentIndexes.test.ts +73 -10
  99. package/vona/src/suite-vendor/a-cabloy/modules/a-rbac/package.json +1 -1
  100. package/vona/src/suite-vendor/a-cabloy/package.json +2 -2
  101. package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
  102. package/vona/src/suite-vendor/a-vona/modules/a-logger/package.json +1 -1
  103. package/vona/src/suite-vendor/a-vona/modules/a-logger/src/bean/middlewareSystem.httpLog.ts +15 -1
  104. package/vona/src/suite-vendor/a-vona/modules/a-logger/src/lib/redactIsolatedReadyHeader.test.ts +24 -0
  105. package/vona/src/suite-vendor/a-vona/modules/a-logger/src/lib/redactIsolatedReadyHeader.ts +11 -0
  106. package/vona/src/suite-vendor/a-vona/modules/a-metrics/package.json +1 -1
  107. package/vona/src/suite-vendor/a-vona/modules/a-metrics/src/bean/middlewareSystem.health.ts +15 -0
  108. package/vona/src/suite-vendor/a-vona/modules/a-orm/package.json +1 -1
  109. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/extend/schemaBuilder.ts +11 -4
  110. package/vona/src/suite-vendor/a-vona/modules/a-orm/src/service/databaseClient_.ts +6 -1
  111. package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/package.json +1 -1
  112. package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/src/bean/databaseDialect.betterSqlite3.ts +41 -1
  113. package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/src/bean/databaseDialect.mysql.ts +4 -1
  114. package/vona/src/suite-vendor/a-vona/modules/a-ormdialect/src/bean/databaseDialect.pg.ts +2 -1
  115. package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
  116. package/vona/src/suite-vendor/a-vona/modules/a-redis/package.json +1 -1
  117. package/vona/src/suite-vendor/a-vona/modules/a-redis/src/lib/redis.ts +8 -0
  118. package/vona/src/suite-vendor/a-vona/modules/a-redis/src/service/redis.ts +23 -6
  119. package/vona/src/suite-vendor/a-vona/modules/a-redis/src/service/redisClient_.ts +18 -1
  120. package/vona/src/suite-vendor/a-vona/modules/a-startup/package.json +1 -1
  121. package/vona/src/suite-vendor/a-vona/modules/a-startup/src/service/startup.ts +43 -15
  122. package/vona/src/suite-vendor/a-vona/modules/a-version/package.json +1 -1
  123. package/vona/src/suite-vendor/a-vona/modules/a-version/src/lib/databaseTestName.ts +3 -37
  124. package/vona/src/suite-vendor/a-vona/modules/a-version/src/service/database.ts +93 -15
  125. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  126. package/vona/tsconfig.json +1 -0
  127. package/scripts/initTestData.ts +0 -25
@@ -0,0 +1,216 @@
1
+ ---
2
+ title: 'AI Coding in Action: One Prompt for CRUD, SSR & Multitasking'
3
+ titleZh: AI 编码实战:一句提示词,实现 CRUD、SSR 与多任务
4
+ titleEn: 'AI Coding in Action: One Prompt for CRUD, SSR & Multitasking'
5
+ subtitle: A hands-on book management walkthrough, from AI-assisted development to using the Admin interface in CabloyJS.
6
+ summary: Give Codex one prompt to create a standalone Book CRUD module in CabloyJS. Then start the servers, open the Admin interface, create a record, refresh the list, and switch between edit, detail, and draft tasks. This walkthrough gives you a practical feel for developing with CabloyJS and using the resulting feature.
7
+ tags:
8
+ - AI Coding
9
+ - Codex
10
+ - CabloyJS
11
+ - Vona
12
+ - Zova
13
+ - CRUD
14
+ - SSR
15
+ - Multitasking
16
+ slug: ai-coding-one-prompt-crud-ssr-multitasking-en
17
+ cover: ./cover-en-v1.png
18
+ date: 2026-10-09
19
+ ---
20
+
21
+ # AI Coding in Action: One Prompt for CRUD, SSR & Multitasking
22
+
23
+ If you ask AI to build a book management feature, what do you expect to get back?
24
+
25
+ A set of APIs and a few source files—or an Admin page you can open in a browser and actually use?
26
+
27
+ Let's try it in **CabloyJS**. We'll give Codex one prompt to develop and test a Book CRUD module. Then we'll start the servers, open the Admin interface, create a book, and keep edit, detail, and create tasks open at the same time.
28
+
29
+ You'll see more than how AI writes code. You'll get a feel for the finished feature: **what happens when you refresh the list, how you switch between tasks, and whether an unsubmitted form keeps its input.**
30
+
31
+ ## 1. Start with One Prompt
32
+
33
+ Open Codex in an existing, configured CabloyJS project and enter:
34
+
35
+ ```text
36
+ Create a standalone demo-book module to implement CRUD operations for books.
37
+ ```
38
+
39
+ This gives Codex two clear goals:
40
+
41
+ - Create a standalone `demo-book` module.
42
+ - Implement CRUD—create, read, update, and delete—for books.
43
+
44
+ We're using a Cabloy Basic project, with **Vona** on the backend and **Zova** on the frontend. The project already has a module structure, development commands, and an Admin interface. Our job is to add a business feature on top of that foundation.
45
+
46
+ The prompt doesn't need to specify every controller, data model, form component, or page layout. Codex first reads the project conventions, examines the existing structure and tools, and follows the project's development workflow.
47
+
48
+ ### What Does AI Actually Do?
49
+
50
+ For this feature, Codex's work follows four steps:
51
+
52
+ ```text
53
+ Understand the project → Run the CRUD generator → Refine fields and integration → Run tests and checks
54
+ ```
55
+
56
+ It examines the project and generator commands, then uses Vona's CRUD generator to create the module. Next, it refines the book fields, validation, menu integration, and supporting code so the resource becomes available in the existing Admin interface.
57
+
58
+ The resulting book form has three fields:
59
+
60
+ | Field | Purpose | Requirement |
61
+ | ----------- | ---------------- | ----------- |
62
+ | Title | Book title | Required |
63
+ | Author | Book author | Required |
64
+ | Description | Book description | Optional |
65
+
66
+ One useful part of the development experience is immediately apparent: **once the business resource is defined, the list, detail view, and forms can reuse CabloyJS's existing schema-driven CRUD pages. There is no need to build a new management interface from scratch.**
67
+
68
+ AI adds the business module; the framework supplies the reusable capabilities. Together, they let you focus on the fields and operations a book needs rather than rebuilding page structures and basic interactions for every resource.
69
+
70
+ ## 2. Development Includes Tests
71
+
72
+ After creating the module, Codex continues with tests, TypeScript checks, linting, and formatting checks.
73
+
74
+ This development task took **13 minutes and 57 seconds**. Codex's completion summary reports:
75
+
76
+ - All five Book module tests passed.
77
+ - The backend test suite had 269 passed, ten skipped, and zero failed.
78
+ - TypeScript, lint, and formatting checks passed.
79
+
80
+ ![Codex's completion summary, including task duration, implemented capabilities, and test results](./images/completion-summary.jpg)
81
+
82
+ The implemented capabilities include create, paginated listing, detail viewing, update, delete, and transactional bulk deletion.
83
+
84
+ At this point, a single business request has led to a module implementation and automated checks. Now let's start the servers and see what the feature feels like in the interface.
85
+
86
+ ## 3. Two Commands to Open Book Management
87
+
88
+ In the project root, open the first terminal and start the backend development server:
89
+
90
+ ```bash
91
+ npm run dev
92
+ ```
93
+
94
+ Open a second terminal and start the Admin frontend development server:
95
+
96
+ ```bash
97
+ npm run dev:zova:admin
98
+ ```
99
+
100
+ Once the servers are ready, open the Admin interface and select **Book** from the menu on the left.
101
+
102
+ The book list is already part of the existing Admin layout: the management menu on the left, search filters and records in the main area, and page navigation above. Creating, searching, and working with records all happen in the same interface.
103
+
104
+ That's the first tangible result of adding the module: **you haven't just gained source files. You have a new business feature you can open and use.**
105
+
106
+ ### Refresh the List and Experience SSR
107
+
108
+ The book list supports SSR, or server-side rendering.
109
+
110
+ Refresh the browser while on the list page. The layout shows no obvious flicker during the refresh and retains the familiar Admin structure.
111
+
112
+ ![The book list, with search filters, a records area, and the management menu](./images/ssr-refresh.jpg)
113
+
114
+ For someone using the application, the first impression is not the implementation details of SSR—it's how the page appears when opened or refreshed. For the developer, the Book module is now part of CabloyJS's SSR-enabled Admin interface, without having to build an SSR mechanism specifically for this CRUD feature.
115
+
116
+ ## 4. Create a Book: From Form to List
117
+
118
+ Click **Create** to open the book form and enter:
119
+
120
+ ```text
121
+ Title: Book One
122
+ Author: Kevin
123
+ ```
124
+
125
+ Description is optional, so leave it empty for now and click **Submit**.
126
+
127
+ Back in the list, you'll see Book One with Kevin as its author.
128
+
129
+ It's a simple operation, but it brings the frontend and backend together: open a form, enter the fields, submit the data, and see the result in the list. The module AI just created can now carry out a real business operation.
130
+
131
+ Next, instead of finishing with this book, let's keep working with it as an open task.
132
+
133
+ ## 5. Keep Three Tasks Open Without Losing Input
134
+
135
+ In an Admin application, it's common to start editing a record and then need to check its details. Or perhaps you've begun filling out a new form but need to deal with something else before submitting it.
136
+
137
+ If leaving the page means reopening it—or entering everything again—your work gets interrupted.
138
+
139
+ CabloyJS's Admin interface provides a second row of task tabs. Let's use Book One and Book Two to see how they work.
140
+
141
+ ### Open Book One for Editing
142
+
143
+ From the list, open Book One's edit page. Its title and author are already filled in.
144
+
145
+ Now return to the book list. Look at the tabs above: **Book One's edit task is still open.** Returning to the list doesn't mean you have to close the edit page.
146
+
147
+ ### Open Book One's Details as Another Task
148
+
149
+ Next, open Book One's detail page from the list.
150
+
151
+ You now have two tasks for the same book: one for editing and one for viewing details. You can choose whichever you need without going back to the list and finding the record each time.
152
+
153
+ ### Start Book Two, but Don't Submit It
154
+
155
+ Return to the list, click Create, and enter a second book:
156
+
157
+ ```text
158
+ Title: Book Two
159
+ Author: Tom
160
+ ```
161
+
162
+ Don't submit this time. Leave the form as a draft.
163
+
164
+ The task tabs now contain three open tasks:
165
+
166
+ | Task | Content |
167
+ | ------ | --------------------------- |
168
+ | Edit | Book One's edit form |
169
+ | Detail | Book One's detail view |
170
+ | Create | Book Two's unsubmitted form |
171
+
172
+ ### Switch Back and Continue Where You Left Off
173
+
174
+ Switch to Book One's edit task, then its detail task, and finally back to Book Two's create task.
175
+
176
+ The title is still `Book Two`, and the author is still `Tom`. The edit and detail tasks also remain open, so you can continue switching among all three.
177
+
178
+ ![Returning to Book Two's create task: the title and author are preserved, with all three task tabs still open above](./images/multitasking-draft.jpg)
179
+
180
+ **Book Two hasn't been submitted, but switching tasks hasn't lost the input.**
181
+
182
+ That makes the task tabs more than navigation shortcuts. Each task keeps its current working context. You can check another piece of information and then return to finish the form, without submitting early just to leave the page or retyping everything when you come back.
183
+
184
+ The experience feels closer to multitasking in a desktop application: several pieces of work can stay open together, rather than forcing you to finish one before moving to the next.
185
+
186
+ ## 6. A Practical Introduction to Developing with CabloyJS
187
+
188
+ Looking back, we started with just one business request:
189
+
190
+ ```text
191
+ Create a standalone demo-book module to implement CRUD operations for books.
192
+ ```
193
+
194
+ AI developed and checked the module. After starting the servers, we could open the book list, create a record, and use the SSR-enabled pages and task tabs.
195
+
196
+ This walkthrough gives you a direct feel for several aspects of CabloyJS:
197
+
198
+ - **Modular development:** the new Book feature is organized as a standalone `demo-book` module.
199
+ - **AI working with existing tools:** AI examines the project and uses its generators and commands instead of introducing a separate implementation approach.
200
+ - **Schema-driven management pages:** business fields become part of the list, detail view, and forms, with reusable page capabilities.
201
+ - **SSR page experience:** the new resource joins the existing SSR-enabled Admin interface.
202
+ - **Multitasking:** edit, detail, and create tasks stay open together, preserving form input when you switch.
203
+
204
+ CRUD is the business feature at the center of the walkthrough. SSR and multitasking show what it's like to use that feature inside an actual Admin application.
205
+
206
+ These aren't infrastructure capabilities AI has rewritten from scratch. They are capabilities CabloyJS already provides. **AI develops the business feature within the framework, and the new feature benefits from the framework's existing pages and interactions.**
207
+
208
+ ## Give AI a Request, Get a Feature You Can Use
209
+
210
+ If you'd like to explore CabloyJS, a small feature like this is a good place to start.
211
+
212
+ Give AI a clear request. See how it creates the module, refines the fields, and runs checks. Then open the browser, create a record yourself, refresh the list, and keep a few tasks open at once.
213
+
214
+ Moving from “What code did it generate?” to “What does this feature feel like to use?” turns modules, schemas, and SSR from abstract terms into a concrete development and application experience.
215
+
216
+ **That's the point of hands-on AI coding: turn a request into a feature you can operate, and make the framework's capabilities something you can see and use.**
@@ -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-coding-one-prompt-crud-ssr-multitasking/" aria-label="Read AI Coding in Action: One Prompt for CRUD, SSR &amp; Multitasking">
14
+ <img src="/blogs/ai-coding-one-prompt-crud-ssr-multitasking/cover-en-v1.png" alt="AI Coding in Action: One Prompt for CRUD, SSR &amp; Multitasking" />
15
+ </a>
16
+ <div class="cabloy-blog-card__content">
17
+ <p class="cabloy-blog-card__date">October 9, 2026</p>
18
+ <h2><a href="/blogs/ai-coding-one-prompt-crud-ssr-multitasking/">AI Coding in Action: One Prompt for CRUD, SSR &amp; Multitasking</a></h2>
19
+ <p>Follow one prompt from AI-assisted Book CRUD development to a working CabloyJS Admin interface, SSR refresh, and task switching that preserves form input.</p>
20
+ <div class="cabloy-blog-card__tags" aria-label="Article topics">
21
+ <span>AI Coding</span><span>CabloyJS</span><span>SSR</span><span>Multitasking</span>
22
+ </div>
23
+ <a class="cabloy-blog-card__read" href="/blogs/ai-coding-one-prompt-crud-ssr-multitasking/">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/ai-spec-driven-development/" aria-label="Read CabloyJS AI Spec-Driven Development Automatically Generates Gantt and Burndown Charts">
14
29
  <img src="/blogs/ai-spec-driven-development/cover-en-v1.png" alt="CabloyJS AI Spec-Driven Development Automatically Generates Gantt and Burndown Charts" />
@@ -51,7 +51,7 @@ The `dev:zova:*` commands start the Zova development server, which uses `9000` i
51
51
 
52
52
  ## Vona integrated SSR browser acceptance
53
53
 
54
- The default Basic Web and Admin sites have browser smoke commands that exercise Vona integrated SSR through Vona SSR dispatch at port `7102`, not through the Zova standalone SSR development-server port.
54
+ The default Basic Web and Admin sites have browser smoke commands that exercise Vona integrated SSR through Vona SSR dispatch at the managed run's dynamically allocated test listener, not through the ordinary Vona development listener (`7102` by default) or the Zova standalone SSR development-server port. Vona supplies the API origin at runtime, so no Zova `.env` change or rebuild is needed solely for the test port; see [Runtime API origins in Vona integrated SSR](/fullstack/ssr-entry-modes#runtime-api-origins-in-vona-integrated-ssr).
55
55
 
56
56
  Prepare current SSR and REST artifacts explicitly when frontend output has changed:
57
57
 
@@ -1,10 +1,10 @@
1
1
  # Parallel Worktree Environment
2
2
 
3
- Use worktree-local environment overrides when two Cabloy Basic or Cabloy Start worktrees need to run ordinary local development or managed E2E checks at the same time.
3
+ Use worktree-local environment overrides when two Cabloy Basic or Cabloy Start worktrees need to run ordinary local development at the same time. In Cabloy Basic, root `npm run test` and `npm run test:e2e` already isolate their test-mode runtime and data on every run, including alongside ordinary dev in the **same** checkout; they do not require this worktree setup for that purpose.
4
4
 
5
- This Cabloy Basic page is the canonical shared recipe for both editions. Detect the active edition before selecting commands, but keep the same two-file environment model.
5
+ This Cabloy Basic page is the canonical shared recipe for both editions. Detect the active edition before selecting commands, but keep the same two-file environment model for worktree-local development.
6
6
 
7
- This workflow creates a standard isolated local runtime without changing committed environment defaults. It does not automatically isolate every external dependency.
7
+ This workflow creates a standard isolated local development runtime without changing committed environment defaults. It does not automatically isolate every external dependency.
8
8
 
9
9
  ## Before creating local overrides
10
10
 
@@ -82,13 +82,13 @@ Use commands from the active repository root.
82
82
  | Cabloy Basic | `__CABLOY_BASIC__` | `npm run dev:zova:admin` or `npm run dev:zova:web` | `npm run test:e2e` |
83
83
  | Cabloy Start | `__CABLOY_START__` | `npm run dev:zova:admin` or `npm run dev:zova:web` | `npm run test:e2e` |
84
84
 
85
- Run one frontend command, not both, in each worktree. Both managed E2E workflows read Vona's effective local `SERVER_LISTEN_PORT` and start one fresh Vona test-mode worker; startup initializes test resources without a separate reset. Before either command, confirm exclusive ownership of the effective test database, `_local` Redis namespace, public/runtime paths, `APP_NAME`, API origin, and listener. A free port alone does not establish ownership. Do not change shared identity or ports to bypass a collision, and leave `E2E_BASE_URL` unset: externally managed targets are unsupported.
85
+ Run one frontend command, not both, in each worktree. In Cabloy Basic, root `npm run test` and `npm run test:e2e` allocate a fresh run-specific identity, test database and Redis namespace, generated/runtime/public paths, and loopback listener independently of the worktree's development environment. Managed E2E starts a fresh Vona test-mode worker, and startup initializes test resources and module seeds without a separate dev-mode reset. This also isolates tests from a dev process in the same checkout. Cabloy Start's managed E2E uses the effective local test-mode Vona listener and its own ownership rules; inspect its root scripts before relying on Basic's isolation. Leave `E2E_BASE_URL` unset: externally managed targets are unsupported. Do not change shared identity or ports merely to bypass a busy development resource.
86
86
 
87
87
  ## What this isolates
88
88
 
89
- A unique `APP_NAME` separates the application identity used by ordinary framework-managed local resources, including managed test database names and Redis-related key prefixes. Worktree-local generated files, runtime files, build output, and coverage output are already separated by their worktree paths.
89
+ A unique worktree `APP_NAME` separates the identity of ordinary development resources across worktrees, including framework-managed development database names and Redis-related key prefixes. Worktree-local generated files, runtime files, build output, and coverage output are already separated by their worktree paths. Cabloy Basic root tests use a separate run-specific identity even when invoked inside a worktree; they do not clean its ordinary development business data.
90
90
 
91
- The generated Vona, Zova development HTTP, and Zova HMR ports let worktrees run their ordinary local processes concurrently. The matching Zova API URL prevents the frontend in one worktree from calling the Vona process in another.
91
+ The generated Vona, Zova development HTTP, and Zova HMR ports let worktrees run their ordinary local processes concurrently. The matching Zova API URL prevents the frontend in one worktree from calling the Vona process in another. Builds, dependency generation, and other shared preparation writes remain accepted prerequisites rather than part of test data cleanup.
92
92
 
93
93
  This is sufficient for the normal local workflows:
94
94
 
@@ -15,6 +15,25 @@ Zova standalone SSR: browser → Zova development server → frontend SSR →
15
15
 
16
16
  In the Cabloy Basic default environment, `SERVER_LISTEN_PORT=7102` is the Vona entry and `DEV_SERVER_PORT=9000` is the Zova development entry. These are separate listeners, not two ports for the same server. Zova's `API_BASE_URL` points to Vona, not to its own development port. Local environment overrides can change the ports; use the effective values in your checkout.
17
17
 
18
+ ## Runtime API origins in Vona integrated SSR
19
+
20
+ Vona integrated SSR supplies API origins at runtime; this mechanism is not specific to an edition or frontend flavor. By default, Vona derives the origin from its effective protocol and host and passes:
21
+
22
+ - `envServer.SSR_API_BASE_URL` to the built Zova server bundle during initialization.
23
+ - `envClient.API_BASE_URL` through the SSR state to the browser during client initialization.
24
+
25
+ These runtime values override Zova's build-time environment defaults used by the normal API client. They do not rewrite hard-coded URLs or arbitrary build-time substitutions in application code.
26
+
27
+ For an isolated `npm run test:e2e` that enters through Vona integrated SSR, the runner's dynamic Vona origin is therefore sufficient: **do not edit Zova `.env` files or rebuild the flavor solely to match the test port**. A new listener address alone does not make the bundle stale.
28
+
29
+ Artifact preparation is a separate concern. When frontend code, routes, metadata, or other build inputs change, refresh the required SSR/REST artifacts and synchronize Vona dependencies as required. Resolve commands and flavors from the active repository's scripts; do not assume every edition's E2E command builds the frontend automatically. See [SSR Build and Deploy Guide](/frontend/ssr-build-deploy-guide) and [Repo Scripts](/reference/repo-scripts).
30
+
31
+ The injected origins are defaults, with these boundaries:
32
+
33
+ - Vona merges the default origins first, then the `a-ssr` module's `site.default` configuration, then the individual SSR Site options. An explicit `envServer.SSR_API_BASE_URL` or `envClient.API_BASE_URL` in those site settings overrides the corresponding default; a value only in Zova's build-time `.env` does not count as a site override.
34
+ - Generated OpenAPI clients prefer their module-specific `OPENAPI_BASE_URL_*`, then `OPENAPI_BASE_URL_DEFAULT`, before the server or browser API-origin fallback. Custom API configuration and hard-coded URLs also require separate inspection when requests reach the wrong origin.
35
+ - Zova standalone SSR and SPA do not automatically receive this Vona integrated SSR handoff. Keep their environment setup separate from this rule, including deliberate [parallel worktree development configuration](/fullstack/parallel-worktree-environment).
36
+
18
37
  ## Start and use them in Cabloy Basic
19
38
 
20
39
  Start the Vona server from the repository root and visit a Vona-served site:
@@ -36,6 +36,8 @@ features:
36
36
 
37
37
  [![CabloyJS AI CRUD video](./assets/img/cabloy-ai-crud-cover-en.png)](https://youtu.be/PV9_By08hDM)
38
38
 
39
+ [Read the hands-on walkthrough: AI Coding in Action — One Prompt for CRUD, SSR & Multitasking](/blogs/ai-coding-one-prompt-crud-ssr-multitasking/)
40
+
39
41
  ## Choose a reading path
40
42
 
41
43
  ### Start a project
@@ -41,6 +41,7 @@ Cabloy Basic exposes these shared root scripts:
41
41
  - `npm run start:one`
42
42
  - `npm run test`
43
43
  - `npm run test:e2e`
44
+ - `npm run db:reset`
44
45
  - `npm run tsc`
45
46
  - `npm run docs:dev`
46
47
  - `npm run docs:build`
@@ -48,6 +49,26 @@ Cabloy Basic exposes these shared root scripts:
48
49
 
49
50
  `npm run init` prepares all Cabloy Basic SSR and REST artifacts with `npm run build:zova:all`, which sequentially builds the Basic and Commerce flavor batches before Vona initialization. Use `build:zova` or `build:zova:commerce` for focused artifact refreshes; use `build:zova:all` only when every shipped Basic flavor must be prepared.
50
51
 
52
+ ## Test isolation and development data in Cabloy Basic
53
+
54
+ Root `npm run test` delegates to Vona's `npm run test`; both it and direct `cd vona && npm run test` use the same isolated runner. Vona's `npm run cov` also uses that runner and enables coverage. Root `npm run test:e2e` retains its own isolated E2E entrypoint. Each command launches a new test-mode run, even in the same checkout as a running development server. The runner chooses a UTC `YYYYMMDDHHmmss` timestamp plus eight lowercase cryptographically random hexadecimal characters for one run ID (for example, `20261014010203-a1b2c3d4`). This ID names both owned Vona directories (`.app-test-<id>` and `.vona-test-<id>`) and the disposable database and Redis namespaces; the runner also allocates a loopback listener. The runner exclusively creates an ignored JSON claim at `vona/.vona-test-claim/<id>.json` before allocating resources. A rare claim or directory collision fails without adopting or deleting the existing paths; rerun the command. Independent checkouts sharing database or Redis services do not share these claims, so their random suffixes reduce, but cannot eliminate, cross-checkout collisions.
55
+
56
+ Claims contain a versioned, nonsecret resource inventory: owned local roots, exact database allocation intents, Redis namespaces and endpoint identities, process ownership, and cleanup progress. Allocators must receive an acknowledgment that their intent is recorded before creating a database or using framework-managed Redis resources. Credentials, full environment/configuration snapshots, and readiness secrets are not stored in claims.
57
+
58
+ Successful unit, coverage, and managed application E2E runs release inventoried database and Redis resources and remove their owned local roots after process shutdown is verified. Managed E2E uses a registered browser fixture to verify detached browser-group shutdown; its run-owned reports, traces, and temporary profiles are deleted on success too. The claim is deleted last. Failed or interrupted runs retain their claim and resources for inspection. Cleanup failure also makes the command fail and retains the journal for retry.
59
+
60
+ Before each new invocation, the runner checks retained claims against their immutable UTC creation timestamp. Claims older than one day are eligible for cleanup; set `VONA_TEST_CLAIM_TTL_SECONDS` to a positive integer to change the default `86400` seconds. Eligibility is not permission to delete a live or unowned resource: the runner retains malformed claims, mismatched owner markers, symlinked paths, changed service endpoints, live or uncertain process identities, incomplete launch registrations, and claims with an existing cleanup lock. Inspect these warnings manually; a lock is never taken over merely because it is old. Cleanup resolves current trusted configuration without starting the application, running seeds, or opening a listener. It drops only recorded database names and scans only exact recorded Redis prefixes, never flushing Redis. Partial progress is journaled so a later eligible cleanup can retry safely. Legacy `.vona-test-claim-<id>` directories and inventory-less run roots are not automatically migrated or deleted.
61
+
62
+ All three commands initialize test databases and run module `seed()` hooks; none resets the ordinary dev-mode database or deletes its business data. A narrow test selection still initializes the test environment. Raw `:bin:test` invocations bypass the public scripts and do not get the runner's isolation guarantee.
63
+
64
+ Coverage reports (`vona/coverage/lcov.info` and, without `--ci`, `vona/coverage/report`) are shared checkout-level artifacts, not run-owned data. Avoid overlapping coverage runs or relying on these reports as independent per-run output.
65
+
66
+ Builds, generated artifacts, dependency installation/generation, and other shared preparation writes remain accepted prerequisites when needed. Test isolation does not make those writes private or authorize an unrelated reset of development data. The run-specific identity namespaces framework-managed resources; separately configured external databases, Redis targets or unprefixed custom keys, and third-party providers still require deliberate ownership and isolation. Inspect the active edition's scripts rather than assuming Cabloy Start already uses the same root runner.
67
+
68
+ For Vona integrated SSR, the allocated E2E origin is supplied to Zova at runtime as server `SSR_API_BASE_URL` and browser `API_BASE_URL`. Do not edit Zova `.env` files or rebuild solely to match the test port. The current root `npm run test:e2e` consumes prebuilt frontend artifacts; refresh affected SSR/REST output and synchronize dependencies when their inputs change. See [Runtime API origins in Vona integrated SSR](/fullstack/ssr-entry-modes#runtime-api-origins-in-vona-integrated-ssr) for configuration overrides and scope.
69
+
70
+ For a deliberate **dev-mode** database recreation, `npm run db:reset` is a separate, destructive command: it drops matching disposable development databases, reruns migrations and instance initialization, then runs module `seed()` hooks. It requires `database.testDatabase: true` in the dev configuration and a connection outside the disposable database. Ordinary `npm run dev` does not seed or reset; never run `db:reset` against development data you need to retain. Upgrades no longer initialize development test data automatically. See [Migration and Changes](/backend/migration-and-changes#local-development-workflow).
71
+
51
72
  ## Specification planning and derived charts
52
73
 
53
74
  The current Cabloy Basic root scripts also expose:
@@ -151,7 +172,7 @@ The public-package upgrade flow does not source or reconcile the Start-owned bas
151
172
 
152
173
  ## SSR browser checks
153
174
 
154
- Cabloy Basic and Cabloy Start each use a single managed local `npm run test:e2e` command. In Basic, the runner checks the effective `normal/test/local` Vona listener port and starts one fresh `--workers=1 --flavor=normal --mode=test` worker. Test-mode startup initializes test resources; the runner does not invoke a separate `db:reset` or a development-mode server. Playwright does not reuse an existing process. Confirm exclusive ownership of the effective test database, `_local` Redis namespace, public/runtime paths, `APP_NAME`, API origin, and listener before starting it; port availability alone is insufficient.
175
+ Cabloy Basic and Cabloy Start each use a managed local `npm run test:e2e` command. In Basic, the root wrapper allocates a run-specific test identity, disposable database and Redis namespaces, Vona generated/runtime/public paths, and an available loopback port, then starts a fresh `--workers=1 --flavor=normal --mode=test` worker. Test-mode startup initializes test resources and invokes module seeds without a separate dev-mode `db:reset`; Playwright does not reuse an existing process. This protects ordinary same-checkout development data and permits a Basic root E2E run alongside a dev server, subject to the shared-preparation and external-resource boundaries above. Inspect the Start repository's current scripts for its own resource-ownership rules.
155
176
 
156
177
  Both editions' browser checks target Vona integrated SSR. In the Cabloy Basic default environment, the Vona listener is `7102`; the Zova standalone SSR development server uses `9000` and is not an acceptance target.
157
178
 
@@ -4,26 +4,44 @@ import { E2E_LOCAL_BASE_URL, E2E_ROOT_DIR } from '../scripts/e2e.ts';
4
4
 
5
5
  export default defineConfig({
6
6
  testDir: `${E2E_ROOT_DIR}/repo-e2e/specs`,
7
+ outputDir: process.env.VONA_TEST_RUN_ID
8
+ ? `${E2E_ROOT_DIR}/vona/.app-test-${process.env.VONA_TEST_RUN_ID}/test-results`
9
+ : `${E2E_ROOT_DIR}/repo-e2e/test-results`,
7
10
  fullyParallel: false,
8
11
  workers: 1,
9
12
  forbidOnly: !!process.env.CI,
10
13
  retries: process.env.CI ? 2 : 0,
11
- reporter: process.env.CI ? [['html', { open: 'never' }], ['list']] : 'list',
14
+ reporter: process.env.CI
15
+ ? [
16
+ [
17
+ 'html',
18
+ {
19
+ open: 'never',
20
+ outputFolder: process.env.VONA_TEST_RUN_ID
21
+ ? `${E2E_ROOT_DIR}/vona/.app-test-${process.env.VONA_TEST_RUN_ID}/playwright-report`
22
+ : `${E2E_ROOT_DIR}/repo-e2e/playwright-report`,
23
+ },
24
+ ],
25
+ ['list'],
26
+ ]
27
+ : 'list',
12
28
  use: {
13
29
  baseURL: E2E_LOCAL_BASE_URL,
14
30
  trace: 'on-first-retry',
15
31
  },
16
- webServer: {
17
- command: 'node repo-e2e/scripts/startE2eVona.ts',
18
- cwd: E2E_ROOT_DIR,
19
- url: `${E2E_LOCAL_BASE_URL}/health/ready`,
20
- timeout: 180_000,
21
- reuseExistingServer: false,
22
- stdout: 'pipe',
23
- stderr: 'pipe',
24
- gracefulShutdown: {
25
- signal: 'SIGINT',
26
- timeout: 10_000,
27
- },
28
- },
32
+ webServer: process.env.VONA_TEST_RUN_ID
33
+ ? undefined
34
+ : {
35
+ command: 'node repo-e2e/scripts/startE2eVona.ts',
36
+ cwd: E2E_ROOT_DIR,
37
+ url: `${E2E_LOCAL_BASE_URL}/health/ready`,
38
+ timeout: 180_000,
39
+ reuseExistingServer: false,
40
+ stdout: 'pipe',
41
+ stderr: 'pipe',
42
+ gracefulShutdown: {
43
+ signal: 'SIGINT',
44
+ timeout: 10_000,
45
+ },
46
+ },
29
47
  });
@@ -25,6 +25,7 @@ interface IArticleGeometry {
25
25
  }
26
26
 
27
27
  const blogArticlePaths = [
28
+ '/blogs/ai-coding-one-prompt-crud-ssr-multitasking/',
28
29
  '/blogs/ai-spec-driven-development/',
29
30
  '/blogs/ai-react-nextjs-enterprise-architecture-cabloy/',
30
31
  '/blogs/cabloy-fullstack-resource-addressing/',
@@ -119,26 +120,26 @@ test(
119
120
  await expect(page.locator('.VPDoc.has-aside')).toHaveCount(0);
120
121
 
121
122
  const cards = page.locator('.cabloy-blog-card');
122
- await expect(cards).toHaveCount(5);
123
- for (let index = 0; index < 5; index++) {
123
+ await expect(cards).toHaveCount(blogArticlePaths.length);
124
+ for (let index = 0; index < blogArticlePaths.length; index++) {
124
125
  await expect(cards.nth(index)).toBeVisible();
125
126
  await expect(cards.nth(index).locator('.cabloy-blog-card__cover img')).toBeVisible();
126
127
  }
127
128
  await expect(
128
129
  cards.nth(0).getByRole('link', {
129
- name: 'CabloyJS AI Spec-Driven Development Automatically Generates Gantt and Burndown Charts',
130
+ name: 'AI Coding in Action: One Prompt for CRUD, SSR & Multitasking',
130
131
  exact: true,
131
132
  }),
132
133
  ).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__date')).toHaveText('October 9, 2026');
134
135
  await expect(cards.nth(0).locator('.cabloy-blog-card__cover')).toHaveAttribute(
135
136
  'href',
136
- '/blogs/ai-spec-driven-development/',
137
+ '/blogs/ai-coding-one-prompt-crud-ssr-multitasking/',
137
138
  );
138
139
 
139
140
  const geometry = await getBlogsGeometry(page);
140
141
  expect(geometry.grid.display).toBe('grid');
141
- expect(geometry.cards).toHaveLength(5);
142
+ expect(geometry.cards).toHaveLength(blogArticlePaths.length);
142
143
 
143
144
  const [firstCard, secondCard, thirdCard, fourthCard, fifthCard] = geometry.cards;
144
145
  const firstRow = [firstCard, secondCard, thirdCard, fourthCard];
@@ -181,6 +182,22 @@ test(
181
182
  await expect(page.locator('.VPDoc.has-sidebar')).toHaveCount(0);
182
183
  await expect(page.locator('.VPDoc.has-aside')).toHaveCount(1);
183
184
  await expect(page.locator('.aside')).toBeVisible();
185
+ if (path === '/blogs/ai-coding-one-prompt-crud-ssr-multitasking/') {
186
+ await expect(
187
+ page.getByRole('heading', {
188
+ name: 'AI Coding in Action: One Prompt for CRUD, SSR & Multitasking',
189
+ level: 1,
190
+ }),
191
+ ).toBeVisible();
192
+ const images = page.locator('.vp-doc img');
193
+ await expect(images).toHaveCount(3);
194
+ for (const image of await images.all()) {
195
+ await expect(image).toBeVisible();
196
+ await expect
197
+ .poll(() => image.evaluate(element => (element as HTMLImageElement).naturalWidth))
198
+ .toBeGreaterThan(0);
199
+ }
200
+ }
184
201
  if (path === '/blogs/ai-spec-driven-development/') {
185
202
  await expect(
186
203
  page.getByRole('heading', {
@@ -15,3 +15,42 @@ test('rejects a defined E2E_BASE_URL before loading local configuration', () =>
15
15
  assert.doesNotMatch(result.stderr, /SERVER_LISTEN_PORT/);
16
16
  }
17
17
  });
18
+
19
+ test('isolated E2E target follows the assigned listener', () => {
20
+ const entrypoint = fileURLToPath(new URL('./e2e.ts', import.meta.url));
21
+ const result = spawnSync(
22
+ process.execPath,
23
+ [
24
+ '--input-type=module',
25
+ '-e',
26
+ `import { E2E_LOCAL_BASE_URL } from ${JSON.stringify(entrypoint)}; console.log(E2E_LOCAL_BASE_URL)`,
27
+ ],
28
+ {
29
+ env: {
30
+ ...process.env,
31
+ E2E_BASE_URL: undefined,
32
+ VONA_TEST_RUN_ID: '20261014010203-a1b2c3d4',
33
+ APP_NAME: 'cabtest20261014010203-a1b2c3d4',
34
+ SERVER_LISTEN_PORT: '42321',
35
+ },
36
+ encoding: 'utf8',
37
+ },
38
+ );
39
+ assert.equal(result.status, 0, result.stderr);
40
+ assert.equal(result.stdout.trim(), 'http://127.0.0.1:42321');
41
+ });
42
+
43
+ test('rejects a malformed isolated E2E run ID', () => {
44
+ const entrypoint = fileURLToPath(new URL('./e2e.ts', import.meta.url));
45
+ const result = spawnSync(process.execPath, [entrypoint], {
46
+ env: {
47
+ ...process.env,
48
+ E2E_BASE_URL: undefined,
49
+ VONA_TEST_RUN_ID: '20260230010203-a1b2c3d4',
50
+ SERVER_LISTEN_PORT: '42321',
51
+ },
52
+ encoding: 'utf8',
53
+ });
54
+ assert.notEqual(result.status, 0);
55
+ assert.match(result.stderr, /Invalid isolated test run ID/);
56
+ });
@@ -13,7 +13,25 @@ const vonaEnv = dotenv.loadEnvs(
13
13
  resolve(E2E_ROOT_DIR, 'vona', 'env'),
14
14
  '.env',
15
15
  );
16
- const e2ePort = Number(vonaEnv?.SERVER_LISTEN_PORT);
16
+ const runId = process.env.VONA_TEST_RUN_ID;
17
+ if (runId) {
18
+ const match = /^(\d{14})-[a-f0-9]{8}$/.exec(runId);
19
+ const timestamp = match?.[1];
20
+ const date = timestamp
21
+ ? new Date(
22
+ `${timestamp.slice(0, 4)}-${timestamp.slice(4, 6)}-${timestamp.slice(6, 8)}T${timestamp.slice(8, 10)}:${timestamp.slice(10, 12)}:${timestamp.slice(12, 14)}Z`,
23
+ )
24
+ : undefined;
25
+ if (
26
+ !timestamp ||
27
+ !date ||
28
+ Number.isNaN(date.getTime()) ||
29
+ date.toISOString().slice(0, 19).replace(/[-:T]/g, '') !== timestamp
30
+ ) {
31
+ throw new Error('Invalid isolated test run ID: expected a UTC timestamp plus eight hex digits');
32
+ }
33
+ }
34
+ const e2ePort = Number(runId ? process.env.SERVER_LISTEN_PORT : vonaEnv?.SERVER_LISTEN_PORT);
17
35
  if (!Number.isInteger(e2ePort) || e2ePort <= 0 || e2ePort > 65535) {
18
36
  throw new Error('Missing or invalid SERVER_LISTEN_PORT for local E2E target');
19
37
  }