@notis_ai/cli 0.2.0-beta.156.1 → 0.2.0-beta.157.1

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 (35) hide show
  1. package/README.md +11 -45
  2. package/config/notis_app_design_rules.json +135 -0
  3. package/dist/agent-hooks/notis-agent-hook.mjs +5168 -7271
  4. package/dist/base-skills/notis-apps/SKILL.md +108 -194
  5. package/dist/base-skills/notis-cli/SKILL.md +64 -131
  6. package/package.json +1 -2
  7. package/skills/notis-apps/cli.md +34 -95
  8. package/skills/notis-cli/AGENT_INSTRUCTIONS.md +1 -1
  9. package/src/command-specs/apps.js +322 -1560
  10. package/src/runtime/agent-browser.js +169 -1
  11. package/src/runtime/app-boundary-validator.js +221 -0
  12. package/src/runtime/app-platform.js +359 -233
  13. package/src/runtime/app-test-server.js +292 -0
  14. package/template/app/page.tsx +45 -44
  15. package/template/components/page-heading.tsx +23 -0
  16. package/template/components/ui/badge.tsx +7 -4
  17. package/template/components/ui/card.tsx +24 -11
  18. package/template/components/ui/native-select.tsx +24 -0
  19. package/template/notis.config.ts +0 -1
  20. package/template/package.json +2 -2
  21. package/template/packages/sdk/package.json +1 -2
  22. package/template/packages/sdk/src/components/MultiSelectActionBar.tsx +20 -7
  23. package/template/packages/sdk/src/config.ts +0 -2
  24. package/template/packages/sdk/src/interactions.ts +2 -1
  25. package/template/packages/sdk/src/styles.css +28 -1
  26. package/src/runtime/app-dev-build-supervisor.js +0 -47
  27. package/src/runtime/app-dev-build.js +0 -41
  28. package/src/runtime/app-dev-consumers.js +0 -154
  29. package/src/runtime/app-dev-host-lock.js +0 -80
  30. package/src/runtime/app-dev-process-identity.js +0 -111
  31. package/src/runtime/app-dev-roots.js +0 -284
  32. package/src/runtime/app-dev-server.js +0 -1136
  33. package/src/runtime/app-dev-sessions.js +0 -185
  34. package/src/runtime/cli-mode.generated.js +0 -5
  35. package/src/runtime/cli-mode.js +0 -34
@@ -20,55 +20,64 @@ All Notis apps are built using the Notis CLI, either locally in a repo workspace
20
20
  - the app is packaged as an ES module bundle
21
21
  - the portal renders it as a React component inside the portal's React tree
22
22
 
23
- ## App Workspace Tool Rules
24
-
25
- - Apps are the top-level packaging unit in Notis.
26
- - Choose the execution path before changing an app. A prompt that says the
27
- shell is a hosted/Vercel sandbox, or a shell rooted at `/vercel/sandbox`, is
28
- the **hosted sandbox** path. A shell on the user's computer with Notis
29
- Desktop available is the **local Desktop** path.
30
- - In a hosted sandbox, do not run `apps dev`: the user's Desktop cannot mount
31
- that sandbox filesystem. Unless the user explicitly requests preview-only,
32
- read-only, or no deployment, a request to create or edit an app authorizes
33
- deploying that app to the user's Workspace after `apps build` and automated
34
- `apps verify` pass. An opt-out stops after those tests with no remote app
35
- create/link, database mutation, deploy, or post-deploy checks. Pulling an existing app provides its exact link. For a new
36
- app, test first, then reconcile profile state and `apps list --json` against
37
- the canonical `notis.config.ts` `name` and intended personal/team scope: link
38
- one exact editable non-development match after a metadata-only
39
- (`include_documents: false`) detail read proves scope, fail on ambiguity or
40
- scope mismatch, or create only when none exists. New CLI-created apps default
41
- to personal scope. Before creation, prove that canonicalizing the config
42
- `title` yields the config `name`. For personal scope, run `apps create
43
- "<canonical-config-title>" . --json` exactly once. For explicitly requested
44
- team scope, discover and inspect `LOCAL_NOTIS_CREATE_APP`, dry-run it, execute
45
- it exactly once with team visibility and the verified current team scope,
46
- verify the returned id/slug/team scope/edit permission, then `apps link` that
47
- exact id. Stop for read-only
48
- reconciliation if creation is ambiguous or outcome-unknown. In the local
49
- Desktop path, use `apps dev [folder]`, let the user test the DEV app, and deploy
50
- that development identity directly only after an explicit request. Use
51
- `LOCAL_NOTIS_CREATE_APP` only for a hosted team-scoped creation or another
52
- non-CLI administrative flow that explicitly requires a server-side app row.
53
- - Use `LOCAL_NOTIS_UPDATE_APP` to update app metadata.
54
- - Use `LOCAL_NOTIS_LIST_APPS` to discover the user's apps.
55
- - The full app lifecycle uses the CLI in the shell. Always run it through the registry-resolved package, for example `npx --package @notis_ai/cli@latest -- notis apps init`; use the same prefix for `build` and `deploy`. In hosted shells, the CLI is pre-authenticated through `NOTIS_JWT`.
56
- - There are no `save_app` or `load_app` tools. Do not attempt to call them. Use only the CLI for app file operations.
57
- - Use `npx --package @notis_ai/cli@latest -- notis apps scaffolds list` (optionally with `--search <term>`) to discover starting points before scaffolding. Every published Store app is a scaffold; the catalog is served from the public registry, not bundled inside the CLI.
58
- - Use `LOCAL_NOTIS_LIST_PUBLIC_APP_STORE` only to help users choose apps to install, not as a source-clone workflow.
59
- - Use `LOCAL_NOTIS_INSTALL_APP` only when the user explicitly wants to install from a listing.
60
- - Before installing, inspect the listing's `required_capabilities`. Explain each
61
- requested capability and obtain explicit approval; only then pass the matching
62
- token in `approved_capabilities`. Never infer capability approval. The current
63
- workspace-wide read token is `workspace_databases_read`; the read-only
64
- cloud computer token is `cloud_computer_read`.
23
+ ## Release-only delivery
24
+
25
+ Workspace runs released app versions only. Local and cloud agents use the same workflow.
26
+ A request to create or edit app source authorizes updating that app in Workspace after checks pass.
27
+ Explicit read-only, preview-only or no-deploy requests stop at local artifacts and checks: no remote
28
+ app/resource creation or mutation, Workspace preview, deployment or live verification. Store
29
+ publication always needs separate explicit approval.
30
+
31
+ 1. Inspect the effective CLI profile and the exact app's current version with `apps list --json`.
32
+ For an existing released app, preserve local edits and pull its exact app ID into the intended
33
+ directory. Retain the profile/app link, deployment version and revision. An unreleased container
34
+ has no source to pull: recover its original local source and edits, or scaffold locally only if
35
+ that source cannot be recovered. Confirm its exact ID, edit permission and personal/team scope,
36
+ then run `apps link <app-id> <source-directory> --expected-version 0` to resume that same container.
37
+ If a release has appeared, preserve local source separately, pull the current release into a fresh
38
+ directory and reapply the intended edits. The link guard compares against the same remote read
39
+ whose version/revision it saves; deploy still rejects a release racing after that read. Do not pull
40
+ missing source or create another remote app to recover a failed first release.
41
+ 2. Scaffold a new app locally, or edit the pulled source. Run `apps build` and automated
42
+ `apps verify` before new remote creation. Missing browser tooling or failed checks blocks delivery;
43
+ printed URLs and `--no-browser` are not passing verification. Install browser tooling with
44
+ `npm exec --yes --package agent-browser@latest -- agent-browser install`; if needed run
45
+ `npx --yes --package @notis_ai/cli@latest --package agent-browser@latest -- notis apps verify`.
46
+ 3. Reconcile `apps list --json` and the exact intended name/slug, edit permission and personal/team
47
+ scope. Default to personal only when no team was requested. Reuse a matching editable identity;
48
+ stop on ambiguous matches or conflicting identity/scope. Create only when none exists, using
49
+ `apps create "<exact app name>" .` (or `--team-id <verified team ID>`). Read back the same ID.
50
+ A failed first release leaves a container: reuse it, never duplicate or automatically delete it.
51
+ 4. Compare existing app-owned schemas. Create only necessary missing databases against that exact
52
+ app ID. Change existing schemas by verified database ID and ownership, and only with backward-
53
+ compatible changes before release. Read back each change. Breaking changes need separate coordination.
54
+ Ordinary note/record edits and existing resource editors remain immediate.
55
+ 5. Run `apps deploy` against the same linked app. It builds, verifies a frozen source/artifact
56
+ snapshot with stubs, then sends that snapshot to the backend. `--skip-build` accepts only unchanged,
57
+ valid output and still verifies. Do not bypass the backend or create implicitly on deploy.
58
+ 6. Read back the exact installed app ID, integer version and Portal URL with `apps list --json`.
59
+ Run `apps verify --mode live` and open the installed app in the actual Portal for surface proof.
60
+ A live harness check alone does not prove the deployed bundle rendered in Portal.
61
+ 7. Report **failed before activation**, **deployed but unverified**, or **outcome unknown** accurately.
62
+ Never blindly replay an uncertain create/deploy response; reconcile its exact identity/version first.
63
+ `apps publish --confirm-ready` is **Publish to Store**, separately approved and listing-gated.
64
+ Workspace delivery is **Update app**, with no Store screenshot/readiness requirement.
65
+
66
+ ### Restore historical source as a new release
67
+
68
+ Pull the current release into a fresh checkout first. Retrieve historical source into a different
69
+ folder (`apps pull <id> <historical-dir> --source-version <n>`). Replace source in the current checkout
70
+ without replacing its `.notis` profile/app link or deployment base. Update `package.json`'s
71
+ `notisAppVersion`, check compatibility with current resources, build, verify and deploy as a new
72
+ release. Preserve app/database/skill IDs. Never decrement the deployment counter, rewrite snapshots,
73
+ or claim to undo user data or external actions.
65
74
 
