caspian-utils 0.0.22 → 0.0.23

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.
@@ -0,0 +1,77 @@
1
+ ---
2
+ title: AI Validation Checklist
3
+ description: Use this page when testing whether AI can find the right Caspian docs, core files, and verification checkpoints for a task after documentation or runtime changes.
4
+ related:
5
+ title: Related docs
6
+ description: Start with the manifest, use the runtime map when needed, then validate against the matching feature guide and owning runtime behavior.
7
+ links:
8
+ - /docs/index
9
+ - /docs/core-runtime-map
10
+ - /docs/routing
11
+ - /docs/auth
12
+ - /docs/fetch-data
13
+ - /docs/pulsepoint
14
+ ---
15
+
16
+ This page gives AI a small validation workflow for checking whether the packaged Caspian docs lead to the correct source files and runtime behaviors.
17
+
18
+ Use it after changing packaged docs, after changing `main.py` or installed `casp` internals, or when you want to verify that AI can resolve a task without drifting across unrelated files.
19
+
20
+ ## Goal
21
+
22
+ The goal is not to restate feature docs. The goal is to verify four things quickly:
23
+
24
+ - AI starts from the correct manifest and feature gate.
25
+ - AI reaches the correct packaged feature doc.
26
+ - AI reaches the controlling app or runtime file.
27
+ - AI reaches a concrete behavior checkpoint before making assumptions about behavior.
28
+
29
+ ## Fast Workflow
30
+
31
+ 1. Read [index.md](./index.md) to discover the local Caspian docs.
32
+ 2. Read `./caspian.config.json` before assuming any optional feature is enabled.
33
+ 3. If the task touches `main.py` or `.venv/Lib/site-packages/casp/**`, read [core-runtime-map.md](./core-runtime-map.md).
34
+ 4. Read the matching feature doc such as [routing.md](./routing.md), [auth.md](./auth.md), [fetch-data.md](./fetch-data.md), [pulsepoint.md](./pulsepoint.md), or [file-uploads.md](./file-uploads.md).
35
+ 5. Verify the claim against the actual project code and the installed runtime.
36
+ 6. Confirm the relevant behavior in the owning files surfaced by [core-runtime-map.md](./core-runtime-map.md) before treating the behavior as settled.
37
+
38
+ ## Pass Criteria
39
+
40
+ A documentation path is in good shape when AI can do all of these without broad repo wandering:
41
+
42
+ - identify whether the feature is enabled from `caspian.config.json`
43
+ - choose the correct packaged doc from [index.md](./index.md)
44
+ - jump to the controlling runtime file through [core-runtime-map.md](./core-runtime-map.md) when needed
45
+ - distinguish app-owned files from installed framework internals
46
+ - identify the concrete behavior that must be verified in the owning file
47
+ - avoid suggesting changes to `main.py` or `.venv/Lib/site-packages/casp/**` unless the task truly requires runtime edits
48
+
49
+ ## Representative Prompt Suite
50
+
51
+ Use prompts like these to check whether AI lands on the correct docs and files.
52
+
53
+ | Prompt | First docs AI should read | Controlling files AI should reach | Verification focus |
54
+ | --- | --- | --- | --- |
55
+ | Create a protected dashboard section with child routes and a shared shell. | [index.md](./index.md), [routing.md](./routing.md), [auth.md](./auth.md), [project-structure.md](./project-structure.md) | `src/app/**`, `src/lib/auth/auth_config.py`, `main.py`, `.venv/Lib/site-packages/casp/layout.py` | section layout ownership, route privacy mode, and child-route wrapping |
56
+ | Add a file manager page with upload progress and persisted metadata. | [index.md](./index.md), [file-uploads.md](./file-uploads.md), [fetch-data.md](./fetch-data.md), [database.md](./database.md) | `src/app/**/index.html`, `src/app/**/index.py`, `src/lib/**`, `prisma/schema.prisma` when Prisma applies | route-owned upload actions, persisted metadata flow, and Prisma-backed storage boundaries |
57
+ | Explain why authored Caspian templates use a plain `<script>` instead of `type="text/pp"`. | [index.md](./index.md), [pulsepoint.md](./pulsepoint.md), [components.md](./components.md), [routing.md](./routing.md) | `main.py`, `.venv/Lib/site-packages/casp/scripts_type.py`, `.venv/Lib/site-packages/casp/components_compiler.py` | script rewriting, `pp-component` injection, and authored-vs-runtime boundaries |
58
+ | Debug why `StateManager` does not persist across a full redirect. | [index.md](./index.md), [state.md](./state.md), [auth.md](./auth.md), [core-runtime-map.md](./core-runtime-map.md) | `main.py`, `.venv/Lib/site-packages/casp/state_manager.py` | wire vs non-wire reset behavior and `request.state.session` dependency |
59
+ | Trace the auth cookie and CSRF cookie names used in development. | [index.md](./index.md), [auth.md](./auth.md), [core-runtime-map.md](./core-runtime-map.md) | `main.py`, `.venv/Lib/site-packages/casp/auth.py` | development cookie scoping and CSRF or session naming ownership |
60
+ | Find how route params and query params reach `page()`. | [index.md](./index.md), [routing.md](./routing.md), [core-runtime-map.md](./core-runtime-map.md) | `main.py`, `.venv/Lib/site-packages/casp/layout.py` | path-dict delivery, query coercion, and `request` injection |
61
+ | Decide whether a dashboard child route should use page caching. | [index.md](./index.md), [cache.md](./cache.md), [routing.md](./routing.md), [fetch-data.md](./fetch-data.md) | `main.py`, `.venv/Lib/site-packages/casp/cache_handler.py`, the route's `index.py` | route-level cache ownership, cacheable HTML scope, and invalidation points |
62
+
63
+ Treat the table as a prompt pack for spot checks, not as a full validation matrix.
64
+
65
+ ## Common Failure Modes
66
+
67
+ - AI skips `caspian.config.json` and assumes an optional feature is enabled because a packaged doc exists.
68
+ - AI reads only the packaged feature doc and never checks `main.py` or the installed runtime.
69
+ - AI edits framework internals when the task only requires app-owned route or helper changes.
70
+ - AI treats runtime HTML examples as authored template examples.
71
+ - AI decides behavior from memory without checking the owning implementation details.
72
+
73
+ ## Decision Rule
74
+
75
+ - If the task is feature-oriented, start from [index.md](./index.md) and the matching feature doc.
76
+ - If the task is behavior-oriented and the owner is unclear, jump to [core-runtime-map.md](./core-runtime-map.md).
77
+ - If the task is still ambiguous after that, verify the controlling file and the specific behavior in scope before widening the search.
package/dist/docs/auth.md CHANGED
@@ -5,6 +5,7 @@ related:
5
5
  title: Related docs
6
6
  description: Use the fetch-data guide when sign-in or signout happens through RPC, then use the state guide for transient auth-adjacent request state, validation to guard credentials, and routing or project structure to place auth files correctly.
7
7
  links:
8
+ - /docs/core-runtime-map
8
9
  - /docs/fetch-data
9
10
  - /docs/state
10
11
  - /docs/validation
