@jskit-ai/agent-docs 0.1.118 → 0.1.120

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 (64) hide show
  1. package/guide/agent/app-extras/assistant.md +0 -1
  2. package/guide/agent/app-extras/mobile-capacitor.md +3 -3
  3. package/guide/agent/app-extras/realtime.md +1 -2
  4. package/guide/agent/app-setup/a-more-interesting-shell.md +36 -14
  5. package/guide/agent/app-setup/authentication.md +7 -15
  6. package/guide/agent/app-setup/console.md +0 -1
  7. package/guide/agent/app-setup/database-layer.md +15 -18
  8. package/guide/agent/app-setup/initial-scaffolding.md +61 -101
  9. package/guide/agent/app-setup/multi-homing.md +2 -10
  10. package/guide/agent/app-setup/quickstart.md +0 -1
  11. package/guide/agent/app-setup/upgrade-beta-1-to-final.md +134 -0
  12. package/guide/agent/app-setup/users.md +8 -15
  13. package/guide/agent/app-setup/working-with-the-jskit-cli.md +168 -997
  14. package/guide/agent/generators/advanced-cruds.md +2 -3
  15. package/guide/agent/generators/crud-generators.md +27 -54
  16. package/guide/agent/generators/row-policies.md +1 -1
  17. package/package.json +1 -1
  18. package/patterns/INDEX.md +1 -1
  19. package/patterns/crud-scaffolding.md +9 -10
  20. package/patterns/generated-ui-contract-tracking.md +3 -3
  21. package/patterns/live-actions.md +5 -4
  22. package/patterns/placements.md +2 -2
  23. package/patterns/ui-testing.md +8 -15
  24. package/reference/autogen/packages/assistant-core.md +0 -6
  25. package/reference/autogen/packages/assistant-runtime.md +0 -6
  26. package/reference/autogen/packages/assistant.md +0 -6
  27. package/reference/autogen/packages/auth-core.md +0 -6
  28. package/reference/autogen/packages/auth-provider-local-core.md +0 -6
  29. package/reference/autogen/packages/auth-provider-local-db-core.md +0 -6
  30. package/reference/autogen/packages/auth-provider-supabase-core.md +0 -6
  31. package/reference/autogen/packages/auth-web.md +0 -6
  32. package/reference/autogen/packages/console-core.md +0 -6
  33. package/reference/autogen/packages/console-web.md +0 -6
  34. package/reference/autogen/packages/crud-core.md +0 -6
  35. package/reference/autogen/packages/crud-server-generator.md +0 -10
  36. package/reference/autogen/packages/crud-ui-generator.md +1 -8
  37. package/reference/autogen/packages/database-runtime-mysql.md +0 -6
  38. package/reference/autogen/packages/database-runtime-postgres.md +0 -6
  39. package/reference/autogen/packages/database-runtime.md +0 -6
  40. package/reference/autogen/packages/feature-server-generator.md +1 -11
  41. package/reference/autogen/packages/google-rewarded-core.md +0 -6
  42. package/reference/autogen/packages/google-rewarded-web.md +0 -6
  43. package/reference/autogen/packages/http-runtime.md +0 -6
  44. package/reference/autogen/packages/json-rest-api-core.md +0 -6
  45. package/reference/autogen/packages/kernel.md +45 -42
  46. package/reference/autogen/packages/mobile-capacitor.md +0 -6
  47. package/reference/autogen/packages/realtime.md +0 -6
  48. package/reference/autogen/packages/resource-core.md +0 -6
  49. package/reference/autogen/packages/resource-crud-core.md +0 -6
  50. package/reference/autogen/packages/shell-web.md +0 -6
  51. package/reference/autogen/packages/storage-runtime.md +0 -6
  52. package/reference/autogen/packages/ui-generator.md +0 -6
  53. package/reference/autogen/packages/uploads-image-web.md +0 -6
  54. package/reference/autogen/packages/uploads-runtime.md +0 -6
  55. package/reference/autogen/packages/users-core.md +0 -14
  56. package/reference/autogen/packages/users-web.md +8 -6
  57. package/reference/autogen/packages/workspaces-core.md +0 -6
  58. package/reference/autogen/packages/workspaces-web.md +0 -6
  59. package/reference/autogen/tooling/create-app.md +0 -14
  60. package/reference/autogen/tooling/jskit-cli.md +65 -190
  61. package/reference/autogen/tooling/testUtils.md +14 -0
  62. package/skills/jskit/SKILL.md +4 -4
  63. package/skills/jskit/references/crud-operations.md +7 -6
  64. package/skills/jskit/references/ui-operations.md +16 -19
@@ -27,7 +27,6 @@ From inside `exampleapp`, run:
27
27
 
28
28
  ```bash
29
29
  npx jskit add package assistant-runtime
30
- npm install
31
30
  npm run db:migrate
32
31
  ```
33
32
 