66
75
  ## Architecture
67
76
 
68
77
  ```
69
78
  Notis CLI (local workspace or Vercel Sandbox)
70
79
  -> Vite + React project with @notis/sdk
71
- -> notis apps init / dev / build / verify / create / link / pull / deploy
80
+ -> notis apps init / build / verify / create / link / pull / deploy
72
81
  -> ES module bundle (app.js + app.css) + manifest
73
82
  -> Portal renders as React component with real tools/databases
74
83
  ```
@@ -84,7 +93,7 @@ Notis CLI (local workspace or Vercel Sandbox)
84
93
  - `@notis/sdk/vite` -- `notisViteConfig()` for vite.config.ts
85
94
  - `@notis/sdk/styles.css` -- shadow-safe app shell styles and base app-surface classes
86
95
 
87
- 2. **CLI** (`packages/cli/src/command-specs/apps.js`) -- local development should center on `apps dev`, plus init, build, verify, create, deploy, link, pull, doctor, and list
96
+ 2. **CLI** (`packages/cli/src/command-specs/apps.js`) -- release delivery uses init, build, verify, create, deploy, link, pull, doctor, and list
88
97
 
89
98
  3. **Server** (`server/routers/portal_views/`) -- Returns signed bundle URLs, proxies tool calls
90
99
 
@@ -94,7 +103,6 @@ Notis CLI (local workspace or Vercel Sandbox)
94
103
 
95
104
  Apps communicate with the platform through the `NotisRuntime` interface, provided by the portal via React context:
96
105
 
97
- - **Portal development**: the portal loads a local bundle for an active dev session and still provides a real `NotisRuntime`.
98
106
  - **Portal**: the portal creates a real `NotisRuntime` and passes it as a prop to `NotisProvider`. All calls go to `/portal_views/runtime_query` via fetch with the user's JWT.
99
107
  - The portal mounts the app inside a shadow-scoped content surface and injects the runtime before app mount. There is no supported window-global runtime fallback.
100
108
 
