cabloy 5.1.58 → 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.
Files changed (112) hide show
  1. package/.claude/skills/cabloy-contract-loop/SKILL.md +16 -0
  2. package/.claude/skills/cabloy-contract-loop/references/contract-loop-map.md +26 -0
  3. package/.claude/skills/cabloy-contract-loop/references/resource-custom-state-pattern.md +144 -0
  4. package/.claude/skills/cabloy-contract-loop/references/verification-checklist.md +18 -0
  5. package/.claude/skills/cabloy-resource-field-update/SKILL.md +267 -0
  6. package/.claude/skills/cabloy-resource-field-update/evals/evals.json +53 -0
  7. package/.claude/skills/cabloy-resource-field-update/references/custom-renderer-demo-checklist.md +102 -0
  8. package/.claude/skills/cabloy-resource-field-update/references/field-update-decision-tree.md +120 -0
  9. package/.claude/skills/cabloy-resource-field-update/references/follow-up-checklist.md +80 -0
  10. package/.claude/skills/cabloy-resource-field-update/references/verification-checklist.md +97 -0
  11. package/.github/workflows/docs-pages.yml +2 -0
  12. package/.github/workflows/vona-cov-pg.yml +2 -0
  13. package/.github/workflows/vona-test-crud.yml +4 -2
  14. package/.github/workflows/vona-test-mysql.yml +2 -0
  15. package/.github/workflows/vona-test-pg.yml +2 -0
  16. package/.github/workflows/vona-test-sqlite3.yml +2 -0
  17. package/.github/workflows/vona-tsc.yml +2 -0
  18. package/.github/workflows/zova-ui.yml +2 -0
  19. package/.gitignore +0 -4
  20. package/CHANGELOG.md +41 -0
  21. package/CLAUDE.md +2 -0
  22. package/README.md +15 -0
  23. package/cabloy-docs/.vitepress/config.mjs +43 -0
  24. package/cabloy-docs/ai/class-placement-rule.md +2 -0
  25. package/cabloy-docs/ai/cli-to-skill-map.md +7 -0
  26. package/cabloy-docs/ai/future-skill-roadmap.md +17 -2
  27. package/cabloy-docs/backend/bean-scene-authoring.md +350 -0
  28. package/cabloy-docs/backend/cli.md +26 -1
  29. package/cabloy-docs/backend/foundation.md +28 -3
  30. package/cabloy-docs/backend/introduction.md +8 -0
  31. package/cabloy-docs/backend/service-guide.md +2 -0
  32. package/cabloy-docs/backend/websocket-call-flow.md +435 -0
  33. package/cabloy-docs/backend/websocket-guide.md +455 -0
  34. package/cabloy-docs/backend/websocket-protocol-guide.md +381 -0
  35. package/cabloy-docs/backend/websocket-usage-guide.md +356 -0
  36. package/cabloy-docs/frontend/bean-scene-authoring.md +372 -0
  37. package/cabloy-docs/frontend/behavior-guide.md +449 -0
  38. package/cabloy-docs/frontend/cli.md +12 -0
  39. package/cabloy-docs/frontend/introduction.md +5 -0
  40. package/cabloy-docs/frontend/ioc-and-beans.md +10 -9
  41. package/cabloy-docs/frontend/router-tabs-admin-web-comparison.md +206 -0
  42. package/cabloy-docs/frontend/router-tabs-introduction.md +106 -0
  43. package/cabloy-docs/frontend/router-tabs-mechanism.md +469 -0
  44. package/cabloy-docs/frontend/router-tabs-overview.md +227 -0
  45. package/cabloy-docs/frontend/router-tabs-route-meta-cookbook.md +343 -0
  46. package/cabloy-docs/frontend/ssr-architecture-overview.md +211 -0
  47. package/cabloy-docs/frontend/ssr-build-deploy-guide.md +308 -0
  48. package/cabloy-docs/frontend/ssr-review-checklist.md +184 -0
  49. package/cabloy-docs/frontend/ssr-troubleshooting-guide.md +301 -0
  50. package/cabloy-docs/fullstack/framework-performance.md +3 -3
  51. package/cabloy-docs/fullstack/introduction.md +29 -0
  52. package/cabloy-docs/fullstack/quickstart.md +7 -1
  53. package/cabloy-docs/fullstack/tutorial-1-first-module.md +111 -0
  54. package/cabloy-docs/fullstack/tutorial-2-first-crud.md +122 -0
  55. package/cabloy-docs/fullstack/tutorial-3-frontend-metadata-sharing.md +131 -0
  56. package/cabloy-docs/fullstack/tutorial-4-custom-level-renderers.md +119 -0
  57. package/cabloy-docs/fullstack/tutorial-5-backend-contract-sharing.md +144 -0
  58. package/cabloy-docs/fullstack/tutorial-6-one-contract-four-uses.md +168 -0
  59. package/cabloy-docs/fullstack/tutorials-overview.md +179 -0
  60. package/cabloy-docs/index.md +4 -3
  61. package/cabloy-docs/reference/bean-scene-boilerplates.md +73 -0
  62. package/cabloy-docs/reference/cli-reference.md +2 -0
  63. package/package.json +6 -2
  64. package/scripts/init.ts +18 -2
  65. package/scripts/upgrade.ts +6 -0
  66. package/vona/packages-cli/cabloy-cli/package.json +2 -2
  67. package/vona/packages-cli/cli/package.json +1 -1
  68. package/vona/packages-cli/cli-set-api/package.json +1 -1
  69. package/vona/packages-cli/cli-set-api/src/lib/bean/cli.create.module.ts +4 -0
  70. package/vona/packages-utils/zod-query/package.json +1 -1
  71. package/vona/packages-vona/vona/package.json +1 -1
  72. package/vona/packages-vona/vona-core/package.json +1 -1
  73. package/vona/packages-vona/vona-mock/package.json +1 -1
  74. package/vona/pnpm-lock.yaml +133 -1088
  75. package/vona/pnpm-workspace.yaml +0 -1
  76. package/vona/src/suite-vendor/a-vona/modules/a-core/assets/static/img/vona.svg +1 -1
  77. package/vona/src/suite-vendor/a-vona/modules/a-core/package.json +1 -1
  78. package/vona/src/suite-vendor/a-vona/modules/a-openapi/package.json +1 -1
  79. package/vona/src/suite-vendor/a-vona/modules/a-openapiutils/package.json +1 -1
  80. package/vona/src/suite-vendor/a-vona/modules/a-permission/package.json +1 -1
  81. package/vona/src/suite-vendor/a-vona/modules/a-permission/src/bean/bean.permission.ts +1 -1
  82. package/vona/src/suite-vendor/a-vona/modules/a-upload/package.json +2 -2
  83. package/vona/src/suite-vendor/a-vona/modules/a-web/package.json +1 -1
  84. package/vona/src/suite-vendor/a-vona/package.json +1 -1
  85. package/zova/package.original.json +1 -1
  86. package/zova/packages-cli/cli/package.json +3 -3
  87. package/zova/packages-cli/cli-set-front/cli/templates/init/icon/boilerplate/icons/default/zova.svg +1 -1
  88. package/zova/packages-cli/cli-set-front/package.json +3 -3
  89. package/zova/packages-cli/cli-set-front/src/lib/bean/cli.create.module.ts +4 -0
  90. package/zova/packages-cli/cli-set-front/src/lib/command/create.bean.ts +5 -1
  91. package/zova/packages-utils/zova-jsx/package.json +2 -2
  92. package/zova/packages-utils/zova-vite/package.json +2 -2
  93. package/zova/packages-zova/zova/package.json +3 -3
  94. package/zova/packages-zova/zova-core/package.json +2 -2
  95. package/zova/pnpm-lock.yaml +284 -1313
  96. package/zova/pnpm-workspace.yaml +0 -1
  97. package/zova/src/suite/a-home/modules/home-icon/icons/social/cabloy.svg +1 -1
  98. package/zova/src/suite/a-home/modules/home-icon/icons/social/vona.svg +1 -1
  99. package/zova/src/suite/a-home/modules/home-icon/icons/social/zova.svg +1 -1
  100. package/zova/src/suite/a-home/modules/home-icon/src/.metadata/icons/groups/social.svg +3 -3
  101. package/zova/src/suite/cabloy-basic/modules/basic-select/src/component/formFieldSelect/controller.tsx +9 -0
  102. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/package.json +1 -1
  103. package/zova/src/suite-vendor/a-cabloy/modules/rest-resource/src/model/resource.ts +66 -16
  104. package/zova/src/suite-vendor/a-cabloy/package.json +2 -2
  105. package/zova/src/suite-vendor/a-zova/modules/a-routertabs/package.json +1 -1
  106. package/zova/src/suite-vendor/a-zova/modules/a-routertabs/src/model/tabs.ts +60 -18
  107. package/zova/src/suite-vendor/a-zova/modules/a-table/cli/tableActionRow/boilerplate/{{sceneName}}.{{beanName}}.tsx_ +6 -1
  108. package/zova/src/suite-vendor/a-zova/modules/a-table/cli/tableCell/boilerplate/{{sceneName}}.{{beanName}}.tsx_ +6 -1
  109. package/zova/src/suite-vendor/a-zova/modules/a-table/package.json +1 -1
  110. package/zova/src/suite-vendor/a-zova/modules/a-zod/package.json +2 -2
  111. package/zova/src/suite-vendor/a-zova/modules/a-zova/package.json +3 -3
  112. package/zova/src/suite-vendor/a-zova/package.json +5 -5
@@ -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
+ }
@@ -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.