@@ -63,6 +64,7 @@ from casp.auth import (
63
64
  - The installed framework implementation lives in `.venv/Lib/site-packages/casp/auth.py`.
64
65
  - Treat `auth_config.py` as project code and `casp/auth.py` as framework code.
65
66
  - If upstream docs and the installed implementation disagree, prefer the installed implementation for local project guidance.
67
+ - Use [core-runtime-map.md](./core-runtime-map.md) when an auth task crosses `main.py` bootstrap behavior such as development cookie scoping or middleware ownership.
66
68
 
67
69
  ## Centralized Auth Settings
68
70
 
@@ -167,6 +169,7 @@ The installed auth code reads several values from `.env` when explicit values ar
167
169
  - `AUTH_COOKIE_NAME` backs `AuthSettings.cookie_name`.
168
170
  - `SESSION_LIFETIME_HOURS` controls `SessionMiddleware.max_age` in the current `main.py` bootstrap.
169
171
  - `APP_ENV=production` enables secure session cookies and the `Secure` flag on the current CSRF cookie.
172
+ - `CASPIAN_BROWSER_SYNC_PORT` can override the development cookie scope suffix used by the current `main.py` bootstrap.
170
173
  - `GOOGLE_CLIENT_ID`, `GOOGLE_CLIENT_SECRET`, and `GOOGLE_REDIRECT_URI` back `GoogleProvider`.
171
174
  - `GITHUB_CLIENT_ID` and `GITHUB_CLIENT_SECRET` back `GithubProvider`.
172
175
 
@@ -174,6 +177,8 @@ Keep these secrets out of route files and out of committed source.
174
177
 
175
178
  In the current bootstrap, set `AUTH_COOKIE_NAME` explicitly in `.env`. The pasted `main.py` uses `session` as the fallback `SessionMiddleware` cookie name, while `AuthSettings` falls back to `auth_token` when the env var is absent.
176
179
 
180
+ In development, the current `main.py` does not always use those base cookie names directly. `_scoped_cookie_name(...)` appends the active BrowserSync or dev port to both the session cookie name and the CSRF cookie name when `APP_ENV` is not `production`. The scope is resolved from `CASPIAN_BROWSER_SYNC_PORT`, then from `settings/bs-config.json`, then from `PORT`. That means a local stack can emit names such as `session_5091` and `pp_csrf_5091` instead of the unsuffixed production names.
181
+
177
182
  ## Startup Wiring
178
183
 
179
184
  Apply the centralized settings once during startup, before normal request handling begins.
@@ -279,7 +284,7 @@ Because Starlette runs the last-added middleware first, the effective request or
279
284
  Current behavior by layer:
280
285
 
281
286
  - `SessionMiddleware` provides `request.session` for the rest of the stack.
282
- - `CSRFMiddleware` ensures `request.session["csrf_token"]` exists and emits a `pp_csrf` cookie.
287
+ - `CSRFMiddleware` ensures `request.session["csrf_token"]` exists and emits a scoped CSRF cookie based on `pp_csrf`, for example `pp_csrf_5091` in development or plain `pp_csrf` when no dev scope is active.
283
288
  - `AuthMiddleware` sets request context with `Auth.set_request(request)`, initializes `StateManager`, runs provider callbacks, skips configured static asset paths, and enforces public, auth, private, and role-based route redirects.
284
289
  - `RPCMiddleware` handles `POST` requests with `X-PP-RPC: true` and forwards them to Caspian's RPC handler after auth and session setup are already available.
285
290
 
@@ -449,6 +454,7 @@ A typical credential-based auth setup often uses this route shape:
449
454
  src/
450
455
  app/
451
456
  (auth)/
457
+ layout.html
452
458
  signin/
453
459
  index.py
454
460
  index.html
@@ -458,12 +464,17 @@ src/
458
464
  signout/
459
465
  index.py
460
466
  dashboard/
461
- index.py
462
467
  index.html
468
+ layout.html
469
+ settings/
470
+ index.py
471
+ index.html
463
472
  ```
464
473
 
465
474
  Use `guest_only()` on signin and signup routes, use `auth.sign_in(...)` inside the owning RPC action after validation and credential checks, prefer RPC for signout UI actions, and protect private destinations with `require_auth()` or central route policy.
466
475
 
476
+ When auth pages share a wrapper without adding a URL segment, use a route group such as `(auth)/layout.html`. When a protected area such as `dashboard` owns multiple child routes, apply the section layout pattern from [routing.md](./routing.md): put the shared shell in `dashboard/layout.html` and place child routes such as `dashboard/settings/index.html` beneath it.
477
+
467
478
  Preferred signout pattern: auth-protected RPC from a page or component.
468
479
 
469
480
  Use this when the logout trigger lives in a page, layout, header, dropdown, or reusable component. The HTML can live at page level or component level.
@@ -531,6 +542,24 @@ def page():
531
542
  })
532
543
  ```
533
544
 
545
+ If the protected destination is a section rather than a single page, prefer a folder-level layout for the shared shell.
546
+
547
+ ```text
548
+ src/
549
+ app/
550
+ dashboard/
551
+ layout.html
552
+ index.html
553
+ settings/
554
+ index.py
555
+ index.html
556
+ billing/
557
+ index.py
558
+ index.html
559
+ ```
560
+
561
+ In that pattern, `dashboard/layout.html` owns the shared sidebar, header, and frame, while each child route decides whether it needs its own `index.py` for auth checks, metadata, RPC actions, or server-side data.
562
+
534
563
  For signup flows, use the same route ownership pattern as signin: validate the input, create the user, build a safe payload without password fields, and then call `auth.sign_in(...)` with the redirect target you want.
535
564
 
536
565
  Signup example:
@@ -765,6 +794,7 @@ If an AI agent is deciding how to handle auth in Caspian, apply these rules firs
765
794
  - Decide route mode early in `src/lib/auth/auth_config.py`: use `is_all_routes_private=True` when most routes should require auth, otherwise keep `is_all_routes_private=False` and list protected routes in `private_routes`.
766
795
  - In app-owned starter config, treat `is_all_routes_private=False` as the default starting point, which means routes begin public until the app opts into stricter protection.
767
796
  - Treat `public_routes` as the public exception list for all-private apps. In the current defaults, `/` stays public and `auth_routes=["/signin", "/signup"]` stays public too.
797
+ - For protected grouped sections, follow the section layout pattern in [routing.md](./routing.md) and keep auth-specific route policy in `src/lib/auth/auth_config.py`.
768
798
  - Apply settings at startup in `main.py` with `configure_auth(build_auth_settings())`.
769
799
  - Register provider instances in `main.py` with `Auth.set_providers(...)` when Google or GitHub OAuth is enabled.
770
800
  - Use `auth.sign_in(...)` and `auth.sign_out(...)` for session lifecycle changes.
@@ -5,6 +5,7 @@ related:
5
5
  title: Related docs
6
6
  description: Use the routing guide to place route-level cache declarations correctly, then pair cache with fetch-data when deciding what can be safely served from reused HTML.
7
7
  links:
8
+ - /docs/core-runtime-map
8
9
  - /docs/routing
9
10
  - /docs/fetch-data
10
11
  - /docs/project-structure
@@ -27,6 +28,8 @@ from casp.cache_handler import Cache, CacheHandler
27
28
 
28
29
  The current installed implementation lives in `.venv/Lib/site-packages/casp/cache_handler.py`.
29
30
 
31
+ Use [core-runtime-map.md](./core-runtime-map.md) when a cache task crosses `main.py` request flow, cache lookup timing, and the installed cache-handler internals.
32
+
30
33
  The real API surface is:
31
34
 
32
35
  - declarative route config with `Cache(ttl=..., enabled=...)`
@@ -41,7 +44,8 @@ Use caching when a route is expensive to render but the final HTML is stable eno
41
44
 
42
45
  - Use `Cache(...)` at module scope in a route's `index.py` when you want that route to participate in Caspian's page cache.
43
46
  - Cache public HTML that is safe to share between visitors.
44
- - Avoid caching dashboards, auth callbacks, request-specific error states, flash-message flows, or any page whose HTML changes per user.
47
+ - Avoid caching personalized section shells such as authenticated dashboards, auth callbacks, request-specific error states, flash-message flows, or any page whose HTML changes per user.
48
+ - If a dashboard, account area, admin area, or grouped subtree uses a parent `layout.html`, evaluate each child route separately. Public child pages may still be cacheable, but personalized section layouts generally are not.
45
49
  - Invalidate cached pages after writes, publishes, deletes, or admin actions that change what those routes should render.
46
50
  - Treat page caching as HTML reuse, not as a replacement for database caching or RPC response caching.
47
51
 
@@ -237,7 +241,7 @@ Good candidates for page caching are:
237
241
 
238
242
  Poor candidates are:
239
243
 
240
- - authenticated dashboards
244
+ - authenticated dashboards and other personalized section layouts
241
245
  - per-user feeds or notification views
242
246
  - forms whose server-rendered HTML includes request-specific or sensitive state
243
247
  - pages that need sub-second freshness unless you have reliable invalidation hooks
@@ -249,6 +253,7 @@ If an AI agent is deciding whether or how to cache a Caspian route, apply these
249
253
  - Use `Cache` from `casp.cache_handler` for route-level page caching.
250
254
  - Prefer `cache_settings = Cache(...)` at module scope over a bare call when writing maintained project code.
251
255
  - Use `enabled=False` for pages whose HTML is personalized, auth-sensitive, or too volatile to reuse safely.
256
+ - When a user asks about caching a dashboard, admin area, account area, or grouped subtree, check whether the shared `layout.html` is personalized before caching any child route. Shared authenticated shells are usually poor cache candidates even when some public child pages are cacheable.
252
257
  - Use `CacheHandler.invalidate_by_uri(...)` after writes that change cached public content.
253
258
  - Use `CacheHandler.reset_cache()` only for targeted maintenance, debugging, or full cache clears.
254
259
  - Use leading-slash URIs consistently when interacting with `CacheHandler`.
@@ -6,6 +6,7 @@ related:
6
6
  description: Use the structure guide for file placement, the routing guide for route templates, the PulsePoint guide for browser-side scripts, and the data guide for component-owned RPC flows.
7
7
  links:
8
8
  - /docs/project-structure
9
+ - /docs/core-runtime-map
9
10
  - /docs/routing
10
11
  - /docs/pulsepoint
11
12
  - /docs/fetch-data
@@ -29,6 +30,17 @@ As the app grows, treat `src/components/` as the default home for reusable appli
29
30
  - Use `render_html(...)` with a same-name `.html` file when the component has more markup, PulsePoint behavior, or clearer separation between Python logic and UI.
30
31
  - Keep page-level workflows in `src/app/`, move reusable UI into `src/components/`, and keep helpers, services, validators, and adapters in `src/lib/`.
31
32
 
33
+ ## Framework Internals Note
34
+
35
+ When the task is about component internals rather than normal app-owned components, these runtime files are the most relevant:
36
+
37
+ - `.venv/Lib/site-packages/casp/component_decorator.py` owns `@component`, `Component`, `render_html(...)`, and component loading.
38
+ - `.venv/Lib/site-packages/casp/components_compiler.py` owns `@import` parsing, `x-*` tag resolution, root validation, and `pp-component` injection.
39
+ - `.venv/Lib/site-packages/casp/html_attrs.py` owns `get_attributes(...)` and the Python-side `merge_classes(...)` contract.
40
+ - `.venv/Lib/site-packages/casp/syntax_compiler.py` owns Caspian template syntax transpilation before Jinja render.
41
+
42
+ Use [core-runtime-map.md](./core-runtime-map.md) when you need the broader runtime-module map.
43
+
32
44
  ## Basic Component
33
45
 
34
46
  This is the simplest pattern: accept props, assemble the final class value, and return HTML.
@@ -106,6 +118,8 @@ Place component imports at the top of the HTML template, above the authored root
106
118
 
107
119
  The import comment is the bridge between the Python component export and the `x-*` tag you author in HTML.
108
120
 
121
+ In section-based apps, follow the same mental model as the Next.js App Router: import shared shell components such as sidebars, topbars, breadcrumbs, and section frames in the parent folder's `layout.html`, then import page-specific components in each child route's `index.html`.
122
+
109
123
  Treat `<!-- @import ... -->` as a file-level directive, not as rendered markup. It does not count as the template root, and it should not be nested inside the root wrapper element.
110
124
 
111
125
  - `<!-- @import Container from "../components" -->` resolves `Container.py` from that folder.
@@ -208,62 +222,28 @@ This keeps the component easy to read: Python owns the server-side logic and tem
208
222
 
209
223
  ## Auto-Injected `pp-component` And PulsePoint Script Type
210
224
 
211
- Treat `pp-component="componentName"` and `type="text/pp"` as framework output, not authored source.
225
+ Treat `pp-component="componentName"` and `type="text/pp"` as framework output, not authored source. The canonical authored-vs-runtime explanation lives in [pulsepoint.md](./pulsepoint.md).
212
226
 
213
227
  - Do not manually add `pp-component="..."` to route templates, layout templates, or component HTML files.
214
228
  - Do not manually add `type="text/pp"` to authored PulsePoint scripts.
215
229
  - The Python render pipeline injects `pp-component` onto the single root element during render.
216
230
  - `main.py` runs `transform_scripts(...)`, which rewrites authored body `<script>` tags to `<script type="text/pp">` before the browser runtime mounts.
217
- - Add a plain `<script>` only when the route or component actually needs PulsePoint logic.
218
- - When you do add a PulsePoint script, keep it inside that single root element.
219
-
220
- Examples in runtime docs often show rendered HTML that already contains `pp-component` and `type="text/pp"`. That is the final output shape, not the normal authoring pattern.
221
-
222
- ### Authored Source vs Rendered Output
223
-
224
- Author this:
225
-
226
- `Counter.html`
227
-
228
- ```html
229
- <div>
230
- <h3>[[ label ]]</h3>
231
- <button onclick="setCount(count + 1)">
232
- {count}
233
- </button>
234
-
235
- <script>
236
- const [count, setCount] = pp.state(0);
237
- </script>
238
- </div>
239
- ```
240
-
241
- Do not author this by hand:
242
-
243
- ```html
244
- <div pp-component="counter_ab12cd34">
245
- <h3>Clicks</h3>
246
- <button onclick="setCount(count + 1)">0</button>
247
-
248
- <script type="text/pp">
249
- const [count, setCount] = pp.state(0);
250
- </script>
251
- </div>
252
- ```
253
-
254
- The second example is the runtime shape after the Python side injects `pp-component` onto the component root.
231
+ - Add a plain `<script>` only when the route or component actually needs PulsePoint logic, and keep it inside that single root element.
232
+ - If a runtime example already shows `pp-component` and `type="text/pp"`, treat it as rendered output rather than authored source.
255
233
 
256
234
  ## Single-Root Rule
257
235
 
258
- Every component HTML template must render exactly one top-level lowercase HTML element.
236
+ Every component HTML template must render exactly one authored top-level parent node.
259
237
 
260
- The same rule applies to route templates such as `src/app/**/index.html`: one root element, no sibling roots, and no manual `pp-component` authoring.
238
+ The same rule applies to route and layout templates such as `src/app/**/index.html` and `src/app/**/layout.html`. See [routing.md](./routing.md) and [pulsepoint.md](./pulsepoint.md) for the non-component versions of the same authoring contract.
261
239
 
262
240
  Top-of-file `<!-- @import ... -->` directives are allowed before that root element and do not violate the single-root rule.
263
241
 
264
- This is not just style guidance. The installed compiler injects `pp-component` onto the root element, and it raises an error when the template has no root, multiple sibling roots, stray top-level text, or a component tag as the root.
242
+ In source, that parent may be a native HTML element or a single imported `x-*` component tag. After component expansion, the template must still resolve to one final native HTML root so Caspian has exactly one place to inject `pp-component`.
265
243
 
266
- For AI-generated templates, treat this as a hard authoring rule: write the HTML the same way a React component returns one parent element. If the template needs a PulsePoint script, keep that script inside the same parent root.
244
+ This is not just style guidance. The installed compiler injects `pp-component` onto the final root element, and it raises an error when the template has no root, multiple sibling roots, or stray top-level text.
245
+
246
+ For AI-generated templates, treat this as a hard authoring rule: write the HTML the same way a React component returns one parent node. If the template needs a PulsePoint script, keep that script inside the same parent root.
267
247
 
268
248
  Good:
269
249
 
@@ -296,9 +276,7 @@ Also bad:
296
276
  <p>Body</p>
297
277
  ```
298
278
 
299
- Also avoid making another component tag the root of the HTML file. The root must be a normal lowercase HTML element such as `<div>`, `<section>`, or `<article>`.
300
-
301
- Think about this rule the same way you would in a React component: one parent element per template root. In Caspian, that requirement exists so the Python renderer has exactly one place to attach `pp-component`.
279
+ If you choose an imported `x-*` component as the authored root of the file, make sure it resolves to one native HTML root when compiled. Do not leave an unresolved component tag as the root, and do not emit multiple top-level component siblings.
302
280
 
303
281
  ## Props, Types, And Children
304
282
 
@@ -355,6 +333,7 @@ Keep synchronous components as the default. Switch to `async def` only when the
355
333
  ## Best Practices
356
334
 
357
335
  - Put reusable components in `src/components/` and keep route files in `src/app/`.
336
+ - For dashboards, admin areas, account sections, and route groups with child routes, put the shared shell in the parent folder's `layout.html` and compose it from reusable components there instead of repeating the same shell in every child `index.html`.
358
337
  - If the component includes PulsePoint behavior, prefer a thin Python wrapper plus a same-name `.html` template.
359
338
  - Keep the component file name, exported function name, and authored tag aligned, such as `Button.py`, `def Button(...)`, and `<x-button />`.
360
339
  - Accept `children` or `**props` when the component should support nested content.
@@ -0,0 +1,83 @@
1
+ ---
2
+ title: Core Runtime Map
3
+ description: Use this page when the task touches `main.py`, installed `casp` internals, or when AI needs to know which core file controls a behavior and which packaged doc explains it.
4
+ related:
5
+ title: Related docs
6
+ description: Start with the structure guide, then jump from the runtime file to the matching feature doc.
7
+ links:
8
+ - /docs/project-structure
9
+ - /docs/routing
10
+ - /docs/auth
11
+ - /docs/fetch-data
12
+ - /docs/pulsepoint
13
+ - /docs/index
14
+ ---
15
+
16
+ This page maps the app entry point and the installed Caspian runtime modules to the packaged docs that explain them.
17
+
18
+ Use it when you already know a behavior is controlled by `main.py` or `.venv/Lib/site-packages/casp/**` and you need the fastest path from symptom to source file.
19
+
20
+ ## When To Read This Page
21
+
22
+ - a packaged doc mentions a runtime contract and you need the exact file that implements it
23
+ - `main.py` or the installed `casp` package disagrees with older notes or examples
24
+ - the task is about framework internals rather than normal app-owned route or component code
25
+ - AI needs to know which packaged doc should be kept aligned with a core-file change
26
+
27
+ ## App Entry Point
28
+
29
+ | Concern | Core file | Read first | Why it matters |
30
+ | --- | --- | --- | --- |
31
+ | App bootstrap and request flow | [main.py](../../../../main.py) | [project-structure.md](./project-structure.md), [routing.md](./routing.md), [auth.md](./auth.md) | FastAPI app creation, static asset routes, middleware order, route registration, cache check and save, exception handlers, and final HTML transforms all live here. |
32
+
33
+ Important current `main.py` behaviors AI should keep in mind:
34
+
35
+ - In development, `_scoped_cookie_name(...)` appends the active BrowserSync or dev port to both the session cookie name and the CSRF cookie name. The scope is resolved from `CASPIAN_BROWSER_SYNC_PORT`, then `settings/bs-config.json`, then `PORT`.
36
+ - `register_single_route(...)` passes path params to `page()` as one positional dict, injects matching query params by name, and injects `request` by keyword when declared.
37
+ - The render pipeline is `transform_components(...)`, then `render_with_nested_layouts(...)`, then `transform_scripts(...)`.
38
+ - Route-level generators returned from `page()` are wrapped in `SSE(...)` before the response is sent.
39
+ - Middleware is added in source order as `RPCMiddleware`, `AuthMiddleware`, `CSRFMiddleware`, `SessionMiddleware`, so the effective request order is reversed at runtime.
40
+
41
+ ## Installed Runtime Map
42
+
43
+ | Runtime file | Primary responsibility | Read these docs |
44
+ | --- | --- | --- |
45
+ | [.venv/Lib/site-packages/casp/layout.py](../../../../.venv/Lib/site-packages/casp/layout.py) | `render_page(...)`, `render_layout(...)`, nested layout discovery, metadata merge, and the synchronous `layout()` contract | [routing.md](./routing.md), [metadata.md](./metadata.md) |
46
+ | [.venv/Lib/site-packages/casp/auth.py](../../../../.venv/Lib/site-packages/casp/auth.py) | `AuthSettings`, route privacy checks, session payloads, OAuth providers, CSRF helper behavior, and redirect logic | [auth.md](./auth.md) |
47
+ | [.venv/Lib/site-packages/casp/rpc.py](../../../../.venv/Lib/site-packages/casp/rpc.py) | `@rpc()` registration, rate limits, request handling, auth-aware action checks, and streamed RPC responses | [fetch-data.md](./fetch-data.md) |
48
+ | [.venv/Lib/site-packages/casp/streaming.py](../../../../.venv/Lib/site-packages/casp/streaming.py) | `SSE`, `ServerSentEvent`, and generator-to-event-stream wrapping | [fetch-data.md](./fetch-data.md) |
49
+ | [.venv/Lib/site-packages/casp/state_manager.py](../../../../.venv/Lib/site-packages/casp/state_manager.py) | request-scoped state, session bucket persistence, listener lifecycle, and wire-request reset behavior | [state.md](./state.md) |
50
+ | [.venv/Lib/site-packages/casp/cache_handler.py](../../../../.venv/Lib/site-packages/casp/cache_handler.py) | `Cache`, cache manifest handling, filename generation, disk-backed HTML storage, and invalidation | [cache.md](./cache.md) |
51
+ | [.venv/Lib/site-packages/casp/validate.py](../../../../.venv/Lib/site-packages/casp/validate.py) | `Validate`, `Rule`, sanitization, and file-validation internals | [validation.md](./validation.md) |
52
+ | [.venv/Lib/site-packages/casp/component_decorator.py](../../../../.venv/Lib/site-packages/casp/component_decorator.py) | `@component`, `render_html(...)`, component loading, and prop normalization before component calls | [components.md](./components.md) |
53
+ | [.venv/Lib/site-packages/casp/components_compiler.py](../../../../.venv/Lib/site-packages/casp/components_compiler.py) | `@import` parsing, `x-*` component resolution, single-root validation, and `pp-component` injection | [components.md](./components.md), [routing.md](./routing.md), [pulsepoint.md](./pulsepoint.md) |
54
+ | [.venv/Lib/site-packages/casp/scripts_type.py](../../../../.venv/Lib/site-packages/casp/scripts_type.py) | rewriting authored body `<script>` tags to `type="text/pp"` in rendered HTML | [pulsepoint.md](./pulsepoint.md), [routing.md](./routing.md), [components.md](./components.md) |
55
+ | [.venv/Lib/site-packages/casp/html_attrs.py](../../../../.venv/Lib/site-packages/casp/html_attrs.py) | `get_attributes(...)`, alias normalization, and the Python-side `merge_classes(...)` contract | [components.md](./components.md) |
56
+ | [.venv/Lib/site-packages/casp/caspian_config.py](../../../../.venv/Lib/site-packages/casp/caspian_config.py) | typed config loading, feature-flag reads, `settings/files-list.json` parsing, and route rule derivation | [project-structure.md](./project-structure.md), [commands.md](./commands.md), [routing.md](./routing.md) |
57
+ | [.venv/Lib/site-packages/casp/syntax_compiler.py](../../../../.venv/Lib/site-packages/casp/syntax_compiler.py) | transpiling Caspian `<[[ ... ]]>` and `<template casp-*>` syntax before template render | [components.md](./components.md), [routing.md](./routing.md) |
58
+
59
+ Secondary helpers such as `html_native.py`, `loading.py`, and `string_helpers.py` support the modules above. Read them only when a higher-level runtime file is still insufficient to explain the behavior you are tracing.
60
+
61
+ ## Verification Focus
62
+
63
+ Use these behavior checkpoints when AI needs the fastest verification path for a runtime behavior before widening the search.
64
+
65
+ | Runtime area | Verify these behaviors |
66
+ | --- | --- |
67
+ | `main.py` routing and request flow | route registration, path and query injection, static asset handling, and exception rendering |
68
+ | `casp.auth` | auth settings, signin and signout flow, provider wiring, and page protection behavior |
69
+ | `casp.rpc` and streamed RPC responses | middleware interception, CSRF and session expectations, registry behavior, and helper-level RPC contracts |
70
+ | `casp.layout` | layout discovery, metadata merge, root handling, and layout rendering rules |
71
+ | `casp.components_compiler`, `casp.component_decorator`, `casp.scripts_type`, and template-root injection | `@import` parsing, `x-*` expansion, syntax transpilation, deterministic root keys, `pp-component` injection, and authored-script rewriting to `type="text/pp"` |
72
+ | `casp.state_manager` | wire vs non-wire reset behavior, request-state persistence assumptions, and AttributeDict access |
73
+ | `casp.cache_handler` | filename generation, manifest writes, TTL handling, and invalidation behavior |
74
+ | `casp.caspian_config` | config parsing, files index building, and Next.js-style route inventory behavior |
75
+
76
+ When a runtime or packaged-doc change touches one of these areas, verify the relevant behavior in the owning source file before inferring behavior from memory.
77
+
78
+ ## Decision Rule
79
+
80
+ - Start with the packaged feature doc that matches the task.
81
+ - Use this runtime map when that doc needs to point AI to the controlling source file.
82
+ - Prefer app code and `main.py` over packaged docs when the task is about the current workspace behavior.
83
+ - Do not modify `main.py` or `.venv/Lib/site-packages/casp/**` just because a packaged doc mentions them. Edit those files only when the user asks for a runtime change or when the task is explicitly about framework behavior.
@@ -8,6 +8,7 @@ related:
8
8
  - /docs/auth
9
9
  - /docs/mcp
10
10
  - /docs/state
11
+ - /docs/core-runtime-map
11
12
  - /docs/database
12
13
  - /docs/file-uploads
13
14
  - /docs/cache
@@ -38,6 +39,8 @@ In practice, most pages use both:
38
39
 
39
40
  If a route only needs UI and does not need first-render data, metadata, or other backend behavior, skip `index.py` and keep the page in `index.html` alone.
40
41
 
42
+ When a page belongs to a grouped subtree such as a dashboard, account area, admin section, or docs section, apply the section layout pattern from [routing.md](./routing.md): keep the shared shell in the parent folder's `layout.html` and let each child route own only its route-specific `index.html` and optional `index.py`.
43
+
41
44
  ## Default Data Rule
42
45
 
43
46
  - Use `page()` for async or route-level data required before HTML renders, and use `layout()` only for synchronous shared props or metadata.
@@ -75,6 +78,7 @@ Notes:
75
78
 
76
79
  - Prefer `async def page()` when your database or API client is async-capable.
77
80
  - Put shared section-level props in `layout.py` when multiple child routes need the same synchronous payload. The current layout engine does not await `layout()`.
81
+ - Use a normal parent folder such as `dashboard/` when the section name should appear in the URL. Use a route-group folder such as `(reports)/` only when the shared wrapper should organize child routes without adding a URL segment.
78
82
  - Keep reusable database or API clients under `src/lib/`; keep route-specific orchestration in `src/app/`.
79
83
 
80
84
  If the data source is Prisma, confirm `caspian.config.json` has `prisma: true`, then see `database.md` for the project's schema, migration, and generation workflow. If the project includes an app-owned Python database layer under `src/lib/prisma/`, reuse it instead of creating another helper.
@@ -86,7 +90,8 @@ Use RPC when the browser needs to call Python after the initial page load.
86
90
  Framework internals note:
87
91
 
88
92
  - The `@rpc()` decorator and Caspian RPC bridge internals live in `.venv/Lib/site-packages/casp/rpc.py`.
89
- - Treat that file as framework code. Read it when you need to understand or debug Caspian's RPC behavior, not when you are adding normal app-level actions.
93
+ - Streamed response helpers such as `SSE` and `ServerSentEvent` live in `.venv/Lib/site-packages/casp/streaming.py`.
94
+ - Treat those files as framework code. Read them when you need to understand or debug Caspian's RPC or streaming behavior, not when you are adding normal app-level actions.
90
95
 
91
96
  Define a server action with `@rpc()`:
92
97
 
@@ -177,6 +182,8 @@ Client example:
177
182
 
178
183
  Use streaming when the user should see partial progress instead of waiting for a single final payload.
179
184
 
185
+ In the current runtime, generator and async-generator RPC results are wrapped with `SSE(...)` inside `casp.rpc.py`, and route-level generator results are also wrapped by `main.py` before the response is returned.
186
+
180
187
  ## File Uploads
181
188
 
182
189
  RPC actions can accept FastAPI upload types, but the preferred Caspian pattern is route-local upload actions in `src/app/**/index.py`, shared persistence helpers in `src/lib/`, and reactive client updates through `pp.rpc()`.
@@ -288,6 +295,23 @@ A common pattern is:
288
295
  - Render that data into the HTML template
289
296
  - Use RPC for subsequent filters, mutations, refreshes, streaming, or uploads
290
297
 
298
+ For section-based apps, that pattern usually lives inside a parent layout:
299
+
300
+ ```text
301
+ src/
302
+ app/
303
+ dashboard/
304
+ layout.html
305
+ reports/
306
+ index.py
307
+ index.html
308
+ users/
309
+ index.py
310
+ index.html
311
+ ```
312
+
313
+ In that pattern, `dashboard/layout.html` owns the shared frame while each child route handles its own first-render data in `index.py` and its own interactive updates through `@rpc()`.
314
+
291
315
  If the first-render HTML is expensive to produce and safe to share between visitors, add route-level caching with [cache.md](./cache.md) and invalidate affected URIs after successful mutations.
292
316
 
293
317
  ## AI Retrieval Notes
@@ -297,6 +321,7 @@ If an AI agent is choosing how to load data in Caspian, apply these rules first.
297
321
  - Put first-render data loading in `src/app/**/index.py`.
298
322
  - Put shared section props in `layout.py` only when multiple child routes need the same synchronous data.
299
323
  - Keep async I/O in `page()` or `@rpc()` because the current layout engine does not await `layout()`.
324
+ - For grouped subtrees, follow the section layout pattern in [routing.md](./routing.md) before deciding where `page()` data and `@rpc()` actions belong.
300
325
  - Treat RPC as the default read and write layer between PulsePoint code and Python route logic, especially for CRUD and interactive backend reads.
301
326
  - Use `@rpc()` for backend functions that should be callable from the browser.
302
327
  - Use `pp.rpc()` for client-side calls; do not prefer older `pp.fetchFunction()` wording.
@@ -18,6 +18,8 @@ This page documents the recommended file upload and file manager pattern for Cas
18
18
 
19
19
  Treat uploads as normal route behavior. Keep the owning browser UI in the route template, keep upload and delete `@rpc()` actions in the owning route `index.py`, and move reusable storage or persistence helpers into `src/lib/`.
20
20
 
21
+ If the file manager lives inside a grouped subtree such as a dashboard, account area, or admin area, apply the section layout pattern from [routing.md](./routing.md): keep the shared shell in the parent folder's `layout.html` and keep the file-manager route itself in a child folder.
22
+
21
23
  ## Default Pattern
22
24
 
23
25
  - Keep first-render file manager data in `page()` so the initial HTML already contains the current asset list and storage summary.
@@ -52,6 +54,20 @@ The normal Caspian file-manager flow is:
52
54
  6. The RPC action returns refreshed manager data.
53
55
  7. PulsePoint replaces the route state and `pp-for` rerenders the list.
54
56
 
57
+ Section example:
58
+
59
+ ```text
60
+ src/
61
+ app/
62
+ dashboard/
63
+ layout.html
64
+ files/
65
+ index.py
66
+ index.html
67
+ ```
68
+
69
+ In that pattern, `dashboard/layout.html` owns the shared dashboard shell, while `dashboard/files/index.py` owns the initial file-manager payload and the upload or delete `@rpc()` actions.
70
+
55
71
  ## Example Route-Local RPC
56
72
 
57
73
  ```python
@@ -171,4 +187,5 @@ const PUBLIC_IGNORE_DIRS = ["uploads"];
171
187
  - Use `database.md` when Prisma models or relations must change for upload metadata.
172
188
  - Use `validation.md` for MIME, extension, and other boundary checks, then keep explicit size and auth checks in the owning RPC action.
173
189
  - Use `project-structure.md` for placement rules, especially `src/app/` versus `src/lib/` and `public/uploads/`.
190
+ - For grouped file-manager sections, follow the section layout pattern in [routing.md](./routing.md) and keep the upload page in a child route folder beneath the shared shell.
174
191
  - Use `commands.md` and `settings/bs-config.ts` when uploads should not trigger BrowserSync reloads during `npm run dev`.
@@ -5,8 +5,10 @@ related:
5
5
  title: Next Steps
6
6
  description: Start with installation, review the CLI commands, then use the structure and component guides to place Caspian files correctly.
7
7
  links:
8
+ - /docs/ai-validation-checklist
8
9
  - /docs/installation
9
10
  - /docs/commands
11
+ - /docs/core-runtime-map
10
12
  - /docs/mcp
11
13
  - /docs/file-uploads
12
14
  - /docs/project-structure
@@ -38,12 +40,15 @@ The packaged Caspian docs referenced by this index live here:
38
40
 
39
41
  ## Available Documents
40
42
 
43
+ - `index.md` - packaged docs manifest and the default first entry point for AI retrieval in this directory
44
+ - `ai-validation-checklist.md` - workflow and representative prompts for checking whether AI can find the correct Caspian docs, core files, and verification checkpoints
41
45
  - `installation.md` - First-time setup flow for creating a new Caspian application
42
46
  - `commands.md` - Main Caspian CLI workflows for project creation, generation, updates, and config-aware maintenance
47
+ - `core-runtime-map.md` - map of `main.py` plus installed `casp` modules to the packaged docs that explain them and the behaviors AI should trace there
43
48
  - `mcp.md` - MCP-specific layout, launch flow, and AI routing rules for projects where `caspian.config.json` enables MCP
44
49
  - `database.md` - Prisma schema, migration, seed, and client-generation workflow for projects where `caspian.config.json` enables Prisma, plus Python-side helper caveats
45
50
  - `auth.md` - Session-backed authentication with `casp.auth`, centralized `auth_config.py`, public-vs-private route mode guidance, RPC-first signout guidance, RBAC, and OAuth provider helpers
46
- - `components.md` - Create reusable Python components, template-backed UI, HTML-first `x-*` component tags, the single-parent root rule for component HTML files, and the Python-side `merge_classes(...)` contract when Tailwind CSS is enabled
51
+ - `components.md` - Create reusable Python components, template-backed UI, HTML-first `x-*` component tags, the single-parent authored-root rule for component HTML files, and the Python-side `merge_classes(...)` contract when Tailwind CSS is enabled
47
52
  - `pulsepoint.md` - Default reactive frontend runtime contract for component scripts, state, effects, directives, client-side behaviors, and direct browser `twMerge(...)` usage when Tailwind CSS is enabled
48
53
  - `fetch-data.md` - Initial server-side data loading and browser-triggered RPC flows with `pp.rpc()`, streaming, uploads, and auth-aware actions
49
54
  - `file-uploads.md` - Route-local file uploads and file-manager flows with `@rpc()`, `pp.rpc()`, Prisma metadata, public asset storage, and BrowserSync ignore rules
@@ -51,7 +56,7 @@ The packaged Caspian docs referenced by this index live here:
51
56
  - `cache.md` - Route-level HTML caching with `Cache`, `CacheHandler`, TTL behavior, file-system storage, and invalidation patterns
52
57
  - `validation.md` - Input validation and sanitization with `Validate`, `Rule`, direct field checks, and multi-rule workflows for routes and RPC actions
53
58
  - `metadata.md` - Static and dynamic metadata, SEO inheritance, and Open Graph or Twitter card tags
54
- - `routing.md` - Next.js App Router-style file-based routing with `src/app`, dynamic segments, route groups, nested layouts, and single-root route templates
59
+ - `routing.md` - Next.js App Router-style file-based routing with `src/app`, dynamic segments, dashboard and section layouts, route groups, nested layouts, single-parent authored templates, and backend Python companions that do not own visible markup
55
60
  - `project-structure.md` - Default Caspian layout and where route files, reusable UI in `src/components/`, reusable non-UI code in `src/lib/`, and database files belong
56
61
 
57
62
  ## AI Retrieval Notes
@@ -63,10 +68,12 @@ Preferred lookup order:
63
68
  1. Read `node_modules/caspian-utils/dist/docs/index.md` to discover available local docs.
64
69
  2. Read `./caspian.config.json` before making any feature assumption. A doc existing in the package does not mean that feature is enabled in the current project.
65
70
  3. Treat `caspian.config.json` as the single source of truth for optional features. Use feature-specific docs only when the matching flag is enabled. If a feature is disabled and the user wants it, ask first, then update `caspian.config.json` and follow the update workflow in `commands.md`.
66
- 4. After the feature is confirmed, inspect the actual project files that decide behavior, such as `package.json`, `main.py`, `src/app/**`, `src/lib/**`, `settings/**`, `prisma/**`, and the installed `casp` runtime.
67
- 5. Use `commands.md` for scaffold and update workflows, `project-structure.md` for placement decisions, and the feature docs such as `mcp.md`, `database.md`, `auth.md`, `fetch-data.md`, and `file-uploads.md` for task-specific guidance.
68
- 6. Prefer packaged Caspian docs before upstream documentation when generating code, commands, or migration guidance.
69
- 7. Keep `index.md` and cross-links aligned so AI can quickly discover the right doc.
71
+ 4. If the task touches `main.py` or `.venv/Lib/site-packages/casp/**`, read `core-runtime-map.md` to jump to the controlling runtime file and the matching feature doc.
72
+ 5. After the feature is confirmed, inspect the actual project files that decide behavior, such as `package.json`, `main.py`, `src/app/**`, `src/lib/**`, `settings/**`, `prisma/**`, and the installed `casp` runtime.
73
+ 6. Use `commands.md` for scaffold and update workflows, `project-structure.md` for placement decisions, and the feature docs such as `mcp.md`, `database.md`, `auth.md`, `fetch-data.md`, and `file-uploads.md` for task-specific guidance.
74
+ 7. Prefer packaged Caspian docs before upstream documentation when generating code, commands, or migration guidance.
75
+ 8. Use `ai-validation-checklist.md` when you want to verify that the docs lead AI to the correct files and behavior checkpoints.
76
+ 9. Keep `index.md` and cross-links aligned so AI can quickly discover the right doc.
70
77
 
71
78
  ## Maintenance
72
79
 
@@ -31,7 +31,9 @@ Metadata is typically defined in one of two files:
31
31
  - `layout.py` for defaults shared by everything below that folder
32
32
  - `index.py` for route-specific metadata
33
33
 
34
- On routes that render UI, keep the page markup in the sibling `index.html`. `index.py` is the metadata and backend companion, not the place to store route HTML. If a route needs no metadata or backend behavior, omit `index.py` and keep the page as `index.html` only.
34
+ For dashboard sections, account areas, docs sections, and route groups, apply the section layout pattern from [routing.md](./routing.md): put shared metadata defaults in the section's `layout.py`, then override them in child `index.py` files only when a leaf page needs different SEO.
35
+
36
+ On routes that render UI, keep the page markup in the sibling `index.html` and keep shared wrapper markup in `layout.html`. `index.py` and `layout.py` are metadata and backend companions, not the places to store visible route or layout HTML. If a route needs no metadata or backend behavior, omit `index.py` and keep the page as `index.html` only.
35
37
 
36
38
  The current `Metadata` implementation supports three fields:
37
39
 
@@ -118,6 +120,8 @@ Priority order:
118
120
  2. Nested `layout.py` files override those defaults for a section.
119
121
  3. `index.py` metadata or runtime `Metadata(...)` inside the route has the highest priority.
120
122
 
123
+ For example, a dashboard section usually keeps shared defaults in `src/app/dashboard/layout.py`, while a grouping folder that should stay out of the URL can keep its shared defaults in `src/app/(marketing)/layout.py`.
124
+
121
125
  Within a single file, the layout engine applies static metadata first and then applies runtime metadata from `Metadata(...)`, so runtime values win for matching keys.
122
126
 
123
127
  If a child route overrides only the title, the description and other fields continue to fall through from the nearest parent that defines them.
@@ -128,6 +132,14 @@ Example structure:
128
132
  src/
129
133
  app/
130
134
  layout.py
135
+ (marketing)/
136
+ layout.py
137
+ pricing/
138
+ index.py
139
+ dashboard/
140
+ layout.py
141
+ settings/
142
+ index.py
131
143
  blog/
132
144
  layout.py
133
145
  [slug]/
@@ -137,6 +149,7 @@ src/
137
149
  Typical result:
138
150
 
139
151
  - Root layout defines the site-wide title template and default description.
152
+ - Route-group or section layouts such as `(marketing)/layout.py` or `dashboard/layout.py` define shared defaults for that subtree.
140
153
  - Blog layout overrides section-level fields for `/blog/*`.
141
154
  - Blog page overrides the final post title and any page-specific social tags.
142
155
 
@@ -185,6 +198,8 @@ Keep visual layout data and SEO metadata separate.
185
198
 
186
199
  - Put site-wide defaults in `src/app/layout.py`.
187
200
  - Put section defaults in nested `layout.py` files such as `src/app/blog/layout.py`.
201
+ - For dashboard, admin, account, docs, or grouped sections with child routes, put the shared defaults in that section folder's `layout.py` and keep the shared wrapper in the sibling `layout.html`.
202
+ - Use a route-group `layout.py` such as `src/app/(marketing)/layout.py` only when the grouping folder should not appear in the final URL.
188
203
  - Put page-specific static metadata in `index.py`.
189
204
  - Put dynamic metadata inside `page()` after you have route params or fetched data.
190
205
 
@@ -196,6 +211,7 @@ If an AI agent is deciding where to put SEO fields, apply these rules first.
196
211
  - Prefer module-level `metadata = Metadata(...)` for static routes.
197
212
  - Instantiate `Metadata(...)` inside `page()` when metadata depends on params, fetched records, or generated content.
198
213
  - Put shared defaults in `layout.py` and let leaf pages override only what they need.
214
+ - For grouped subtrees, follow the section layout pattern in [routing.md](./routing.md) and keep shared metadata defaults in the parent folder's `layout.py`.
199
215
  - If a single route only needs to tweak a wrapping layout, return `(render_page(__file__, ...), {"dashboard_body_class": ...})` from `page()` instead of moving that prop into metadata.
200
216
  - Use `extra` for Open Graph and Twitter card tags.
201
217
  - Access `extra` values in templates with bracket syntax such as `metadata['og:image']`.
@@ -6,6 +6,7 @@ related:
6
6
  description: Start with installation for new apps, then use the component guide for reusable UI, the auth guide for bootstrap and session wiring, the MCP guide for server layout, the routing guide to map URLs correctly, and the cache guide when route HTML should be reused safely.
7
7
  links:
8
8
  - /docs/installation
9
+ - /docs/core-runtime-map
9
10
  - /docs/components
10
11
  - /docs/auth
11
12
  - /docs/mcp
@@ -105,11 +106,13 @@ This is the main application area. It contains route files, templates, styles, a
105
106
 
106
107
  This directory handles file-based routing. Route templates and route-specific backend logic live here.
107
108
 
108
- For any route that renders UI, keep that markup in `src/app/**/index.html`. If the route is UI-only, `index.html` alone is enough. Add `src/app/**/index.py` only as a companion when the same route needs metadata, `page()`, `@rpc()` actions, auth checks, caching, redirects, or other server-side behavior. Use a lone `index.py` only for non-visual routes such as redirect-only or action-only handlers.
109
+ For any route that renders UI, keep that markup in `src/app/**/index.html`. If the route is UI-only, `index.html` alone is enough. Add `src/app/**/index.py` only as a companion when the same route needs metadata, `page()`, `@rpc()` actions, auth checks, caching, redirects, or other server-side behavior. Keep shared wrappers in `layout.html` and use `layout.py` only for shared synchronous props or metadata. Use a lone `index.py` only for non-visual routes such as redirect-only or action-only handlers.
109
110
 
110
- When authoring a route HTML file such as `src/app/**/index.html`, keep the whole template inside exactly one top-level lowercase HTML element. Treat it the same way you would a React component returning one parent element: wrap the route markup and any owned PulsePoint script in the same root.
111
+ When a folder represents a section with child routes, such as `dashboard`, `account`, `settings`, or `docs`, create `layout.html` in that folder and let the child routes live beneath it. See [routing.md](./routing.md) for the canonical section layout pattern.
111
112
 
112
- Do not handwrite `pp-component="..."` or `type="text/pp"` in those source templates. Author a plain `<script>` inside the root when you need PulsePoint logic, and let Caspian add the runtime attributes during render.
113
+ If the shared wrapper should not add a URL segment, use a parenthesized route-group folder such as `(marketing)/layout.html` instead of a normal folder name. Use a normal folder such as `dashboard/` only when that segment should be part of the public URL.
114
+
115
+ When authoring route or layout HTML, follow the authoring contract documented in [routing.md](./routing.md) and [pulsepoint.md](./pulsepoint.md): keep one authored root, keep `<!-- @import ... -->` directives above that root, use a plain `<script>` inside the root when needed, and do not handwrite `pp-component` or `type="text/pp"`.
113
116
 
114
117
  See `routing.md` for the full App Router-style rules for dynamic segments, route groups, and nested layouts.
115
118
 
@@ -123,9 +126,7 @@ The common Caspian pattern is a Python file such as `Button.py` with `@component
123
126
 
124
127
  One Python file can also export multiple related `@component` functions. When that happens, import those tags from that exact file path in HTML, for example `<!-- @import { Breadcrumb, BreadcrumbItem, BreadcrumbList } from "../components/Breadcrumb.py" -->`, and render them as `<x-breadcrumb />`, `<x-breadcrumb-item />`, and `<x-breadcrumb-list />` instead of assuming each tag has its own sibling `.py` file.
125
128
 
126
- For component HTML files, follow the same one-parent rule as route HTML files: one top-level lowercase HTML element only, with no sibling roots and no top-level script sitting outside that root.
127
-
128
- Do not handwrite `pp-component="..."` or `type="text/pp"` in component source templates either. Write plain `<script>` inside the single root and let the render pipeline inject the runtime shape.
129
+ For component HTML files, follow the component authoring rules in [components.md](./components.md): one top-level parent node, no sibling roots, plain `<script>` inside that root when needed, and no handwritten `pp-component` or `type="text/pp"`.
129
130
 
130
131
  The directories listed in `componentScanDirs` determine where component tooling scans. When that list includes `src/`, `src/components/` is a conventionally clean location, not a hard-coded runtime requirement.
131
132
 
@@ -211,6 +212,12 @@ When runtime uploads write into `public/uploads/`, keep the public-root-relative
211
212
 
212
213
  `settings/bs-config.ts` matches ignore entries against paths relative to the `public/` root, so nested uploaded files do not trigger reloads.
213
214
 
215
+ ### `settings/bs-config.json`
216
+
217
+ When the local BrowserSync stack is running, this generated file is the active URL snapshot.
218
+
219
+ Use it when AI needs the current local, external, or UI URL instead of the configured defaults, and use it when tracing the development cookie scope logic in `main.py` because `_dev_cookie_scope()` can read the active port from this file.
220
+
214
221
  ### `src/lib/auth/auth_config.py`
215
222
 
216
223
  The project auth configuration file. Use this path when changing authentication behavior.
@@ -231,6 +238,16 @@ The root layout shared across pages.
231
238
 
232
239
  If this layout imports reusable components, place each `<!-- @import ... -->` comment above the authored root `<html>` element instead of nesting the comment inside `<html>` or `<body>`.
233
240
 
241
+ Nested section layouts follow the same pattern in child folders such as `src/app/dashboard/layout.html` or `src/app/(marketing)/layout.html`. Use those nested layouts for shared dashboard shells, grouped page wrappers, sidebars, headers, or section-level metadata defaults.
242
+
243
+ Keep visible wrapper markup in `layout.html`, not in `layout.py`.
244
+
245
+ ### `src/app/layout.py`
246
+
247
+ The backend companion for a layout. Use this file for shared synchronous props, metadata defaults, and other server-side preparation for the sibling `layout.html`.
248
+
249
+ Do not store layout HTML in `layout.py`. Keep the authored wrapper in `layout.html` and let `layout.py` return props or metadata.
250
+
234
251
  ### `src/app/index.py`
235
252
 
236
253
  The backend logic companion for the route. Use this file when the same route needs first-render data, metadata, `@rpc()` actions, auth helpers, `casp.validate`, route-level `Cache(...)` declarations, redirects, or other server behavior.
@@ -247,6 +264,8 @@ If a route renders UI and needs no backend behavior, this file alone is sufficie
247
264
 
248
265
  Treat import comments as top-of-file directives that belong above the route's single authored root element.
249
266
 
267
+ That authored root may be a native HTML element or a single imported `x-*` component tag, but after component expansion it must still resolve to one final HTML root.
268
+
250
269
  Use `components.md` when the task involves authoring reusable `<x-my-component />` tags backed by Python files.
251
270
 
252
271
  ### `src/app/globals.css`
@@ -257,12 +276,21 @@ Global application styles.
257
276
 
258
277
  The installed Caspian framework package. This is framework internals, not normal application code.
259
278
 
279
+ Use [core-runtime-map.md](./core-runtime-map.md) when you need the fastest concern-to-file lookup across this directory.
280
+
260
281
  Notable internal files include:
261
282
 
262
283
  - `.venv/Lib/site-packages/casp/rpc.py` for the RPC decorator and related server-action internals
263
284
  - `.venv/Lib/site-packages/casp/layout.py` for layout rendering, metadata handling, and routing-related internals
264
285
  - `.venv/Lib/site-packages/casp/cache_handler.py` for route-level cache declarations, cache manifest handling, disk-backed HTML writes, and invalidation internals
265
286
  - `.venv/Lib/site-packages/casp/validate.py` for direct validators, rule-based validation, sanitization, and file-validation internals
287
+ - `.venv/Lib/site-packages/casp/auth.py` for auth settings, route-protection checks, and OAuth internals
288
+ - `.venv/Lib/site-packages/casp/state_manager.py` for request-scoped server state and persistence caveats
289
+ - `.venv/Lib/site-packages/casp/component_decorator.py` for `@component`, `render_html(...)`, and component loading
290
+ - `.venv/Lib/site-packages/casp/components_compiler.py` for `@import` parsing, `x-*` resolution, root validation, and `pp-component` injection
291
+ - `.venv/Lib/site-packages/casp/scripts_type.py` for converting authored `<script>` tags to `type="text/pp"`
292
+ - `.venv/Lib/site-packages/casp/caspian_config.py` for config loading and route inventory parsing
293
+ - `.venv/Lib/site-packages/casp/streaming.py` for `SSE` and streamed response helpers
266
294
 
267
295
  ### `node_modules/caspian-utils/dist/docs/`
268
296
 
@@ -280,13 +308,14 @@ If an AI agent is deciding where to make changes, use these rules first.
280
308
  - Inspect `settings/component-map.json` and `settings/files-list.json` when you need the generated component or route inventory, but do not hand-edit them. The workspace regenerates them from `settings/component-map.ts` and `settings/files-list.ts`.
281
309
  - Put route templates and route-specific backend logic in `src/app/`.
282
310
  - As the app grows, keep route-owned code in `src/app/`, reusable rendered UI in `src/components/`, and reusable non-UI support code in `src/lib/`.
311
+ - When the user asks for a dashboard, admin area, account area, or any grouped set of child routes, create a parent folder in `src/app/` with `layout.html` and place the child routes beneath it. Use `(group)/layout.html` only when that parent should not appear in the URL.
283
312
  - Read [file-uploads.md](./file-uploads.md) when the task involves upload widgets, media libraries, or file-manager flows.
284
313
  - Check [routing.md](./routing.md) when you need URL segment rules, layout nesting behavior, or dynamic route conventions.
285
314
  - Put reusable component files in `src/components/` and check [components.md](./components.md) for `@component`, `render_html(__file__)`, `x-*` component tags, import comments, and single-root template rules.
286
315
  - When deciding between `src/components/` and `src/lib/`, use `src/components/` for anything rendered as reusable UI and `src/lib/` for helpers, services, validators, adapters, and shared business logic.
287
316
  - Use [mcp.md](./mcp.md) only when `caspian.config.json` enables MCP and the task involves FastMCP tool definitions, nested config discovery, or local MCP commands.
288
317
  - Keep `<!-- @import ... -->` comments above the single authored root element in route, layout, and component HTML files.
289
- - For route and component HTML files, always emit one top-level lowercase HTML element. Good: one wrapper containing the content and a plain `<script>` when needed. Bad: a wrapper element followed by a sibling top-level `<script>`, or handwritten `pp-component="..."` and `type="text/pp"` attributes in source.
318
+ - Follow the single-root authoring contract in [routing.md](./routing.md), [components.md](./components.md), and [pulsepoint.md](./pulsepoint.md): one authored root, any owned `<script>` inside that root, and no handwritten `pp-component` or `type="text/pp"` in source templates.
290
319
  - Put shared helpers and reusable libraries in `src/lib/`.
291
320
  - Use `settings/bs-config.ts` when uploaded public assets should not trigger BrowserSync reloads during the local stack.
292
321
  - Put app-owned FastMCP code in `src/lib/mcp/` only when `caspian.config.json` enables MCP.
@@ -301,6 +330,7 @@ If an AI agent is deciding where to make changes, use these rules first.
301
330
  - Put static files in `public/`.
302
331
  - Put entry-point changes in `main.py`.
303
332
  - Put feature and framework-level project configuration in `caspian.config.json`.
333
+ - Read [core-runtime-map.md](./core-runtime-map.md) when the task touches `main.py` or installed `casp` internals and the controlling file is not obvious yet.
304
334
  - Treat `.venv/Lib/site-packages/casp/` as framework internals unless the task is specifically about Caspian core behavior.
305
335
  - Use `.venv/Lib/site-packages/casp/rpc.py` when investigating or documenting Caspian RPC internals.
306
336
  - Use `.venv/Lib/site-packages/casp/layout.py` when investigating or documenting Caspian routing, layout, or metadata internals.
@@ -8,6 +8,7 @@ related:
8
8
  - /docs/components
9
9
  - /docs/routing
10
10
  - /docs/fetch-data
11
+ - /docs/core-runtime-map
11
12
  - /docs/project-structure
12
13
  - /docs/index
13
14
  ---
@@ -36,10 +37,14 @@ Important current facts:
36
37
 
37
38
  - `public/js/pp-reactive-v2.js` exposes the global `pp` runtime and auto-mounts on DOM ready.
38
39
  - `main.py` renders the final HTML, runs `transform_components(...)`, then runs `transform_scripts(...)` before returning the response.
40
+ - `.venv/Lib/site-packages/casp/components_compiler.py` injects `pp-component` on the final resolved root after component expansion.
41
+ - `.venv/Lib/site-packages/casp/scripts_type.py` rewrites authored body `<script>` tags to `type="text/pp"`.
39
42
  - Authored route and component templates compose reusable server components as HTML-first `x-*` tags before the browser runtime mounts.
40
43
 
41
44
  If docs, generated examples, or older notes disagree with `public/js/pp-reactive-v2.js` plus `main.py`, follow the code that actually runs.
42
45
 
46
+ Use [core-runtime-map.md](./core-runtime-map.md) when the controlling runtime file is not obvious yet.
47
+
43
48
  ## Default Frontend Rule
44
49
 
45
50
  When a Caspian page needs reactive browser behavior, use PulsePoint.
@@ -48,10 +53,13 @@ When a Caspian page needs reactive browser behavior, use PulsePoint.
48
53
  - Use PulsePoint state, effects, refs, and template directives as the default reactivity model in authored Caspian templates.
49
54
  - When the browser needs CRUD operations or follow-up reads from the backend, call `pp.rpc()` from PulsePoint code and back it with route or backend `@rpc()` actions.
50
55
  - Keep server-rendered HTML plus PulsePoint enhancement as the baseline architecture.
56
+ - For dashboards, admin areas, account sections, docs sections, and other grouped subtrees, keep shared shell markup and shared PulsePoint behavior in the parent folder's `layout.html`, then keep child-route PulsePoint state local to each `index.html`. Follow the same mental model as the Next.js App Router.
51
57
  - Only introduce another frontend runtime when the user explicitly asks for it or the project already depends on one.
52
58
 
53
59
  ## Authoring Model
54
60
 
61
+ Treat this section as the canonical authored-vs-runtime contract for Caspian templates. When another packaged doc needs this rule set, link here instead of restating the full explanation.
62
+
55
63
  PulsePoint authoring is split into two layers:
56
64
 
57
65
  - The authored layer: route, layout, and component templates under `src/` with plain HTML plus a plain `<script>`.
@@ -59,11 +67,14 @@ PulsePoint authoring is split into two layers:
59
67
 
60
68
  For authored Caspian templates:
61
69
 
62
- - Keep exactly one top-level lowercase HTML root element.
70
+ - Keep exactly one authored top-level parent node.
71
+ - In source, that parent may be a native HTML element or a single imported `x-*` component tag, but after component expansion it must resolve to one final HTML root.
63
72
  - Put the component logic inside a plain `<script>` inside that same root.
64
73
  - Do not handwrite `pp-component="..."`.
65
74
  - Do not handwrite `type="text/pp"`.
66
75
 
76
+ Keep visible route, layout, and component markup in the HTML templates. Treat `index.py` and `layout.py` as backend companions for data, metadata, props, RPC actions, auth, caching, redirects, and other server-side preparation, not as template bodies.
77
+
67
78
  Caspian already handles those details for you during render.
68
79
 
69
80
  That means AI-generated examples should default to authored template source, not raw runtime HTML.
@@ -111,7 +122,7 @@ Important:
111
122
  - At runtime, the owned script is `script[type="text/pp"]`.
112
123
  - The script lookup walks the current root and skips nested `pp-component` boundaries, so a parent does not consume a child component's script.
113
124
  - If multiple matching runtime scripts exist in the same root, the first matching owned script wins. Generate one script per root.
114
- - Authored route, layout, and component templates still need one top-level lowercase HTML root so Caspian can inject the component boundary correctly.
125
+ - Authored route, layout, and component templates still need one top-level parent node so Caspian can inject the component boundary correctly after component expansion.
115
126
  - A scriptless component root still mounts and can receive props, refs, events, and nested children, but it has no local runtime scope beyond its props.
116
127
  - Component scripts are plain JavaScript executed with `new Function(...)`. Do not use `import`, `export`, or top-level `await` inside them.
117
128
  - The runtime auto-returns supported top-level bindings from the script. Do not rely on manual `return { ... }` objects.
@@ -421,6 +432,7 @@ Use these rules when generating or editing PulsePoint runtime code:
421
432
  - Keep `main.py` in view because it injects the runtime-facing attributes and rewrites authored scripts before the browser sees them.
422
433
  - If a development-only source tree exists behind the shipped runtime, treat it as optional implementation detail rather than something generated apps are guaranteed to contain.
423
434
  - In authored Caspian templates, do not handwrite `pp-component` or `type="text/pp"`; let the render pipeline inject them.
435
+ - For grouped subtrees, follow the section layout pattern in [routing.md](./routing.md), keep the shared interactive shell in the parent folder's `layout.html`, and keep route-specific PulsePoint code in each child `index.html`.
424
436
  - Prefer PulsePoint state and template directives over manual DOM mutation for reactive updates.
425
437
  - If you are explicitly editing raw runtime HTML or internals, keep `pp-component` unique per live instance.
426
438
  - In authored templates, use a plain `<script>` inside the root. In runtime HTML, the owned script appears as `script[type="text/pp"]`.
@@ -5,6 +5,7 @@ related:
5
5
  title: Related docs
6
6
  description: Read the structure guide first, then use the components guide for reusable UI, the metadata guide for SEO fields, the cache guide for route-level HTML reuse, and the PulsePoint runtime guide for interactive route templates.
7
7
  links:
8
+ - /docs/core-runtime-map
8
9
  - /docs/project-structure
9
10
  - /docs/components
10
11
  - /docs/cache
@@ -29,8 +30,9 @@ Start with these rules:
29
30
  - If a route is UI-only, `index.html` by itself is enough.
30
31
  - Add `index.py` only when the same route needs metadata, `page()`, `@rpc()` actions, auth checks, caching, redirects, or other server-side logic.
31
32
  - Use a standalone `index.py` only for non-visual routes such as redirects or action-only handlers.
32
- - Use `layout.html` to wrap child routes.
33
+ - When a folder owns child routes, use `layout.html` to wrap them. This is the default pattern for dashboards, admin sections, account areas, settings trees, and route groups.
33
34
  - Use `layout.py` when a layout needs shared synchronous props or metadata before rendering.
35
+ - Keep visible route and layout markup in `index.html` and `layout.html`. Treat `index.py` and `layout.py` as backend companions, not as places to author visible HTML.
34
36
 
35
37
  Use [cache.md](./cache.md) when an `index.py` route also needs declarative page caching via `Cache(...)`.
36
38
 
@@ -38,6 +40,7 @@ Framework internals note:
38
40
 
39
41
  - Caspian's layout and route-resolution internals live in `.venv/Lib/site-packages/casp/layout.py`.
40
42
  - Treat that file as framework code. Read it when the task is about routing internals, layout resolution, or metadata behavior inside Caspian itself.
43
+ - Use [core-runtime-map.md](./core-runtime-map.md) when a routing task crosses `main.py` route registration, parameter injection, and installed layout internals.
41
44
 
42
45
  See [metadata.md](./metadata.md) when a page or layout needs SEO fields.
43
46
 
@@ -61,6 +64,75 @@ This means most App Router habits carry over directly:
61
64
  - Use nested layouts for shared wrappers.
62
65
  - Use route groups to organize code without changing the public path.
63
66
 
67
+ ## Section Layout Pattern
68
+
69
+ Treat this section as the canonical grouped-subtree structure rule for the packaged docs. Other pages should point here instead of repeating the full folder pattern.
70
+
71
+ When a user asks for a dashboard, admin area, account section, docs section, or any grouped set of child routes, model it exactly like a Next.js App Router subtree.
72
+
73
+ - Create a parent folder for the section.
74
+ - Add `layout.html` in that folder for the shared shell.
75
+ - Add `layout.py` only when that shared shell needs synchronous props or metadata.
76
+ - Put each child page in its own route folder with `index.html` and an optional `index.py` companion.
77
+ - Use a normal folder name such as `dashboard/` when the section name should appear in the URL.
78
+ - Use a route-group folder such as `(marketing)/` when the folder should organize code and own a layout without adding a URL segment.
79
+
80
+ Examples:
81
+
82
+ ```text
83
+ src/
84
+ app/
85
+ dashboard/
86
+ layout.html
87
+ index.html
88
+ settings/
89
+ index.html
90
+ users/
91
+ index.html
92
+ ```
93
+
94
+ This produces `/dashboard`, `/dashboard/settings`, and `/dashboard/users`, all wrapped by the `dashboard/layout.html` shell.
95
+
96
+ ```text
97
+ src/
98
+ app/
99
+ (marketing)/
100
+ layout.html
101
+ pricing/
102
+ index.html
103
+ about/
104
+ index.html
105
+ ```
106
+
107
+ This produces `/pricing` and `/about`, both wrapped by the `(marketing)/layout.html` shell even though `(marketing)` does not appear in the URL.
108
+
109
+ Canonical end-to-end example:
110
+
111
+ ```text
112
+ src/
113
+ app/
114
+ layout.html
115
+ layout.py
116
+ (marketing)/
117
+ layout.html
118
+ pricing/
119
+ index.html
120
+ about/
121
+ index.html
122
+ dashboard/
123
+ layout.html
124
+ layout.py
125
+ index.html
126
+ settings/
127
+ index.html
128
+ index.py
129
+ reports/
130
+ index.html
131
+ index.py
132
+ ```
133
+
134
+ Use this pattern when the app has a public grouped section that should stay out of the URL and a private dashboard section that should appear in the URL. The root layout owns app-wide chrome, `(marketing)/layout.html` owns the shared public marketing shell, and `dashboard/layout.html` owns the shared dashboard shell for `/dashboard/*` child routes.
135
+
64
136
  ## Core Concepts
65
137
 
66
138
  Every route lives inside `src/app`. For routes that render UI, `index.html` owns the markup and `index.py` is only an optional companion for server logic or metadata.
@@ -93,13 +165,9 @@ Route templates can import reusable Python components with `<!-- @import ... -->
93
165
 
94
166
  Place those import comments at the top of the file, above the authored root element. They are file-level directives, not children of the route root.
95
167
 
96
- Do not manually add `pp-component="..."` to the route root. The Python render pipeline injects that attribute onto the route's single top-level lowercase HTML element.
97
-
98
- Do not manually add `type="text/pp"` to a route-owned script either. In source templates, write a plain `<script>` inside the root and let `main.py` call `transform_scripts(...)` before the browser runtime sees the HTML.
99
-
100
- That means route templates follow the same single-root discipline as component templates: one root element, no sibling roots, and PulsePoint scripts kept inside that root when needed.
168
+ Route templates follow the same authored-vs-runtime contract documented in [pulsepoint.md](./pulsepoint.md) and the same single-root discipline documented in [components.md](./components.md): keep one authored parent node, keep any `<!-- @import ... -->` directives above that root, use a plain `<script>` inside that root when needed, and do not handwrite `pp-component` or `type="text/pp"`. That root may be a native HTML element or a single imported `x-*` component tag, but after expansion it must resolve to one final HTML root.
101
169
 
102
- For AI-generated route templates, treat `src/app/**/index.html` the same way you would a React component body: return one parent element that contains the entire route markup.
170
+ For AI-generated route templates, treat `src/app/**/index.html` the same way you would a React component body: return one parent node that contains the entire route markup.
103
171
 
104
172
  Good:
105
173
 
@@ -134,35 +202,7 @@ Also bad:
134
202
  </section>
135
203
  ```
136
204
 
137
- Example authored route template:
138
-
139
- ```html
140
- <!-- @import StatsCard from "../components" -->
141
-
142
- <section class="dashboard-shell">
143
- <x-stats-card title="Users" value="42" />
144
-
145
- <script>
146
- const [filter, setFilter] = pp.state("all");
147
- </script>
148
- </section>
149
- ```
150
-
151
- Rendered shape at runtime:
152
-
153
- ```html
154
- <section pp-component="page_a1b2c3d4" class="dashboard-shell">
155
- <div pp-component="statscard_e5f6g7h8" title="Users" value="42">
156
- ...
157
- </div>
158
-
159
- <script type="text/pp">
160
- const [filter, setFilter] = pp.state("all");
161
- </script>
162
- </section>
163
- ```
164
-
165
- Write the first form. Caspian produces the second form by injecting `pp-component` on the root and `type="text/pp"` on the owned script.
205
+ Use [pulsepoint.md](./pulsepoint.md) when you need the full authored-vs-rendered example instead of this routing-focused reminder.
166
206
 
167
207
  ### `index.py`
168
208
 
@@ -280,14 +320,29 @@ Route groups are useful when you want to:
280
320
  - Split routes under different layout boundaries.
281
321
  - Improve project organization without changing the public URL structure.
282
322
 
323
+ If the group needs a shared wrapper, put `layout.html` inside the `(group)` folder. That layout applies to every child route in the group while the group name stays out of the URL.
324
+
325
+ Use this decision rule:
326
+
327
+ - Use `dashboard/`, `account/`, or another normal folder when that segment should be part of the public path.
328
+ - Use `(dashboard)`, `(marketing)`, or another parenthesized folder when you want shared organization or a shared layout without adding a public path segment.
329
+
283
330
  ## Layouts And Nesting
284
331
 
285
332
  Layouts work like the Next.js App Router layout system. A `layout.html` file wraps the routes beneath its folder, and nested layouts compose automatically.
286
333
 
334
+ In practice, this means a dashboard is usually a folder-level layout, not a single oversized page. Put the shared sidebar, header, and frame in `dashboard/layout.html`, then create child routes such as `dashboard/settings/index.html` and `dashboard/reports/index.html` beneath it.
335
+
287
336
  When a layout imports components, keep each `<!-- @import ... -->` comment above the layout's authored wrapper element, such as the root `<section>` in a nested layout or the root `<html>` in the app layout.
288
337
 
289
338
  Resolved SEO fields are exposed to layouts as `[[ metadata.* ]]`, while values returned from `layout.py` are exposed separately as `[[ layout.* ]]`.
290
339
 
340
+ ### `layout.html`
341
+
342
+ Use `layout.html` for the shared wrapper markup of a subtree. Keep the visible shell here, not in `layout.py`.
343
+
344
+ Follow the same authoring contract used by route templates: one authored parent node, top-of-file `<!-- @import ... -->` directives above that root, plain `<script>` inside the root when needed, and no handwritten `pp-component` or `type="text/pp"`. See [pulsepoint.md](./pulsepoint.md) for the canonical authored-vs-runtime explanation.
345
+
291
346
  For example, a page inside `/dashboard/settings` is wrapped by the root layout first and then by the dashboard layout.
292
347
 
293
348
  Example root layout:
@@ -308,7 +363,7 @@ Example root layout:
308
363
 
309
364
  ### `layout.py`
310
365
 
311
- If a layout needs shared synchronous props or metadata, add a `layout.py` file next to the HTML layout.
366
+ If a layout needs shared synchronous props or metadata, add a `layout.py` file next to the HTML layout. Treat it as the backend companion for the layout, not as the place to author visible wrapper markup.
312
367
 
313
368
  Example:
314
369
 
@@ -376,6 +431,10 @@ src/
376
431
  index.html
377
432
  about/
378
433
  index.html
434
+ (marketing)/
435
+ layout.html
436
+ pricing/
437
+ index.html
379
438
  users/
380
439
  [id]/
381
440
  index.html
@@ -395,9 +454,13 @@ src/
395
454
  index.py
396
455
  dashboard/
397
456
  layout.html
457
+ layout.py
458
+ index.html
398
459
  settings/
399
460
  index.html
400
461
  index.py
462
+ reports/
463
+ index.html
401
464
  ```
402
465
 
403
466
  ## AI Retrieval Notes
@@ -408,11 +471,14 @@ If an AI agent is choosing where to add or update route code, apply these rules
408
471
  - Use folder names to model URL segments.
409
472
  - If a route renders UI, create or update `index.html` for the markup.
410
473
  - Add `index.py` only when the same route needs metadata or server behavior; do not place route HTML in `index.py`.
474
+ - Keep visible page markup in `index.html` and shared subtree shells in `layout.html`; do not place route HTML in `index.py` or layout HTML in `layout.py`.
475
+ - When the user asks for a dashboard, admin area, account section, or any grouped subtree of child routes, create a parent folder with `layout.html` and place the child routes beneath it. Follow the same mental model as the Next.js App Router.
476
+ - Use a normal folder such as `dashboard/` when the segment should appear in the URL. Use `(group)/` only when the folder should organize or wrap child routes without adding a path segment.
411
477
  - Use [cache.md](./cache.md) when an `index.py` route should opt into page-level HTML caching.
412
478
  - Use `layout.html` for shared wrappers and `layout.py` for layout-level synchronous props or metadata.
413
479
  - When one route needs to change a parent layout, return `(render_page(__file__, ...), {"dashboard_body_class": ...})` from `page()` and read that value as `[[ layout.dashboard_body_class ]]` in the wrapping `layout.html`.
414
480
  - Use `layout.py` for layout props that should apply across an entire subtree. Use `render_layout(__file__, {...})` only when the layout should consume direct local variables such as `[[ my_class ]]` instead of the standard `[[ layout.* ]]` namespace.
415
- - Keep `<!-- @import ... -->` directives at the top of `index.html` and `layout.html`, above the single authored root element.
481
+ - Keep `<!-- @import ... -->` directives at the top of `index.html` and `layout.html`, above the single authored parent node.
416
482
  - Use [metadata.md](./metadata.md) when a route or layout needs SEO fields.
417
483
  - Use `[segment]` for single dynamic parameters.
418
484
  - Use `[...segment]` for catch-all route matching.
@@ -5,6 +5,7 @@ related:
5
5
  title: Related docs
6
6
  description: Pair server request state with auth and RPC flows, then read the PulsePoint guide when the state really belongs in the browser instead of the request lifecycle.
7
7
  links:
8
+ - /docs/core-runtime-map
8
9
  - /docs/auth
9
10
  - /docs/fetch-data
10
11
  - /docs/pulsepoint
@@ -28,6 +29,8 @@ from casp.state_manager import StateManager
28
29
 
29
30
  The current installed implementation lives in `.venv/Lib/site-packages/casp/state_manager.py`.
30
31
 
32
+ Use [core-runtime-map.md](./core-runtime-map.md) when a state task crosses `main.py` middleware setup, wire-request behavior, and the installed state-manager internals.
33
+
31
34
  The real API surface is:
32
35
 
33
36
  - request initialization with `StateManager.init(request)`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "caspian-utils",
3
- "version": "0.0.22",
3
+ "version": "0.0.23",
4
4
  "description": "Caspian tooling",
5
5
  "main": "index.js",
6
6
  "scripts": {