@@ -110,7 +118,7 @@ App code never accesses the runtime directly -- it uses SDK hooks (`useTool`, `u
110
118
  5. **Declarative tools** -- Tool access is declared in `notis.config.ts` by the final names returned by tool discovery and enforced server-side. Views can call native Notis, connected integrations, PostForMe, and MCP tools directly; metered calls use the same credit-cap and usage-billing path as the CLI.
111
119
  6. **shadcn + Notis theme** -- Apps must use shadcn components with the live Notis theme provided by the portal
112
120
  7. **Phosphor icons only** -- Always `phosphor:` prefix. Never emojis.
113
- 8. **Database refs only** -- `notis.config.ts` references existing databases by slug. The schema source of truth lives in the `databases` table, not in the manifest. Every native database is owned by exactly one app (`databases.owner_app_id`): creating one through `LOCAL_NOTIS_DATABASE_UPSERT_DATABASE` requires the owning app's slug or id in the `app` argument, install/dev materialization stamps ownership automatically, and deleting an app deletes its databases and their documents.
121
+ 8. **Database refs only** -- `notis.config.ts` references existing databases by slug. The schema source of truth lives in the `databases` table, not in the manifest. Every native database is owned by exactly one app (`databases.owner_app_id`): creating one through `LOCAL_NOTIS_DATABASE_UPSERT_DATABASE` requires the owning app's slug or id in the `app` argument, installation stamps ownership automatically, and deleting an app deletes its databases and their documents.
114
122
  An app-owned database slug is a stable deployed contract because bundles and
115
123
  collection routes may call it directly. Do not try to rename that slug with
116
124
  a schema tool; rename the display title instead.
@@ -120,12 +128,12 @@ App code never accesses the runtime directly -- it uses SDK hooks (`useTool`, `u
120
128
  12. **Portal-owned sidebars stay portal-owned** -- If a route uses `collection.sidebar`, treat that sidebar as platform chrome. Do not remove it, recreate it inside app JSX, or replace it with a custom in-app folder rail.
121
129
  13. **Portal globals are off-limits** -- Never use `window.__NOTIS_RUNTIME__`, query portal-owned DOM hooks, or create global DOM portals.
122
130
  14. **Prefer inline optimistic edits** -- Rename-like edits for collections, app-owned rows, and sidebar-backed entities should use inline editing with an optimistic UI update, then roll back on backend failure. Use modals only when the edit requires multiple fields or destructive confirmation.
123
- 15. **The execution environment determines the deploy gate** -- In the local Desktop path, run `apps dev [folder]`, let the **user** test the automatically mounted DEV app, and do not deploy until the user asks; first deploy promotes that `dev_app_id` directly, so never create a second app first. In a hosted sandbox, `apps dev` cannot reach the user's Desktop; bootstrap `agent-browser`, build and verify first, then resolve exact identity/resources and deploy to the user's Workspace, verify the remote version and live runtime, and return the exact Portal URL. Automatic deployment is the default for create/edit requests only; an explicit preview-only, read-only, or no-deploy request wins. This standing sandbox authorization does not authorize Store submission.
124
- 16. **Installed app identity is exact, editable, and scope-proven** -- Validate an explicit persisted link for this API/user profile before using it. Otherwise inspect every accessible exact-canonical-slug row, including development rows; link only one editable non-development candidate whose exact detail proves the intended personal/team scope. Fail closed on a development collision, multiple matches, missing scope proof, or scope mismatch, and never infer identity from display name. After first install, keep the validated profile-scoped link so Portal and CLI update the same app instead of creating duplicates.
125
- 17. **Development identities stay separate** -- `.notis/state.json` uses `dev_app_id` for the hidden development-runtime row and `app_id` only for an accessible installed workspace app, scoped under the authenticated environment. Never pass a runtime app whose manifest has `is_dev: true` to `notis apps link`.
126
- 18. **Automatic mounts are multi-instance and least-authority** -- Prod, Beta, and source-development Desktop instances may mount the same source simultaneously with independent authenticated runtimes. Automatic mounting never grants capabilities: reuse existing grants and leave restricted capabilities denied until approved. Consumer leases expire after crashes so the shared host exits after the last live instance. There are no offline rows or manual start/stop controls.
131
+ 15. **One delivery gate** -- Follow Release-only delivery on local and cloud computers. No DEV runtime exists.
132
+ 16. **Exact identity** -- Preserve the intended profile, editable app ID, personal/team scope and current deployment base. Never silently advance a stale checkout.
133
+ 17. **Automatic source updates** -- Build and check requested app source changes, then update Workspace unless the user opted out. Store publication stays separate.
134
+ 18. **Runtime permissions stay least-authority** -- Release activation preserves existing grants and keeps restricted capabilities denied until approved.
127
135
  19. **Store submission is user-gated** -- Run `apps publish --confirm-ready` only after the user explicitly confirms the current App Details page and Store listing are ready. Deploy the exact approved local state first. The command must reject missing confirmation, incomplete listing media, a local/deployed version mismatch, private visibility, or an existing pending review.
128
- 20. **Bump `notisAppVersion` before linked development and every Store update** -- `package.json` must contain a semver `notisAppVersion`. A linked local source substitutes its installed Workspace app only when the local version is strictly greater than the installed manifest's `release_version`; equal, lower, missing, or invalid versions keep serving the online bundle. `apps pull` retrieves the online version, so increment `notisAppVersion` before `apps dev` when continuing development. For an existing Store app, also increment it beyond the currently published registry version before deploy and submission; registry CI rejects equal or lower versions.
136
+ 20. **Source restoration** -- Restore historical source as a new release using the current deployment base; never revert data or decrement versions.
129
137
  21. **`CHANGELOG.md` owns release history** -- Keep the complete release history in one root `CHANGELOG.md`, newest entry first. Do not add new `versionNotes` values to `notis.config.ts`. Use `## [Release title] - YYYY-MM-DD`, or `{PR_MERGE_DATE}` for an unpublished entry. App Details reads **What’s New** and **Version History** from the deployed package manifest, while the Store reads them from the latest published snapshot; unpublished workspace edits must never change the Store page. The manifest also exposes `package.json` `notisAppVersion` as the package version shown in App Details.
130
138
  22. **Database rows are private unless explicitly seeded** -- A string declaration such as `databases: ['notes']` publishes schema only and never includes the developer's rows. Use `{ slug: 'templates', seedDocuments: true }` only for small, intentional starter content that every installer should receive. Never enable it for user-created notes, history, leads, or other personal data.
131
139
  23. **Public submissions are complete, reviewable packages** -- The registry PR must contain the full editable source tree, Store assets, exact source-declared database schemas, and only explicitly seeded starter rows. Registry CI validates those boundaries before merge; do not hand-edit `notis-listing.json` or strip source files to make a check pass. Fix the app locally, redeploy, and resubmit.
@@ -137,9 +145,9 @@ App code never accesses the runtime directly -- it uses SDK hooks (`useTool`, `u
137
145
  These are the most common mistakes agents make. Each one wastes time and produces broken results.
138
146
 
139
147
  - **NEVER assume app deploys create databases for you** -- Create or update databases through native Notis database tools or the assistant first, then reference them by slug in `notis.config.ts`. Database creation requires the owning app to exist: pass its slug or id in the `app` argument of `LOCAL_NOTIS_DATABASE_UPSERT_DATABASE` (create the app first with `LOCAL_NOTIS_CREATE_APP` if needed). A database can only be referenced by the app that owns it.
140
- - **NEVER bypass the supported workflow by manually stitching together low-level save or lint calls from a local workspace** -- Local agents should go through the NPX Notis CLI for `apps pull`, `apps dev`, `apps build`, `apps verify`, `apps create`, `apps link`, and `apps deploy`.
148
+ - **NEVER bypass the supported workflow by manually stitching together low-level save or lint calls from a local workspace** -- Local agents should go through the NPX Notis CLI for `apps pull`, `apps build`, `apps verify`, `apps create`, `apps link`, and `apps deploy`.
141
149
  - **NEVER use `apps pull` to clone a Store listing** -- `npx --package @notis_ai/cli@latest -- notis apps pull` only pulls source for an app the user can already access as an installed app. To fork a published Store app, run `npx --package @notis_ai/cli@latest -- notis apps init "My App" --from <slug>` instead: it downloads that app's source from the public registry, and installing the app first is not required.
142
- - **NEVER apply the local deploy gate to a hosted sandbox** -- On the user's local computer, a clean `apps build` + `apps verify` is not deploy consent: hand off the DEV app and wait. In a hosted sandbox, the user's create or edit request is deploy consent for that app because `apps dev` cannot reach their Desktop; deploy only after both commands pass, then verify the remote version. Neither path authorizes Store submission.
150
+ - **One local/cloud delivery contract** -- App source create/edit requests authorize Workspace delivery after checks. Explicit read-only, preview-only and no-deploy requests prohibit remote mutations. Neither authorizes Store publication.
143
151
  - **NEVER submit without explicit approval** -- A deploy request alone does not authorize Store submission. Run `npx --package @notis_ai/cli@latest -- notis apps publish --confirm-ready` only when the user confirms App Details is ready for Store review.
144
152
  - **NEVER write raw `views/<slug>/index.js` files** -- Write standard React pages in `app/`.
145
153
  - **NEVER invent `npx --package @notis_ai/cli@latest -- notis apps push` or bypass the review flow** -- Source moves through `apps pull` and `apps deploy`; `apps publish --confirm-ready` submits the deployed snapshot through the same authenticated review endpoint as App Details.
@@ -147,108 +155,13 @@ These are the most common mistakes agents make. Each one wastes time and produce
147
155
  - **NEVER explore server code or tool schemas to invent an alternative app workflow** -- Use the Notis CLI.
148
156
  - **NEVER work around a missing `collection.sidebar` portal tree by rendering a duplicate sidebar inside the app** -- keep the route manifest as the source of truth and escalate the missing portal sidebar as a platform bug instead.
149
157
  - **NEVER invent a custom visual language** -- Do not ship full-screen gradients, glassmorphism, bright neon palettes, or raw HTML controls as the primary UI. Apps should look like a natural extension of the portal.
150
- - **NEVER hand-roll buttons/cards/badges when the scaffold already provides shadcn primitives** -- Prefer `@/components/ui/*` and portal token classes such as `bg-background`, `bg-card`, `border-border`, and `text-muted-foreground`.
158
+ - **NEVER hand-roll buttons/cards/badges when the scaffold already provides flat primitives** -- Prefer `@/components/ui/*` and portal token classes such as `bg-background`, `bg-muted`, and `text-muted-foreground`. Never add `border` or `shadow` classes to `Card`; a `Card` nested in a `Card` is flat automatically. See Design bar.
151
159
 
152
160
  ## Workflow
153
161
 
154
- **Default to the Store scaffold catalog, not a blank project.** Every published Store app is a scaffold: `notis apps scaffolds list` reads the catalog from the public registry, and `notis apps init --from <slug>` downloads that app's source. Most user requests overlap with a published app, and starting from one is faster than a bare app.
155
-
156
- 1. **Find a starting point.** Run `npx --package @notis_ai/cli@latest -- notis apps scaffolds list` (add `--search <term>` to filter) to list the published Store apps. If something close matches, run `npx --package @notis_ai/cli@latest -- notis apps init "My App" --from <slug>` to download that app's source from the registry. Only run plain `notis apps init "My App"` when no published app fits. Either way the project lands in `~/.notis/apps/<slug>`; add a `[dir]` argument when the user wants it somewhere else (a tracked git repo, an existing monorepo), and report the path you used.
157
- 2. **Pull your own apps; fork Store apps with `--from`.** `apps pull` is for apps the user already has installed or deployed: run `npx --package @notis_ai/cli@latest -- notis apps list`, preserve any local edits in the target directory, then run `npx --package @notis_ai/cli@latest -- notis apps pull <app-id>` (lands in `~/.notis/apps/<app-slug>`; pass a `[dir]` argument to place it elsewhere). A pull reproduces the installed release, so increment `package.json` `notisAppVersion` above that release before `apps dev`; until then the online bundle remains active. To fork a published Store app, use `apps init --from <slug>` instead -- it downloads the source from the registry and does not require installing the app first.
158
- 3. **Edit the listing source.** In `notis.config.ts`, set `name` to the stable lowercase kebab-case identity and set `title` to the correctly cased human-facing name; for example, `name: 'link-building'` with `title: 'Link Building'`. Treat acronym and brand casing as editorial input, not something to derive mechanically from the slug. Then update description, icon, accent, author, categories, tagline, databases, routes, and tools. Declare a database as a string for schema-only Store packaging; use `{ slug: 'templates', seedDocuments: true }` only when its rows are deliberate starter content for every installer. Keep the complete Store release history in the root `CHANGELOG.md`, newest entry first, using `## [Release title] - YYYY-MM-DD` (or `{PR_MERGE_DATE}` before publication). The first entry powers **What’s New** and the same file powers **Version History**. `icon` is a `phosphor:<name>` value or `metadata/icon.png`; when unset the app shows its **two-letter initials** everywhere (store, sidebar, app details). `accent` optionally pins the avatar color to one of `blue|violet|emerald|amber|rose|sky|fuchsia|teal` (default derived from the app id). Icon/accent flow through deploy onto the app row + listing and can also be set later via the `update_app` tool.
159
- 4. **Build pages in `app/`.** Reuse scaffold code wherever it fits.
160
- 5. **Test the source before remote mutation.** Before changing a linked installed app, increment `package.json` `notisAppVersion` above the installed release. In a fresh hosted sandbox, bootstrap Agent Browser with `npm exec --yes --package agent-browser@latest -- agent-browser install`. Generate configured screenshots; use `theme: 'dark'` or `theme: 'light'` where appropriate and reserve screenshot `--raw` for diagnostics. Run `npm install`, run `npx --package @notis_ai/cli@latest -- notis apps build`, then run `npx --yes --package @notis_ai/cli@latest --package agent-browser@latest -- notis apps verify` in the sandbox (or the normal NPX verification command locally). Fix every failure. Do not create an app, mutate a database, or deploy before both checks pass. `--no-browser` is manual triage, not a passing automated gate.
161
- 6. **Finish the local Desktop path at the DEV handoff.** Run `npx --package @notis_ai/cli@latest -- notis apps dev [folder]`; it creates the development identity and materializes available scaffold database snapshots without requiring a hosted app id. Hand off after the user can see and test the app in its DEV-badged Workspace row. Do not deploy until the user asks, and then run `apps deploy` directly so the existing `dev_app_id` is promoted in place; never run `apps create` after `apps dev`. **Before handing off, complete all three acceptance checks:**
162
- 1. Root: `apps roots list` contains the intended folder (or the app is under the implicit default root).
163
- 2. Bundle: the loopback `/snapshot` responds successfully and contains the expected manifest/routes.
164
- 3. Mount and render: the app appears exactly once with a compact `DEV` badge and its default route renders. For multi-instance work, verify each requested Desktop independently.
165
- See Troubleshooting → *App is missing from the sidebar* if any check fails.
166
- 7. **Gate and resolve one hosted identity after tests pass.** If the request is preview-only, read-only, or no-deploy, stop after step 5: do not create or link an app, mutate a database, deploy, or run post-deploy checks. Otherwise, existing edits keep the exact profile-scoped id linked by `apps pull`, after a metadata-only (`include_documents: false`) app-detail read validates its edit permission and scope without materializing databases. For a new hosted app, default the intended scope to personal unless the user explicitly requests team scope, inspect `.notis/state.json`, and run `apps list --json`. Consider every accessible exact canonical-slug row, including development rows. Link only one editable, non-development candidate whose metadata-only exact app-detail read proves the intended scope; fail on multiple matches, development-row collisions, missing scope proof, or scope mismatch. Create only when there are zero exact-slug rows. First prove that lowercasing the config `title`, replacing non-alphanumeric runs with `-`, and trimming hyphens yields the config `name`. For personal scope, run `npx --package @notis_ai/cli@latest -- notis apps create "<canonical-config-title>" . --json` exactly once and verify the returned id, remote slug, edit permission, and personal scope. For explicitly requested team scope, use `notis tools search` to discover the team-capable app-creation tool, inspect its schema, dry-run it, then execute `LOCAL_NOTIS_CREATE_APP` exactly once with the canonical display title, `visibility: "team"`, and the exact current `team_id` when resolved. Verify the result's id, canonical slug, `team_id`, team visibility, and edit permission, then run `notis apps link <returned-id> .` before database reconciliation. Never retry an outcome-unknown create; reconcile read-only and stop on ambiguity or any returned identity/scope mismatch.
167
- 8. **Reconcile hosted database schemas safely.** Read the exact app detail and current schemas first; mutate only missing or changed declarations. For creation, pass the exact app id in the database tool's `app` argument. For an update, resolve the exact `database_id`, verify its `owner_app_id` equals the linked app id, update by that `database_id`, then read back slug, owner, and schema. Apply only backward-compatible schema expansion before deployment. Stage breaking or destructive changes through an expand-contract sequence and obtain the required destructive approval; never make the currently deployed bundle incompatible before its replacement is live.
168
- 9. **Deploy and prove the hosted sandbox result.** Use only the exact id established in step 7. Run `npx --package @notis_ai/cli@latest -- notis apps deploy`, read the matching row back with `npx --package @notis_ai/cli@latest -- notis apps list --json`, confirm its id and deployed version, then run `npx --yes --package @notis_ai/cli@latest --package agent-browser@latest -- notis apps verify --mode live`. Return that row's exact profile-appropriate `portal_url` only after every proof passes. Report state precisely: a definite pre-commit rejection is **tested but not deployed**; a timeout/network/incomplete mutation response is **tested, deployment outcome unknown**; a confirmed deploy followed by failed readback is **deployed but not remotely verified**; a failed live check is **deployed but live verification failed**. Never retry an outcome-unknown mutation.
169
- 10. **Submit only after confirmation.** When the user explicitly confirms the current App Details page is ready, ensure the approved state is deployed, then run `npx --package @notis_ai/cli@latest -- notis apps publish --confirm-ready`. The command submits Team apps immediately or opens the Public Store registry review PR. Without that confirmation, stop after deploy.
170
-
171
- ### Quick start
172
-
173
- Choose the local or hosted finish after verification. Local deployment is
174
- user-gated; hosted-sandbox deployment is the default for app create or edit
175
- tasks unless the user explicitly requests preview-only, read-only, or no deploy.
176
-
177
- ```bash
178
- # 1. Pick a published Store app as the scaffold (catalog comes from the public registry)
179
- # The project lands in ~/.notis/apps/<slug>; append a directory argument when
180
- # the user wants the app in a repo they track.
181
- npx --package @notis_ai/cli@latest -- notis apps scaffolds list
182
- npx --package @notis_ai/cli@latest -- notis apps init "My App" --from <slug>
183
- cd ~/.notis/apps/my-app
184
- npm install
185
-
186
- # 2. LOCAL COMPUTER: register with Desktop and iterate. After the user approves
187
- # deployment, run deploy directly to promote the existing dev_app_id.
188
- npx --package @notis_ai/cli@latest -- notis apps dev
189
- # ... user tests the DEV-badged app ...
190
- npx --package @notis_ai/cli@latest -- notis apps build
191
- npx --package @notis_ai/cli@latest -- notis apps screenshot
192
- npx --package @notis_ai/cli@latest -- notis apps verify
193
- npx --package @notis_ai/cli@latest -- notis apps deploy
194
- ```
195
-
196
- Hosted sandbox finish — never run `apps dev`. Test first, then reconcile the
197
- exact app identity and changed databases before deployment:
198
-
199
- ```bash
200
- npm exec --yes --package agent-browser@latest -- agent-browser install
201
- npx --yes --package @notis_ai/cli@latest --package agent-browser@latest -- notis apps screenshot
202
- npx --package @notis_ai/cli@latest -- notis apps build
203
- npx --yes --package @notis_ai/cli@latest --package agent-browser@latest -- notis apps verify
204
- # New unlinked app only: reconcile exact canonical slug with apps list. If no
205
- # match exists, create once with the config title and verify the returned slug.
206
- npx --package @notis_ai/cli@latest -- notis apps list --json
207
- # Before create, prove canonicalize(config.title) == config.name.
208
- npx --package @notis_ai/cli@latest -- notis apps create "<canonical-config-title>" . --json
209
- # Compare schemas, then create only missing databases or update changed ones by
210
- # verified database_id and read back owner/schema before continuing.
211
- npx --package @notis_ai/cli@latest -- notis apps deploy
212
- npx --package @notis_ai/cli@latest -- notis apps list --json
213
- npx --yes --package @notis_ai/cli@latest --package agent-browser@latest -- notis apps verify --mode live
214
- ```
215
-
216
- Store submission on either path remains a separate approval-gated action:
217
-
218
- ```bash
219
- npx --package @notis_ai/cli@latest -- notis apps publish --confirm-ready
220
- ```
221
-
222
- For an existing app, link the checkout first so every later command uses the
223
- same profile-scoped installed identity:
224
-
225
- ```bash
226
- npx --package @notis_ai/cli@latest -- notis apps link <app-id> .
227
- npx --package @notis_ai/cli@latest -- notis apps deploy
228
- ```
229
-
230
- Or if editing an installed app locally:
231
-
232
- ```bash
233
- npx --package @notis_ai/cli@latest -- notis apps list
234
- npx --package @notis_ai/cli@latest -- notis apps pull <installed-app-id>
235
- cd ~/.notis/apps/my-app
236
- npm install
237
- # Increment package.json notisAppVersion above the pulled online release.
238
- npx --package @notis_ai/cli@latest -- notis apps dev
239
- npx --package @notis_ai/cli@latest -- notis apps build
240
- npx --package @notis_ai/cli@latest -- notis apps verify
241
- npx --package @notis_ai/cli@latest -- notis apps link <installed-app-id> .
242
- # Only after the user explicitly asks to deploy:
243
- npx --package @notis_ai/cli@latest -- notis apps deploy
244
- ```
245
-
246
- In a hosted sandbox, use the same pull/build/verify sequence but omit `apps
247
- dev`; bootstrap `agent-browser`, materialize any new or changed database schemas
248
- against the exact linked id, deploy automatically after verification, read back
249
- the exact app id/version and `portal_url` with `apps list --json`, run live verification, and return that
250
- exact Portal URL. Never run `apps publish --confirm-ready` without separate
251
- Store approval.
162
+ Follow **Release-only delivery** above. Use `apps scaffolds list` to discover public Store starting
163
+ points, `apps init` to scaffold locally, and `apps pull` for existing source. App file operations go
164
+ through the CLI, never raw storage/database writes. Run all Notis commands through NPX.
252
165
 
253
166
  ## Building an App
254
167
 
@@ -290,7 +203,7 @@ For arbitrary app-owned resources that are not Notis collection rows, set `resou
290
203
 
291
204
  ### Step 2: Build pages
292
205
 
293
- Standard React pages in `app/`. Use generic SDK tool hooks for data and build on top of the scaffolded shadcn components and portal shell classes (`notis-app-shell`, `notis-app-surface`):
206
+ Standard React pages in `app/`. Use generic SDK tool hooks for data and build on top of the scaffolded flat components and portal shell classes (`notis-app-shell` for ordinary pages, `notis-app-split` for list-plus-detail pages, `notis-app-surface` for a flat panel):
294
207
 
295
208
  ```tsx
296
209
  import { useDocuments, ViewSkeleton } from '@notis/sdk';
@@ -361,22 +274,37 @@ await upsertTask.call({
361
274
 
362
275
  Do NOT pass Notion-style wrappers (`{select: {name: "Todo"}}`) when upserting.
363
276
 
364
- ### Design rules
277
+ ### Design bar (enforced)
278
+
279
+ Every page must read as a native, flat Notis page. `npx --package @notis_ai/cli@latest -- notis apps build` and the deploy endpoint fail on the banned patterns below with the exact file and line; the only override is an inline `// notis-design-allow: <rule-id> <reason>` comment on the line before (reason required, at least 12 characters). Do not work around a failure by moving the markup elsewhere; fix it.
280
+
281
+ Banned in `app/` and `components/` (form controls in `components/ui/{input,textarea,checkbox,switch,button}.tsx` are exempt):
282
+
283
+ - Four-side `border` boxes, `border-dashed`, `divide-*`, `<hr>`, thick `border-l-2` bars, `ring-*` as a box or selection indicator (`focus-visible:ring-2` on controls is fine).
284
+ - `shadow-*` on panels, tiles, rows, or bubbles. Only a floating popover or menu may use `shadow-lg` together with `bg-popover`.
285
+ - Tailwind palette hues (`emerald-500`, `slate-200`, ...), hex colors, gradients, `backdrop-blur`, `font-serif`.
286
+ - Uppercase `tracking-wide` eyebrows and marketing headlines. Page titles are plain nouns matching the route ("Dashboard", "Meetings").
287
+ - Text below 12px (`text-[11px]`); use `text-xs` at minimum and `text-sm` for body.
288
+ - `Badge variant="outline"`, raw `<select>`, in-app search inputs, duplicate sidebars, untouched scaffold placeholder copy.
289
+ - Loading text ("Loading...") or whole-page spinners. Keep headings visible and render `Skeleton` / `ViewSkeleton` from `@notis/sdk` only in the missing region (see the Instant-view contract).
290
+
291
+ Use instead:
365
292
 
366
- - Start from the scaffolded `@/components/ui/*` components before writing new UI primitives.
367
- - Use restrained portal surfaces: `bg-background`, `bg-card`, `border-border`, `text-foreground`, `text-muted-foreground`.
368
- - Keep layouts compact and dashboard-like. Prefer cards, sections, badges, and tables over marketing-style hero treatments.
369
- - Respect the portal theme. Do not hardcode dark mode or create an app-specific palette.
370
- - If a screen looks like a standalone microsite instead of a portal tool, it is too custom.
293
+ - `Card` from the scaffold: a flat `bg-muted` panel that becomes `bg-background` when nested. Page sections can also be plain `h2` + content with `space-y-8`.
294
+ - `.list-row` / `.list-row-selected` from `@notis/sdk/styles.css` for rows and table bodies (tinted on mobile, transparent with hover tint on desktop, selection by tint). Tables are flat on the page: `text-xs` muted header, `text-sm` rows, no wrapping panel.
295
+ - Stats as bare figures: `text-xs` label over `text-2xl font-semibold tabular-nums`. Tiles (`rounded-2xl bg-muted p-5`) only when they are the page's single grouping device.
296
+ - `PageHeading` for the header, `NativeSelect` for filters, `Badge` variants `default | secondary | destructive`, tokens only (`text-foreground`, `text-muted-foreground`, `text-primary`, `bg-primary/10`, `text-destructive`, `bg-destructive/10`), `tabular-nums` on numbers, `min-w-0` on every grid item that can hold long text.
297
+ - One hairline (`border-t` / `border-b border-border`) between major sections or large list entries is the only allowed line.
298
+ - List-plus-detail pages are full-bleed: `notis-app-split` with `notis-app-pane-list` (tinted, one `border-r` hairline, fixed width on desktop, stacked on mobile) and `notis-app-pane-detail` (`bg-background`), never the centered `notis-app-shell`.
299
+ - Respect the portal theme in both modes. Never hardcode dark mode or an app palette.
371
300
  - For Notes-style apps, the folder tree belongs to the portal sidebar when configured via `collection.sidebar`. The page content should complement that chrome, not duplicate or replace it.
372
- - Never indicate selected items with a heavy left-border bar (e.g. `border-l-2 border-l-foreground` paired with a muted background). It looks dated and clashes with the portal chrome. Use a single subtle background change (`bg-muted` for selected, `hover:bg-muted/50` for hover) and let typography or an icon carry the rest of the state.
373
301
  - Do not render any search input inside the app (in-page search rails, "Ask Notis…" pills, command-palette-style bars, etc.). The portal already owns the top-bar search field. Wire your view to it with `useTopBarSearch({ value, onChange, placeholder, onSubmit })` from `@notis/sdk` and let the page filter or refetch on the values it receives. Use its `setLoading` only for an explicit submitted search, never initial view loading or background refresh.
374
302
 
375
303
  ### Sidebar invariants
376
304
 
377
305
  - When a user asks for folders, sections, or hierarchy in the app sidebar, express that through `routes` and `collection.sidebar` in `notis.config.ts`.
378
306
  - Treat an existing collection-tree sidebar as a locked structural requirement unless the user explicitly asks to change navigation architecture.
379
- - If the sidebar appears missing for the substituted DEV entry or deployed portal build, do not silently redesign around it. Preserve the manifest contract, call out the discrepancy, and treat it as a portal/runtime bug.
307
+ - If the sidebar appears missing for the installed app, do not silently redesign around it. Preserve the manifest contract, call out the discrepancy, and treat it as a portal/runtime bug.
380
308
 
381
309
  ### Step 3: Root layout
382
310
 
@@ -454,7 +382,7 @@ For a read-only database catalog app, declare `["LOCAL_NOTIS_DATABASE_LIST_DATAB
454
382
  Every row in the `databases` table carries `owner_app_id` (uuid FK to
455
383
  `apps.id`, `ON DELETE CASCADE`): a database belongs to exactly one app, and
456
384
  deleting the app deletes its databases and their documents (`documents` cascade
457
- from `databases`). Install, dev materialization, and store updates stamp
385
+ from `databases`). Install, resource preparation, and Store updates stamp
458
386
  `owner_app_id` automatically; standalone creation requires the `app` argument.
459
387
 
460
388
  ### Storage (Supabase)
@@ -507,7 +435,7 @@ already installs `ShortcutProvider`; app code should not add a second provider.
507
435
  | `useNotisNavigation()` | `() => { toRoute, toDocument, toApp }` | Navigate between routes (including `toRoute(path, { resourceId })`), documents, or the app root |
508
436
  | `useTopBarSearch(opts)` | `({ value, onChange, placeholder?, onSubmit? }) => { setLoading }` | Bind the current view to the Portal-owned top-bar search input |
509
437
  | `useBackend()` | `() => { request }` | Raw backend request proxy with JWT auth |
510
- | `useDatabaseSubscription(slug, opts?)` | `(slug: string, opts?) => { rows, documents, loading, error, refetch, live }` | Query a database and refetch it when its rows change. `live` is false on hosts without a change feed (dev harness, vite preview) -- keep a manual refresh for those |
438
+ | `useDatabaseSubscription(slug, opts?)` | `(slug: string, opts?) => { rows, documents, loading, error, refetch, live }` | Query a database and refetch it when its rows change. `live` is false on hosts without a change feed (temporary test harness, vite preview) -- keep a manual refresh for those |
511
439
  | `useHandover()` | `() => { handover, pending, error, available }` | Open manager chat with app/resource context plus an optional starter prompt or declared skill. Omit `prompt` for a context-only composer. `available` is false on hosts with no chat -- fall back to a copyable prompt |
512
440
  | `useCloudComputer()` | `() => { facts, loading, error, refresh }` | Read-only cloud computer facts: sandbox existence/status and whether the GitHub CLI is signed in. Requires `capabilities.cloudComputer: 'read'` plus the user's approval; `facts.available === false` means answer from the app's own fallback |
513
441
  | `useActiveResource(resource)` | `(ContextResource \| null) => void` | Publish the record currently open in the app so manager handover and context menus stay grounded |
@@ -529,9 +457,6 @@ editor should accept media or file blocks.
529
457
 
530
458
  ### App configuration additions
531
459
 
532
- - `devSlug` is the stable local-development identity. Set it when a display
533
- rename must not create a second DEV app; otherwise the CLI derives it from
534
- `name`.
535
460
  - `toolBindings` is only for provider-generated public tool names whose upstream
536
461
  action cannot be reconstructed. Keep the exact final public `name` in
537
462
  `tools`, then bind it to `providerToolName`; the public name remains the
@@ -558,32 +483,23 @@ const result = await queryTasks.call({ database_id: 'tasks-db-id', query: { page
558
483
  // result.documents[0].properties.Status is typed as string
559
484
  ```
560
485
 
561
- ## Development Modes
562
-
563
- ### Hosted sandbox development
564
-
565
- Do not run `apps dev` in a hosted sandbox. The sandbox filesystem is not on the
566
- user's computer, so Desktop cannot mount it. Bootstrap sandbox `agent-browser`,
567
- then build and verify before any remote mutation. For a create or edit request,
568
- unless the user explicitly says preview-only, read-only, or no-deploy, resolve
569
- one exact identity, safely materialize only missing or changed database schemas,
570
- deploy, read back the exact app id/version, run live verification, and return
571
- the exact Portal URL. Inspection, review, and diagnosis remain read-only.
572
-
573
- ### Canonical local development
574
-
575
- ```bash
576
- npx --package @notis_ai/cli@latest -- notis apps dev
577
- ```
486
+ ## Testing
578
487
 
579
- Runs the real desktop-local development workflow. The CLI should discover all apps in the target workspace and serve their bundles from loopback. Unpublished apps appear in the Electron Portal's Workspace group; linked apps substitute their installed entry only when the local `notisAppVersion` is strictly greater than the installed `release_version`.
488
+ Build and automated stub verification precede release. `verify` and `screenshot` start temporary,
489
+ explicit test servers only: no folder discovery, watchers, Desktop registration, persistent roots,
490
+ consumer leases or Workspace mounting. They close server/browser resources at completion or interruption.
491
+ Build also enforces the design bar and refreshes the app's embedded SDK copy. Automated verification
492
+ checks every route at desktop (1280px) and phone (390px) widths, including nested boxes, text below
493
+ 12px, lingering loading placeholders and horizontal overflow. Standalone verification writes a
494
+ local diagnostic report; deploy always verifies its own frozen snapshot, with no stamp or environment bypass.
495
+ After release, verify live runtime integration and open the installed bundle in Portal. Source edits
496
+ and Desktop restarts cannot change the running version.
580
497
 
581
- ## Testing
498
+ ### Screenshots
582
499
 
583
- 1. **Build validation**: `npx --package @notis_ai/cli@latest -- notis apps build` must succeed without errors. Vite surfaces TypeScript and bundling errors during this step.
584
- 2. **Headless render verification** (recommended after every build): run `npx --package @notis_ai/cli@latest -- notis apps verify` locally. In a hosted sandbox, first run `npm exec --yes --package agent-browser@latest -- agent-browser install`, then run `npx --yes --package @notis_ai/cli@latest --package agent-browser@latest -- notis apps verify`. It builds unless `--skip-build` is passed, spins up a loopback harness, drives `agent-browser` against every route, and reports per-route pass/fail with captured render errors and runtime calls.
585
- 3. **Local development acceptance**: Run `notis apps dev [folder]` once to register the root, then verify each signed-in Desktop instance independently. For an unpublished app, expect one DEV-badged Workspace row. For a linked app, first confirm local `notisAppVersion` is strictly greater than installed `release_version`, then expect one substituted DEV-badged row; equal or lower must keep the online row and bundle. Verify the default route renders and live edits appear without restarting the CLI or Desktop. Use `notis apps roots list` as the persistence proof. Loopback bundle health alone does not prove that an authenticated instance mounted or rendered the app.
586
- 4. **Post-deploy**: Read back the exact app id, version, and `portal_url` with `apps list --json`, run `apps verify --mode live`, verify the deployed bundle via `/portal_views/get` -> `runtime_descriptor.bundle.js_url`, and return that profile-appropriate exact Portal URL. A confirmed deploy followed by failed readback is deployed but not remotely verified; a failed live check is deployed but live verification failed. The portal renders app bundles directly as React components, so navigate to the app page when an authenticated browser is available.
500
+ `apps screenshot` supports declared screenshot scenarios and stub fixtures. A scenario can set
501
+ `theme: 'dark'`; use `--raw` for uncomposited captures. Store screenshots and listing readiness
502
+ are required only for Publish to Store, never for Update app.
587
503
 
588
504
  ### Headless harness verification
589
505
 
@@ -600,17 +516,15 @@ Run `npx --package @notis_ai/cli@latest -- notis apps verify` after `npx --packa
600
516
  #### What the harness does not catch
601
517
 
602
518
  - Bugs that only manifest with real backend data (auth-scoped filters, RLS, malformed prod records). For those, swap the stub runtime for a real one that posts to `/portal_views/runtime_query` with a JWT.
603
- - Visual regressions (use `agent-browser screenshot` + a baseline compare if you need this).
519
+ - Pixel-level visual regressions beyond the automated design checks (the harness does flag nested boxes, sub-12px text, lingering loading placeholders, and horizontal overflow at 390px). For anything else, use `agent-browser screenshot` + a baseline compare.
604
520
  - Bugs that depend on the portal's shadow-DOM stylesheet wrapping. The harness mounts in light DOM, so global Tailwind/shadcn classes work normally; portal-specific theme tokens injected as inline styles are not present.
605
521
 
606
522
  ## Troubleshooting
607
523
 
608
524
  ### Common issues
609
525
 
610
- - **Deploy fails with network error**: Run `notis doctor`, then retry with the
611
- authenticated API available. Do not bypass DEV-app promotion or installed-app
612
- identity with a direct database/storage write.
526
+ - **Deploy transport failure**: Run `notis doctor` and read back the exact app/version. Never blindly retry an unknown outcome or write directly to storage.
613
527
  - **App shows old code after deploy**: Bundle cache is stale. Hard refresh (Cmd+Shift+R) or clear site data in DevTools.
614
- - **App is missing from the sidebar**: Run `apps roots list`, confirm the app is at the root, one direct child, or `apps/*`, and confirm its first build succeeds. For a linked app, compare `package.json` `notisAppVersion` with the installed manifest's `release_version`: equal, lower, missing, or invalid intentionally keeps the online app without a DEV badge. If source is stale, preserve any local edits, run `apps pull <app-id> <dir> --force` to refresh it, then increment `notisAppVersion` before continuing development. Restarting Desktop reattaches the same persistent roots; no terminal process or manual sidebar action is required.
528
+ - **App is missing from Workspace**: Inspect its exact installed version. Unreleased containers have no runnable routes. A successful release appears through ordinary refresh/navigation.
615
529
  - **`LOCAL_NOTIS_DATABASE_QUERY` returns empty documents**: Check that the database ID passed to the tool matches the intended database. Use `npx --package @notis_ai/cli@latest -- notis tools exec LOCAL_NOTIS_DATABASE_LIST_DATABASES --arguments '{}'` to verify the ID; use the database slug only as a fallback.
616
530
  - **Properties are `undefined`**: Keep app-local result types for `useTool<TArgs, TResult>` and guard optional nested properties when reading live data.