@@ -36,9 +36,9 @@ npx jskit mobile android doctor
36
36
  ```
37
37
 
38
38
  If doctor passes, the app is ready for the normal mobile workflow.
39
- The `jskit mobile android ...` commands expect the mobile runtime package to be
40
- installed in `package.json` and recorded in `.jskit/lock.json`; they do not
41
- install `@jskit-ai/mobile-capacitor` for you.
39
+ The `jskit mobile android ...` commands expect
40
+ `@jskit-ai/mobile-capacitor` in the installed npm graph; they do not install the
41
+ runtime package for you.
42
42
 
43
43
  ## What you need on your machine
44
44
 
@@ -14,10 +14,9 @@ From inside `exampleapp`, run:
14
14
 
15
15
  ```bash
16
16
  npx jskit add package realtime
17
- npm install
18
17
  ```
19
18
 
20
- The first command records the runtime package in the app and updates the existing scaffold. The second command downloads the new dependencies, especially `socket.io`, `socket.io-client`, and the optional Redis adapter pieces.
19
+ The command records the exact runtime package in the app, installs its npm graph, and updates the existing scaffold, including `socket.io`, `socket.io-client`, and the optional Redis adapter pieces.
21
20
 
22
21
  Unlike the database, users, console, and workspace chapters, this one does **not** need `npm run db:migrate`. `realtime` does not add schema files. It is transport infrastructure, not persistence.
23
22
 
@@ -23,10 +23,9 @@ The normal `create-app` template already has `shell-web`. If you deliberately cr
23
23
 
24
24
  ```bash
25
25
  npx jskit add package shell-web
26
- npm install
27
26
  ```
28
27
 
29
- That install is intentionally strict: `shell-web` only takes over scaffold files if they are still **exactly** the same as the files that `create-app --minimal` originally wrote. If you have already edited those starter files, `shell-web` refuses to claim them instead of overwriting your work.
28
+ That package addition is intentionally strict: `shell-web` only takes over scaffold files if they are still **exactly** the same as the files that `create-app --minimal` originally wrote. If you have already edited those starter files, `shell-web` refuses to claim them instead of overwriting your work.
30
29
 
31
30
  Open `http://localhost:5173/` in the browser. The app lands in the `home` surface inside a real shell with an app bar, a navigation drawer, and a settings route at `/home/settings`.
32
31
 
@@ -305,14 +304,14 @@ src/
305
304
 
306
305
  This chapter is where the default scaffold starts to feel layered instead of flat.
307
306
 
308
- ### `package.json` and `.jskit/lock.json`
307
+ ### `package.json`
309
308
 
310
309
  The first file worth reopening is still `package.json`. Because the default app includes `shell-web`, the important shell dependency entries are already present:
311
310
 
312
311
  ```json
313
312
  {
314
313
  "dependencies": {
315
- "@jskit-ai/shell-web": "0.x",
314
+ "@jskit-ai/shell-web": "0.1.152",
316
315
  "@mdi/js": "^7.4.47"
317
316
  }
318
317
  }
@@ -322,9 +321,9 @@ The important part is not just that `@jskit-ai/shell-web` appears. The package b
322
321
 
323
322
  It is also worth noticing what does **not** happen here. The `placed-element` and `page` commands from this chapter mutate app-owned files, but they do not add a permanent runtime dependency to `package.json`. They are tooling actions, not runtime package installs.
324
323
 
325
- The lock file records this too. In a default app, `.jskit/lock.json` already records `@local/main`, `@jskit-ai/shell-web`, and the exact files and text mutations that the shell package owns.
326
-
327
- That is worth noticing because the default scaffold is not just copied files. It starts with a JSKIT-managed runtime package that owns concrete changes in your app tree.
324
+ The installed package exposes its runtime and generator metadata through
325
+ `package.json.jskit`. The source created in the application is then reviewed
326
+ and maintained as normal application code.
328
327
 
329
328
  ### The `home` surface gets a real wrapper
330
329
 
@@ -681,13 +680,21 @@ navigation.
681
680
 
682
681
  The open drawer is content-aware by default. After the visible semantic
683
682
  placements and fonts settle, `ShellLayout` measures the rendered labels and
684
- uses one `navigationItemSpacing` value for the icon-to-label gap and the space
685
- between the widest label and the drawer edge. It defaults to 12 CSS pixels and
686
- can be set from 8–24px. The shell remeasures when placements, localization,
687
- fonts, spacing, the active surface, or the window layout change, and clamps
688
- unusually long labels to a safe range. The
689
- closed desktop rail remains 80 CSS pixels wide by default, with every icon and
690
- its Material selected-state indicator centred in the rail. Rail tooltips use
683
+ uses Material's 12px outer item inset and one `navigationItemSpacing` value for
684
+ the icon-to-label gap and the space between the widest label and the drawer
685
+ edge. The spacing defaults to 12 CSS pixels and can be set from 8–24px. The
686
+ shell remeasures when placements, localization, fonts, spacing, the active
687
+ surface, or the window layout change, and clamps unusually long labels to a safe range. The
688
+ surface label appears once in the top app bar; the drawer starts with its
689
+ destinations instead of repeating that label as an uppercase subheader.
690
+
691
+ The closed desktop rail remains 80 CSS pixels wide by default, with every icon
692
+ and its Material selected-state indicator centred in the rail. The expanded
693
+ drawer uses the same icon centreline, so toggling the drawer reveals or hides
694
+ labels without making the icons jump sideways. The empty space around a 24px
695
+ icon in the default rail is intentional Material touch/indicator space, not a
696
+ second drawer padding. Products that deliberately need a denser rail can set
697
+ `railWidth`; the shell still enforces a 48px minimum target. Rail tooltips use
691
698
  one explicit opaque theme color pair and open on pointer hover or keyboard
692
699
  focus.
693
700
 
@@ -720,6 +727,21 @@ unsafe share of the viewport or shrink below its touch targets. The app-owned
720
727
  `navigation-item-spacing`, so an application does not need to copy or restyle
721
728
  the drawer implementation.
722
729
 
730
+ ### Updating an existing shell app
731
+
732
+ Commit the app's work and run:
733
+
734
+ ```bash
735
+ npm run jskit:update
736
+ ```
737
+
738
+ Keep the generated/app-owned `src/components/ShellLayout.vue` wrapper and let
739
+ it continue forwarding attributes to the package component. No copied drawer,
740
+ `.v-navigation-drawer` CSS, duplicate surface heading, or replacement smoke
741
+ test is required. If the product intentionally wants a denser desktop rail,
742
+ set the supported prop in that wrapper, for example `:rail-width="64"`; omit it
743
+ to keep the Material 3 80px default.
744
+
723
745
  `useShellErrorPresentationStore()` exposes the current banner, snackbar, and dialog presentation state behind `ShellErrorHost`.
724
746
 
725
747
  The simplest direct store usage looks like this:
@@ -29,7 +29,6 @@ From inside `exampleapp`, run:
29
29
  ```bash
