@jskit-ai/agent-docs 0.1.88 → 0.1.91

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.
@@ -974,7 +974,7 @@ The generated `playwright.config.mjs` is a thin delegate to `@jskit-ai/jskit-cli
974
974
 
975
975
  - without `PLAYWRIGHT_BASE_URL`, it builds the app and starts the local server on `http://127.0.0.1:4173`
976
976
  - with `PLAYWRIGHT_BASE_URL`, it uses that managed preview and does not start another server
977
- - with `JSKIT_PLAYWRIGHT_STORAGE_STATE`, it loads the supplied authenticated state into Playwright contexts
977
+ - with `VIBE64_PLAYWRIGHT_STORAGE_STATE`, it loads the Vibe64-supplied authenticated state into Playwright contexts
978
978
 
979
979
  Tests should therefore navigate with relative paths such as `page.goto("/w/acme/admin/contacts")`.
980
980
 
@@ -1032,7 +1032,7 @@ A managed host authenticates outside the project browser context. It performs it
1032
1032
 
1033
1033
  ```bash
1034
1034
  PLAYWRIGHT_BASE_URL=https://managed-preview.example.test \
1035
- JSKIT_PLAYWRIGHT_STORAGE_STATE=/secure/temp/playwright-state.json \
1035
+ VIBE64_PLAYWRIGHT_STORAGE_STATE=/secure/temp/playwright-state.json \
1036
1036
  playwright test tests/e2e/contacts.spec.ts
1037
1037
  ```
1038
1038
 
@@ -197,11 +197,15 @@ In JSKIT persistence code, **visibility** means "which rows should this request
197
197
  - `public`
198
198
  - the record is not scoped by owner columns
199
199
  - `workspace`
200
- - the record belongs to one workspace, usually through `workspace_id`
200
+ - the record belongs to one workspace through the exact reserved column `workspace_id`
201
201
  - `user`
202
- - the record belongs to one user, usually through `user_id`
202
+ - the record belongs to one user through the exact reserved column `user_id`
203
203
  - `workspace_user`
204
- - the record belongs to one workspace and one user together
204
+ - the record belongs to one workspace and one user through both reserved columns
205
+
206
+ Only `workspace_id` and `user_id` carry this standard ownership contract. Specifically named foreign keys such as `recipient_user_id`, `created_by_user_id`, and `assignee_user_id` describe domain relationships; they are not alternate owner columns. Keep both fields when a row has an owner and a separate related actor, and never rename the relationship to an owner column merely to make a tool accept the schema.
207
+
208
+ The selected ownership filter must match the direct reserved columns exactly. A table with only `workspace_id` is `workspace`; a table with only `user_id` is `user`; and a table with both is `workspace_user`. A declaration cannot override or ignore either column.
205
209
 
206
210
  That is why the shared helpers exist. Repositories should not have to re-implement the same ownership rules by hand every time they filter a query or build an insert payload.
207
211
 
@@ -209,7 +209,7 @@ Published JSKIT libraries and tooling support Node.js 22 from 22.12.0 onward, No
209
209
 
210
210
  That matters because JSKIT maintenance policy changes over time. If the scaffold copied a large shell script into every app, existing apps would freeze the old behavior forever. By delegating to `jskit app verify`, `jskit app update-packages`, and `jskit app release`, the app keeps the nice `npm run` shortcuts while the maintained behavior stays in the installed CLI package.
211
211
 
212
- The Playwright scaffold follows the same rule. `playwright.config.mjs` delegates to `@jskit-ai/jskit-cli/test/playwright`, and the starter browser specs delegate their shared responsive checks to published JSKIT helpers. The generated files stay small while later JSKIT package updates can change local server startup, managed `PLAYWRIGHT_BASE_URL` handling, and `JSKIT_PLAYWRIGHT_STORAGE_STATE` support without copying that logic into each new app.
212
+ The Playwright scaffold follows the same rule. `playwright.config.mjs` delegates to `@jskit-ai/jskit-cli/test/playwright`, and the starter browser specs delegate their shared responsive checks to published JSKIT helpers. The generated files stay small while later JSKIT package updates can change local server startup, managed `PLAYWRIGHT_BASE_URL` handling, and `VIBE64_PLAYWRIGHT_STORAGE_STATE` support without copying that logic into each new app.
213
213
 
