@jskit-ai/agent-docs 0.1.27 → 0.1.28

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 (40) hide show
  1. package/DISTR_AGENT.md +1 -0
  2. package/guide/agent/app-extras/assistant.md +2 -2
  3. package/guide/agent/app-extras/mobile-capacitor.md +10 -3
  4. package/guide/agent/app-extras/realtime.md +11 -11
  5. package/guide/agent/app-setup/a-more-interesting-shell.md +52 -44
  6. package/guide/agent/app-setup/authentication.md +40 -22
  7. package/guide/agent/app-setup/console.md +5 -5
  8. package/guide/agent/app-setup/database-layer.md +15 -15
  9. package/guide/agent/app-setup/initial-scaffolding.md +37 -5
  10. package/guide/agent/app-setup/multi-homing.md +28 -28
  11. package/guide/agent/app-setup/users.md +22 -22
  12. package/guide/agent/app-setup/working-with-the-jskit-cli.md +13 -6
  13. package/guide/agent/generators/advanced-cruds.md +111 -68
  14. package/guide/agent/generators/crud-generators.md +5 -5
  15. package/guide/agent/generators/ui-generators.md +15 -13
  16. package/guide/agent/index.md +1 -1
  17. package/package.json +1 -1
  18. package/patterns/INDEX.md +4 -1
  19. package/patterns/client-requests.md +23 -6
  20. package/patterns/crud-repository-mapping.md +1 -1
  21. package/patterns/crud-scaffolding.md +10 -1
  22. package/patterns/filters.md +28 -10
  23. package/patterns/generated-ui-contract-tracking.md +62 -0
  24. package/patterns/page-scaffolding.md +17 -0
  25. package/patterns/placements.md +60 -0
  26. package/patterns/surfaces.md +5 -1
  27. package/patterns/ui-testing.md +3 -0
  28. package/reference/autogen/KERNEL_MAP.md +30 -1
  29. package/reference/autogen/packages/assistant.md +1 -1
  30. package/reference/autogen/packages/crud-ui-generator.md +19 -8
  31. package/reference/autogen/packages/kernel.md +38 -6
  32. package/reference/autogen/packages/realtime.md +1 -0
  33. package/reference/autogen/packages/shell-web.md +35 -3
  34. package/reference/autogen/packages/ui-generator.md +26 -5
  35. package/reference/autogen/packages/users-core.md +2 -0
  36. package/reference/autogen/packages/users-web.md +92 -3
  37. package/reference/autogen/packages/workspaces-web.md +3 -1
  38. package/reference/autogen/tooling/jskit-cli.md +24 -8
  39. package/templates/app/AGENTS.md +1 -0
  40. package/workflow/bootstrap.md +1 -0
@@ -25,7 +25,7 @@ npm run db:migrate
25
25
  - `console-core` owns the console schema, owner check, bootstrap flag, and API routes
26
26
  - `console-web` owns the console surface scaffold, settings shell, and menu placement
27
27
 
28
- So the console is a real vertical slice now. The users packages no longer know about it.
28
+ The console is a real vertical slice. The users packages do not own it.
29
29
 
30
30
  ## What the console is for
31
31
 
@@ -70,7 +70,7 @@ Then sign in.
70
70
 
71
71
  Once you are authenticated, two things matter:
72
72
 
73
- - the app now knows who you are as a persistent JSKIT user
73
+ - the app knows who you are as a persistent JSKIT user
74
74
  - the console bootstrap logic can determine whether you are the console owner
75
75
 
76
76
  Open:
@@ -92,7 +92,7 @@ This is the most important idea in the chapter.
92
92
 
93
93
  The account surface only requires authentication. The console surface requires a specific access flag: `console_owner`.
94
94
 
95
- That means the guide now has two different kinds of authenticated routes:
95
+ That means the guide has two different kinds of authenticated routes:
96
96
 
97
97
  - normal authenticated routes such as `/account`
98
98
  - privileged authenticated routes such as `/console`
@@ -163,7 +163,7 @@ This is the first clear example in the guide of a surface guarded by a named fla
163
163
 
164
164
  ### The starter console routes are app-owned
165
165
 
166
- After the previous chapter, the app now has:
166
+ After the previous chapter, the app has:
167
167
 
168
168
  ```text
169
169
  src/pages/console.vue
@@ -308,7 +308,7 @@ That stricter access model is the key idea to keep:
308
308
  - `/account` is for any signed-in user
309
309
  - `/console` is only for the console owner
310
310
 
311
- So the app now has both:
311
+ So the app has both:
312
312
 
313
313
  - a normal authenticated user area
314
314
  - a privileged operator area with its own server-side ownership check
@@ -49,11 +49,11 @@ Installing `database-runtime-mysql` gives the app three important things right a
49
49
 
50
50
  ### A real database runtime
51
51
 
52
- The server can now build a real Knex client from environment variables. That gives later packages a standard way to ask for a database connection instead of each package inventing its own wiring.
52
+ The server can build a real Knex client from environment variables. That gives later packages a standard way to ask for a database connection instead of each package inventing its own wiring.
53
53
 
54
54
  ### A migration workflow
55
55
 
56
- The app now gets three new scripts in `package.json`:
56
+ The app gets three database scripts in `package.json`:
57
57
 
58
58
  ```json