30
30
  npx jskit add package auth-provider-local-core
31
31
  npx jskit add package auth-web
32
- npm install
33
32
  ```
34
33
 
35
34
  These package installs add the provider-neutral auth core, the auth web layer, and the local provider. They give the app working register, login, logout, session, and password recovery flows without requiring an external auth service.
@@ -483,9 +482,9 @@ The first new place to inspect is `package.json`:
483
482
  "build:auth": "VITE_SURFACE=auth vite build"
484
483
  },
485
484
  "dependencies": {
486
- "@jskit-ai/auth-core": "0.x",
487
- "@jskit-ai/auth-provider-local-core": "0.x",
488
- "@jskit-ai/auth-web": "0.x"
485
+ "@jskit-ai/auth-core": "0.1.146",
486
+ "@jskit-ai/auth-provider-local-core": "0.1.49",
487
+ "@jskit-ai/auth-web": "0.1.148"
489
488
  }
490
489
  }
491
490
  ```
@@ -1042,22 +1041,15 @@ The storage-state file is a secret because it can contain authenticated cookies.
1042
1041
 
1043
1042
  Tests using managed state do not call `loginAsExistingUser()`. They begin with the runner-provided identity already present and navigate using relative paths. An ordinary browser or proxy request to `/api/dev-auth/login-as` without the private header must continue to fail with `403 Dev auth exchange is not authorized.`
1044
1043
 
1045
- #### Recording the result
1044
+ #### Running the verification
1046
1045
 
1047
- After the actual Playwright flow succeeds, record it through JSKIT:
1046
+ Run the focused Playwright flow directly:
1048
1047
 
1049
1048
  ```bash
1050
- npx jskit app verify-ui \
1051
- --command "npx playwright test tests/e2e/contacts.spec.ts -g filters" \
1052
- --feature "contacts filters" \
1053
- --auth-mode dev-auth-login-as
1049
+ npx playwright test tests/e2e/contacts.spec.ts -g filters
1054
1050
  ```
1055
1051
 
1056
- Use `--auth-mode session-bootstrap` when a managed runner supplied authenticated storage state.
1057
-
1058
- `jskit app verify-ui` executes the command and records the command, auth-mode label, feature, and changed UI files. The `--auth-mode` value describes how the Playwright command was authenticated. It does not create a session, inject a secret, or alter the browser context.
1059
-
1060
- For local pre-merge review, follow the recorded run with:
1052
+ For local pre-merge review, follow the focused run with:
1061
1053
 
1062
1054
  ```bash
1063
1055
  npx jskit doctor --against origin/main
@@ -16,7 +16,6 @@ From inside `exampleapp`, run:
16
16
 
17
17
  ```bash
18
18
  npx jskit add package console-web
19
- npm install
20
19
  npm run db:migrate
21
20
  ```
22
21
 
@@ -23,7 +23,6 @@ npx jskit add package database-runtime-mysql \
23
23
  --db-name "$DB_NAME" \
24
24
  --db-user "$DB_USER" \
25
25
  --db-password "$DB_PASSWORD"
26
- npm install
27
26
  ```
28
27
 
29
28
  The first command adds the MySQL driver package and its generic JSKIT database runtime dependency, using the explicit connection values from those `DB_*` variables:
@@ -58,7 +57,7 @@ The app gets three database scripts in `package.json`:
58
57
  ```json
59
58
  {
60
59
  "scripts": {
61
- "db:migrations:sync": "jskit migrations changed",
60
+ "db:migrations:sync": "jskit migrations sync",
62
61
  "db:migrate": "npm run db:migrations:sync && knex --knexfile ./knexfile.js migrate:latest",
63
62
  "db:migrate:rollback": "knex --knexfile ./knexfile.js migrate:rollback",
64
63
  "db:migrate:status": "npm run db:migrations:sync && knex --knexfile ./knexfile.js migrate:list"
@@ -106,9 +105,9 @@ Never modify or replace a generator-owned baseline migration. Later schema
106
105
  evolution must use a new immutable, package-owned additive migration in the
107
106
  table's app-local package, declared through `install-migration`.
108
107
 
109
- The npm scripts hide the easy-to-miss first step for normal use. `npm run db:migrate` and `npm run db:migrate:status` run `npm run db:migrations:sync` first, then run Knex. That means package upgrades can add new JSKIT-managed migration files before Knex checks what is pending.
108
+ The npm scripts run `npm run db:migrations:sync` first, then run Knex. That means newly installed package migrations are present before Knex checks what is pending.
110
109
 
111
- ### `jskit migrations ...` writes managed migration files
110
+ ### `jskit migrations sync` writes package migration files
112
111
 
113
112
  If you run the sync script directly:
114
113
 
@@ -116,7 +115,7 @@ If you run the sync script directly:
116
115
  npm run db:migrations:sync
117
116
  ```
118
117
 
119
- JSKIT checks the installed package state in `.jskit/lock.json` and materializes any managed migration files that need to exist in `migrations/`.
118
+ JSKIT reads `install-migration` entries from `package.json.jskit` across the installed npm graph and writes any missing immutable migration files into `migrations/`.
120
119
 
121
120
  That command is about the app scaffold on disk.
122
121
 
@@ -143,26 +142,25 @@ So the clean mental model is:
143
142
 
144
143
  ### When you need each step
145
144
 
146
- In many normal `jskit add package ...` flows, JSKIT already materializes a package's managed migration files while the package is being applied.
145
+ In normal `jskit add package ...` flows, JSKIT synchronizes package migration files after installation.
147
146
 
148
147
  That means the most common flow is still:
149
148
 
150
149
  ```bash
151
150
  npx jskit add package users-web
152
- npm install
153
151
  npm run db:migrate
154
152
  ```
155
153
 
156
- But if you ever need JSKIT to re-materialize or refresh the managed migration files for installed packages, that is when you use:
154
+ You can also synchronize explicitly:
157
155
 
158
156
  ```bash
159
- npx jskit migrations changed
157
+ npx jskit migrations sync
160
158
  npm run db:migrate
161
159
  ```
162
160
 
163
161
  So:
164
162
 
165
- - use `jskit migrations ...` when you need JSKIT to write or refresh managed migration files
163
+ - use `jskit migrations sync` when you need JSKIT to write package migration files
166
164
  - use `npm run db:migrate` when you need Knex to apply pending migration files to the real database
167
165
  - sometimes you need only `npm run db:migrate`
168
166
  - sometimes, after repair or re-materialization work, you need **both**
@@ -185,7 +183,7 @@ This command:
185
183
  2. rejects duplicate or unsafe migration ids
186
184
  3. creates an editable template under the package's
187
185
  `templates/migrations/` directory
188
- 4. adds the matching `install-migration` mutation to the package descriptor
186
+ 4. adds the matching `install-migration` mutation to `package.json.jskit`
189
187
  5. leaves the migration unmaterialized so its implementation can still be
190
188
  completed
191
189
 
@@ -193,12 +191,11 @@ Implement and test the template first. It intentionally fails if someone tries
193
191
  to apply the untouched scaffold. Then materialize and apply it:
194
192
 
195
193
  ```bash
196
- npx jskit migrations package @local/workflow-record-report-values
194
+ npx jskit migrations sync
197
195
  npm run db:migrate
198
196
  ```
199
197
 
200
- The materialized migration and its lock record are managed artifacts. Once
201
- installed, the migration id and content are immutable. Any later correction
198
+ Once synchronized, the migration id and content are immutable. Any later correction
202
199
  must use another additive migration with a new id.
203
200
 
204
201
  SQL inside the source-controlled migration is supported when Knex does not