214
214
  `jskit app verify` is worth noticing specifically. Linting, tests, and builds check your source code and runtime behavior. The JSKIT part of that flow runs `doctor`, which checks JSKIT-managed app state: installed package visibility, lock-file-backed managed files, and other JSKIT-specific health rules. It is there because a JSKIT app is not only code. It is also a descriptor-driven managed project.
215
215
 
@@ -804,7 +804,7 @@ That command does two things:
804
804
  - runs the targeted Playwright command you give it
805
805
  - writes a receipt describing the verified feature, auth mode, and current dirty UI file set
806
806
 
807
- The auth mode is receipt metadata, not an authentication implementation. `jskit app verify-ui` does not log a user in, inject a secret, or rewrite the Playwright context. A direct local run uses `loginAsExistingUser()` from `@jskit-ai/auth-web/test/playwright`; a managed runner supplies authenticated state through `JSKIT_PLAYWRIGHT_STORAGE_STATE`. The generated `playwright.config.mjs` consumes that state and honors `PLAYWRIGHT_BASE_URL` without starting a second server.
807
+ The auth mode is receipt metadata, not an authentication implementation. `jskit app verify-ui` does not log a user in, inject a secret, or rewrite the Playwright context. A direct local run uses `loginAsExistingUser()` from `@jskit-ai/auth-web/test/playwright`; Vibe64 supplies authenticated state through `VIBE64_PLAYWRIGHT_STORAGE_STATE`. The generated `playwright.config.mjs` consumes that state and honors `PLAYWRIGHT_BASE_URL` without starting a second server.
808
808
 
809
809
  `doctor` then compares the current changed UI files to that receipt. If you edit the UI again afterwards, the receipt is stale and `doctor` tells you to rerun `jskit app verify-ui`.
810
810
 
@@ -33,7 +33,8 @@ npx jskit generate crud-server-generator scaffold \
33
33
  --namespace contacts \
34
34
  --surface admin \
35
35
  --ownership-filter workspace \
36
- --table-name contacts
36
+ --table-name contacts \
37
+ --grant-role member
37
38
 
38
39
  npx jskit generate crud-ui-generator crud \
39
40
  w/[workspaceSlug]/admin/contacts \
@@ -140,6 +141,8 @@ So the distinction is:
140
141
 
141
142
  That is why `--internal` is not a permissions shortcut and not a UI setting. It is a server-route exposure choice on top of the same CRUD ownership model.
142
143
 
144
+ The workspace role-grant decision is separate too. Every workspace-required generation must choose `--grant-role <role-id>` or `--no-role-grant`; there is no implicit role. An application that assigns generated CRUD permissions to `member` uses `--grant-role member`. In particular, `--internal` does not imply `--no-role-grant`.
145
+
143
146
  ### Ownership controls which owner columns are expected
144
147
 
145
148
  The repository layer ultimately applies visibility through the standard owner columns:
@@ -147,6 +150,12 @@ The repository layer ultimately applies visibility through the standard owner co
147
150
  - `workspace_id`
148
151
  - `user_id`
149
152
 
153
+ Those names are exact and reserved. `workspace_id` is workspace ownership; `user_id` is user ownership; both together are workspace-user ownership. Other foreign keys such as `recipient_user_id`, `created_by_user_id`, and `assignee_user_id` remain domain relationships and must not be treated as ownership aliases.
154
+
155
+ The generated ownership filter must match that exact set of reserved columns. Neither an explicit filter nor generated metadata can override what those direct columns mean.
156
+
157
+ If a row is workspace-owned but refers to a recipient, keep both concepts explicit: use `workspace_id` for ownership and `recipient_user_id` for the relationship. Renaming the relationship to `user_id` would opt the field into JSKIT's hidden owner-field and user-filtering behavior.
158
+
150
159
  That means the generated CRUD behaves like this:
151
160
 
152
161
  - `public`
@@ -99,6 +99,27 @@ The supported values are:
99
99
  | `workspace` | rows belong to one workspace | `workspace_id` |
100
100
  | `workspace_user` | rows belong to one workspace and one user together | `workspace_id` and `user_id` |
101
101
 
102
+ ### Ownership column names are exact and reserved
103
+
104
+ JSKIT ownership is explicit and materialized. Only these exact database columns carry the standard generated ownership contract:
105
+
106
+ - `workspace_id`
107
+ - `user_id`
108
+
109
+ They are not ordinary foreign-key names. Generated CRUD infrastructure uses them for visibility filtering, create-time owner stamping, and hidden owner-field handling.
110
+
111
+ Other foreign keys are domain relationships, even when their names end in `_user_id` or `_workspace_id`. For example:
112
+
113
+ - `recipient_user_id` identifies a recipient
114
+ - `created_by_user_id` records an author
115
+ - `assignee_user_id` identifies an assignee
116
+
117
+ Those fields do not become ownership aliases merely because they point to a user-owned table.
118
+
119
+ The ownership filter must match the direct reserved columns exactly. A table with only `workspace_id` is `workspace`; one with only `user_id` is `user`; and one with both is `workspace_user`. Neither the CLI declaration nor generated metadata overrides those columns.
120
+
121
+ Decide ownership first, select the matching ownership filter, and then model other actors with specifically named foreign keys. A workspace-owned notification outbox item that targets a user normally has `workspace_id` for ownership and `recipient_user_id` for the recipient relationship. Do not rename `recipient_user_id` to `user_id` merely to satisfy a generator or Doctor check; that would change the resource's ownership semantics.
122
+
102
123
  ### What each value means
103
124
 
104
125
  #### `public`
@@ -257,7 +278,8 @@ npx jskit generate crud-server-generator scaffold \
257
278
  --namespace contacts \
258
279
  --surface admin \
259
280
  --ownership-filter workspace \
260
- --table-name contacts
281
+ --table-name contacts \
282
+ --grant-role member
261
283
  ```
262
284
 
263
285
  This creates an app-local package under `packages/contacts/`.
@@ -278,6 +300,46 @@ Use that when:
278
300
 
279
301
  Do **not** use `--internal` as a substitute for ownership or permissions design. It is only about whether the public HTTP CRUD routes exist.
280
302
 
303
+ ### Choose workspace role grants explicitly
304
+
305
+ Workspace-required CRUD actions use the normal named permission ids:
306
+
307
+ - `crud.<namespace>.list`
308
+ - `crud.<namespace>.view`
309
+ - `crud.<namespace>.create`
310
+ - `crud.<namespace>.update`
311
+ - `crud.<namespace>.delete`
312
+
313
+ The generator never guesses which role should receive all five permissions. Every workspace-required CRUD command must choose one of these options:
314
+
315
+ ```bash
316
+ --grant-role administrator
317
+ ```
318
+
319
+ or:
320
+
321
+ ```bash
322
+ --no-role-grant
323
+ ```
324
+
325
+ `--grant-role` must name a configured role. `--no-role-grant` keeps the generated actions and their permission requirements but leaves the app's role catalog unchanged. The options are mutually exclusive.
326
+
327
+ To assign the generated permissions to `member`, make that decision explicit:
328
+
329
+ ```bash
330
+ --grant-role member
331
+ ```
332
+
333
+ This decision is independent of `--internal`. For an internal, workspace-owned persistence resource whose generated actions should not be assigned to any actor role, use both:
334
+
335
+ ```bash
336
+ --ownership-filter workspace \
337
+ --internal \
338
+ --no-role-grant
339
+ ```
340
+
341
+ If neither explicit grant option is supplied, generation fails during preflight before writing files, even when a `member` role exists.
342
+
281
343
  Before generating anything, decide these with the developer:
282
344
 
283
345
  - which operations are allowed for this CRUD
@@ -420,7 +482,8 @@ npx jskit generate crud-server-generator scaffold \
420
482
  --namespace addresses \
421
483
  --surface admin \
422
484
  --ownership-filter workspace \
423
- --table-name addresses
485
+ --table-name addresses \
486
+ --grant-role member
424
487
  ```
