@jskit-ai/agent-docs 0.1.94 → 0.1.96

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.
@@ -230,6 +230,24 @@ w/[workspaceSlug]/admin/...
230
230
 
231
231
  then `workspace` is usually the normal default. If you are generating a CRUD for a global operator or account area, `public` or `user` may make more sense.
232
232
 
233
+ ## Schema contract for generated CRUD
234
+
235
+ Generated CRUD tables use one single-column integer primary key, normally
236
+ `id`. Each foreign key must also be single-column and must reference the target
237
+ table's single-column primary key. Composite indexes remain useful for business
238
+ uniqueness such as `(workspace_id, slug)`, but they are never relationship
239
+ targets.
240
+
241
+ Tenant isolation comes from a direct `workspace_id` and/or `user_id` ownership
242
+ column, the exactly matching generated ownership filter, scoped service
243
+ lookups, and cross-tenant tests. Do not encode ownership into a composite
244
+ foreign key such as `(workspace_id, parent_id) -> (workspace_id, id)`.
245
+
246
+ The generator rejects unsupported key shapes. If an approved application truly
247
+ needs a different persistence model, record the durable decision in
248
+ `.jskit/APP_BLUEPRINT.md` and use the explicit table-ownership exception path
249
+ instead of modifying generated CRUD files.
250
+
233
251
  ## Example 1: `contacts`
234
252
 
235
253
  This is the baseline pattern. If you understand this example, the rest of the chapter becomes much easier.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/agent-docs",
3
- "version": "0.1.94",
3
+ "version": "0.1.96",
4
4
  "description": "Distributed JSKIT agent references, prompts, guides, and generated reference maps.",
5
5
  "type": "module",