@@ -595,13 +592,13 @@ After installing the MySQL runtime, the important new pieces in `package.json` l
595
592
  ```json
596
593
  {
597
594
  "dependencies": {
598
- "@jskit-ai/database-runtime": "0.x",
599
- "@jskit-ai/database-runtime-mysql": "0.x",
595
+ "@jskit-ai/database-runtime": "0.1.148",
596
+ "@jskit-ai/database-runtime-mysql": "0.1.146",
600
597
  "knex": "^3.1.0",
601
598
  "mysql2": "^3.11.2"
602
599
  },
603
600
  "scripts": {
604
- "db:migrations:sync": "jskit migrations changed",
601
+ "db:migrations:sync": "jskit migrations sync",
605
602
  "db:migrate": "npm run db:migrations:sync && knex --knexfile ./knexfile.js migrate:latest",
606
603
  "db:migrate:rollback": "knex --knexfile ./knexfile.js migrate:rollback",
607
604
  "db:migrate:status": "npm run db:migrations:sync && knex --knexfile ./knexfile.js migrate:list"
@@ -618,7 +615,7 @@ Those new dependencies divide into two roles:
618
615
 
619
616
  The migration scripts are also worth reading carefully:
620
617
 
621
- - `db:migrations:sync` writes or refreshes JSKIT-managed migration files in `migrations/`
618
+ - `db:migrations:sync` writes missing immutable package migration files in `migrations/`
622
619
  - `db:migrate` syncs JSKIT-managed migration files, then applies all pending Knex migrations
623
620
  - `db:migrate:rollback` rolls back the last migration batch
624
621
  - `db:migrate:status` syncs JSKIT-managed migration files, then lists applied and pending migrations
@@ -26,13 +26,15 @@ That seed writes only `AGENTS.md`. It is not a runnable app yet. The agent shoul
26
26
  ```bash
27
27
  npx @jskit-ai/create-app exampleapp --target . --force --tenancy-mode <mode>
28
28
  npm install
29
+ npx jskit migrations sync
30
+ npx jskit ci generate
29
31
  ```
30
32
 
31
33
  After that promotion, the overwritten app `AGENTS.md` stays deliberately small. Use it with the distributed JSKIT agent docs when planning or implementing app changes. The durable app memory lives in `.jskit/APP_BLUEPRINT.md` and should describe product and architecture decisions, not become an implementation task list.
32
34
 
33
35
  After creating the real app scaffolding (the default shell-web app, not the seed wrapper), you will need to run `npm install` to install dependencies.
34
36
 
35
- If you deliberately need the older bare scaffold, use `--minimal` or `--template minimal-shell`. That is useful for descriptor tests or unusual package-development flows, but it is not the normal starting point for a JSKIT app:
37
+ If you deliberately need the bare scaffold, use `--minimal` or `--template minimal-shell`. That is useful for package-development flows, but it is not the normal starting point for a JSKIT app:
36
38
 
37
39
  ```bash
38
40
  npx @jskit-ai/create-app exampleapp --minimal --tenancy-mode none
@@ -45,9 +47,9 @@ claims have not been edited first. After the initial `npm install`, prefer
45
47
  fails clearly.
46
48
 
47
49
  When a minimal app's first feature is generated CRUD, do not pre-install the
48
- shell as a workaround. Add the database runtime, run `npm install`, create the
49
- live disposable table, run `crud-server-generator scaffold`, run
50
- `npm install` again, and then run `crud-ui-generator crud`. The server
50
+ shell as a workaround. Add the database runtime, create the live disposable
51
+ table, run `crud-server-generator scaffold`, and then run
52
+ `crud-ui-generator crud`. The server
51
53
  generator installs the shell/realtime dependency closure in the correct order.
