cabloy 5.1.59 → 5.1.60
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.
- package/.claude/skills/cabloy-contract-loop/SKILL.md +16 -0
- package/.claude/skills/cabloy-contract-loop/references/contract-loop-map.md +26 -0
- package/.claude/skills/cabloy-contract-loop/references/resource-custom-state-pattern.md +144 -0
- package/.claude/skills/cabloy-contract-loop/references/verification-checklist.md +18 -0
- package/.claude/skills/cabloy-resource-field-update/SKILL.md +267 -0
- package/.claude/skills/cabloy-resource-field-update/evals/evals.json +53 -0
- package/.claude/skills/cabloy-resource-field-update/references/custom-renderer-demo-checklist.md +102 -0
- package/.claude/skills/cabloy-resource-field-update/references/field-update-decision-tree.md +120 -0
- package/.claude/skills/cabloy-resource-field-update/references/follow-up-checklist.md +80 -0
- package/.claude/skills/cabloy-resource-field-update/references/verification-checklist.md +97 -0
- package/.github/workflows/docs-pages.yml +2 -0
- package/.github/workflows/vona-cov-pg.yml +2 -0
- package/.github/workflows/vona-test-crud.yml +4 -2
- package/.github/workflows/vona-test-mysql.yml +2 -0
- package/.github/workflows/vona-test-pg.yml +2 -0
- package/.github/workflows/vona-test-sqlite3.yml +2 -0
- package/.github/workflows/vona-tsc.yml +2 -0
- package/.github/workflows/zova-ui.yml +2 -0
- package/.gitignore +0 -4
- package/CHANGELOG.md +30 -0
- package/CLAUDE.md +2 -0
- package/README.md +15 -0
- package/cabloy-docs/.vitepress/config.mjs +43 -0
- package/cabloy-docs/ai/class-placement-rule.md +2 -0
- package/cabloy-docs/ai/cli-to-skill-map.md +7 -0
- package/cabloy-docs/ai/future-skill-roadmap.md +17 -2
- package/cabloy-docs/backend/bean-scene-authoring.md +350 -0
- package/cabloy-docs/backend/cli.md +26 -1
- package/cabloy-docs/backend/foundation.md +28 -3
- package/cabloy-docs/backend/introduction.md +8 -0
- package/cabloy-docs/backend/service-guide.md +2 -0
- package/cabloy-docs/backend/websocket-call-flow.md +435 -0
- package/cabloy-docs/backend/websocket-guide.md +455 -0
- package/cabloy-docs/backend/websocket-protocol-guide.md +381 -0
- package/cabloy-docs/backend/websocket-usage-guide.md +356 -0
- package/cabloy-docs/frontend/bean-scene-authoring.md +372 -0
- package/cabloy-docs/frontend/behavior-guide.md +449 -0
- package/cabloy-docs/frontend/cli.md +12 -0
- package/cabloy-docs/frontend/introduction.md +5 -0
- package/cabloy-docs/frontend/ioc-and-beans.md +10 -9
- package/cabloy-docs/frontend/router-tabs-admin-web-comparison.md +206 -0
- package/cabloy-docs/frontend/router-tabs-introduction.md +106 -0
- package/cabloy-docs/frontend/router-tabs-mechanism.md +469 -0
- package/cabloy-docs/frontend/router-tabs-overview.md +227 -0
- package/cabloy-docs/frontend/router-tabs-route-meta-cookbook.md +343 -0
- package/cabloy-docs/frontend/ssr-architecture-overview.md +211 -0
- package/cabloy-docs/frontend/ssr-build-deploy-guide.md +308 -0
- package/cabloy-docs/frontend/ssr-review-checklist.md +184 -0
- package/cabloy-docs/frontend/ssr-troubleshooting-guide.md +301 -0
- package/cabloy-docs/fullstack/framework-performance.md +3 -3
- package/cabloy-docs/fullstack/introduction.md +29 -0
- package/cabloy-docs/fullstack/quickstart.md +7 -1
- package/cabloy-docs/fullstack/tutorial-1-first-module.md +111 -0
- package/cabloy-docs/fullstack/tutorial-2-first-crud.md +122 -0
- package/cabloy-docs/fullstack/tutorial-3-frontend-metadata-sharing.md +131 -0
- package/cabloy-docs/fullstack/tutorial-4-custom-level-renderers.md +119 -0
- package/cabloy-docs/fullstack/tutorial-5-backend-contract-sharing.md +144 -0
- package/cabloy-docs/fullstack/tutorial-6-one-contract-four-uses.md +168 -0
- package/cabloy-docs/fullstack/tutorials-overview.md +179 -0
- package/cabloy-docs/index.md +4 -3
- package/cabloy-docs/reference/bean-scene-boilerplates.md +73 -0
- package/cabloy-docs/reference/cli-reference.md +2 -0
- package/package.json +6 -2
- package/scripts/init.ts +18 -2
- package/vona/packages-cli/cabloy-cli/package.json +2 -2
- package/vona/packages-cli/cli/package.json +1 -1
- package/vona/packages-cli/cli-set-api/package.json +1 -1
- package/vona/packages-cli/cli-set-api/src/lib/bean/cli.create.module.ts +4 -0
- package/vona/packages-vona/vona/package.json +1 -1
- package/vona/pnpm-lock.yaml +133 -1088
- package/vona/pnpm-workspace.yaml +0 -1
- package/vona/src/suite-vendor/a-vona/modules/a-core/assets/static/img/vona.svg +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/bean.permission.ts +1 -1
- package/vona/src/suite-vendor/a-vona/modules/a-upload/package.json +2 -2
- package/vona/src/suite-vendor/a-vona/package.json +1 -1
- package/zova/package.original.json +1 -1
- package/zova/packages-cli/cli/package.json +3 -3
- package/zova/packages-cli/cli-set-front/cli/templates/init/icon/boilerplate/icons/default/zova.svg +1 -1
- package/zova/packages-cli/cli-set-front/package.json +3 -3
- package/zova/packages-cli/cli-set-front/src/lib/bean/cli.create.module.ts +4 -0
- package/zova/packages-cli/cli-set-front/src/lib/command/create.bean.ts +5 -1
- package/zova/packages-utils/zova-vite/package.json +2 -2
- package/zova/packages-zova/zova/package.json +2 -2
- package/zova/pnpm-lock.yaml +284 -1313
- package/zova/pnpm-workspace.yaml +0 -1
- package/zova/src/suite/a-home/modules/home-icon/icons/social/cabloy.svg +1 -1
- package/zova/src/suite/a-home/modules/home-icon/icons/social/vona.svg +1 -1
- package/zova/src/suite/a-home/modules/home-icon/icons/social/zova.svg +1 -1
- package/zova/src/suite/a-home/modules/home-icon/src/.metadata/icons/groups/social.svg +3 -3
- package/zova/src/suite/cabloy-basic/modules/basic-select/src/component/formFieldSelect/controller.tsx +9 -0
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
- package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +66 -16
- package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/modules/a-routertabs/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-routertabs/src/model/tabs.ts +60 -18
- package/zova/src/suite-vendor/a-zova/modules/a-table/cli/tableActionRow/boilerplate/{{sceneName}}.{{beanName}}.tsx_ +6 -1
- package/zova/src/suite-vendor/a-zova/modules/a-table/cli/tableCell/boilerplate/{{sceneName}}.{{beanName}}.tsx_ +6 -1
- package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +1 -1
- package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +2 -2
- package/zova/src/suite-vendor/a-zova/package.json +4 -4
|
@@ -7,6 +7,17 @@ description: Use this skill whenever a Cabloy task crosses the Vona-to-Zova cont
|
|
|
7
7
|
|
|
8
8
|
Use this skill when a backend contract change needs to be reflected in frontend consumers, or when frontend consumers appear stale and you need to diagnose whether the backend contract loop is the real source of drift.
|
|
9
9
|
|
|
10
|
+
## Important recovery note for stale local file consumers
|
|
11
|
+
|
|
12
|
+
When generated `.zova-rest` output or other generated consumer artifacts already contain the expected new keys or types but Vona still behaves as if old consumer types are installed, treat that first as a local file-dependency installation problem rather than a source-editing problem.
|
|
13
|
+
|
|
14
|
+
In that situation:
|
|
15
|
+
|
|
16
|
+
1. run the normal sync or regeneration flow first
|
|
17
|
+
2. if stale behavior remains after `deps:vona`, rebuild `vona/node_modules` and reinstall dependencies
|
|
18
|
+
|
|
19
|
+
Do not keep debugging source-level contract or renderer changes until the local file-package installation state is known to be healthy.
|
|
20
|
+
|
|
10
21
|
## Goals
|
|
11
22
|
|
|
12
23
|
1. detect whether the active repository is Cabloy Basic or Cabloy Start
|
|
@@ -71,6 +82,7 @@ For deeper reference material, read:
|
|
|
71
82
|
|
|
72
83
|
- `references/contract-loop-map.md`
|
|
73
84
|
- `references/verification-checklist.md`
|
|
85
|
+
- `references/resource-custom-state-pattern.md`
|
|
74
86
|
|
|
75
87
|
## Step 3: Identify the contract source of truth deliberately
|
|
76
88
|
|
|
@@ -130,6 +142,8 @@ Typical Zova commands include:
|
|
|
130
142
|
- `npm run zova :openapi:config ...`
|
|
131
143
|
- `npm run zova :openapi:generate ...`
|
|
132
144
|
|
|
145
|
+
When the target is a module-local SDK, constrain `openapi.config.ts` with `operations.match` unless the module intentionally owns a broad API surface. This prevents unrelated APIs from being generated into the module.
|
|
146
|
+
|
|
133
147
|
### Path B: REST/type generation by flavor
|
|
134
148
|
|
|
135
149
|
Use the edition-specific Zova REST/type build path when the workflow depends on the built flavor outputs.
|
|
@@ -150,6 +164,8 @@ After generation, inspect whether the frontend still needs follow-up in:
|
|
|
150
164
|
- schema-driven UI
|
|
151
165
|
- page or component assumptions
|
|
152
166
|
|
|
167
|
+
If a custom endpoint still belongs to an existing resource, prefer one resource-state owner instead of letting a module-local model create a second cache tree. Reuse the resource-owned custom state pattern in `references/resource-custom-state-pattern.md`.
|
|
168
|
+
|
|
153
169
|
## Step 6: Keep edition-aware differences explicit
|
|
154
170
|
|
|
155
171
|
The collaboration model is shared across Basic and Start, but the operational details may differ.
|
|
@@ -32,6 +32,32 @@ Likely next step:
|
|
|
32
32
|
- confirm whether backend contract really changed
|
|
33
33
|
- regenerate instead of hand-patching
|
|
34
34
|
|
|
35
|
+
## Module-local OpenAPI generation boundary
|
|
36
|
+
|
|
37
|
+
When generating a module-local OpenAPI SDK, do not leave the module config effectively unconstrained if the module should only own a narrow resource surface.
|
|
38
|
+
|
|
39
|
+
Preferred rule:
|
|
40
|
+
|
|
41
|
+
- set `operations.match` in `openapi.config.ts` so the module generates only the intended API operations
|
|
42
|
+
|
|
43
|
+
Reason:
|
|
44
|
+
|
|
45
|
+
- an unconstrained or overly broad generation pass can pull unrelated APIs into the module
|
|
46
|
+
- that expands generated SDK files, metadata exports, and downstream type surfaces far beyond the module’s real ownership boundary
|
|
47
|
+
- the result may still compile, but it weakens module boundaries and makes maintenance harder
|
|
48
|
+
|
|
49
|
+
Representative example:
|
|
50
|
+
|
|
51
|
+
- a module-level OpenAPI config such as `zova/src/module/demo-student/cli/openapi.config.ts`
|
|
52
|
+
- narrow the generated surface with a module-specific matcher such as `operations.match: [/^DemoStudent_*/]`
|
|
53
|
+
|
|
54
|
+
Treat that module path as an example, not as a durable dependency of the rule. The durable rule is to align `operations.match` with the module’s true API ownership boundary.
|
|
55
|
+
|
|
56
|
+
Practical check after generation:
|
|
57
|
+
|
|
58
|
+
- confirm the generated API files only contain the intended resource operations
|
|
59
|
+
- confirm the module metadata and exports were not polluted by unrelated APIs
|
|
60
|
+
|
|
35
61
|
## Shared rule
|
|
36
62
|
|
|
37
63
|
The sequence is usually:
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Resource Custom State Pattern
|
|
2
|
+
|
|
3
|
+
Use this reference when a contract-loop task adds or refactors a custom API that still belongs to an existing resource.
|
|
4
|
+
|
|
5
|
+
Typical examples:
|
|
6
|
+
|
|
7
|
+
- `summary/:id`
|
|
8
|
+
- `restore/:id`
|
|
9
|
+
- `deleteForce/:id`
|
|
10
|
+
- `history/:id`
|
|
11
|
+
- any other row-scoped endpoint that should stay synchronized with standard resource list or entry flows
|
|
12
|
+
|
|
13
|
+
## The ownership rule
|
|
14
|
+
|
|
15
|
+
Prefer one owner for all resource-bound server state.
|
|
16
|
+
|
|
17
|
+
In this codebase, the preferred owner is:
|
|
18
|
+
|
|
19
|
+
- `rest-resource.model.resource`
|
|
20
|
+
|
|
21
|
+
Do not let a module-local model become a second state owner for the same resource rows unless the boundary is explicitly intentional.
|
|
22
|
+
|
|
23
|
+
## The split to avoid
|
|
24
|
+
|
|
25
|
+
Avoid this pattern:
|
|
26
|
+
|
|
27
|
+
1. standard resource list and entry pages consume `rest-resource.model.resource`
|
|
28
|
+
2. a module-local model introduces separate query or mutation state for the same rows
|
|
29
|
+
3. custom mutation success invalidates only the local model keys
|
|
30
|
+
4. the generic resource pages keep reading stale row or list state
|
|
31
|
+
|
|
32
|
+
This usually happens when a task starts with a module-local generated SDK wrapper and stops there. The pattern is generic and should not depend on any one demo module continuing to exist.
|
|
33
|
+
|
|
34
|
+
## Preferred pattern
|
|
35
|
+
|
|
36
|
+
Use this shape instead:
|
|
37
|
+
|
|
38
|
+
1. keep `ModelResource` as the single owner of resource-bound server state
|
|
39
|
+
2. add reusable resource-owned helpers for custom query and mutation state
|
|
40
|
+
3. keep module-local models only as thin semantic facades when the task still benefits from a business-local API surface
|
|
41
|
+
|
|
42
|
+
A good semantic facade may still expose methods such as:
|
|
43
|
+
|
|
44
|
+
- `summary(id)`
|
|
45
|
+
- `deleteForce(id)`
|
|
46
|
+
|
|
47
|
+
But those methods should delegate to `ModelResource`, not create a competing cache owner.
|
|
48
|
+
|
|
49
|
+
## Cache-key convention
|
|
50
|
+
|
|
51
|
+
For row-scoped state, group keys under the item identity.
|
|
52
|
+
|
|
53
|
+
Preferred structure:
|
|
54
|
+
|
|
55
|
+
- row root:
|
|
56
|
+
- `['item', id]`
|
|
57
|
+
- row query scenes:
|
|
58
|
+
- `['item', id, 'get']`
|
|
59
|
+
- `['item', id, 'summary']`
|
|
60
|
+
- `['item', id, 'history']`
|
|
61
|
+
- row mutation scenes may extend the same structure, for example:
|
|
62
|
+
- `['item', id, 'deleteForce', 'mutation']`
|
|
63
|
+
|
|
64
|
+
For list-scoped resource queries, keep the select-style grouping, for example:
|
|
65
|
+
|
|
66
|
+
- `['select', actionPath ?? '', hashkey(query)]`
|
|
67
|
+
|
|
68
|
+
## Invalidation rule
|
|
69
|
+
|
|
70
|
+
For a row-affecting mutation:
|
|
71
|
+
|
|
72
|
+
- invalidate `['item', id]`
|
|
73
|
+
|
|
74
|
+
For a list-affecting mutation:
|
|
75
|
+
|
|
76
|
+
- invalidate `['select']`
|
|
77
|
+
|
|
78
|
+
If both are affected:
|
|
79
|
+
|
|
80
|
+
- invalidate both
|
|
81
|
+
|
|
82
|
+
This is the key benefit of the grouped row-root convention: one invalidation clears all row-specific query scenes for the same item.
|
|
83
|
+
|
|
84
|
+
## Helper naming guidance
|
|
85
|
+
|
|
86
|
+
When adding reusable row-scoped helper options, prefer `action` over `scene` or `handler`.
|
|
87
|
+
|
|
88
|
+
Reason:
|
|
89
|
+
|
|
90
|
+
- `action` matches resource semantics such as `get`, `summary`, `deleteForce`
|
|
91
|
+
- `scene` already has stronger UI and form-scene meaning elsewhere in the codebase
|
|
92
|
+
- `handler` describes implementation shape rather than resource meaning
|
|
93
|
+
|
|
94
|
+
Preferred examples:
|
|
95
|
+
|
|
96
|
+
- `queryItem({ id, action: 'summary', ... })`
|
|
97
|
+
- `mutationItem({ id, action: 'deleteForce', ... })`
|
|
98
|
+
|
|
99
|
+
## Typed SDK guidance
|
|
100
|
+
|
|
101
|
+
Keep generated SDK calls typed and module-local.
|
|
102
|
+
|
|
103
|
+
Preferred split:
|
|
104
|
+
|
|
105
|
+
- module-local facade keeps the generated SDK call
|
|
106
|
+
- `ModelResource` owns the query or mutation state and invalidation policy
|
|
107
|
+
|
|
108
|
+
That means the facade passes typed closures into the generic helpers, conceptually like:
|
|
109
|
+
|
|
110
|
+
- `queryItem({ id, action: 'summary', queryFn: () => api.summary(...) })`
|
|
111
|
+
- `mutationItem({ id, action: 'deleteForce', mutationFn: () => api.deleteForce(...) })`
|
|
112
|
+
|
|
113
|
+
This preserves strong typing without duplicating state ownership.
|
|
114
|
+
|
|
115
|
+
## When to reuse this pattern
|
|
116
|
+
|
|
117
|
+
Reuse it when all of the following are true:
|
|
118
|
+
|
|
119
|
+
- the endpoint still belongs logically to an existing resource
|
|
120
|
+
- standard resource list or entry pages already consume that resource through `rest-resource.model.resource`
|
|
121
|
+
- stale row or list state would matter after the mutation or fetch
|
|
122
|
+
- the task needs a custom API shape beyond standard CRUD, but not a separate application-state subsystem
|
|
123
|
+
|
|
124
|
+
## When a separate owner may still be acceptable
|
|
125
|
+
|
|
126
|
+
A separate owner may still be fine when the data is not really part of the resource state surface, for example:
|
|
127
|
+
|
|
128
|
+
- unrelated dashboard data
|
|
129
|
+
- global application state
|
|
130
|
+
- purely local UI state
|
|
131
|
+
- intentionally unsynchronized auxiliary data
|
|
132
|
+
|
|
133
|
+
Even then, be explicit about the boundary.
|
|
134
|
+
|
|
135
|
+
## Quick checklist
|
|
136
|
+
|
|
137
|
+
1. confirm whether the resource pages already use `rest-resource.model.resource`
|
|
138
|
+
2. decide whether the custom endpoint is row-scoped or list-scoped
|
|
139
|
+
3. reuse or extend the generic resource helpers instead of creating a second owner
|
|
140
|
+
4. group row keys under `['item', id, action]`
|
|
141
|
+
5. invalidate `['item', id]` for row-affecting mutations
|
|
142
|
+
6. invalidate `['select']` for list-affecting mutations
|
|
143
|
+
7. keep module-local models semantic-only when possible
|
|
144
|
+
8. verify both standard resource flows and the new custom action flow
|
|
@@ -27,6 +27,24 @@ After a contract-loop change, check both sides.
|
|
|
27
27
|
- affected flavor confirmed
|
|
28
28
|
- generation path matches the active edition
|
|
29
29
|
|
|
30
|
+
## Recovery rule for stale local file consumers
|
|
31
|
+
|
|
32
|
+
If all of these are true:
|
|
33
|
+
|
|
34
|
+
- generated `.zova-rest` or related generated consumer artifacts already contain the expected new keys or types
|
|
35
|
+
- the normal regeneration or sync flow already ran
|
|
36
|
+
- Vona still behaves as if old consumer types are installed
|
|
37
|
+
|
|
38
|
+
Then suspect a stale or unhealthy local installation state in `vona/node_modules`.
|
|
39
|
+
|
|
40
|
+
Recovery action:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
cd vona && rm -rf node_modules && pnpm install
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Use this as a recovery path when normal sync steps did not restore the local file-package installation state cleanly.
|
|
47
|
+
|
|
30
48
|
## Done rule
|
|
31
49
|
|
|
32
50
|
A contract-loop task is not done when only the backend compiles or only the frontend builds. It is done when the contract source and the generated consumer path agree.
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cabloy-resource-field-update
|
|
3
|
+
description: Use this skill whenever the user wants to update a field on an existing Cabloy backend resource: add a new persisted field, refine validation, add enum-like constraints, attach or change ZovaRender.field / ZovaRender.cell metadata, decide whether vonaModule.fileVersion should change, or demonstrate a custom frontend renderer for a backend field. Trigger when the request is specifically about modifying an existing resource field thread rather than creating a new CRUD/resource thread. Prefer it for backend-first field-update work that may branch into renderer-aware frontend follow-up. Do not use it for initial backend scaffolding, generic frontend scaffolding, or stale generated contract diagnosis.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Cabloy Resource Field Update
|
|
7
|
+
|
|
8
|
+
Use this skill when the user wants to change a field on an existing Vona backend resource.
|
|
9
|
+
|
|
10
|
+
## Important recovery note for stale generated consumers
|
|
11
|
+
|
|
12
|
+
When generated `.zova-rest` output already contains the expected new keys or types but Vona still behaves as if old consumer types are installed, treat that first as a local file-dependency installation problem rather than a source-editing problem.
|
|
13
|
+
|
|
14
|
+
In that situation:
|
|
15
|
+
|
|
16
|
+
1. run the normal sync flow such as `deps:vona`
|
|
17
|
+
2. if the stale behavior remains, rebuild `vona/node_modules` and reinstall dependencies
|
|
18
|
+
|
|
19
|
+
Keep this recovery rule visible during renderer-aware or contract-loop follow-up work. Do not keep debugging source-level renderer registrations until the local file-package installation state is known to be healthy.
|
|
20
|
+
|
|
21
|
+
## Goals
|
|
22
|
+
|
|
23
|
+
1. detect whether the active repository is Cabloy Basic or Cabloy Start
|
|
24
|
+
2. classify the task as a new persisted field or a metadata-only field refinement
|
|
25
|
+
3. force the correct `fileVersion` decision before persistence edits
|
|
26
|
+
4. keep the workflow entity-first and inferred-DTO-first
|
|
27
|
+
5. prefer shared renderer reuse unless the task explicitly asks for a custom renderer demo
|
|
28
|
+
6. finish with verification guidance that matches backend and renderer follow-up scope
|
|
29
|
+
|
|
30
|
+
## Step 1: Detect repo and confirm existing-resource scope
|
|
31
|
+
|
|
32
|
+
Check the repository root for these marker files:
|
|
33
|
+
|
|
34
|
+
- `__CABLOY_BASIC__`
|
|
35
|
+
- `__CABLOY_START__`
|
|
36
|
+
|
|
37
|
+
Interpretation:
|
|
38
|
+
|
|
39
|
+
- `__CABLOY_BASIC__` present → this is Cabloy Basic
|
|
40
|
+
- `__CABLOY_START__` present → this is Cabloy Start
|
|
41
|
+
- neither present → inspect nearby scripts and ask before making edition-specific assumptions
|
|
42
|
+
|
|
43
|
+
Then confirm the request is about an **existing** resource field.
|
|
44
|
+
|
|
45
|
+
Use this skill for requests such as:
|
|
46
|
+
|
|
47
|
+
- add a field to an existing resource
|
|
48
|
+
- tighten validation for an existing field
|
|
49
|
+
- add enum-like constraints to an existing field
|
|
50
|
+
- add or change `ZovaRender.field(...)` / `ZovaRender.cell(...)`
|
|
51
|
+
- decide whether a persisted field change needs a new `fileVersion`
|
|
52
|
+
- demonstrate a custom renderer workflow for a backend field
|
|
53
|
+
|
|
54
|
+
Do **not** use this skill when the user is really asking to create a new module, bean, CRUD thread, page thread, or stale consumer diagnosis flow.
|
|
55
|
+
|
|
56
|
+
If the request is really about initial backend thread creation, prefer `cabloy-backend-scaffold`.
|
|
57
|
+
If the request is really about stale generated frontend/backend consumers, prefer `cabloy-contract-loop`.
|
|
58
|
+
If the request is mainly about choosing a workflow, prefer `cabloy-workflow`.
|
|
59
|
+
|
|
60
|
+
## Step 2: Classify the field change before editing
|
|
61
|
+
|
|
62
|
+
Branch the request into one of two cases.
|
|
63
|
+
|
|
64
|
+
### Case A: new persisted field
|
|
65
|
+
|
|
66
|
+
Examples:
|
|
67
|
+
|
|
68
|
+
- add `level: number`
|
|
69
|
+
- add `status: string`
|
|
70
|
+
- add a new stored relation key
|
|
71
|
+
|
|
72
|
+
This case affects persistence and versioning.
|
|
73
|
+
|
|
74
|
+
### Case B: metadata-only or validation/render-only refinement
|
|
75
|
+
|
|
76
|
+
Examples:
|
|
77
|
+
|
|
78
|
+
- add enum validation to an existing field
|
|
79
|
+
- add or change `ZovaRender.field(...)`
|
|
80
|
+
- add or change `ZovaRender.cell(...)`
|
|
81
|
+
- refine locale labels
|
|
82
|
+
- tighten validation without changing storage shape
|
|
83
|
+
|
|
84
|
+
This case usually does **not** require a `fileVersion` bump, because the persisted field already exists.
|
|
85
|
+
|
|
86
|
+
## Step 3: Force the `fileVersion` decision for new persisted fields
|
|
87
|
+
|
|
88
|
+
If the task is a new persisted field on an existing resource, ask whether `vonaModule.fileVersion` should be incremented **before** changing:
|
|
89
|
+
|
|
90
|
+
- `meta.version.ts`
|
|
91
|
+
- the module schema version path
|
|
92
|
+
- the module `package.json` `fileVersion`
|
|
93
|
+
|
|
94
|
+
### If the user says yes
|
|
95
|
+
|
|
96
|
+
Then:
|
|
97
|
+
|
|
98
|
+
1. bump `vonaModule.fileVersion`
|
|
99
|
+
2. add a new migration branch in `meta.version.ts`
|
|
100
|
+
3. preserve older version branches as historical snapshots
|
|
101
|
+
4. introduce the new persisted field in the new version branch
|
|
102
|
+
|
|
103
|
+
Important warning:
|
|
104
|
+
|
|
105
|
+
- do not add the same new column to an older create path and again to a new migration branch
|
|
106
|
+
- fresh install may run version branches sequentially
|
|
107
|
+
- that pattern can produce duplicate-column failures
|
|
108
|
+
|
|
109
|
+
### If the user says no
|
|
110
|
+
|
|
111
|
+
Then:
|
|
112
|
+
|
|
113
|
+
1. keep the current `fileVersion`
|
|
114
|
+
2. fold the schema change into the current version path
|
|
115
|
+
3. do not create a new migration branch
|
|
116
|
+
|
|
117
|
+
## Step 4: Inspect the current backend thread first
|
|
118
|
+
|
|
119
|
+
Before proposing or making edits, inspect the existing thread:
|
|
120
|
+
|
|
121
|
+
- entity
|
|
122
|
+
- model
|
|
123
|
+
- DTOs
|
|
124
|
+
- controller
|
|
125
|
+
- service
|
|
126
|
+
- `meta.version.ts`
|
|
127
|
+
- module `package.json`
|
|
128
|
+
- locale files
|
|
129
|
+
- tests
|
|
130
|
+
|
|
131
|
+
Also inspect the shared entrypoints first:
|
|
132
|
+
|
|
133
|
+
- root `package.json`
|
|
134
|
+
- `npm run vona`
|
|
135
|
+
- `npm run zova`
|
|
136
|
+
|
|
137
|
+
Use these references for compact support material:
|
|
138
|
+
|
|
139
|
+
- `references/field-update-decision-tree.md`
|
|
140
|
+
- `references/follow-up-checklist.md`
|
|
141
|
+
- `references/verification-checklist.md`
|
|
142
|
+
|
|
143
|
+
## Step 5: Update the entity first and reuse inferred DTO flow
|
|
144
|
+
|
|
145
|
+
Treat the entity as the primary field-definition surface.
|
|
146
|
+
|
|
147
|
+
Typical field-update changes belong here first:
|
|
148
|
+
|
|
149
|
+
- `@Api.field(...)`
|
|
150
|
+
- `v.required()` / `v.optional()`
|
|
151
|
+
- `v.title($locale(...))`
|
|
152
|
+
- `ZovaRender.order(...)`
|
|
153
|
+
- explicit zod schema for constrained values
|
|
154
|
+
|
|
155
|
+
For enum-like numeric or string values, prefer an explicit constrained schema such as:
|
|
156
|
+
|
|
157
|
+
- `z.union([z.literal(1), z.literal(2), z.literal(3)])`
|
|
158
|
+
|
|
159
|
+
Then check whether DTOs are already inferred through patterns such as:
|
|
160
|
+
|
|
161
|
+
- `$Dto.create(...)`
|
|
162
|
+
- `$Dto.update(...)`
|
|
163
|
+
- `$Dto.get(...)`
|
|
164
|
+
|
|
165
|
+
If the DTOs are inferred from the entity/model chain, let the entity change propagate. Do not hand-edit DTO field lists unless the source clearly requires it.
|
|
166
|
+
|
|
167
|
+
## Step 6: Apply the renderer branch deliberately
|
|
168
|
+
|
|
169
|
+
### Default rule: prefer shared renderer reuse
|
|
170
|
+
|
|
171
|
+
If the field needs form or table rendering metadata, inspect shared renderers first.
|
|
172
|
+
|
|
173
|
+
Default preference order:
|
|
174
|
+
|
|
175
|
+
1. reuse an existing shared renderer
|
|
176
|
+
2. configure it with field-level options
|
|
177
|
+
3. only create a new custom renderer if the shared surface cannot express the needed behavior
|
|
178
|
+
|
|
179
|
+
For enum-like values, apply an edition-aware default.
|
|
180
|
+
|
|
181
|
+
In Cabloy Basic, the usual default is:
|
|
182
|
+
|
|
183
|
+
- `ZovaRender.field('basic-select:formFieldSelect', { items, placeholder })`
|
|
184
|
+
- `ZovaRender.cell('basic-select:select', { items })`
|
|
185
|
+
|
|
186
|
+
In Cabloy Start:
|
|
187
|
+
|
|
188
|
+
- do not assume the same renderer keys or placeholder behavior from Basic
|
|
189
|
+
- inspect the `start-select` wrapper and its underlying component semantics before choosing renderer keys or copying Basic-specific select logic
|
|
190
|
+
|
|
191
|
+
When using a field-rendering select component, default to providing a user-visible `placeholder` unless the UX clearly requires an always-preselected value.
|
|
192
|
+
|
|
193
|
+
In Cabloy Basic, prefer `placeholder` over artificial empty-item injection when the goal is to keep the select initially unchosen.
|
|
194
|
+
|
|
195
|
+
### Custom renderer demo rule
|
|
196
|
+
|
|
197
|
+
If the user explicitly wants to **demonstrate** the custom renderer workflow, branch into a frontend follow-up path.
|
|
198
|
+
|
|
199
|
+
Use these references:
|
|
200
|
+
|
|
201
|
+
- `references/custom-renderer-demo-checklist.md`
|
|
202
|
+
- `.docs-internal/architecture/backend-resource-field-workflow.md`
|
|
203
|
+
|
|
204
|
+
Recommended shape:
|
|
205
|
+
|
|
206
|
+
- module-local FormField component for backend field rendering
|
|
207
|
+
- module-local `@TableCell(...)` bean for backend table-cell rendering
|
|
208
|
+
- metadata regeneration
|
|
209
|
+
- frontend build
|
|
210
|
+
- `deps:vona`
|
|
211
|
+
- Vona-side typecheck and targeted backend test
|
|
212
|
+
|
|
213
|
+
Important warning:
|
|
214
|
+
|
|
215
|
+
- a plain frontend component alone is not enough for backend `ZovaRender.cell(...)`
|
|
216
|
+
- the backend table-cell render key should be backed by a registered `@TableCell(...)` bean
|
|
217
|
+
|
|
218
|
+
## Step 7: Always close the remaining layers
|
|
219
|
+
|
|
220
|
+
### Locale
|
|
221
|
+
|
|
222
|
+
If titles, enum labels, or helper text are user-visible, update locale files in the same task.
|
|
223
|
+
|
|
224
|
+
Typical additions:
|
|
225
|
+
|
|
226
|
+
- field title like `Level`
|
|
227
|
+
- enum labels like `LevelBeginner`, `LevelIntermediate`, `LevelAdvanced`
|
|
228
|
+
- custom renderer helper text when applicable
|
|
229
|
+
|
|
230
|
+
### Tests
|
|
231
|
+
|
|
232
|
+
Minimum expected backend coverage usually includes:
|
|
233
|
+
|
|
234
|
+
- create with the field
|
|
235
|
+
- select/list still works
|
|
236
|
+
- update persists the field
|
|
237
|
+
- get-by-id/view returns the field
|
|
238
|
+
- delete flow still works if relevant
|
|
239
|
+
|
|
240
|
+
For constrained enum-like fields, add a negative test such as:
|
|
241
|
+
|
|
242
|
+
- invalid value is rejected
|
|
243
|
+
|
|
244
|
+
### Verification
|
|
245
|
+
|
|
246
|
+
Always end with a verification path matched to the scope.
|
|
247
|
+
|
|
248
|
+
Typical checks include:
|
|
249
|
+
|
|
250
|
+
- `npm run test`
|
|
251
|
+
- `npm run tsc`
|
|
252
|
+
- `npm run build`
|
|
253
|
+
- narrow resource test runs
|
|
254
|
+
- renderer/build/deps synchronization when custom frontend renderers are involved
|
|
255
|
+
|
|
256
|
+
## Response pattern
|
|
257
|
+
|
|
258
|
+
When helpful, structure the response around these points:
|
|
259
|
+
|
|
260
|
+
1. detected edition
|
|
261
|
+
2. persisted-field vs metadata-only classification
|
|
262
|
+
3. `fileVersion` decision if needed
|
|
263
|
+
4. backend thread surfaces to inspect or modify
|
|
264
|
+
5. shared-renderer reuse vs custom-renderer branch
|
|
265
|
+
6. verification steps
|
|
266
|
+
|
|
267
|
+
Keep the response practical. The value of this skill is turning existing-resource field updates into the correct Cabloy decision tree with the right backend and renderer follow-up, not writing a broad architecture essay.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"skill_name": "cabloy-resource-field-update",
|
|
3
|
+
"evals": [
|
|
4
|
+
{
|
|
5
|
+
"id": 1,
|
|
6
|
+
"prompt": "Please add a new persisted level field to the existing student resource in Cabloy Basic. Before doing anything else, tell me whether fileVersion needs a decision and what layers must be updated.",
|
|
7
|
+
"expected_output": "Must classify the task as a new persisted field on an existing resource, must require a fileVersion decision before migration edits, and must mention entity, meta.version, package.json, locale, and tests as relevant follow-up layers.",
|
|
8
|
+
"files": []
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
"id": 2,
|
|
12
|
+
"prompt": "I want to add a new persisted field to an existing Cabloy resource, but do not bump fileVersion — fold it into the current version path. What workflow should Claude follow?",
|
|
13
|
+
"expected_output": "Must recognize the no-bump branch, keep the current fileVersion, avoid creating a new migration version branch, and still mention schema-path, entity, locale, and tests as required follow-up surfaces.",
|
|
14
|
+
"files": []
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"id": 3,
|
|
18
|
+
"prompt": "The level field already exists in storage. I only want to tighten it to an enum-like 1/2/3 field and attach select-style render metadata. Keep this as a field refinement workflow, not a migration plan.",
|
|
19
|
+
"expected_output": "Must classify the task as metadata-only or validation/render-only refinement, must avoid unnecessary fileVersion escalation, and must mention explicit validation, renderer metadata, locale, and invalid-value testing.",
|
|
20
|
+
"files": []
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": 4,
|
|
24
|
+
"prompt": "In Cabloy Basic, please update the existing student resource field so it uses the best existing shared renderer first. I want the Cabloy way, not a custom component unless it is really needed.",
|
|
25
|
+
"expected_output": "Must prefer shared renderer reuse first, should mention basic-select as the default Cabloy Basic select-style baseline for enum-like fields, and should not jump straight into custom renderer creation.",
|
|
26
|
+
"files": []
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": 5,
|
|
30
|
+
"prompt": "I know the shared renderer would work, but I explicitly want a custom renderer demo for this existing backend field so future AI can learn the workflow. What should Claude do?",
|
|
31
|
+
"expected_output": "Must branch into the custom renderer demo workflow, must mention a module-local FormField component plus a module-local @TableCell bean, and must include metadata generation, frontend build, deps:vona, and Vona verification steps.",
|
|
32
|
+
"files": []
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"id": 6,
|
|
36
|
+
"prompt": "Create a brand new CRUD resource for courses in Cabloy and tell me the best generation path.",
|
|
37
|
+
"expected_output": "Must not treat this as an existing-resource field-update workflow. It should indicate that initial CRUD/resource creation belongs to the backend scaffold or a broader workflow, not this skill.",
|
|
38
|
+
"files": []
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"id": 7,
|
|
42
|
+
"prompt": "In Cabloy Basic, the level field already exists and I want select-style field rendering for it. Claude should use the shared renderer path, and unless the UX truly requires a preselected value, I want the field to start unchosen with a visible hint. What guidance should Claude follow?",
|
|
43
|
+
"expected_output": "Must classify this as an existing-field refinement, prefer the shared Cabloy Basic select renderer path, default to providing a user-visible placeholder for the field-rendering select component unless the UX clearly requires a preselected value, and prefer placeholder over artificial empty-item injection for Cabloy Basic rather than assuming the same behavior in Cabloy Start.",
|
|
44
|
+
"files": []
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"id": 8,
|
|
48
|
+
"prompt": "In Cabloy Start, the level field already exists and now needs select-style field rendering. I want Claude to reuse the right shared renderer path if possible, and I also care about how an initially unchosen state should work. What guidance should Claude follow?",
|
|
49
|
+
"expected_output": "Must classify this as an existing-field refinement, must detect that this is Cabloy Start, must still prefer shared renderer reuse first rather than jumping straight to a custom renderer, must not jump straight to Cabloy Basic renderer keys such as basic-select, and must say to inspect the Start-side select wrapper and underlying component semantics before deciding renderer keys or copying Cabloy Basic placeholder or empty-item behavior.",
|
|
50
|
+
"files": []
|
|
51
|
+
}
|
|
52
|
+
]
|
|
53
|
+
}
|
package/.claude/skills/cabloy-resource-field-update/references/custom-renderer-demo-checklist.md
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Custom Renderer Demo Checklist
|
|
2
|
+
|
|
3
|
+
Use this checklist when the user explicitly wants to demonstrate how a backend resource field can switch from a shared renderer to a custom module-local renderer.
|
|
4
|
+
|
|
5
|
+
## Goal
|
|
6
|
+
|
|
7
|
+
Preserve the field’s backend business semantics while deliberately exercising the full frontend renderer registration path.
|
|
8
|
+
|
|
9
|
+
## 1. Confirm the task is really instructional
|
|
10
|
+
|
|
11
|
+
Use this branch when the user wants to show:
|
|
12
|
+
|
|
13
|
+
- how to build a custom FormField component
|
|
14
|
+
- how to build a custom backend-usable TableCell renderer
|
|
15
|
+
- how to connect them back to `ZovaRender.field(...)` / `ZovaRender.cell(...)`
|
|
16
|
+
|
|
17
|
+
If the user only wants the simplest production implementation, prefer shared renderer reuse instead.
|
|
18
|
+
|
|
19
|
+
## 2. Keep business semantics unchanged if possible
|
|
20
|
+
|
|
21
|
+
Do not rework field meaning unless the request also asks for it.
|
|
22
|
+
|
|
23
|
+
Preserve existing semantics such as:
|
|
24
|
+
|
|
25
|
+
- allowed values
|
|
26
|
+
- locale labels
|
|
27
|
+
- `items`, `itemValue`, `itemTitle`
|
|
28
|
+
- backend field name and DTO flow
|
|
29
|
+
|
|
30
|
+
## 3. Build the correct frontend pair
|
|
31
|
+
|
|
32
|
+
### FormField
|
|
33
|
+
|
|
34
|
+
Add a module-local FormField component such as:
|
|
35
|
+
|
|
36
|
+
- `src/component/formFieldX/controller.tsx`
|
|
37
|
+
|
|
38
|
+
Best practice:
|
|
39
|
+
|
|
40
|
+
- reuse the option shape of the closest shared renderer
|
|
41
|
+
- copy the data flow from the shared baseline first
|
|
42
|
+
- default to providing a user-visible `placeholder` for field-rendering select components unless the UX clearly requires an always-preselected value
|
|
43
|
+
- in Cabloy Basic, keep placeholder handling aligned with `basic-select` semantics instead of adding artificial empty items by default
|
|
44
|
+
- in Cabloy Start, verify the `start-select` wrapper behavior before copying Basic-specific placeholder or empty-item logic
|
|
45
|
+
- keep customizations minimal and clearly demo-oriented
|
|
46
|
+
|
|
47
|
+
### TableCell
|
|
48
|
+
|
|
49
|
+
Add a module-local `@TableCell(...)` bean such as:
|
|
50
|
+
|
|
51
|
+
- `src/bean/tableCell.x.tsx`
|
|
52
|
+
|
|
53
|
+
Important:
|
|
54
|
+
|
|
55
|
+
- backend `ZovaRender.cell(...)` should point to a registered table-cell key
|
|
56
|
+
- a plain frontend component is not enough for this role
|
|
57
|
+
|
|
58
|
+
## 4. Use the best existing baseline
|
|
59
|
+
|
|
60
|
+
For select-like enum fields, start from an edition-correct baseline.
|
|
61
|
+
|
|
62
|
+
In Cabloy Basic, start from:
|
|
63
|
+
|
|
64
|
+
- `basic-select` FormField behavior
|
|
65
|
+
- `basic-select` table-cell bean behavior
|
|
66
|
+
|
|
67
|
+
In Cabloy Start:
|
|
68
|
+
|
|
69
|
+
- identify the Start-side select baseline first rather than reusing Basic components by name
|
|
70
|
+
- verify placeholder and empty-state behavior before copying Basic-specific logic
|
|
71
|
+
|
|
72
|
+
The goal is to demonstrate module-local customization, not to reinvent field-state handling.
|
|
73
|
+
|
|
74
|
+
## 5. Regenerate and synchronize in the right order
|
|
75
|
+
|
|
76
|
+
Recommended order:
|
|
77
|
+
|
|
78
|
+
1. create or update renderer source files
|
|
79
|
+
2. regenerate frontend metadata
|
|
80
|
+
3. run the relevant frontend build so both bundle output and type surface update
|
|
81
|
+
4. run `deps:vona`
|
|
82
|
+
5. run Vona typecheck
|
|
83
|
+
6. run the narrow backend resource test
|
|
84
|
+
|
|
85
|
+
For Cabloy Basic admin, the representative flow is:
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npm run zova :tools:metadata <module-name>
|
|
89
|
+
npm run build:zova:admin
|
|
90
|
+
npm run deps:vona
|
|
91
|
+
cd vona && npm run tsc
|
|
92
|
+
cd vona && npm test -- <resource-test>.test.ts
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## 6. Recovery rule when generated keys still look stale
|
|
96
|
+
|
|
97
|
+
If the generated `.zova-rest` files already contain the new renderer keys but Vona still behaves as if old types are installed:
|
|
98
|
+
|
|
99
|
+
- suspect a stale or unhealthy `vona/node_modules` installation state
|
|
100
|
+
- after normal `deps:vona`, rebuild `vona/node_modules` and reinstall dependencies if needed
|
|
101
|
+
|
|
102
|
+
This is an installation-state recovery step, not the normal first move.
|