6
6
  "files": [
@@ -14,9 +14,38 @@ Check first:
14
14
  - `jskit show crud-server-generator --details`
15
15
  - whether the request is server-only CRUD or server-plus-UI CRUD
16
16
 
17
+ ## Non-negotiable database contract
18
+
19
+ Before database, schema, CRUD, repository, or persistence work, read this
20
+ pattern completely. Use the database selected for this app's development
21
+ runtime; never alter a production, legacy, historical, or other valuable
22
+ database to develop or verify schema changes. Prove the complete migration
23
+ chain against a fresh disposable database before reporting completion.
24
+
25
+ For normal app-owned CRUD tables:
26
+
27
+ - use exactly one non-null integer primary-key column, normally
28
+ `id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY`
29
+ - make every foreign key single-column and point it directly to the referenced
30
+ table's single-column primary key
31
+ - use multi-column unique indexes only for business uniqueness, never as
32
+ relationship targets
33
+ - use only direct `workspace_id` and/or `user_id` columns for generated JSKIT
34
+ ownership, and select the ownership filter that matches those columns exactly
35
+ - express tenant-safe relationships as direct ownership plus a normal
36
+ `parent_id -> parent.id` relationship; resolve related IDs through the
37
+ workspace-scoped service and test both allowed and cross-workspace cases
38
+
39
+ Do not use composite primary keys or composite foreign keys to encode tenant
40
+ ownership, target a business key from a foreign key, or duplicate parent
41
+ identity when the related row ID already identifies it. Stop before generation
42
+ if the proposed table violates these constraints.
43
+
17
44
  Rules:
18
45
 
19
- - For a CRUD-backed entity, start with `jskit generate crud-server-generator scaffold ...`.
46
+ - For a CRUD-backed entity, create the validated table in the managed
47
+ development database first. Then make
48
+ `jskit generate crud-server-generator scaffold ...` the first JSKIT scaffold.
20
49
  - Unless the table is already owned by a JSKIT baseline package or is an explicit narrow exception recorded in `.jskit/table-ownership.json`, every persisted app-owned table must go through that server CRUD step first.
21
50
  - That server scaffold is the crucial first step even if no CRUD UI will be created yet.
22
51
  - If the table should already be CRUD-owned but should not expose public CRUD HTTP routes yet, scaffold it with `jskit generate crud-server-generator scaffold ... --internal` instead of dropping to direct knex or a hand-built pseudo-repository.
@@ -64,6 +93,19 @@ When a weird-custom persistence lane is proposed:
64
93
  - Record the exact approval and the approved exception in `.jskit/WORKBOARD.md` before coding.
65
94
  - If that exception changes the durable architecture rather than only the current chunk, record it in `.jskit/APP_BLUEPRINT.md` too.
66
95
  - Without that explicit approval record, do not take the weird-custom persistence path.
96
+ - Record durable, approved schema exceptions in `.jskit/APP_BLUEPRINT.md` as
97
+ well as the table-ownership exception; an exception must not silently weaken
98
+ the normal generated CRUD contract.
99
+
100
+ Before reporting completion:
101
+
102
+ - verify there are no composite primary or foreign keys in the generated CRUD
103
+ tables
104
+ - verify every generated CRUD foreign key targets a single-column primary key
105
+ - apply the generated migrations from zero to a fresh disposable database
106
+ - compare the recreated schema with the intended development schema
107
+ - run positive and negative cross-workspace relationship tests where ownership
108
+ applies, then run JSKIT Doctor and the project's normal verifier
67
109
 
68
110
  Avoid:
69
111
 
@@ -68,6 +68,7 @@ Local functions
68
68
  - `renderJsonRestSearchSchemaLines(columns = [])`
69
69
  - `renderJsonRestDefaultSortLine(columns = [])`
70
70
  - `renderResourceDefaultSortLiteral(columns = [])`
71
+ - `renderCurrentTimestampExpression(expression, column)`
71
72
  - `renderMigrationDefaultClause(column)`
72
73
  - `renderMigrationSpecificStringType(column, { tableCollation = "" } = {})`
73
74
  - `renderTemporalColumnBuilder(column, methodName)`
@@ -44,15 +44,19 @@ Local functions
44
44
  - `toBoolean(value)`
45
45
  - `toNullableNumber(value)`
46
46
  - `normalizeColumnDefault(value)`
47
+ - `normalizeCurrentTimestampExpression(value)`
48
+ - `normalizeOnUpdateExpression(value)`
47
49
  - `parseEnumValues(columnType = "")`
48
50
  - `resolveTypeKind(column)`
49
51
  - `normalizeColumn(row = {})`
50
52
  - `normalizePrimaryKeyColumns(rows = [])`
53
+ - `normalizePrimaryKeyColumnsByTable(rows = [])`
51
54
  - `normalizeIndexes(rows = [])`
52
55
  - `normalizeForeignKeys(rows = [])`
53
56
  - `normalizeCheckConstraints(rows = [])`
54
57
  - `requireIdColumn(columns, idColumn)`
55
58
  - `requirePrimaryKeyContainsId(primaryKeyColumns, idColumn)`
59
+ - `requireSupportedForeignKeys(foreignKeys, primaryKeyColumnsByTable)`
56
60
 
57
61
  ### root
58
62
 
@@ -353,6 +353,8 @@ Exports
353
353
  ### `templates/src/surfaces/admin/index.vue`
354
354
  Exports
355
355
  - None
356
+ Local functions
357
+ - `adminChildPath(suffix = "")`
356
358
 
357
359
  ### `templates/src/surfaces/admin/root.vue`
358
360
  Exports
@@ -465,7 +465,7 @@ Exports
465
465
  - `isSensitiveManagedTextRecord({ packageEntry = {}, record = {} } = {})`
466
466
  - `isSensitivePackageOption(packageEntry = {}, optionName = "")`
467
467
  - `isSensitiveTextMutation({ packageEntry = {}, mutation = {}, resolvedKey = "" } = {})`
468
- - `resolveSensitiveOptionEnvFallbacks({ packageEntry = {}, appRoot = "", optionInput = {}, readFileBufferIfExists } = {})`
468
+ - `resolveSensitiveOptionEnvFallbacks({ packageEntry = {}, appRoot = "", optionInput = {}, readFileBufferIfExists, environment = process.env } = {})`
469
469
  - `sanitizeInstalledPackageRecordForLock(record = {}, packageEntry = {})`
470
470
  - `sanitizeLockSecretsForWrite(lock = {}, packageRegistry = null)`
471
471
  - `sanitizeManagedTextForLock(packageEntry = {}, managedText = {})`
@@ -550,8 +550,9 @@ Local functions
550
550
  - `cookieHeader(setCookie = [])`
551
551
  - `responsePayload(response = {})`
552
552
  - `upstreamError(payload = {}, response = {}, extra = {})`
553
+ - `request(fetchImpl, href, options)`
553
554
  - `postJson(fetchImpl, href, body, headers = {})`
554
- - `readSession(fetchImpl, targetOrigin, setCookie = [])`
555
+ - `readSession(fetchImpl, targetOrigin)`
555
556
  - `logout(fetchImpl, targetOrigin, session)`
556
557
  - `login(fetchImpl, targetOrigin, identity, secret, session)`
557
558
 
@@ -7,4 +7,7 @@ Recommended references:
7
7
  - `node_modules/@jskit-ai/agent-docs/guide/agent/index.md`
8
8
  - `node_modules/@jskit-ai/agent-docs/patterns/INDEX.md`
9
9
 
10
+ Before database, schema, CRUD, repository, or persistence work, read and follow
11
+ `node_modules/@jskit-ai/agent-docs/patterns/crud-scaffolding.md`.
12
+
10
13
  Keep changes scoped to the user request and verify runtime behavior with tests or explicit checks.