52
54
  The complete Notes command lane is in [CRUD Generators](/guide/generators/crud-generators#fresh-minimal-notes-app-the-complete-command-order).
53
55
 
@@ -77,7 +79,6 @@ npx jskit add package database-runtime-mysql \
77
79
  npx jskit add package users-web
78
80
  npx jskit add package console-web
79
81
 
80
- npm install
81
82
  npm run db:migrate
82
83
  ```
83
84
 
@@ -191,9 +192,10 @@ The most important parts look like this:
191
192
  "dependencies": {
192
193
  "@local/main": "file:packages/main",
193
194
  "@fastify/static": "^9.1.3",
194
- "@jskit-ai/kernel": "0.x",
195
+ "@jskit-ai/kernel": "0.1.148",
195
196
  "@tanstack/vue-query": "^5.101.0",
196
- "@jskit-ai/http-runtime": "0.x",
197
+ "@jskit-ai/http-runtime": "0.1.146",
198
+ "@jskit-ai/shell-web": "0.1.152",
197
199
  "fastify": "^5.8.5",
198
200
  "json-rest-schema": "^1.0.17",
199
201
  "pinia": "^3.0.4",
@@ -202,9 +204,9 @@ The most important parts look like this:
202
204
  "vuetify": "^4.1.2"
203
205
  },
204
206
  "devDependencies": {
205
- "@jskit-ai/agent-docs": "0.x",
206
- "@jskit-ai/config-eslint": "0.x",
207
- "@jskit-ai/jskit-cli": "0.x",
207
+ "@jskit-ai/agent-docs": "0.1.119",
208
+ "@jskit-ai/config-eslint": "0.1.145",
209
+ "@jskit-ai/jskit-cli": "0.2.179",
208
210
  "@playwright/test": "1.61.1",
209
211
  "@vitejs/plugin-vue": "^6.0.7",
210
212
  "eslint": "^10.8.0",
@@ -214,7 +216,7 @@ The most important parts look like this:
214
216
  }
215
217
  ```
216
218
 
217
- Published JSKIT libraries and tooling support Node.js 22 from 22.13.0 onward, Node.js 24, and Node.js 26. Newly generated applications deliberately require Node 26: their app-level `engines` contract, `.nvmrc`, and JSKIT-managed verification workflow all name that runtime. The app-level contract is the runtime boundary for the app and its installed JSKIT runtime packages, while independently consumed JSKIT CLI and tooling packages retain the wider supported range. The dependency on `@local/main` points at `file:packages/main`, which means your app already contains its own local JSKIT package. The maintenance scripts are also useful to notice early, because they show an important ownership boundary in JSKIT.
219
+ Published JSKIT libraries and tooling support Node.js 22 from 22.13.0 onward, Node.js 24, and Node.js 26. Newly generated applications deliberately require Node 26: their app-level `engines` contract, `.nvmrc`, and generated verification workflow all name that runtime. The app-level contract is the runtime boundary for the app and its installed JSKIT runtime packages, while independently consumed JSKIT CLI and tooling packages retain the wider supported range. The dependency on `@local/main` points at `file:packages/main`, which means your app already contains its own local JSKIT package. The maintenance scripts are also useful to notice early, because they show an important ownership boundary in JSKIT.
218
220
 
219
221
  `verify`, `jskit:update`, and `release` are intentionally thin wrappers. They stay in `package.json` because they are convenient app-local shortcuts, but the real implementation lives in `jskit app ...`, not in copied scaffold scripts.
220
222
 
@@ -224,11 +226,11 @@ The Playwright scaffold follows the same rule. `playwright.config.mjs` delegates
224
226
 
225
227
  The scaffold also configures `src/typed-router.d.ts` as generated output. It is intentionally absent from a fresh scaffold and ignored by Git; the Vue Router Vite plugin writes it during the first `npm run dev` or `npm run build` route scan.
226
228
 
227
- `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.
229
+ `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 the installed package graph, capabilities, migrations, generated CI, surfaces, placements, and other JSKIT-specific health rules.
228
230
 
229
- The starter scaffold also writes `.github/workflows/jskit-verify.yml`. JSKIT generates and owns that workflow as a projection of installed package `ci` contracts. The baseline runs checkout, Node 26 setup, `npm ci`, and `npm run verify`. Packages can add job environment values, service containers, and explicit `before-verify` steps without taking ownership of the whole YAML file. For example, the database runtime adds migrations before verification. Its MySQL driver adds a MariaDB service with synthetic CI-only credentials and `DB_CLIENT=mysql2`; its Postgres driver adds the equivalent Postgres service and `DB_CLIENT=pg`.
231
+ After `npm install`, run `npx jskit ci generate` to write `.github/workflows/jskit-verify.yml` from installed package `ci` contracts. The baseline runs checkout, Node 26 setup, `npm ci`, and `npm run verify`. Packages can add job environment values, service containers, and explicit `before-verify` steps. For example, the database runtime adds migrations before verification. Its MySQL driver adds a MariaDB service with synthetic CI-only credentials and `DB_CLIENT=mysql2`; its Postgres driver adds the equivalent Postgres service and `DB_CLIENT=pg`.
230
232
 
231
- The workflow content hash is recorded in `.jskit/lock.json`. Package lifecycle commands refresh it when installed requirements change and refuse to overwrite a workflow that differs from its recorded version. Application-specific CI belongs in another workflow. Use `npx jskit app sync-ci` to refresh an unmodified managed file, or `npx jskit app sync-ci --force` when you explicitly intend to replace an edited file that is already recorded as JSKIT-owned. The `--against <base-ref>` review mode still exists for local pre-merge checks and advanced CI pipelines, but the starter workflow does not assume it.
233
+ `npx jskit ci generate` replaces that one workflow in full. Do not edit it. Put application-specific CI in separate workflow files, and use `npx jskit ci generate --check` in validation lanes.
232
234
 
233
235
  The surface-specific script names are also worth noticing early, even in this tiny app. `dev:home`, `server:home`, and `build:home` are the first concrete places where surface selection shows up in the scaffold. They work by setting `VITE_SURFACE=home` on the client side and `SERVER_SURFACE=home` on the server side. In this first chapter, where `home` is the only surface, those variants behave almost the same as the default commands. Later, once more surfaces exist, those scripts become the simplest way to run or build just one surface at a time.
234
236
 
@@ -463,7 +465,7 @@ async function bootInstalledClientModules(context = {}) {
463
465
  }
464
466
  ```
465
467
 
466
- That is why Vite is involved. The browser cannot safely discover installed packages by itself at runtime. It cannot scan `.jskit/lock.json`, inspect `node_modules`, and turn that into bundler-visible imports. Vite needs a normal import graph up front. The plugin creates that graph for the app.
468
+ That is why Vite is involved. The browser cannot inspect the installed npm graph and turn it into bundler-visible imports at runtime. Vite needs a normal import graph up front. The plugin creates that graph for the app.
467
469
 
468
470
  If this were plain Vue without that plugin, you would have to maintain the list yourself:
469
471
 
@@ -494,16 +496,22 @@ In a brand-new shell app, there are no extra installed client modules yet, so th
494
496
 
495
497
  #### The main package (client side)
496
498
 
497
- One more client-side piece is worth seeing before looking at page files: the starter app already has its own client provider. The app-local package declares it in `packages/main/package.descriptor.mjs` like this:
499
+ One more client-side piece is worth seeing before looking at page files: the starter app already has its own client provider. The app-local package declares it in `packages/main/package.json` like this:
498
500
 
499
- ```js
500
- client: {
501
- providers: [
502
- {
503
- entrypoint: "src/client/providers/MainClientProvider.js",
504
- export: "MainClientProvider"
501
+ ```json
502
+ {
503
+ "jskit": {
504
+ "runtime": {
505
+ "client": {
506
+ "providers": [
507
+ {
508
+ "entrypoint": "src/client/providers/MainClientProvider.js",
509
+ "export": "MainClientProvider"
510
+ }
511
+ ]
512
+ }
505
513
  }
506
- ]
514
+ }
507
515
  }
508
516
  ```
509
517
 
@@ -711,35 +719,36 @@ The small `server/lib/` directory exists to keep that server boot code tidy. `ru
711
719
 
712
720
  #### The main package (server side)
713
721
 
714
- The most unusual part of the scaffold, if you are new to JSKIT, is `packages/main/`. This is the app-local runtime package. It is not there by accident, and it is not just a convenience folder. JSKIT treats your app itself as a local package with a descriptor, client provider hooks, and server provider hooks. That is why the folder contains `package.descriptor.mjs` and a small `src/` tree of its own.
722
+ The most unusual part of the scaffold, if you are new to JSKIT, is `packages/main/`. This is the app-local runtime package. It is not there by accident, and it is not just a convenience folder. JSKIT treats your app itself as a local package with package metadata, client provider hooks, and server provider hooks. That is why the folder contains `package.json` and a small `src/` tree of its own.
715
723
 
716
- You already saw the client-side provider in the client bootstrap path. The server side uses the same model: the descriptor tells JSKIT which provider class belongs to the local package, and the runtime calls `register()` and then `boot()`.
724
+ You already saw the client-side provider in the client bootstrap path. The server side uses the same model: `package.json.jskit` tells JSKIT which provider class belongs to the local package, and the runtime calls `register()` and then `boot()`.
717
725
 
718
- The server part of that descriptor looks like this:
726
+ The server part of that metadata looks like this:
719
727
 
720
- ```js
721
- export default Object.freeze({
722
- packageVersion: 1,
723
- packageId: "@local/main",
724
- version: "0.1.0",
725
- kind: "runtime",
726
- runtime: {
727
- server: {
728
- providerEntrypoint: "src/server/MainServiceProvider.js",
729
- providers: [
730
- {
731
- entrypoint: "src/server/MainServiceProvider.js",
732
- export: "MainServiceProvider"
733
- }
734
- ]
735
- }
736
- },
737
- metadata: {
738
- server: {
739
- routes: []
728
+ ```json
729
+ {
730
+ "name": "@local/main",
731
+ "version": "0.1.0",
732
+ "jskit": {
733
+ "kind": "runtime",
734
+ "runtime": {
735
+ "server": {
736
+ "providerEntrypoint": "src/server/MainServiceProvider.js",
737
+ "providers": [
738
+ {
739
+ "entrypoint": "src/server/MainServiceProvider.js",
740
+ "export": "MainServiceProvider"
741
+ }
742
+ ]
743
+ }
744
+ },
745
+ "metadata": {
746
+ "server": {
747
+ "routes": []
748
+ }
740
749
  }
741
750
  }
742
- });
751
+ }
743
752
  ```
744
753
 
745
754
  This is the moment where the scaffold stops looking like "just a Vue app". The app is declaring itself as a runtime package that JSKIT can discover, load, and mutate safely.
@@ -775,59 +784,10 @@ npx jskit generate feature-server-generator scaffold booking-engine
775
784
 
776
785
  That keeps the ownership boundary clear: `packages/main` stays composition-only, while real server features get their own provider, service, and optional repository seams. The client side uses the same provider lifecycle; you already saw the matching pattern earlier in the client boot path.
777
786
 
778
- The `.jskit/lock.json` file is also important. Treat it like JSKIT's own lock and state file. It records which runtime packages JSKIT believes are installed and which managed changes they introduced. When you use `jskit add`, `jskit update`, or generators that depend on installed package state, this file is part of the source of truth. It belongs in version control, and you should not hand-edit it.
779
-
780
- This file is narrower than `package.json`. `package.json` lists every npm dependency the app needs, including plain libraries such as Vue, Fastify, and Vuetify. `.jskit/lock.json` tracks JSKIT package-install state: which JSKIT runtime packages were installed, which files, text mutations, and dependency entries JSKIT manages on their behalf, and the owned hash of app-level projections such as the composed CI workflow.
781
-
782
- On a brand-new default app, the lock file is telling you that the app-local package and the standard shell package are installed from the start:
783
-
784
- ```json
785
- {
786
- "lockVersion": 1,
787
- "installedPackages": {
788
- "@local/main": {
789
- "packageId": "@local/main",
790
- "version": "0.1.0",
791
- "source": {
792
- "type": "local-package",
793
- "packagePath": "packages/main",
794
- "descriptorPath": "packages/main/package.descriptor.mjs"
795
- },
796
- "managed": { "...": "..." }
797
- },
798
- "@jskit-ai/shell-web": {
799
- "packageId": "@jskit-ai/shell-web",
800
- "source": {
801
- "type": "catalog"
802
- },
803
- "managed": {
804
- "packageJson": {
805
- "dependencies": {
806
- "@jskit-ai/shell-web": {
807
- "value": "0.x"
808
- },
809
- "@mdi/js": {
810
- "value": "^7.4.47"
811
- }
812
- }
813
- },
814
- "files": { "...": "..." },
815
- "text": { "...": "..." }
816
- }
817
- }
818
- },
819
- "managed": {
820
- "ciWorkflow": {
821
- "path": ".github/workflows/jskit-verify.yml",
822
- "hash": "<generated-sha256>"
823
- }
824
- }
825
- }
826
- ```
827
-
828
- That is a useful anchor point. Before you add anything else, JSKIT already knows about the runtime package that belongs to your app and the shell runtime package that owns the default shell files, placement config, and error host wiring.
829
-
830
- That is why you saw `@jskit-ai/kernel` and `@jskit-ai/http-runtime` earlier in `package.json`, but you do not see them as separate installed packages here. They are npm dependencies of the scaffold, while `.jskit/lock.json` records JSKIT package install state and managed app mutations.
787
+ The root `package.json`, its exact JSKIT dependency versions, and
788
+ `package-lock.json` define the installed graph. JSKIT reads each installed
789
+ package's `package.json.jskit` metadata directly. The local `@local/main`
790
+ package participates through its normal `file:packages/main` dependency.
831
791
 
832
792
  ### Other files and options
833
793
 
@@ -843,4 +803,4 @@ Use `--minimal` only when you deliberately need the bare scaffold. If you later
843
803
 
844
804
  ## Summary
845
805
 
846
- At the end of this first step, you should have more than a generated folder. You should have a mental map. `src/` is the web app, `server.js` is the runtime server, `config/` defines surfaces and shared behavior, `packages/main/` is your app's own local JSKIT package, and `.jskit/lock.json` records what JSKIT has done to the project. That is the foundation the next chapters will build on.
806
+ At the end of this first step, you should have more than a generated folder. You should have a mental map. `src/` is the web app, `server.js` is the runtime server, `config/` defines surfaces and shared behavior, `packages/main/` is your app's own local JSKIT package, and the npm package graph supplies JSKIT runtime metadata. That is the foundation the next chapters will build on.
@@ -27,7 +27,7 @@ JSKIT currently accepts three tenancy modes:
27
27
  - workspace slugs are user-selected rather than derived from the username
28
28
  - auto-provisioning is off by default, and self-creation is a separate policy choice
29
29
 
30
- Both `personal` and `workspaces` are workspace-capable modes, so they allow the workspace package descriptors to install the full workspace scaffold.
30
+ Both `personal` and `workspaces` are workspace-capable modes, so they allow the workspace package metadata to install the full workspace scaffold.
31
31
 
32
32
  This chapter teaches `personal`, not `workspaces`.
33
33
 
@@ -41,24 +41,16 @@ If your app is already on `tenancyMode = "personal"`, run:
41
41
 
42
42
  ```bash
43
43
  npx jskit add package workspaces-core
44
- npm install
45
44
  npx jskit add package workspaces-web
46
- npm install
47
45
  npm run db:migrate
48
46
  ```
49
47
 
50
- **Important: This Block Is Only The Fresh Workspace Install Path**
51
-
52
- These commands are complete only if the app was already on `tenancyMode = "personal"` when `users-web` / `users-core` were originally installed, or if you already ran the recovery `npx jskit update package users-core` step above.
53
-
54
- If you changed tenancy after installing users, do not skip that update. The workspace packages add workspace runtime and routes, but `users-core` is what rewrites the app-local users scaffold into its workspace-aware shape.
55
-
56
48
  `workspaces-core` adds the server-side workspace runtime and schema migrations. `workspaces-web` adds the workspace-facing client surfaces, shell placements, and app-owned route files.
57
49
 
58
50
  If you want to inspect that package before installing it, this is a very good moment to use the CLI chapter's inspection command:
59
51
 
60
52
  ```bash
61
- npx jskit show @jskit-ai/workspaces-web --details
53
+ npx jskit show package @jskit-ai/workspaces-web
62
54
  ```
63
55
 
64
56
  That output makes the package feel much less mysterious, because it shows the exact workspace shell contributions, settings outlets, client tokens, app-owned file writes, and capability requirements before you mutate the app.
@@ -79,7 +79,6 @@ npx jskit generate assistant settings-page \
79
79
  --surface admin \
80
80
  --name "Admin Assistant"
81
81
 
82
- npm install
83
82
  npm run db:migrate
84
83
  ```
85
84