425
488
 
426
489
  Then install the generated local package:
@@ -596,7 +659,8 @@ npx jskit generate crud-server-generator scaffold \
596
659
  --namespace comments \
597
660
  --surface admin \
598
661
  --ownership-filter workspace \
599
- --table-name comments
662
+ --table-name comments \
663
+ --grant-role member
600
664
  ```
601
665
 
602
666
  Then install the generated local package:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/agent-docs",
3
- "version": "0.1.88",
3
+ "version": "0.1.91",
4
4
  "description": "Distributed JSKIT agent references, prompts, guides, and generated reference maps.",
5
5
  "type": "module",
6
6
  "files": [
@@ -24,6 +24,9 @@ Rules:
24
24
  - If `crud-server-generator` is going to own the CRUD, do not hand-write a separate CRUD migration for that table. The generator installs and manages the CRUD migration scaffold itself.
25
25
  - Do not scaffold CRUD UI, hand-build CRUD routes, or hand-build CRUD endpoints before the server CRUD package and shared resource file exist.
26
26
  - Treat the generated shared resource file as the canonical CRUD contract for later UI scaffolding and CRUD behavior changes.
27
+ - Treat the exact columns `workspace_id` and `user_id` as reserved JSKIT ownership columns. They are the only standard columns used for generated ownership filtering and create-time owner stamping.
28
+ - Treat other foreign keys such as `recipient_user_id`, `created_by_user_id`, `assignee_user_id`, and similarly specific names as domain relationships, not ownership aliases. Do not rename a relationship to `user_id` or `workspace_id` merely to satisfy tooling.
29
+ - Require the resolved ownership filter to match the reserved columns exactly: neither the filter nor the schema may silently add or omit `workspace_id` or `user_id` ownership.
27
30
  - `feature-server-generator` is not the default lane for ordinary persisted entities. Use it for workflows or orchestration that sit on top of CRUD-owned tables, or for rare explicit non-CRUD exceptions.
28
31
  - Generated CRUD UI must be compact-first. Lists need searchable cards on compact widths and tables only for medium/expanded layouts.
29
32
  - Generated CRUD list screens need real loading, empty, and error states. Empty copy should name the resource, such as "No customers yet", and offer the create action when available.
@@ -41,6 +44,14 @@ Meaning of `--internal`:
41
44
  - it keeps the generated repository, service, actions, provider, resource, and CRUD migration ownership chain
42
45
  - it only suppresses public HTTP CRUD route registration
43
46
  - it is not a substitute for ownership columns or action/route permissions
47
+ - it does not suppress generated action permission ids or decide which role receives them
48
+
49
+ Workspace role grants:
50
+
51
+ - every workspace-required CRUD generation must explicitly choose `--grant-role <role-id>` or `--no-role-grant`
52
+ - applications that assign generated CRUD permissions to `member` must say `--grant-role member`; fixed-role applications must name one of their real roles or choose no automatic grant
53
+ - `--grant-role` and `--no-role-grant` are permission-design choices independent of `--internal`
54
+ - never invent a `member` role only to satisfy the generator
44
55
 
45
56
  When a weird-custom persistence lane is proposed:
46
57
 
@@ -16,7 +16,7 @@ Rules:
16
16
  - Generated `playwright.config.mjs` delegates to `@jskit-ai/jskit-cli/test/playwright`. Do not copy base-URL, web-server, or storage-state logic into app tests.
17
17
  - Use relative paths such as `page.goto("/home")`. The shared config owns the browser base URL.
18
18
  - A managed runner supplies `PLAYWRIGHT_BASE_URL`. When it is set, JSKIT does not start another app server.
19
- - A managed runner supplies an authenticated context through `JSKIT_PLAYWRIGHT_STORAGE_STATE`. Treat that file as a temporary secret: do not commit it, print it, or retain it after the run.
19
+ - Vibe64 supplies an authenticated context through `VIBE64_PLAYWRIGHT_STORAGE_STATE`. Treat that file as a temporary secret: do not commit it, print it, or retain it after the run.
20
20
  - Do not install a browser when the environment provides a managed browser runner.
21
21
 
22
22
  ## Direct local authentication
@@ -61,7 +61,7 @@ A managed preview must not expose `AUTH_DEV_BYPASS_SECRET` to project code or fo
61
61
 
62
62
  ```bash