59
59
  {
@@ -73,7 +73,7 @@ If you run the status command immediately after this chapter:
73
73
  npm run db:migrate:status
74
74
  ```
75
75
 
76
- you should still see that there are no completed migrations and no pending migration files yet. The runtime and the Knex wiring exist now, but no package has added real schema files until the next chapter.
76
+ you should still see that there are no completed migrations and no pending migration files yet. The runtime and the Knex wiring exist at this point, but no package has added real schema files until the next chapter.
77
77
 
78
78
  ### A place for future schema files
79
79
 
@@ -88,7 +88,7 @@ That is the key idea of this chapter:
88
88
 
89
89
  This chapter is the right place to make one distinction very explicit.
90
90
 
91
- There are now two different migration-related layers in the app:
91
+ The app has two different migration-related layers:
92
92
 
93
93
  - JSKIT-managed migration files on disk
94
94
  - Knex actually applying those files to the database
@@ -171,7 +171,7 @@ import {
171
171
  } from "@jskit-ai/database-runtime/shared";
172
172
  ```
173
173
 
174
- This is worth calling out now because the database layer is not only "Knex plus migrations".
174
+ This is worth calling out here because the database layer is not only "Knex plus migrations".
175
175
 
176
176
  It also gives your later repositories and services a standard persistence toolbox so every package does not have to solve the same problems differently. The main point is consistency:
177
177
 
@@ -561,7 +561,7 @@ The three new scripts are also worth reading carefully:
561
561
 
562
562
  They are not special JSKIT commands. They are ordinary project scripts, which makes them easy to run in any environment.
563
563
 
564
- ### `.env` now owns the database connection settings
564
+ ### `.env` owns the database connection settings
565
565
 
566
566
  The package install also writes the database settings into `.env`:
567
567
 
@@ -583,7 +583,7 @@ This matters because the generic runtime is written to support more than one dri
583
583
 
584
584
  ### `knexfile.js` is for migration commands, not normal page code
585
585
 
586
- The migration scripts in `package.json` work because the app now has a top-level `knexfile.js`:
586
+ The migration scripts in `package.json` work because the app has a top-level `knexfile.js`:
587
587
 
588
588
  ```js
589
589
  import path from "node:path";
@@ -628,7 +628,7 @@ It is not the main runtime API that your app code imports during a request. It i
628
628
  npm run db:migrate
629
629
  ```
630
630
 
631
- So there are really two separate database entry points now:
631
+ So there are really two separate database entry points:
632
632
 
633
633
  - `knexfile.js` for migration commands
634
634
  - the JSKIT server provider runtime for application code
@@ -695,7 +695,7 @@ At first glance it can feel strange that the database layer is installed but the
695
695
 
696
696
  The reason is simple:
697
697
 
698
- - the runtime is now available
698
+ - the runtime is available
699
699
  - but almost no installed package is using it yet
700
700
 
701
701
  Right now:
@@ -739,7 +739,7 @@ So the auth layer keeps behaving the same way it did before:
739
739
  - JSKIT still mirrors just enough profile data locally
740
740
  - that JSKIT-side mirror is still not persistent yet
741
741
 
742
- The database runtime is now ready, but the users layer that will actually use it has not been installed yet.
742
+ The database runtime is ready, but the users layer that will actually use it has not been installed yet.
743
743
 
744
744
  ### Why the empty `migrations/` directory is important
745
745
 
@@ -747,7 +747,7 @@ The new `migrations/` directory can look almost silly at first because it only c
747
747
 
748
748
  It means:
749
749
 
750
- - the app now has a migration system
750
+ - the app has a migration system
751
751
  - the app does **not** yet have a schema of its own
752
752
 
753
753
  That is exactly the right state at this stage of the guide.
@@ -758,9 +758,9 @@ The database runtime chapter should give the app a database foundation first. Th
758
758
 
759
759
  This chapter did not make the app feel dramatically different in the browser, but it changed the server foundation in an important way.
760
760
 
761
- - the app now has a real JSKIT database runtime
762
- - the app now has a standard Knex migration workflow
763
- - the app now has a place for future schema files
761
+ - the app has a real JSKIT database runtime
762
+ - the app has a standard Knex migration workflow
763
+ - the app has a place for future schema files
764
764
 
765
765
  But just as importantly, this chapter also defined what has **not** changed yet:
766
766
 
@@ -771,5 +771,5 @@ But just as importantly, this chapter also defined what has **not** changed yet:
771
771
  So the right mental model at the end of this chapter is:
772
772
 
773
773
  - Supabase already handles real authentication
774
- - MySQL is now wired up and ready
774
+ - MySQL is wired up and ready
775
775
  - the persistent JSKIT-side user model arrives in the next chapter
@@ -14,7 +14,7 @@ npm install
14
14
 
15
15
  The first command creates a new folder called `exampleapp` and fills it with JSKIT's base shell template. The `exampleapp` name is used in a few template replacements, such as the package name and the browser title. The `--tenancy-mode none` flag tells JSKIT to start with the smallest routing model. In this mode, the app is not workspace-aware (more of this later in the guide, when multihoming is introduced). That keeps the first scaffold easier to read because there is no workspace slug handling yet.
16
16
 
17
- If you are working with an AI agent and want the agent to drive the initial JSKIT setup conversation, there is now a dedicated seed path:
17
+ If you are working with an AI agent and want the agent to drive the initial JSKIT setup conversation, use the dedicated seed path:
18
18
 
19
19
  ```bash
20
20
  npx @jskit-ai/create-app exampleapp --template ai-seed
@@ -166,23 +166,35 @@ The most important parts look like this:
166
166
  "build": "vite build",
167
167
  "build:all": "vite build",
168
168
  "build:home": "VITE_SURFACE=home vite build",
169
+ "preview": "vite preview",
170
+ "lint": "eslint .",
169
171
  "test": "node --test",
170
172
  "test:client": "vitest run tests/client",
173
+ "test:e2e": "playwright test tests/e2e",
171
174
  "verify": "jskit app verify && npm run --if-present verify:app",
172
175
  "release": "jskit app release",
173
176
  "jskit:update": "jskit app update-packages"
174
177
  },
175
178
  "dependencies": {
176
179
  "@local/main": "file:packages/main",
180
+ "@fastify/static": "^9.1.1",
177
181
  "@jskit-ai/kernel": "0.x",
182
+ "@tanstack/vue-query": "^5.90.5",
178
183
  "@jskit-ai/http-runtime": "0.x",
179
184
  "fastify": "^5.7.4",
185
+ "json-rest-schema": "^1.0.13",
180
186
  "pinia": "^3.0.4",
181
187
  "vue": "^3.5.13",
188
+ "vue-router": "^5.0.4",
182
189
  "vuetify": "^4.0.0"
183
190
  },
184
191
  "devDependencies": {
192
+ "@jskit-ai/agent-docs": "0.x",
193
+ "@jskit-ai/config-eslint": "0.x",
185
194
  "@jskit-ai/jskit-cli": "0.x",
195
+ "@playwright/test": "^1.57.0",
196
+ "@vitejs/plugin-vue": "^5.2.1",
197
+ "eslint": "^9.39.1",
186
198
  "vite": "^6.1.0",
187
199
  "vitest": "^4.0.18"
188
200
  }
@@ -191,7 +203,7 @@ The most important parts look like this:
191
203
 
192
204
  There are two details worth noticing immediately. 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.
193
205
 
194
- `verify`, `jskit:update`, `devlinks`, and `release` are intentionally thin wrappers. They stay in `package.json` because they are convenient app-local shortcuts, but the real implementation now lives in `jskit app ...`, not in copied scaffold scripts.
206
+ `verify`, `jskit:update`, `devlinks`, 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.
195
207
 
196
208
  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`, `jskit app link-local-packages`, and `jskit app release`, the app keeps the nice `npm run` shortcuts while the maintained behavior stays in the installed CLI package.
197
209
 
@@ -257,6 +269,7 @@ The important part looks like this:
257
269
  ```js
258
270
  import { createApp } from "vue";
259
271
  import { createPinia } from "pinia";
272
+ import { QueryClient, VueQueryPlugin } from "@tanstack/vue-query";
260
273
  import { createRouter, createWebHistory } from "vue-router/auto";
261
274
  import { routes } from "vue-router/auto-routes";
262
275
  import "vuetify/styles";
@@ -265,6 +278,10 @@ import * as components from "vuetify/components";
265
278
  import * as directives from "vuetify/directives";
266
279
  import { aliases as mdiAliases, mdi } from "vuetify/iconsets/mdi-svg";
267
280
  import { createSurfaceRuntime } from "@jskit-ai/kernel/shared/surface/runtime";
281
+ import {
282
+ shouldRetryTransientQueryFailure,
283
+ transientQueryRetryDelay
284
+ } from "@jskit-ai/kernel/shared/support";
268
285
  import {
269
286
  bootstrapClientShellApp,
270
287
  createShellRouter
@@ -308,13 +325,28 @@ const vuetify = createVuetify({
308
325
  }
309
326
  });
310
327
  const pinia = createPinia();
328
+ const queryClient = new QueryClient({
329
+ defaultOptions: {
330
+ queries: {
331
+ refetchOnWindowFocus: false,
332
+ refetchOnReconnect: true,
333
+ retry: shouldRetryTransientQueryFailure,
334
+ retryDelay: transientQueryRetryDelay
335
+ }
336
+ }
337
+ });
311
338
 
312
339
  void bootstrapClientShellApp({
313
340
  createApp,
314
341
  rootComponent: App,
315
342
  appConfig: config,
316
- appPlugins: [pinia, vuetify],
343
+ appPlugins: [
344
+ pinia,
345
+ [VueQueryPlugin, { queryClient }],
346
+ vuetify
347
+ ],
317
348
  pinia,
349
+ queryClient,
318
350
  router,
319
351
  bootClientModules: bootInstalledClientModules,
320
352
  surfaceRuntime,
@@ -326,7 +358,7 @@ void bootstrapClientShellApp({
326
358
  });
327
359
  ```
328
360
 
329
- The flow is simple once you read it in order: config in, runtime in memory, router built from that runtime, UI plugin installed, app bootstrapped.
361
+ The flow is simple once you read it in order: config in, runtime in memory, router built from that runtime, app-owned Vue plugins created once, app bootstrapped. Pinia, Vue Query, the router, and Vuetify are owned by the base app bootstrap so runtime packages share the same instances instead of carrying independent copies.
330
362
 
331
363
  <DocsInDepth title="In depth" preview-height="15rem">
332
364
 
@@ -766,7 +798,7 @@ That is why you saw `@jskit-ai/kernel` and `@jskit-ai/http-runtime` earlier in `
766
798
 
767
799
  ### Other files and options
768
800
 
769
- The remaining files are easier to understand once you know the core pieces above. `vite.config.mjs` configures the frontend build and the `/api` proxy used during development. `index.html` is the HTML shell Vite uses to mount Vue. `tests/` contains basic smoke tests so the app has a verification path from day one. The `scripts/` directory is now much smaller than it used to be, because JSKIT maintenance helpers such as `verify`, `jskit:update`, `devlinks`, and `release` are package-owned CLI commands rather than copied app scripts.
801
+ The remaining files are easier to understand once you know the core pieces above. `vite.config.mjs` configures the frontend build and the `/api` proxy used during development. `index.html` is the HTML shell Vite uses to mount Vue. `tests/` contains basic smoke tests so the app has a verification path from day one. The `scripts/` directory is intentionally small because JSKIT maintenance helpers such as `verify`, `jskit:update`, `devlinks`, and `release` are package-owned CLI commands rather than copied app scripts.
770
802
 
771
803
  The `create-app` command also accepts a few other flags that are useful without changing the basic meaning of this chapter's setup. `--title <text>` lets you replace the browser title and other template text with a friendlier app name. `--target <path>` lets you choose a different output directory instead of the default `./exampleapp`. `--tenancy-mode <mode>` can seed `none`, `personal`, or `workspaces`; for this chapter we intentionally use `none` so the first scaffold stays small and non-workspace. `--force` allows writing into a non-empty target directory when you know that is what you want. `--dry-run` prints the planned file writes without touching the filesystem, which is useful when you want to inspect what the generator would do. `-h` or `--help` prints the command help.
772
804
 
@@ -63,7 +63,7 @@ npx jskit show @jskit-ai/workspaces-web --details
63
63
 
64
64
  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.
65
65
 
66
- ## What changes now
66
+ ## What workspaces add
67
67
 
68
68
  This chapter is where the app stops being a collection of global surfaces and starts supporting workspace-dependent ones.
69
69
 
@@ -107,13 +107,13 @@ That is why this chapter feels larger than the previous ones. It is not just add
107
107
 
108
108
  ### The shell and account surface gain workspace-aware controls
109
109
 
110
- The placement registry now grows a workspace selector in the top-left of the shell, a pending-invites cue in the top-right area, and workspace tools in the admin shell. The workspaces package also plugs an `Invites` section into the existing `/account` settings screen through the account-settings extension seam that `users-web` exposes.
110
+ The placement registry gets a workspace selector in `shell.identity`, pending-invites and workspace tools in `shell.status`, and an `Invites` section in the existing `/account` settings screen through the account-settings extension seam that `users-web` exposes. The default shell topology maps identity/status placements to the visible shell chrome.
111
111
 
112
112
  That means the shell itself starts adapting to workspace context.
113
113
 
114
- - on any authenticated surface, the shell can now expose a workspace selector
115
- - signed-in users can now see a pending-invites cue without `users-web` owning that workspace feature
116
- - on admin workspace surfaces, the shell can also expose workspace-specific tools and settings
114
+ - on any authenticated surface, the shell can expose a workspace selector
115
+ - signed-in users can see a pending-invites cue without `users-web` owning that workspace feature
116
+ - on admin workspace surfaces, the shell can expose workspace-specific tools and settings
117
117
 
118
118
  This is the first time the guide shows the shell reacting not just to authentication state, but to workspace state as well.
119
119
 
@@ -139,7 +139,7 @@ This is also important to notice:
139
139
 
140
140
  The new workspace surfaces are added on top of the existing app, not instead of it.
141
141
 
142
- That is exactly what multi-homing should feel like. The app now has both:
142
+ That is exactly what multi-homing should feel like. The app has both:
143
143
 
144
144
  - global surfaces
145
145
  - workspace-scoped surfaces
@@ -153,7 +153,7 @@ npm run dev
153
153
  npm run server
154
154
  ```
155
155
 
156
- After you sign in, the app now has the routing structure needed for workspace-aware paths such as:
156
+ After you sign in, the app has the routing structure needed for workspace-aware paths such as:
157
157
 
158
158
  ```text
159
159
  /w/your-personal-slug
@@ -166,15 +166,15 @@ At this stage of the guide, the starter workspace pages are still intentionally
166
166
 
167
167
  The most important new visible ideas are:
168
168
 
169
- - a workspace slug now appears in the route
169
+ - a workspace slug appears in the route
170
170
  - the shell can expose workspace selection and workspace tools
171
171
  - workspace-dependent surfaces can show a dedicated unavailable-state card when the requested workspace cannot be resolved
172
172
 
173
- First, even the old global `/home` surface now reacts to workspace state. The selector in the top-left and the invites cue in the top-right come from placement entries and workspace bootstrap context, not from the `home` page itself.
173
+ First, even the global `/home` surface reacts to workspace state. The selector and invites cue come from semantic placement entries and workspace bootstrap context, not from the `home` page itself.
174
174
 
175
- Then open your personal workspace route. The page itself is deliberately light. `src/pages/w/[workspaceSlug]/index.vue` mostly exists to prove that the `app` surface is now real and ready to host later modules.
175
+ Then open your personal workspace route. The page itself is deliberately light. `src/pages/w/[workspaceSlug]/index.vue` mostly exists to prove that the `app` surface is real and ready to host later modules.
176
176
 
177
- Next open the admin surface for the same workspace. This is the matching pattern on the admin side: `src/pages/w/[workspaceSlug]/admin.vue` is the shell wrapper, `src/pages/w/[workspaceSlug]/admin/index.vue` is the starter page, and the top-right workspace tools button is coming from placements rather than being hand-built into that page file.
177
+ Next open the admin surface for the same workspace. This is the matching pattern on the admin side: `src/pages/w/[workspaceSlug]/admin.vue` is the shell wrapper, `src/pages/w/[workspaceSlug]/admin/index.vue` is the starter page, and the workspace tools button is coming from placements rather than being hand-built into that page file.
178
178
 
179
179
  Finally open the nested workspace settings route. This is useful to inspect because it shows the container pattern most clearly. `src/pages/w/[workspaceSlug]/admin/workspace/settings.vue` owns the section frame and the child outlet, while the actual settings sub-pages can keep arriving later under that shell.
180
180
 
@@ -231,7 +231,7 @@ config.workspaceInvitations = {
231
231
  };
232
232
  ```
233
233
 
234
- Those lines are the public contract that tells both client and server, "this app is workspace-aware now."
234
+ Those lines are the public contract that tells both client and server that this app is workspace-aware.
235
235
 
236
236
  ### `config/surfaceAccessPolicies.js` gains `workspace_member`
237
237
 
@@ -252,7 +252,7 @@ That is the core idea of multi-homing in JSKIT:
252
252
  - the server resolves that workspace
253
253
  - access depends on whether the current user belongs to it
254
254
 
255
- ### The migrations now include workspace schema
255
+ ### The migrations include workspace schema
256
256
 
257
257
  After `workspaces-core`, the migration directory grows again with files such as:
258
258
 
@@ -269,7 +269,7 @@ That is why the chapter needs another `npm run db:migrate`. Without those tables
269
269
 
270
270
  ### The route tree gains workspace-dependent pages
271
271
 
272
- The app now gets:
272
+ The app gets:
273
273
 
274
274
  ```text
275
275
  src/pages/w/[workspaceSlug].vue
@@ -583,13 +583,13 @@ addPlacement({
583
583
  });
584
584
  ```
585
585
 
586
- That one block explains a lot of the new shell behavior.
586
+ That one block explains a lot of the workspace shell behavior.
587
587
 
588
- - the authenticated profile menu can now switch into workspace surfaces
589
- - the top-left area now has a workspace selector
590
- - the top-right area can show a pending-invites cue
591
- - the admin surface gets workspace tools in the top-right area
592
- - the admin workspace settings menu is now another nested placement host with both `Settings` and `Members`
588
+ - the authenticated profile menu can switch into workspace surfaces
589
+ - `shell.identity` carries the workspace selector
590
+ - `shell.status` can show a pending-invites cue
591
+ - the admin surface gets workspace tools through `shell.status`
592
+ - the admin workspace settings menu is another nested placement host with both `Settings` and `Members`
593
593
 
594
594
  If you want to add your own app-owned page into that top cog menu, first ask JSKIT which semantic placements exist:
595
595
 
@@ -597,7 +597,7 @@ If you want to add your own app-owned page into that top cog menu, first ask JSK
597
597
  npx jskit list-placements
598
598
  ```
599
599
 
600
- In a workspace-enabled app, that list now includes:
600
+ In a workspace-enabled app, that list includes:
601
601
 
602
602
  ```text
603
603
  - admin.tools-menu: Admin surface tools menu actions.
@@ -620,7 +620,7 @@ That command creates `src/pages/w/[workspaceSlug]/admin/catalogue/index.vue` and
620
620
 
621
621
  You also do **not** need a renderer flag here. `admin.tools-menu` defines its link renderer in topology, so JSKIT resolves that when it renders the placement entry.
622
622
 
623
- So the placement system from the shell chapter is still doing the same job as before. The app just has a richer routing and tenancy context now.
623
+ So the placement system from the shell chapter is doing the same job with a richer routing and tenancy context.
624
624
 
625
625
  ### The local client provider gets one more app-owned token
626
626
 
@@ -666,7 +666,7 @@ registerProfileSyncLifecycleContributor(app, "workspaces.core.profileSyncLifecyc
666
666
 
667
667
  That means the workspace package does not need to patch auth directly to learn that a user was added. It listens through the `users-core` lifecycle registry instead.
668
668
 
669
- For this chapter's `tenancyMode = "personal"` setup, that contributor now does real work. When an authenticated JSKIT user is synchronized from auth, `workspaces-core` ensures that user's personal workspace exists.
669
+ For this chapter's `tenancyMode = "personal"` setup, that contributor does real work. When an authenticated JSKIT user is synchronized from auth, `workspaces-core` ensures that user's personal workspace exists.
670
670
 
671
671
  That detail matters for the retrofit path described earlier in this chapter. If the app started on `none`, then later switched to `personal`, the first sign-in after that switch still needs to backfill the personal workspace for the already-existing user record. The lifecycle contributor handles that because the workspace provision step is idempotent.
672
672
 
@@ -712,9 +712,9 @@ Before:
712
712
  After:
713
713
 
714
714
  - global surfaces still exist
715
- - workspace-scoped surfaces now exist too
715
+ - workspace-scoped surfaces exist too
716
716
  - the shell can navigate between workspaces
717
- - access to some surfaces now depends on workspace membership
717
+ - access to some surfaces depends on workspace membership
718
718
 
719
719
  That is why multi-homing deserves its own chapter. It is not just another feature package. It is the moment the app becomes tenancy-aware.
720
720
 
@@ -722,11 +722,11 @@ That is why multi-homing deserves its own chapter. It is not just another featur
722
722
 
723
723
  This chapter is the real routing and tenancy pivot in the guide.
724
724
 
725
- - the app now uses `tenancyMode = "personal"`
725
+ - the app uses `tenancyMode = "personal"`
726
726
  - `workspaces-core` added the persistent schema and server runtime for workspaces
727
727
  - `workspaces-web` added the first workspace-scoped surfaces, shell controls, and the workspace-owned account invites extension
728
728
 
729
- At the end of this chapter, the app now has both:
729
+ At the end of this chapter, the app has both:
730
730
 
731
731
  - global surfaces such as `home`, `auth`, `account`, and `console`
732
732
  - workspace-scoped surfaces such as `/w/[workspaceSlug]` and `/w/[workspaceSlug]/admin`
@@ -736,7 +736,7 @@ That is the most important mental shift to keep:
736
736
  - earlier chapters added features inside one global app shell
737
737
  - this chapter changed the topology of the app itself
738
738
 
739
- From here on, later modules are no longer only adding pages or widgets. They can add features inside either:
739
+ From here on, later modules can add features inside either:
740
740
 
741
741
  - the global surfaces
742
742
  - the workspace-specific surfaces
@@ -42,7 +42,7 @@ This is the first chapter where the migration step is not just "nice to have."
42
42
  - `AUTH_PROFILE_MODE=users` into `.env`
43
43
  - real users/account schema migrations into `migrations/`
44
44
 
45
- That means the app is now expected to use the persistent users-backed profile sync service. If you skip `npm run db:migrate`, the code and routes are installed, but the required tables are still missing.
45
+ That means the app is expected to use the persistent users-backed profile sync service. If you skip `npm run db:migrate`, the code and routes are installed, but the required tables are still missing.
46
46
 
47
47
  So the correct flow is:
48
48
 
@@ -57,24 +57,24 @@ Most of the time, step 3 is not needed because `jskit add package users-web` alr
57
57
  - `jskit migrations changed` writes or refreshes JSKIT-managed migration files in `migrations/`
58
58
  - `npm run db:migrate` actually applies pending migrations to MySQL
59
59
 
60
- ## What changes now
60
+ ## What `users-web` adds
61
61
 
62
62
  This chapter is the real transition from "authentication exists" to "the app knows about users."
63
63
 
64
64
  ### Authentication becomes users-backed
65
65
 
66
- In the database chapter, JSKIT still used the standalone in-memory profile mirror. After installing `users-web`, that changes. JSKIT now expects to synchronize authenticated users into real JSKIT tables.
66
+ In the database chapter, JSKIT used the standalone in-memory profile mirror. After installing `users-web`, JSKIT expects to synchronize authenticated users into real JSKIT tables.
67
67
 
68
68
  That is the biggest architectural change in this chapter.
69
69
 
70
70
  - Supabase still owns the real auth identity and session
71
- - JSKIT now also owns a persistent users/account data model in MySQL
71
+ - JSKIT owns a persistent users/account data model in MySQL
72
72
 
73
- So after this chapter, a signed-in user is no longer only "someone Supabase knows about." They are also a persistent JSKIT-side user with settings and profile state in the app database.
73
+ So after this chapter, a signed-in user is not only "someone Supabase knows about." They are also a persistent JSKIT-side user with settings and profile state in the app database.
74
74
 
75
75
  ### The app gets an authenticated account surface
76
76
 
77
- The app now has a new authenticated surface at `/account`.
77
+ The app has an authenticated surface at `/account`.
78
78
 
79
79
  This is where the starter account settings UI lives. It already has real sections for:
80
80
 
@@ -84,17 +84,17 @@ This is where the starter account settings UI lives. It already has real section
84
84
 
85
85
  Later chapters can extend this account screen with more sections. For example, the multi-homing chapter adds workspace invitation UI through `workspaces-web`, not through `users-web` itself.
86
86
 
87
- The important point is that this is no longer just a placeholder route. It is the first app-owned screen that assumes there is a real persistent user model behind it.
87
+ The important point is that this is a real account route, not a placeholder. It is the first app-owned screen that assumes there is a persistent user model behind it.
88
88
 
89
89
  ### The shell changes for signed-in users
90
90
 
91
91
  Once a user is signed in, the shell becomes noticeably richer.
92
92
 
93
93
  - the profile menu gets a `Settings` entry that leads to `/account`
94
- - the home surface gets a small users tools widget in the top-right area
95
- - the auth bootstrap payload now includes persistent user settings instead of only the fallback mirror data
94
+ - the home surface gets a small users tools widget in `shell.status`
95
+ - the auth bootstrap payload includes persistent user settings instead of only the fallback mirror data
96
96
 
97
- So this chapter is also the first one where logging in changes more than just "guest vs signed in." It now changes what persistent user-facing surfaces the app can expose.
97
+ So this chapter is also the first one where logging in changes more than just "guest vs signed in." It changes what persistent user-facing surfaces the app can expose.
98
98
 
99
99
  ## What to look at in the browser
100
100
 
@@ -107,17 +107,17 @@ npm run server
107
107
 
108
108
  Then sign in through `http://localhost:5173/auth/login`.
109
109
 
110
- After a successful sign-in, you should notice three concrete differences compared with the previous chapter:
110
+ After a successful sign-in, check these concrete differences compared with the previous chapter:
111
111
 
112
- - the profile menu now contains `Settings`
113
- - the top-right area now also has a small users tools widget
114
- - `/account` now exists and is authenticated
112
+ - the profile menu contains `Settings`
113
+ - `shell.status` includes the users tools widget
114
+ - `/account` exists and is authenticated
115
115
 
116
116
  This is the first chapter where the app starts to feel like it has a real user model behind it.
117
117
 
118
118
  ## What `users-web` adds to the app
119
119
 
120
- The most interesting files now are spread across config, migrations, routing, and the app-owned account UI.
120
+ The most interesting files are spread across config, migrations, routing, and the app-owned account UI.
121
121
 
122
122
  ### `.env` flips auth into users mode
123
123
 
@@ -239,18 +239,18 @@ src/components/account/settings/
239
239
 
240
240
  Those three section components stay app-owned so you can reshape the actual UI freely.
241
241
 
242
- The host itself now lives in `users-web` and resolves every account section through the semantic `settings.sections` placement with owner `account-settings`, including the default `profile`, `preferences`, and `notifications` entries.
242
+ The host itself lives in `users-web` and resolves every account section through the semantic `settings.sections` placement with owner `account-settings`, including the default `profile`, `preferences`, and `notifications` entries.
243
243
 
244
244
  So the screen follows the same rule as the rest of JSKIT UI: sections are added by placement rather than being hardcoded into an app-owned host component.
245
245
 
246
- That is worth noticing because the guide has now reached a new level of scaffolding:
246
+ That is worth noticing because this is a higher level of scaffolding:
247
247
 
248
248
  - earlier chapters mostly introduced shells and routes
249
249
  - this chapter introduces app-owned leaf section UI while the generic section host stays in the package
250
250
 
251
251
  ## Under the hood
252
252
 
253
- ### Why auth now behaves differently
253
+ ### Why auth uses the users layer
254
254
 
255
255
  In the previous chapter, auth still defaulted to the standalone profile sync fallback. The core logic in `AuthSupabaseServiceProvider` looks like this:
256
256
 
@@ -268,7 +268,7 @@ if (authProfileMode === AUTH_PROFILE_MODE_USERS) {
268
268
  }
269
269
  ```
270
270
 
271
- The important part is no longer hypothetical.
271
+ The important part is concrete.
272
272
 
273
273
  After `users-web`:
274
274
 
@@ -276,9 +276,9 @@ After `users-web`:
276
276
  - `users-core` supplies the users-backed sync service
277
277
  - the migrations supply the required tables
278
278
 
279
- So auth now has everything it needs to stop using the fallback mirror and start using the persistent users-backed one.
279
+ So auth has everything it needs to stop using the fallback mirror and start using the persistent users-backed one.
280
280
 
281
- That is the true point of this chapter. The app is no longer just authenticated. It now has a real users layer.
281
+ That is the true point of this chapter. The app is not just authenticated. It has a real users layer.
282
282
 
283
283
  ### `users-core` also owns the profile-sync lifecycle registry
284
284
 
@@ -361,7 +361,7 @@ This chapter is where the app stopped treating signed-in people as only Supabase
361
361
 
362
362
  That is why this chapter feels bigger than a normal page install. It changes both the browser experience and the server-side meaning of "a signed-in user."
363
363
 
364
- At the end of this chapter, the app now has:
364
+ At the end of this chapter, the app has:
365
365
 
366
366
  - real JSKIT-side `users` and `user_settings` tables
367
367
  - a real authenticated `/account` surface
@@ -71,14 +71,15 @@ In the current scaffold, those scripts are intentionally thin:
71
71
 
72
72
  That is a deliberate design choice.
73
73
 
74
- The app keeps the handy `npm run` names, but the real maintenance policy now lives in the installed CLI package instead of copied shell scripts inside the app. That means if JSKIT later changes how package updates, local linking, or baseline verification should work, apps can pick up the new behavior by updating `@jskit-ai/jskit-cli` instead of hand-editing frozen scaffold files.
74
+ The app keeps the handy `npm run` names, but the real maintenance policy lives in the installed CLI package instead of copied shell scripts inside the app. That means if JSKIT later changes how package updates, local linking, or baseline verification should work, apps can pick up the new behavior by updating `@jskit-ai/jskit-cli` instead of hand-editing frozen scaffold files.
75
75
 
76
76
  This gives you a clean ownership split:
77
77
 
78
78
  - app-owned scripts still describe how *this app* runs, builds, and tests
79
+ - `npm run devlinks` re-applies local checkout links after `npm install` when you are testing an app against this monorepo
79
80
  - JSKIT-owned wrapper scripts delegate framework maintenance to `jskit app ...`
80
81
 
81
- That split is worth keeping in mind through the rest of the guide. When you see `npm run verify`, that is now shorthand for "run the app's JSKIT baseline verification policy, then any app-specific extra verification hook".
82
+ That split is worth keeping in mind through the rest of the guide. When you see `npm run verify`, read it as "run the app's JSKIT baseline verification policy, then any app-specific extra verification hook".
82
83
 
83
84
  The starter scaffold also includes `.github/workflows/verify.yml`. That workflow is intentionally conservative: it runs `npm run verify` and does not assume that every app can stand up full browser, auth, seed-data, and database verification inside hosted CI.
84
85
 
@@ -505,7 +506,13 @@ npx jskit add package shell-web
505
506
  npm install
506
507
  ```
507
508
 
508
- `jskit add` changes the app. `npm install` downloads the dependencies that the changed app now requires.
509
+ `jskit add` changes the app. `npm install` downloads the dependencies that the changed app requires.
510
+
511
+ If the app is being tested against a local JSKIT checkout with linked packages, run the local-link wrapper again after every `npm install`:
512
+
513
+ ```bash
514
+ npm run devlinks
515
+ ```
509
516
 
510
517
  ### `add bundle`
511
518
 
@@ -700,7 +707,7 @@ That is a different job from:
700
707
 
701
708
  Those commands can all pass while JSKIT-managed state is still inconsistent. `doctor` is the command that checks that JSKIT's own view of the app still makes sense.
702
709
 
703
- That is exactly why the starter scaffold now routes `npm run verify` through `jskit app verify`.
710
+ That is exactly why the starter scaffold routes `npm run verify` through `jskit app verify`.
704
711
 
705
712
  It belongs there because JSKIT apps are not only source trees. They also have:
706
713
 
@@ -732,7 +739,7 @@ Good times to run it manually include:
732
739
 
733
740
  One important nuance: `doctor` is checking for broken JSKIT ownership and visibility, not trying to stop you from editing app-owned files. For example, a managed file that still exists but whose contents changed is normally fine. The problem is when JSKIT expects a managed file to exist and it is gone, or when the installed package state no longer resolves cleanly.
734
741
 
735
- There is one intentional exception now for user-facing UI work.
742
+ There is one intentional exception for user-facing UI work.
736
743
 
737
744
  If the current git working tree has changed UI files under the app's normal UI paths, `doctor` expects a matching `.jskit/verification/ui.json` receipt. The intended way to create that receipt is:
738
745
 
@@ -962,7 +969,7 @@ Good fits include:
962
969
  - a custom integration package that talks to one external API
963
970
  - app-specific auth or policy behavior
964
971
  - a local runtime package that owns both client and server behavior for one feature area
965
- - a package that starts small now but may later grow providers, shared helpers, and routes
972
+ - a package that starts small but may later grow providers, shared helpers, and routes
966
973
 
967
974
  Use a generator when the problem is already understood well enough that JSKIT can scaffold the feature shape for you.
968
975