cabloy 5.1.150 → 5.1.151

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 (50) hide show
  1. package/.cabloy-version +1 -1
  2. package/.claude/commands/release.md +12 -1
  3. package/.claude/hooks/contract-loop-gate.ts +35 -17
  4. package/.claude/skills/cabloy-backend-scaffold/SKILL.md +4 -3
  5. package/.claude/skills/cabloy-backend-scaffold/references/follow-up-checklist.md +1 -1
  6. package/.claude/skills/cabloy-contract-loop/SKILL.md +4 -3
  7. package/.claude/skills/cabloy-domain-planning/SKILL.md +4 -3
  8. package/.claude/skills/cabloy-frontend-scaffold/SKILL.md +4 -3
  9. package/.claude/skills/cabloy-master-detail/SKILL.md +4 -3
  10. package/.claude/skills/cabloy-module-removal/SKILL.md +5 -4
  11. package/.claude/skills/cabloy-resource-field-update/SKILL.md +8 -5
  12. package/.claude/skills/cabloy-spec-execution/SKILL.md +169 -0
  13. package/.claude/skills/cabloy-spec-execution/evals/evals.json +71 -0
  14. package/.claude/skills/cabloy-spec-execution/references/execution-protocol.md +137 -0
  15. package/.claude/skills/cabloy-spec-execution/references/status-and-evidence.md +117 -0
  16. package/.claude/skills/cabloy-spec-generation/SKILL.md +227 -0
  17. package/.claude/skills/cabloy-spec-generation/evals/evals.json +83 -0
  18. package/.claude/skills/cabloy-spec-generation/references/repo-aware-discovery.md +101 -0
  19. package/.claude/skills/cabloy-spec-generation/references/repo-specs-document-set.md +267 -0
  20. package/.claude/skills/cabloy-spec-generation/references/traceability-and-status-rules.md +135 -0
  21. package/.claude/skills/cabloy-spec-generation/scripts/generate-implementation-charts.mjs +350 -0
  22. package/.claude/skills/cabloy-spec-generation/scripts/generate-implementation-charts.test.mjs +202 -0
  23. package/.claude/skills/cabloy-workflow/SKILL.md +21 -10
  24. package/.claude/skills/cabloy-workflow/evals/evals.json +8 -2
  25. package/.claude/skills/cabloy-workflow/references/edition-detection.md +5 -3
  26. package/.claude/skills/cabloy-zova-source-reading/SKILL.md +4 -3
  27. package/CHANGELOG.md +19 -0
  28. package/CLAUDE.md +11 -11
  29. package/package.json +4 -2
  30. package/repo-docs/.vitepress/config.mjs +4 -0
  31. package/repo-docs/ai/docs-skills-rules-mapping.md +6 -6
  32. package/repo-docs/ai/future-skill-roadmap.md +2 -2
  33. package/repo-docs/ai/introduction.md +5 -5
  34. package/repo-docs/ai/playbook-contract-regeneration.md +3 -9
  35. package/repo-docs/ai/repo-guidance.md +5 -4
  36. package/repo-docs/ai/rules-and-config.md +2 -2
  37. package/repo-docs/ai/virtual-decorator-guidance.md +1 -3
  38. package/repo-docs/backend/cache-guide.md +36 -0
  39. package/repo-docs/backend/introduction.md +1 -1
  40. package/repo-docs/backend/migration-and-changes.md +27 -2
  41. package/repo-docs/backend/model-guide.md +1 -1
  42. package/repo-docs/backend/resource-field-update.md +156 -0
  43. package/repo-docs/frontend/router-tabs-introduction.md +2 -4
  44. package/repo-docs/fullstack/a-pay-payment-suite.md +1 -1
  45. package/repo-docs/fullstack/contract-loop-playbook.md +4 -0
  46. package/repo-docs/fullstack/one-to-one-companion-resource-guide.md +1 -1
  47. package/repo-docs/reference/package-map.md +1 -1
  48. package/scripts/release.ts +48 -1
  49. package/scripts/upgrade.ts +18 -7
  50. package/zova/pnpm-lock.yaml +5 -5