63
63
  PLAYWRIGHT_BASE_URL=https://managed-preview.example.test \
64
- JSKIT_PLAYWRIGHT_STORAGE_STATE=/secure/temp/playwright-state.json \
64
+ VIBE64_PLAYWRIGHT_STORAGE_STATE=/secure/temp/playwright-state.json \
65
65
  playwright test tests/e2e/contacts.spec.ts
66
66
  ```
67
67
 
@@ -21,13 +21,16 @@ Exports
21
21
  - `resolveGenerationSnapshot({ appRoot, tableName, idColumnOption } = {})`
22
22
  - `renderCanonicalResourceFieldSchema(column, { fieldContractEntry = null } = {})`
23
23
  - `buildFieldContractEntries({ outputColumns = [], writableColumns = [], snapshot = {} } = {})`
24
- - `resolveCrudGenerationSurfaceId({ appRoot, options } = {})`
24
+ - `resolveCrudGenerationSurfaceId({ appRoot, options, appConfig = null } = {})`
25
+ - `prepareInstallHook({ appRoot, packageOptions = {} } = {})`
25
26
  - `__testables`
26
27
  Local functions
27
28
  - `resolveAllowedValues(schema = {}, fallbackValues = [])`
28
29
  - `resolveGlobalScaffoldCache()`
29
30
  - `asRecord(value)`
31
+ - `resolveBooleanFlagOption(options = {}, optionName = "")`
30
32
  - `resolveInternalRouteOption(options = {})`
33
+ - `resolveNoRoleGrantOption(options = {})`
31
34
  - `normalizeRequestedOwnershipFilter(value, { strict = false } = {})`
32
35
  - `inferOwnershipFilterFromSnapshot(snapshot)`
33
36
  - `assertOwnershipColumnsForFilter(snapshot, filter)`
@@ -36,7 +39,7 @@ Local functions
36
39
  - `loadEnvFromApp(appRoot)`
37
40
  - `createAppRequire(appRoot)`
38
41
  - `importModuleFromApp(appRequire, moduleId, contextLabel)`
39
- - `resolveCrudSurfaceRequiresWorkspace({ appRoot, options, surface = "" } = {})`
42
+ - `resolveCrudSurfaceRequiresWorkspace({ appRoot, options, surface = "", appConfig = null } = {})`
40
43
  - `loadCrudAppConfig(appRoot = "")`
41
44
  - `resolveSurfaceDefinitions(appConfig = {})`
42
45
  - `resolveDefaultCrudSurfaceIdFromAppConfig(appConfig = {})`
@@ -84,8 +87,11 @@ Local functions
84
87
  - `normalizeFieldMetaUiOptions(rawOptions = [])`
85
88
  - `resolveEnumFieldMetaUiOptions(enumValues = [])`
86
89
  - `buildCrudPermissionIds(namespace = "")`
90
+ - `resolveConfiguredRoleEntries(appConfig = {})`
91
+ - `formatAvailableRoleIds(roleEntries = [])`
92
+ - `resolveCrudPermissionGrantRole(appConfig = {}, options = {}, { requiresNamedPermissions = true } = {})`
87
93
  - `normalizeCrudOperation(operation = "", context = "CRUD operation")`
88
- - `renderRoleCatalogPermissionGrants(namespace = "", { requiresNamedPermissions = true } = {})`
94
+ - `renderRoleCatalogPermissionGrants(namespace = "", { requiresNamedPermissions = true, grantRoleId = "" } = {})`
89
95
  - `renderActionPermissionSupport(namespace = "", { requiresNamedPermissions = true } = {})`
90
96
  - `renderActionPermissionExpression(operation = "", { requiresNamedPermissions = true } = {})`
91
97
  - `renderRouteWorkspaceSupportImports({ surfaceRequiresWorkspace = true } = {})`
@@ -96,7 +102,7 @@ Local functions
96
102
  - `renderObjectSchemaDefinition(lines = [], { mode = "patch" } = {})`
97
103
  - `renderActionInputExpressions({ surfaceRequiresWorkspace = true } = {})`
98
104
  - `renderRouteValidatorConstants({ surfaceRequiresWorkspace = true } = {})`
99
- - `buildReplacementsFromSnapshot({ namespace = "", snapshot, resolvedOwnershipFilter, surfaceRequiresWorkspace = true, surfaceId = "", routeInternal = false })`
105
+ - `buildReplacementsFromSnapshot({ namespace = "", snapshot, resolvedOwnershipFilter, surfaceRequiresWorkspace = true, surfaceId = "", routeInternal = false, permissionGrantRoleId = "" })`
100
106
  - `resolveCrudGenerationTableName(options = {})`
101
107
  - `createCacheKey({ appRoot, options })`
102
108
  - `buildCrudTemplateContext(input = {})`
@@ -534,6 +534,27 @@ Local functions
534
534
  - `applyTextReplacements(sourceText = "", replacements = [])`
535
535
  - `collectCrudFormFieldCandidateFiles(appRoot = "")`
536
536
 
537
+ ### `src/server/commandHandlers/appCommands/previewIdentity.js`
538
+ Exports
539
+ - `PREVIEW_IDENTITY_PROTOCOL`
540
+ - `executeJskitPreviewIdentityRequest(value = {}, { env = process.env, fetchImpl = globalThis.fetch } = {})`
541
+ - `runAppPreviewIdentityCommand(_ctx = {}, { env = process.env, fetchImpl = globalThis.fetch, stdin = process.stdin, stdout = process.stdout } = {})`
542
+ Local functions
543
+ - `previewIdentityResponse(requestId = "", values = {})`
544
+ - `previewIdentityFailure(requestId = "", error = {})`
545
+ - `commandError(message = "", code = "jskit_preview_identity_failed", extra = {})`
546
+ - `readInput(stream)`
547
+ - `normalizeRequest(value = {})`
548
+ - `identityFromSubject(subject = {})`
549
+ - `responseSetCookie(response = {})`
550
+ - `cookieHeader(setCookie = [])`
551
+ - `responsePayload(response = {})`
552
+ - `upstreamError(payload = {}, response = {}, extra = {})`
553
+ - `postJson(fetchImpl, href, body, headers = {})`
554
+ - `readSession(fetchImpl, targetOrigin, setCookie = [])`
555
+ - `logout(fetchImpl, targetOrigin, session)`
556
+ - `login(fetchImpl, targetOrigin, identity, secret, session)`
557
+
537
558
  ### `src/server/commandHandlers/appCommands/release.js`
538
559
  Exports
539
560
  - `runAppReleaseCommand(ctx = {}, { appRoot = "", options = {}, stdout, stderr })`