@@ -0,0 +1,156 @@
1
+ # Existing Resource Field Update
2
+
3
+ <Badge type="tip" text="Common" />
4
+
5
+ Use this workflow when changing a field on an existing Vona resource. It covers a new persisted field, validation or metadata refinement, enum-like constraints, and the optional renderer follow-up that connects backend `ZovaRender.*(...)` metadata to Zova resources.
6
+
7
+ This is a field-update workflow, not a substitute for initial module or CRUD scaffolding. For a new backend thread, start from [CRUD Workflow](/backend/crud-workflow). For an already stale generated consumer, start from the [Contract Loop Playbook](/fullstack/contract-loop-playbook).
8
+
9
+ ## When to use this workflow
10
+
11
+ Use it when you need to:
12
+
13
+ - add a stored field to an existing resource;
14
+ - refine validation, titles, OpenAPI metadata, or `ZovaRender.field(...)` / `ZovaRender.cell(...)` metadata for an existing field;
15
+ - add enum-like field constraints;
16
+ - decide whether a persisted field change increments `vonaModule.fileVersion`;
17
+ - add a custom form-field or table-cell renderer because shared renderer options are insufficient.
18
+
19
+ Before changing anything, detect the active edition from the root marker. Explain the common backend workflow once, then resolve renderer keys, frontend flavors, build commands, and generated-output paths from the active edition.
20
+
21
+ ## Persisted field or metadata-only change
22
+
23
+ Classify the work before editing migration code.
24
+
25
+ ### New persisted field
26
+
27
+ Examples include a new `level: number`, `status: string`, or stored relation key. This changes storage shape and requires an explicit `fileVersion` decision.
28
+
29
+ ### Metadata-only or validation/rendering refinement
30
+
31
+ Examples include:
32
+
33
+ - enum validation for a field that already exists in storage;
34
+ - a new field or table rendering hint;
35
+ - locale-label changes;
36
+ - stricter validation that does not change storage shape.
37
+
38
+ This usually does not require a `fileVersion` increment. Confirm the current schema first instead of assuming the field already exists.
39
+
40
+ ## Decide whether to increment fileVersion
41
+
42
+ For a new persisted field, decide with the user **before** editing `meta.version.ts`, a versioned schema path, or the module `package.json`.
43
+
44
+ ### Increment fileVersion
45
+
46
+ When the change needs a new sequential migration:
47
+
48
+ 1. increment `vonaModule.fileVersion`;
49
+ 2. add a new `meta.version.ts` migration branch;
50
+ 3. preserve older branches as historical snapshots;
51
+ 4. introduce the field only in the new version branch.
52
+
53
+ Do not add the same column to an older create path and again to a later migration branch. A fresh installation can apply the branches sequentially and fail on a duplicate column.
54
+
55
+ ### Keep the current fileVersion
56
+
57
+ When the user decides not to create new migration history:
58
+
59
+ 1. keep the current `fileVersion`;
60
+ 2. fold the schema change into the current version path;
61
+ 3. do not create a new migration branch.
62
+
63
+ For the complete migration lifecycle and test requirement, see [Migration and Changes](/backend/migration-and-changes#decide-whether-to-increment-fileversion).
64
+
65
+ ## Inspect the existing resource thread
66
+
67
+ Read the current implementation before proposing changes:
68
+
69
+ - entity and model;
70
+ - DTOs, controller, and service;
71
+ - `meta.version.ts` and module `package.json`;
72
+ - locale files;
73
+ - resource tests;
74
+ - root `package.json`, `npm run vona`, and `npm run zova`.
75
+
76
+ Use source patterns to determine whether DTOs are inferred. Do not hand-edit generated consumers or duplicate existing schema metadata.
77
+
78
+ ## Update entity truth first
79
+
80
+ Treat the entity as the primary field-definition surface. Typical changes begin with `@Api.field(...)`, validation helpers, titles, and `ZovaRender.*(...)` metadata.
81
+
82
+ For enum-like values, prefer a constrained schema that expresses the allowed values explicitly, for example `z.union([z.literal(1), z.literal(2), z.literal(3)])`.
83
+
84
+ Structure-shaping schema helpers remain order-sensitive. When a field uses `v.object(...)`, `v.array(...)`, `v.optional()`, `v.nullable()`, `v.default(...)`, preprocess/transform wrappers, or an explicit zod schema, keep the final structure-defining schema-like argument last. Then verify emitted schema/OpenAPI output rather than assuming argument reordering is safe.
85
+
86
+ For the underlying contract surface, see [Entity Guide](/backend/entity-guide#api-field-as-the-shared-contract-surface).
87
+
88
+ ## Preserve the inferred DTO flow
89
+
90
+ When DTOs use `$Dto.create(...)`, `$Dto.update(...)`, `$Dto.get(...)`, or another inferred projection, let the entity change propagate. Add hand-authored field lists only when the current source demonstrates an intentional business projection or a separate contract-only field.
91
+
92
+ For projected fields, use `$makeMetadata(...)` for metadata-only refinement and `$makeSchema(...)` for schema or validation refinement. Use a class-body `@Api.field(...)` member only for a genuinely new declared field.
93
+
94
+ Read [DTO Infer and Generation](/backend/dto-infer-generation) when inference cannot express the intended contract. If serialization metadata controls the returned value, also confirm that the target controller action opts into `@Core.serializer()`.
95
+
96
+ ## Choose the renderer branch
97
+
98
+ Prefer the smallest rendering change that expresses the requirement:
99
+
100
+ 1. reuse an existing shared renderer;
101
+ 2. configure it with field-level options;
102
+ 3. create a custom module-local renderer only when the shared surface cannot express the needed behavior.
103
+
104
+ For a field-rendering select, provide a visible placeholder unless the UX genuinely requires a preselected value.
105
+
106
+ ### Shared renderer reuse
107
+
108
+ Resolve renderer keys and component behavior in the active edition. Cabloy Basic and Cabloy Start can differ in select wrappers, placeholder behavior, UI library, flavors, and generated outputs. Do not copy a Basic renderer key or empty-item convention into Start without inspecting the Start baseline.
109
+
110
+ ### Custom renderer follow-up
111
+
112
+ A custom backend-rendered field normally needs both sides of the frontend pair:
113
+
114
+ - a module-local FormField component;
115
+ - a registered module-local `@TableCell(...)` bean when backend `ZovaRender.cell(...)` references a table cell.
116
+
117
+ A plain frontend component is not sufficient for backend table-cell metadata. Reuse the closest shared renderer’s option/data flow and keep the customization focused.
118
+
119
+ For public implementation patterns, see [Form Guide](/frontend/form-guide), [TableCell Cookbook](/frontend/table-cell-cookbook#pattern-7-backend-contract-to-frontend-cell-handoff), and the Basic-only [Custom Form/Table Renderers tutorial](/fullstack/tutorial-4-custom-level-renderers).
120
+
121
+ ## Follow the contract loop
122
+
123
+ A field change commonly has two possible handoffs:
124
+
125
+ - a persisted entity/DTO/OpenAPI change begins with the **forward chain**: update backend truth, verify emitted contract output, regenerate frontend consumers, then make thin follow-up changes;
126
+ - a new frontend-owned FormField or TableCell resource begins the **reverse chain**: update frontend source, regenerate metadata when needed, run the active edition’s complete frontend build, then run `npm run deps:vona`.
127
+
128
+ Do not treat `build:rest:*` alone as sufficient for reverse-chain work. The SSR bundle and REST output must move together. If generated `.zova-rest` output already contains the expected keys but Vona still sees stale types after the normal build and dependency sync, treat the issue as local dependency drift and repair the local installation state before hand-patching generated links.
129
+
130
+ Use the [Contract Loop Playbook](/fullstack/contract-loop-playbook) for edition-aware commands and recovery details.
131
+
132
+ ## Update locale and tests
133
+
134
+ Update locale files when users can see field titles, enum labels, placeholders, or renderer helper text.
135
+
136
+ Minimum backend coverage generally includes:
137
+
138
+ - create with the field;
139
+ - select/list behavior;
140
+ - update persistence;
141
+ - get-by-id or view response;
142
+ - delete behavior when it is relevant.
143
+
144
+ For constrained enum-like values, add a negative test that proves an invalid value is rejected. Test-local persisted resources must be deleted in `finally` using precise owned identities and reverse dependency order.
145
+
146
+ ## Verification checklist
147
+
148
+ Choose checks that match the layers changed:
149
+
150
+ - inspect generated schema/OpenAPI after structure-shaping field changes;
151
+ - run the narrow resource test and relevant typecheck;
152
+ - run `npm run test` for any `meta.version.ts` change so the test database is recreated and migration consistency is exercised;
153
+ - run the relevant frontend metadata/build/dependency synchronization when custom renderer resources are involved;
154
+ - verify action-level serializer behavior with an API test when `v.serializer*` metadata changes returned fields.
155
+
156
+ Finish by confirming the backend contract, frontend resources, generated handoff, and user-visible locale labels all describe the same field behavior.
@@ -110,15 +110,13 @@ Recommended order:
110
110
 
111
111
  1. [Router Tabs Mechanism](/frontend/router-tabs-mechanism)
112
112
  2. [Router Tabs Admin and Web Comparison](/frontend/router-tabs-admin-web-comparison)
113
- 3. `repo-docs-internal/architecture/router-tabs-design-boundaries.md`
113
+ 3. inspect the current implementation and public comparison guidance for refactor boundaries
114
114
 
115
115
  ## Scope boundary
116
116
 
117
117
  The public router-tabs docs explain the shared frontend mechanism and how to use it.
118
118
 
119
- For internal design boundaries, maintenance invariants, and refactor safety rules, see:
120
-
121
- - `repo-docs-internal/architecture/router-tabs-design-boundaries.md`
119
+ For maintainer-only design boundaries, maintenance invariants, and refactor safety rules, consult an established internal-documentation home when the active repository provides one. The public mechanism, layout, route-meta, and Admin/Web comparison guides remain the shared implementation baseline.
122
120
 
123
121
  ## See also
124
122
 
@@ -265,7 +265,7 @@ The Commerce payment route requires authentication and uses the session SSR prof
265
265
 
266
266
  The invariant is more important than a particular visual shell: server HTML and the hydration-time initial render must agree about private payment/order data. Do not render customer-owned payment state on the server when the request cannot authorize it, and do not let the provider return itself settle Commerce.
267
267
 
268
- For the deeper SSR return and Passport recovery rationale, maintainers can consult the internal architecture note. The public rule remains: settle on verified server facts; recover private browser state only after hydration and authorization.
268
+ When the active repository provides the corresponding internal architecture note, maintainers can consult it for deeper SSR return and Passport recovery rationale. The public rule remains: settle on verified server facts; recover private browser state only after hydration and authorization.
269
269
 
270
270
  ## Reliability and security invariants
271
271
 
@@ -113,6 +113,9 @@ Examples:
113
113
  - controller request or response changed
114
114
  - validation changed
115
115
  - OpenAPI metadata changed
116
+ - an existing resource field changed
117
+
118
+ For an existing resource-field change, first use [Existing Resource Field Update](/backend/resource-field-update) to make the persistence, DTO, locale, renderer, and migration decision explicit. Then continue with the applicable contract-loop branch.
116
119
 
117
120
  Then use the **forward chain**:
118
121
 
@@ -357,6 +360,7 @@ Use the tutorial series as examples of the two chains:
357
360
 
358
361
  - [Fullstack Introduction](/fullstack/introduction)
359
362
  - [Backend OpenAPI to Frontend SDK](/fullstack/openapi-to-sdk)
363
+ - [Existing Resource Field Update](/backend/resource-field-update)
360
364
  - [Frontend Metadata Back to Backend](/fullstack/frontend-metadata-to-backend)
361
365
  - [Vona + Zova Integration](/fullstack/vona-zova-integration)
362
366
  - [OpenAPI SDK Guide](/frontend/openapi-sdk-guide)
@@ -295,7 +295,7 @@ The dependency should be directional:
295
295
  companion mutation → parent query cache invalidation
296
296
  ```
297
297
 
298
- Keep the cache graph acyclic and avoid duplicate or reverse edges. Cache invalidation is not a lock, a uniqueness constraint, or a transaction. Continue to use normal Model mutation APIs so the framework’s cache behavior remains active. Read [Cache Guide](/backend/cache-guide) for general cache rules.
298
+ Keep the cache graph acyclic and avoid duplicate or reverse edges. Cache invalidation is not a lock, a uniqueness constraint, or a transaction. Continue to use normal Model mutation APIs so the framework’s cache behavior remains active. Read [Cross-model query-cache dependencies](/backend/cache-guide#cross-model-query-cache-dependencies) for the graph rules.
299
299
 
300
300
  ## DTO and contract design
301
301
 
@@ -6,7 +6,7 @@ This monorepo is organized around a small set of stable top-level areas.
6
6
 
7
7
  - `package.json` — shared scripts and release entrypoints
8
8
  - `lerna.json` — package topology
9
- - `repo-docs-internal/` — internal engineering docs
9
+ - an established internal-documentation home, when present — edition-local maintainer notes and ADRs
10
10
  - `.claude/` — Claude commands, skills, and local settings
11
11
  - `repo-docs/` — unified public docs
12
12
 
@@ -88,10 +88,55 @@ function readJson(filePath: string): Record<string, any> {
88
88
  return JSON.parse(readFileSync(filePath, 'utf-8'));
89
89
  }
90
90
 
91
+ type CabloyEdition = 'basic' | 'start';
92
+
93
+ function resolveEdition(): CabloyEdition {
94
+ const hasBasic = existsSync(resolve(ROOT_DIR, '__CABLOY_BASIC__'));
95
+ const hasStart = existsSync(resolve(ROOT_DIR, '__CABLOY_START__'));
96
+ if (hasBasic === hasStart) {
97
+ throw new Error(
98
+ hasBasic
99
+ ? 'Ambiguous Cabloy edition: both __CABLOY_BASIC__ and __CABLOY_START__ markers are present'
100
+ : 'Not a Cabloy project: no __CABLOY_BASIC__ or __CABLOY_START__ marker found',
101
+ );
102
+ }
103
+ return hasBasic ? 'basic' : 'start';
104
+ }
105
+
91
106
  function readPackageJson(): Record<string, any> {
92
107
  return readJson(PACKAGE_JSON_PATH);
93
108
  }
94
109
 
110
+ function assertPublicBasicReleaseSurface(): void {
111
+ const edition = resolveEdition();
112
+ if (edition !== 'basic') {
113
+ throw new Error(
114
+ 'The public Cabloy release workflow is available only in Cabloy Basic. Use an explicitly configured Cabloy Start release workflow instead.',
115
+ );
116
+ }
117
+
118
+ const pkg = readPackageJson();
119
+ const requiredScripts: Record<string, string> = {
120
+ 'release-patch': 'node scripts/release.ts patch',
121
+ 'release-minor': 'node scripts/release.ts minor',
122
+ 'release-major': 'node scripts/release.ts major',
123
+ 'release': 'node scripts/release.ts',
124
+ 'release:dry-run': 'node scripts/release.ts --dry-run',
125
+ 'release:changelog': 'node scripts/release.ts --changelog-only',
126
+ 'release:publish': 'node scripts/release.ts --publish-only',
127
+ 'release:github': 'node scripts/release.ts --release-only',
128
+ };
129
+ if (
130
+ pkg.name !== 'cabloy' ||
131
+ !isValidVersion(pkg.version) ||
132
+ Object.entries(requiredScripts).some(([name, command]) => pkg.scripts?.[name] !== command)
133
+ ) {
134
+ throw new Error(
135
+ 'The active package.json does not expose the Cabloy Basic public release surface required by scripts/release.ts.',
136
+ );
137
+ }
138
+ }
139
+
95
140
  function writePackageJson(pkg: Record<string, any>): void {
96
141
  writeFileSync(PACKAGE_JSON_PATH, `${JSON.stringify(pkg, null, 2)}\n`);
97
142
  }
@@ -274,7 +319,7 @@ function refreshZovaDependencyGraph(): void {
274
319
  execInherited('pnpm install', false, { cwd: ZOVA_DIR });
275
320
  execInherited('npm run zova :tools:deps', false, { cwd: ZOVA_DIR });
276
321
  execInherited('npm run build:zova:admin');
277
- if (existsSync(resolve(ROOT_DIR, '__CABLOY_BASIC__'))) {
322
+ if (resolveEdition() === 'basic') {
278
323
  execInherited('npm run build:zova:web');
279
324
  }
280
325
  }
@@ -882,6 +927,8 @@ async function release(options: ReleaseOptions): Promise<void> {
882
927
  // eslint-disable-next-line
883
928
  console.log('🔧 Cabloy Release\n');
884
929
 
930
+ assertPublicBasicReleaseSurface();
931
+
885
932
  // Pre-flight checks
886
933
  try {
887
934
  execSync('git rev-parse --is-inside-work-tree', {
@@ -152,8 +152,21 @@ function copyDirectory(src: string, dest: string): void {
152
152
  cpSync(src, dest, { recursive: true, filter: shouldCopyPath });
153
153
  }
154
154
 
155
+ function resolveEdition(): 'basic' | 'start' {
156
+ const hasBasic = existsSync(resolve(ROOT_DIR, '__CABLOY_BASIC__'));
157
+ const hasStart = existsSync(resolve(ROOT_DIR, '__CABLOY_START__'));
158
+ if (hasBasic === hasStart) {
159
+ throw new Error(
160
+ hasBasic
161
+ ? 'Ambiguous Cabloy edition: both __CABLOY_BASIC__ and __CABLOY_START__ markers are present'
162
+ : 'Not a Cabloy project: no __CABLOY_BASIC__ or __CABLOY_START__ marker found',
163
+ );
164
+ }
165
+ return hasBasic ? 'basic' : 'start';
166
+ }
167
+
155
168
  function isCabloyBasic(): boolean {
156
- return existsSync(resolve(ROOT_DIR, '__CABLOY_BASIC__'));
169
+ return resolveEdition() === 'basic';
157
170
  }
158
171
 
159
172
  function readPackageJson(filePath: string): PackageJson {
@@ -337,12 +350,10 @@ function reconcileFrameworkE2ePackageJson(dryRun?: boolean): void {
337
350
  // --- Step 1: Pre-flight ---
338
351
 
339
352
  function preflight(): void {
340
- const markers = ['__CABLOY_BASIC__', '__CABLOY_START__'];
341
- const found = markers.find(m => existsSync(resolve(ROOT_DIR, m)));
342
- if (!found) {
343
- console.error(
344
- 'Error: Not a cabloy project (no __CABLOY_BASIC__ or __CABLOY_START__ marker found)',
345
- );
353
+ try {
354
+ resolveEdition();
355
+ } catch (error) {
356
+ console.error(`Error: ${(error as Error).message}`);
346
357
  process.exit(1);
347
358
  }
348
359
  }
@@ -870,7 +870,7 @@ importers:
870
870
  specifier: ^5.1.88
871
871
  version: link:../zova-core
872
872
  zova-suite-a-zova:
873
- specifier: ^5.1.153
873
+ specifier: ^5.1.154
874
874
  version: link:../../src/suite-vendor/a-zova
875
875
  devDependencies:
876
876
  clean-package:
@@ -977,7 +977,7 @@ importers:
977
977
  specifier: ^5.0.1
978
978
  version: link:modules/a-rbac
979
979
  zova-module-rest-resource:
980
- specifier: ^5.1.46
980
+ specifier: ^5.1.47
981
981
  version: link:modules/rest-resource
982
982
 
983
983
  src/suite-vendor/a-cabloy/modules/a-rbac:
@@ -1076,7 +1076,7 @@ importers:
1076
1076
  specifier: ^5.1.25
1077
1077
  version: link:modules/a-fetch
1078
1078
  zova-module-a-form:
1079
- specifier: ^5.1.49
1079
+ specifier: ^5.1.50
1080
1080
  version: link:modules/a-form
1081
1081
  zova-module-a-icon:
1082
1082
  specifier: ^5.1.27
@@ -1091,10 +1091,10 @@ importers:
1091
1091
  specifier: ^5.1.22
1092
1092
  version: link:modules/a-meta
1093
1093
  zova-module-a-model:
1094
- specifier: ^5.1.35
1094
+ specifier: ^5.1.36
1095
1095
  version: link:modules/a-model
1096
1096
  zova-module-a-openapi:
1097
- specifier: ^5.1.50
1097
+ specifier: ^5.1.51
1098
1098
  version: link:modules/a-openapi
1099
1099
  zova-module-a-router:
1100
1100
  specifier: ^5.1.34