mythik 0.1.5 → 0.2.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 (120) hide show
  1. package/README.md +96 -36
  2. package/dist/actions/dispatcher.d.ts +2 -0
  3. package/dist/actions/dispatcher.d.ts.map +1 -1
  4. package/dist/actions/dispatcher.js +11 -2
  5. package/dist/actions/dispatcher.js.map +1 -1
  6. package/dist/actions/transaction-engine.d.ts +10 -0
  7. package/dist/actions/transaction-engine.d.ts.map +1 -1
  8. package/dist/actions/transaction-engine.js +13 -1
  9. package/dist/actions/transaction-engine.js.map +1 -1
  10. package/dist/agent-context/inventory.d.ts +62 -0
  11. package/dist/agent-context/inventory.d.ts.map +1 -0
  12. package/dist/agent-context/inventory.js +248 -0
  13. package/dist/agent-context/inventory.js.map +1 -0
  14. package/dist/contract/extractor.js +3 -0
  15. package/dist/contract/extractor.js.map +1 -1
  16. package/dist/data/data-sources.d.ts +16 -0
  17. package/dist/data/data-sources.d.ts.map +1 -1
  18. package/dist/data/data-sources.js +16 -1
  19. package/dist/data/data-sources.js.map +1 -1
  20. package/dist/editor-session/engine.d.ts +1 -0
  21. package/dist/editor-session/engine.d.ts.map +1 -1
  22. package/dist/editor-session/engine.js +5 -4
  23. package/dist/editor-session/engine.js.map +1 -1
  24. package/dist/fetch/framework-fetch.d.ts +1 -0
  25. package/dist/fetch/framework-fetch.d.ts.map +1 -1
  26. package/dist/fetch/framework-fetch.js +34 -4
  27. package/dist/fetch/framework-fetch.js.map +1 -1
  28. package/dist/fetch/interceptors/auth.d.ts +2 -0
  29. package/dist/fetch/interceptors/auth.d.ts.map +1 -1
  30. package/dist/fetch/interceptors/auth.js +31 -2
  31. package/dist/fetch/interceptors/auth.js.map +1 -1
  32. package/dist/fetch/types.d.ts +4 -0
  33. package/dist/fetch/types.d.ts.map +1 -1
  34. package/dist/index.d.ts +12 -2
  35. package/dist/index.d.ts.map +1 -1
  36. package/dist/index.js +5 -0
  37. package/dist/index.js.map +1 -1
  38. package/dist/renderer/engine.d.ts.map +1 -1
  39. package/dist/renderer/engine.js +29 -4
  40. package/dist/renderer/engine.js.map +1 -1
  41. package/dist/renderer/prop-schemas.d.ts +5 -0
  42. package/dist/renderer/prop-schemas.d.ts.map +1 -1
  43. package/dist/renderer/prop-schemas.js +11 -2
  44. package/dist/renderer/prop-schemas.js.map +1 -1
  45. package/dist/reveal/context.d.ts +22 -0
  46. package/dist/reveal/context.d.ts.map +1 -0
  47. package/dist/reveal/context.js +159 -0
  48. package/dist/reveal/context.js.map +1 -0
  49. package/dist/reveal/index.d.ts +7 -0
  50. package/dist/reveal/index.d.ts.map +1 -0
  51. package/dist/reveal/index.js +7 -0
  52. package/dist/reveal/index.js.map +1 -0
  53. package/dist/reveal/protocol.d.ts +28 -0
  54. package/dist/reveal/protocol.d.ts.map +1 -0
  55. package/dist/reveal/protocol.js +2 -0
  56. package/dist/reveal/protocol.js.map +1 -0
  57. package/dist/reveal/recorder.d.ts +20 -0
  58. package/dist/reveal/recorder.d.ts.map +1 -0
  59. package/dist/reveal/recorder.js +31 -0
  60. package/dist/reveal/recorder.js.map +1 -0
  61. package/dist/reveal/redaction.d.ts +12 -0
  62. package/dist/reveal/redaction.d.ts.map +1 -0
  63. package/dist/reveal/redaction.js +85 -0
  64. package/dist/reveal/redaction.js.map +1 -0
  65. package/dist/reveal/truncation.d.ts +4 -0
  66. package/dist/reveal/truncation.d.ts.map +1 -0
  67. package/dist/reveal/truncation.js +48 -0
  68. package/dist/reveal/truncation.js.map +1 -0
  69. package/dist/reveal/types.d.ts +104 -0
  70. package/dist/reveal/types.d.ts.map +1 -0
  71. package/dist/reveal/types.js +2 -0
  72. package/dist/reveal/types.js.map +1 -0
  73. package/dist/runtime/mount-spec-runtime.d.ts +7 -0
  74. package/dist/runtime/mount-spec-runtime.d.ts.map +1 -1
  75. package/dist/runtime/mount-spec-runtime.js +5 -1
  76. package/dist/runtime/mount-spec-runtime.js.map +1 -1
  77. package/dist/security/spec-validator.d.ts.map +1 -1
  78. package/dist/security/spec-validator.js +61 -0
  79. package/dist/security/spec-validator.js.map +1 -1
  80. package/dist/spec-stores/file.js +1 -1
  81. package/dist/spec-stores/file.js.map +1 -1
  82. package/dist/types.d.ts +8 -0
  83. package/dist/types.d.ts.map +1 -1
  84. package/docs/consumer/README.md +11 -0
  85. package/docs/consumer/WHERE-TO-LOOK.md +1 -1
  86. package/docs/consumer/ai-context-patterns.md +42 -7
  87. package/docs/consumer/ai-context-primitives.md +25 -2
  88. package/docs/consumer/ai-context-runtime-semantics.md +32 -19
  89. package/docs/consumer/ai-context.md +106 -20
  90. package/docs/consumer/reference-doc.md +170 -35
  91. package/docs/llms.txt +6 -1
  92. package/docs/wiki/compiled/README.md +13 -11
  93. package/docs/wiki/compiled/_gaps.md +1 -1
  94. package/docs/wiki/compiled/_index.md +11 -7
  95. package/docs/wiki/compiled/_inventory.md +22 -14
  96. package/docs/wiki/compiled/_lint.md +21 -17
  97. package/docs/wiki/compiled/antipattern-store-save-bypass.md +13 -8
  98. package/docs/wiki/compiled/cli-agent.md +70 -0
  99. package/docs/wiki/compiled/cli-existing-spec-edit-loop.md +9 -8
  100. package/docs/wiki/compiled/cli-overview.md +12 -7
  101. package/docs/wiki/compiled/cli-patch.md +14 -11
  102. package/docs/wiki/compiled/cli-programmatic-api.md +17 -7
  103. package/docs/wiki/compiled/cli-push.md +36 -24
  104. package/docs/wiki/compiled/cli-reveal.md +64 -0
  105. package/docs/wiki/compiled/cli-toon.md +9 -9
  106. package/docs/wiki/compiled/cli-versioning-author.md +19 -15
  107. package/docs/wiki/compiled/concept-agent-context-protocol.md +76 -0
  108. package/docs/wiki/compiled/concept-component-variants.md +39 -10
  109. package/docs/wiki/compiled/concept-mythik-reveal.md +63 -0
  110. package/docs/wiki/compiled/concept-package-layout.md +7 -6
  111. package/docs/wiki/compiled/concept-public-package-names.md +9 -5
  112. package/docs/wiki/compiled/concept-shape-animations.md +4 -2
  113. package/docs/wiki/compiled/concept-spec-store-interface.md +7 -4
  114. package/docs/wiki/compiled/concept-spec-store-layering.md +5 -5
  115. package/docs/wiki/compiled/concept-templates-vs-variants.md +77 -13
  116. package/docs/wiki/compiled/concept-versioned-store.md +8 -5
  117. package/docs/wiki/compiled/pattern-git-vs-db-versioning.md +8 -6
  118. package/docs/wiki/compiled/pattern-push-vs-patch.md +13 -12
  119. package/docs/wiki/compiled/pattern-reusable-components.md +42 -12
  120. package/package.json +1 -1
@@ -180,7 +180,7 @@ Parametrize via `$prop`. Use `$children` in template's `children` array to slot
180
180
 
181
181
  ### When to use `tokens.components.{type}.{variantName}`
182
182
 
183
- Define a reusable style set for a PRIMITIVE (button, input, card) that merges into every element declaring `variant: "name"`. Use for STYLE-ONLY reuse (same type, different visual variant).
183
+ Define a reusable style set for a PRIMITIVE (button, input, card, table root, touchable chip shell) that merges into every element declaring `props.variant: "name"`. Use for STYLE-ONLY reuse (same type, different visual variant).
184
184
 
185
185
  **Defining a variant:**
186
186
  ```json
@@ -204,10 +204,44 @@ Define a reusable style set for a PRIMITIVE (button, input, card) that merges in
204
204
  }
205
205
  ```
206
206
 
207
- **Using in any spec:**
208
- ```json
209
- { "type": "button", "props": { "variant": "ctaPulse", "label": "..." } }
210
- ```
207
+ **Using in any spec:**
208
+ ```json
209
+ { "type": "button", "props": { "variant": "ctaPulse", "label": "..." } }
210
+ ```
211
+
212
+ Variants intentionally cover visual slots only: `style`, `hover`, `active`, `focus`, `transition`, and `animations`. They do not carry arbitrary primitive props. If the repeated pattern includes table `headerStyle`/`rowStyle`, `columns`, `onRowClick`, child composition, or action bindings, use a template or custom element instead.
213
+
214
+ **Data table chrome pattern:** put the panel shell in a `box` variant, then wrap the table in an AppSpec template so the shared `headerStyle`/`rowStyle` lives once.
215
+
216
+ ```json
217
+ {
218
+ "tokens": {
219
+ "components": {
220
+ "box": {
221
+ "tableCard": { "style": { "padding": 0, "overflow": "hidden" } }
222
+ }
223
+ }
224
+ },
225
+ "templates": {
226
+ "ops-table": {
227
+ "type": "table",
228
+ "defaults": {
229
+ "headerStyle": { "minHeight": 44, "background": "#F8FAFC" },
230
+ "rowStyle": { "base": { "minHeight": 43 }, "hover": { "backgroundColor": "#F8FAFC" } }
231
+ },
232
+ "props": {
233
+ "data": { "$prop": "data" },
234
+ "columns": { "$prop": "columns" },
235
+ "onRowClick": { "$prop": "onRowClick" },
236
+ "headerStyle": { "$prop": "headerStyle" },
237
+ "rowStyle": { "$prop": "rowStyle" }
238
+ }
239
+ }
240
+ }
241
+ }
242
+ ```
243
+
244
+ **Filter chip pattern:** for chips composed from `touchable` + `text`, define static shell/label variants, then keep active-state `$state`/`$item` color expressions on the actual chip elements. Variants do not evaluate `$state`/`$item`; templates/custom elements are the heavier option when the composition itself should be reusable.
211
245
 
212
246
  ### Rule: don't duplicate style blocks across specs
213
247
 
@@ -235,8 +269,9 @@ If the same rich style object appears in 2+ specs (or 2+ places in same spec), e
235
269
 
236
270
  | Situation | Use |
237
271
  |---|---|
238
- | Same primitive type, different style set | `tokens.components.{type}.{variant}` |
239
- | Composite (custom type wrapping primitive + children slot) | `appSpec.templates` |
272
+ | Same primitive type, different style set | `tokens.components.{type}.{variant}` |
273
+ | Primitive style plus repeated primitive props (e.g. table `headerStyle`/`rowStyle`) | `appSpec.templates` |
274
+ | Composite (custom type wrapping primitive + children slot) | `appSpec.templates` or Layer 3 custom element |
240
275
  | Style varies by state (hover/active/focus) | Variants (built-in state slots) |
241
276
  | Parametrized via props + children | Templates (with `$prop` + `$children`) |
242
277
 
@@ -41,6 +41,8 @@ Container element. Props: `className`, `surface`.
41
41
  | `variant` | string | — | `heading` (h2), `body` (p), `caption` (span), `label` (label), `mono` (code) |
42
42
  | `className` | string | — | CSS class |
43
43
 
44
+ **Events:** `on.press` is supported for inline interactive text. The renderer supplies button semantics and keyboard activation on web and `accessibilityRole="button"` on React Native. Use `button` or `touchable` for primary actions and larger tap targets.
45
+
44
46
  ### image
45
47
 
46
48
  | Prop | Type | Default | Description |
@@ -61,7 +63,7 @@ Container element. Props: `className`, `surface`.
61
63
 
62
64
  Names are kebab-case, library-agnostic.
63
65
 
64
- **Icon renderer contract:** Mythik does not bundle an icon pack. The built-in `icon` primitive renders a placeholder unless the host app registers a renderer with `plugins.setIconRenderer(Component)`. Register it from `MythikApp.onPlugins`; the built-in primitive keeps identity behavior (container, default weight) and calls your renderer with `{ name, size, weight, color, style }`.
66
+ **Icon renderer contract:** Mythik does not bundle an icon pack. Register icons once with `plugins.setIconRenderer(Component)`. The built-in `icon` primitive, table action icons, toast icons, and React Native icon consumers use that same registration. The built-in primitive keeps identity behavior (container, default weight) and calls your renderer with `{ name, size, weight, color, style }`. Use `overridePrimitive('icon')` only for full primitive replacement.
65
67
 
66
68
  ```tsx
67
69
  import { MythikApp } from 'mythik-react';
@@ -102,7 +104,7 @@ function LucideIcon({ name, size, color, style }) {
102
104
 
103
105
  **Troubleshooting:** if you see the placeholder circle, the renderer was not present in resolved tokens when the app applied plugins. Verify `onPlugins` calls `setIconRenderer`, verify the consumer is installed from a tarball/package that includes the `applyPlugins()` icon renderer fix, and avoid using source aliases when validating published-package behavior.
104
106
 
105
- **Advanced override:** `plugins.overridePrimitive('icon', ...)` is still supported, but it replaces the whole primitive and bypasses the built-in identity wrapper. The renderer function must return a Mythik `RenderNode`, not JSX:
107
+ **Full primitive replacement:** `plugins.overridePrimitive('icon', ...)` is still supported, but it replaces the whole primitive and bypasses the built-in identity wrapper. The renderer function must return a Mythik `RenderNode`, not JSX:
106
108
 
107
109
  ```ts
108
110
  plugins.overridePrimitive('icon', (props, children) => ({
@@ -120,8 +122,25 @@ plugins.overridePrimitive('icon', (props, children) => ({
120
122
  | `gap` | number | — | Space between children (px) |
121
123
  | `align` | string | — | Cross-axis: `start`, `center`, `end`, `stretch` |
122
124
  | `justify` | string | — | Main-axis: `start`, `center`, `end`, `between`, `around` |
125
+ | `wrap` | boolean | `false` | Allow horizontal rows to wrap |
123
126
  | `className` | string | — | CSS class |
124
127
 
128
+ `repeat.layout` lets a repeated container render its item rows horizontally without changing the child primitive:
129
+
130
+ ```json
131
+ {
132
+ "type": "stack",
133
+ "repeat": {
134
+ "source": { "$state": "/filters" },
135
+ "key": "id",
136
+ "layout": { "direction": "horizontal", "gap": 8, "wrap": true }
137
+ },
138
+ "children": ["filter-chip"]
139
+ }
140
+ ```
141
+
142
+ Grouped repeats keep group headers/footers vertical and apply `repeat.layout` only to each group's item row.
143
+
125
144
  ### grid
126
145
 
127
146
  | Prop | Type | Default | Description |
@@ -197,6 +216,8 @@ Props: `size` (px), `direction` (`vertical`/`horizontal`). Renders empty space.
197
216
  | `options` | array | — | Strings or `[{ label, value }]` objects |
198
217
  | `placeholder` | string | — | Placeholder text |
199
218
  | `label` | string/expression | — | Field label |
219
+ | `labelKey` | string | — | Label field for catalog-shaped options |
220
+ | `valueKey` | string | — | Value field for catalog-shaped options |
200
221
  | `disabled` | boolean/expression | `false` | Disable |
201
222
  | `required` | boolean | `false` | Visual indicator |
202
223
 
@@ -209,6 +230,8 @@ Options formats:
209
230
  "options": { "$state": "/cat/services/data" }, "labelKey": "name", "valueKey": "id"
210
231
  ```
211
232
 
233
+ `select` matches the current value against options by normalized scalar key. DB integer IDs and string option values match for display. `0` is a valid selected value. Null/undefined show the placeholder. Existing `onChange` output remains string-compatible.
234
+
212
235
  `labelKey` defaults to `"label"` and `valueKey` defaults to `"value"`. Use them for catalog rows such as `{ "id": 1, "name": "Cambio de aceite" }`. Values are emitted as strings from `on.change`. Malformed object options render as disabled diagnostics instead of blank clickable rows.
213
236
 
214
237
  ### checkbox
@@ -14,7 +14,7 @@
14
14
  1. 🔴 [`/ui/selectedRow` magic path (§2.1)](#21-uiselectedrow--blocker-1)
15
15
  2. 🔴 [CRUD 1-endpoint 3-route auto-generation (§3.1)](#31-crud--one-endpoint-three-routes--blocker-3)
16
16
  3. 🟢 [dataSources + derive lifecycle (§5.2 + §5.3)](#52-datasources-lifecycle) — SHIPPED v0.1.0 Item E
17
- 4. 🟢 [authDomains hostname-only matching (§4.1)](#41-authdomains-hostname-only--blocker-5)
17
+ 4. 🟢 [`apiBaseUrl` + `authDomains` auth target matching (§4.1)](#41-apibaseurl--authdomains--auth-target-matching)
18
18
 
19
19
  These five behaviors caused the most source-reading in the 2026-04 consumer-simulation experiment. If you read only one section of this doc, read these.
20
20
 
@@ -612,26 +612,35 @@ Implementation: `packages/server/src/server.ts` (search `buildScopeWhereClause`
612
612
 
613
613
  ## 4. Matcher Semantics
614
614
 
615
- ### 4.1 `authDomains` — hostname-only 🟢 BLOCKER #5
615
+ ### 4.1 `apiBaseUrl` + `authDomains` — auth target matching
616
616
 
617
- **Contract:** auth headers (Bearer tokens) are auto-injected on fetch/submitForm requests when the URL's hostname matches any entry in `authDomains`. Port is stripped; only the hostname is compared.
617
+ **Contract:** framework-owned runtime fetches resolve their URL before URL guards, auth interceptors, and fetch execution. When `MythikApp` receives `apiBaseUrl`, relative runtime URLs such as `/api/orders` resolve against that base. With web auth enabled, if `authDomains` is omitted or empty, the exact origin of `apiBaseUrl` (`protocol + host + port`) becomes the default effective auth target for framework-owned requests.
618
618
 
619
- **Matching rules:**
619
+ **Default `apiBaseUrl` auth rule:**
620
+
621
+ | Host config | Request URL in spec | Effective URL | Bearer token? |
622
+ |---|---|---|---|
623
+ | `apiBaseUrl: "http://localhost:3010"` | `/api/orders` | `http://localhost:3010/api/orders` | yes |
624
+ | `apiBaseUrl: "http://localhost:3010"` | `http://localhost:3010/api/orders` | unchanged | yes |
625
+ | `apiBaseUrl: "http://localhost:3010"` | `http://localhost:5173/api/orders` | unchanged | no |
626
+ | `apiBaseUrl: "http://localhost:3010"` | `https://third-party.example/api` | unchanged | no |
627
+
628
+ Same-host services on different ports do not inherit the `apiBaseUrl` token. Absolute third-party URLs never inherit that token unless explicitly listed in `authDomains`.
629
+
630
+ **Explicit `authDomains` rule:** when `authDomains` is provided, it remains hostname-based for absolute `http://` and `https://` URLs. Port is stripped because `new URL(fetchUrl).hostname` excludes the port; subdomain matching is supported.
620
631
 
621
632
  | `authDomains` entry | Matches | Does NOT match |
622
633
  |---|---|---|
623
634
  | `"api.example.com"` | `https://api.example.com/...` (any port) | `https://notapi.example.com`, `https://example.com` |
624
635
  | `"example.com"` | `https://example.com`, `https://api.example.com`, `https://foo.example.com` (exact + subdomain) | `https://notexample.com` |
625
636
  | `"localhost"` | `http://localhost:5173/...`, `http://localhost:3010/...`, `http://localhost/...` | `http://127.0.0.1` |
626
- | `"localhost:5173"` | nothing (port is stripped from the URL side, but the domain entry literally contains `:5173` which is never the hostname of any URL) | everything |
627
-
628
- **Mechanism:** `new URL(fetchUrl).hostname` comparison. Port stripping is a side-effect of the URL API — `.hostname` excludes the port. Subdomain matching: an entry `"example.com"` matches any URL whose hostname ends with `.example.com` (note the leading dot in the comparison — prevents `"api.com"` from matching `"notapi.com"`).
637
+ | `"localhost:5173"` | nothing (domain entries must be hostnames, not `host:port`) | everything |
629
638
 
630
- **Dev-mode guidance:** configure `authDomains: ["localhost"]` (no port). Tokens inject on any localhost port during dev.
639
+ **Dev-mode guidance:** prefer `apiBaseUrl` for the app's own backend (`<MythikApp apiBaseUrl="http://localhost:3010" ... />`). If `authDomains` is omitted or empty, Mythik injects Bearer only for that exact `apiBaseUrl` origin. If you provide `authDomains`, the automatic `apiBaseUrl` origin allowlist is disabled; list every trusted absolute host explicitly. Do not add `:port` inside `authDomains[]`.
631
640
 
632
- **URL scheme filter:** only `http://` and `https://` URLs are considered. Relative paths (`/api/rooms`), `file:`, `data:`, and other schemes return `false` before matching — auth headers are not injected on those requests.
641
+ **URL scheme filter:** only `http://` and `https://` URLs are considered for auth injection. `file:`, `data:`, and other schemes never receive auth headers.
633
642
 
634
- Implementation: `packages/core/src/fetch/interceptors/auth.ts:17-28`.
643
+ Implementation: `packages/core/src/fetch/framework-fetch.ts` (base URL resolution) + `packages/core/src/fetch/interceptors/auth.ts` (auth target matching).
635
644
 
636
645
  ### 4.2 Contract URL template matching
637
646
 
@@ -755,12 +764,12 @@ The persistence layer has distinct paths used at different levels. Understanding
755
764
  - **`store.save(id, doc)`** - low-level persistence primitive. Accepts `doc: unknown`. No validation. Called by validated orchestration after checks succeed. **Never called from application code** - treat as `@internal`. Calling `store.save()` from your app bypasses validation and produces silently-broken specs.
756
765
  - **`store.saveVersion(id, doc, meta)`** - versioned write. Writes a row to `screen_versions` table and updates the current spec. Enables diff/rollback/promote/bisect.
757
766
  - **`SpecEngine.patch(id, patches)`** - validated orchestration. Applies RFC-6902 patches, runs DocumentHandler validation, short-circuits on error, and persists the patched document through `store.save`.
758
- - **CLI/API `mythik push` / `runPush`** - validates and handles `--author` / `author` by writing through `saveVersion` when the resolved store is versioned.
759
- - **CLI/API `mythik patch` / `runPatch`** - uses `SpecEngine.patch` for patch application + validation. Without `author`, it persists through the normal `store.save` path. With a versioned store + `author`, it captures the patched document and writes once through `saveVersion`, so lazy bootstrap preserves the pre-patch current spec as v1 and the patch becomes the next version. JSON/TOON success output includes `versioned: boolean` and `version?: number`.
767
+ - **CLI/API `mythik push` / `runPush`** - validates and handles `--author` / `author` by writing through `saveVersion` when the resolved store is versioned. Existing specs require explicit `--replace` for full replacement.
768
+ - **CLI/API `mythik patch` / `runPatch`** - uses `SpecEngine.patch` for patch application + validation. With a versioned store + `author`, it captures the patched document and writes once through `saveVersion`, so lazy bootstrap preserves the pre-patch current spec as v1 and the patch becomes the next version. JSON/TOON success output includes `versioned: boolean` and `version?: number`. Without `author`, persisted writes are rejected unless `allowUnversioned` / `--allow-unversioned` is explicit.
760
769
 
761
770
  **Consumer rule (to be enforced by Item I linter):** never call `store.save()` from application code. Use:
762
771
 
763
- - `mythik push <id>` / `mythik patch <id>` for interactive/shell work
772
+ - `mythik push <id> --from-file spec.json --author <name>` / `mythik patch <id> --from-file patch.json --author <name>` for interactive/shell work
764
773
  - `runPush` / `runPatch` from `mythik-cli/api` for programmatic work (Item F exposes these as public exports)
765
774
 
766
775
  Implementation:
@@ -810,14 +819,14 @@ The framework supports two axes of workflow choice: **how you modify specs** (pu
810
819
  **Push paradigm:**
811
820
 
812
821
  - Specs live in git under `specs/*.json`
813
- - Modify locally, run `mythik push <id>`, CLI sends the full spec doc to the DB
822
+ - Modify locally, run `mythik push <id> --from-file spec.json --author <name>`, CLI sends the full spec doc to the DB
814
823
  - Diff-reviewable in CI (normal git diff on the JSON file)
815
824
  - Full-spec bandwidth cost per write
816
825
 
817
826
  **Patch paradigm:**
818
827
 
819
828
  - Specs live in the DB; local files are optional (working copy)
820
- - Modify via `mythik patch <id> --from-file patch.json` for shell-safe surgical writes
829
+ - Modify via `mythik patch <id> --from-file patch.json --author <name>` for shell-safe surgical writes
821
830
  - ~11x token efficiency vs full push (framework-internal measurement for typical single-element edits)
822
831
  - Requires `mythik pull <id>` to sync any local `specs/*.json` snapshot after a DB patch
823
832
 
@@ -840,8 +849,11 @@ The framework supports two axes of workflow choice: **how you modify specs** (pu
840
849
  **Git-backed history:**
841
850
 
842
851
  - Specs in repo, `git log` + `git blame` serve as audit trail
843
- - No `--author` flag needed on push/patch
844
- - CLI writes via `store.save` (unversioned path, see §5.4)
852
+ - Persisted CLI writes must still be explicit: use `--author <name>`
853
+ whenever the resolved store supports versioning, or
854
+ `--allow-unversioned` only when the project intentionally uses a
855
+ non-versioned store
856
+ - CLI writes via `store.save` only on the explicit unversioned path (see §5.4)
845
857
  - Best for: single-env apps, dev-loop iteration, specs-as-code
846
858
 
847
859
  **DB-versioned history:**
@@ -921,7 +933,7 @@ The `$state` resolves at click time against the just-written `/ui/selectedRow`.
921
933
 
922
934
  **Root:** §4.1 — matcher uses `parsed.hostname` (strips port). Entry `"localhost:5173"` never matches any URL's hostname (which is just `"localhost"`).
923
935
 
924
- **Solution:** `authDomains: ["localhost"]`. Matches all localhost ports (5173, 3010, etc.).
936
+ **Solution:** prefer `apiBaseUrl: "http://localhost:3010"` on the host app for the app's own backend. If you intentionally need an explicit additional host, use `authDomains: ["localhost"]`, not `["localhost:5173"]`.
925
937
 
926
938
  ### 7.5 Wiring a reactive data source with a debounced filter
927
939
 
@@ -1012,7 +1024,8 @@ Login spec binds input to `/login/email`; `loginBody` maps it to `username` at d
1012
1024
  - `packages/core/src/device/context.ts` — DeviceContext interface
1013
1025
 
1014
1026
  **Fetch + auth:**
1015
- - `packages/core/src/fetch/interceptors/auth.ts:17-28` — authDomains matcher
1027
+ - `packages/core/src/fetch/framework-fetch.ts` — `apiBaseUrl` resolution
1028
+ - `packages/core/src/fetch/interceptors/auth.ts` — auth target matcher
1016
1029
 
1017
1030
  **Server:**
1018
1031
  - `packages/server/src/server.ts:104-116` — `/api/auth/login` endpoint
@@ -88,7 +88,7 @@ Every screen is a flat tree: `root` ID + `elements` map + optional `initialActio
88
88
  | `style` | object | CSS styles (can contain expressions) |
89
89
  | `visible` | expression | Show/hide condition |
90
90
  | `permission` | object | `{ visible, editable, readonly }` per role |
91
- | `repeat` | object | Iterate over array: `{ statePath, key }` or `{ source, key }` |
91
+ | `repeat` | object | Iterate over array: `{ statePath, key }` or `{ source, key }`; optional `layout` controls item row direction/gap/wrap |
92
92
  | `on` | object | Event handlers: `{ press, change, submit }` |
93
93
  | `hover` | object | Style overrides on pointer enter |
94
94
  | `active` | object | Style overrides on press |
@@ -108,10 +108,17 @@ mythik init-store --dialect postgres --dry-run # Print SQL store DDL f
108
108
  mythik init-store --dialect sqlserver --server localhost --database Mythik --user "$DB_USER" --password "$DB_PASSWORD" --encrypt false --trust-server-certificate
109
109
  mythik manifest <screen> # See structural tree
110
110
  mythik elements <screen> <id1,id2> # Get element details
111
- mythik patch <screen> --from-file patch.json # Apply RFC 6902 patches
111
+ mythik patch <screen> --from-file patch.json --author <agent> # Apply versioned RFC 6902 patches
112
112
  mythik pull <screen> # Export full spec for backup/review
113
- mythik push <screen> # Create or intentionally replace a full spec
113
+ mythik push <screen> --from-file spec.json --author <agent> # Create a new spec
114
+ mythik push <screen> --from-file spec.json --replace --author <agent> # Intentional full replacement
114
115
  mythik validate <screen> # Validate spec
116
+ mythik reveal start # Start local runtime context bridge
117
+ mythik reveal apps --json # List connected running apps
118
+ mythik reveal context --app <name> # Read live app contract/state/diagnostics
119
+ mythik reveal element <id> --app <name> # Inspect one live element
120
+ mythik agent init codex|claude|all # Install project-local AI operating protocol
121
+ mythik agent context --app <app-id> --include-screens --out .mythik/agent/context.md
115
122
  ```
116
123
 
117
124
  ### Required edit loop for existing specs
@@ -121,20 +128,78 @@ When an AI agent modifies an existing Mythik spec, use this loop by default:
121
128
  1. `mythik manifest <screen>` - inspect the current structure and identify candidate element IDs.
122
129
  2. `mythik elements <screen> <id1,id2,...>` - inspect only the exact elements and nearby containers that will change.
123
130
  3. Write a small RFC 6902 patch file.
124
- 4. `mythik patch <screen> --from-file patch.json` - apply the surgical change through the validated CLI path.
125
- 5. Re-run `mythik manifest <screen>` or `mythik elements <screen> <ids>` to verify the changed surface.
131
+ 4. `mythik patch <screen> --from-file patch.json --author <agent>` - apply the surgical change through the validated, versioned CLI path.
132
+ 5. Run `mythik validate <screen>`.
133
+ 6. Re-run `mythik manifest <screen>` or `mythik elements <screen> <ids>` to verify the changed surface.
126
134
 
127
135
  Do not edit database rows directly, do not call `SpecStore.save()` from app code, and do not replace a whole screen with `push` when a targeted `patch` can express the change. `pull` is for backup, migration, review, or full-spec creation workflows; it is not the normal first step for a small edit.
128
136
 
129
- **Patch rules:** `"op": "add"` with numeric index **inserts before** (not replace). Use `/-` to append. Use `"op": "replace"` to overwrite. Prefer `mythik patch <id> --from-file patch.json` for any patch containing `$state`, `$template`, `$auth`, or other shell-sensitive strings.
137
+ Mythik Sentinel is built into the CLI to help fresh agents stay on that loop. It prints advisory guidance on first project use and when it detects repeated full-replacement intent with little/no patch usage. Sentinel is best-effort: it does not change command exit codes, it does not store specs or credentials, and it preserves JSON/TOON machine-readable outputs. Disable only for exceptional automation with `MYTHIK_SENTINEL=off`.
130
138
 
131
- **Patch input precedence:** explicit sources win. `--from-file <path>` reads that file even if the host process has non-TTY stdin (common in PowerShell/agent runners). Stdin still works via `--from-file -` or by piping without `--from-file`: `cat patch.json | mythik patch <id>`.
139
+ **Patch rules:** `"op": "add"` with numeric index **inserts before** (not replace). Use `/-` to append. Use `"op": "replace"` to overwrite. Prefer `mythik patch <id> --from-file patch.json --author <agent>` for any patch containing `$state`, `$template`, `$auth`, or other shell-sensitive strings.
140
+
141
+ **Patch input precedence:** explicit sources win. `--from-file <path>` reads that file even if the host process has non-TTY stdin (common in PowerShell/agent runners). Stdin still works via `--from-file -` or by piping without `--from-file`: `cat patch.json | mythik patch <id> --author <agent>`.
132
142
 
133
143
  All commands accept `--json`, `--table <name>`, `--store`, `--url`, `--key`. Never pass API keys inline — use `.mythikrc` + env vars.
134
144
 
145
+ ### Mythik Reveal: live context for AI agents
146
+
147
+ Use Mythik Reveal when debugging or changing a running app. Lint,
148
+ validate, `manifest`, and `elements` inspect stored specs; Reveal
149
+ inspects the live runtime. It exposes the active renderer, environment,
150
+ mounted spec summary, selected screen/element, resolved public props,
151
+ included state paths, render errors, warnings, and action/dataSource
152
+ events as structured JSON.
153
+
154
+ Start the local bridge:
155
+
156
+ ```bash
157
+ mythik reveal start --port 17373
158
+ ```
159
+
160
+ Pass the printed `MYTHIK_REVEAL_URL` and `MYTHIK_REVEAL_TOKEN` to the
161
+ host app's `reveal` config. In React, memoize the object so the bridge
162
+ client is stable:
163
+
164
+ ```tsx
165
+ const reveal = React.useMemo(() => ({
166
+ enabled: import.meta.env.DEV,
167
+ appName: 'my-app',
168
+ bridgeUrl: import.meta.env.VITE_MYTHIK_REVEAL_URL,
169
+ token: import.meta.env.VITE_MYTHIK_REVEAL_TOKEN,
170
+ environment: { id: 'dev', source: 'host' as const },
171
+ includeStatePaths: ['/ui', '/form'],
172
+ redactStatePaths: ['/auth', '/secrets'],
173
+ }), []);
174
+
175
+ <MythikApp appSpec={appSpec} specStore={specStore} reveal={reveal} />
176
+ ```
177
+
178
+ Then inspect the running app:
179
+
180
+ ```bash
181
+ mythik reveal apps --json
182
+ mythik reveal context --app my-app
183
+ mythik reveal current --app my-app
184
+ mythik reveal screen dashboard --app my-app
185
+ mythik reveal element save-button --app my-app
186
+ ```
187
+
188
+ For React Native Android emulator smoke, reverse the bridge port:
189
+
190
+ ```bash
191
+ adb reverse tcp:17373 tcp:17373
192
+ ```
193
+
194
+ Reveal is development tooling. Do not enable it in production, do not
195
+ commit tokens, and expose only the state paths the agent needs.
196
+ Redact auth, secrets, tokens, passwords, API keys, and session values.
197
+ When a runtime symptom is visible but the cause is unknown, agents
198
+ should ask Reveal before proposing a patch.
199
+
135
200
  ### CLI store configuration
136
201
 
137
- The CLI can read and write specs from `memory`, `file`, `supabase`, `sqlserver`, `postgres`, `mysql`, and `sqlite` stores. SQL stores share the same commands and the same required edit loop: `manifest -> elements -> patch -> validate`.
202
+ The CLI can read and write specs from `memory`, `file`, `supabase`, `sqlserver`, `postgres`, `mysql`, and `sqlite` stores. SQL stores share the same commands and the same required edit loop: `manifest -> elements -> patch --from-file --author -> validate -> verify`.
138
203
 
139
204
  ```bash
140
205
  # SQLite: local development, tests, demos, lightweight deployments
@@ -166,12 +231,14 @@ Environment equivalents: `MYTHIK_STORE`, `MYTHIK_DATABASE_URL`, `MYTHIK_SQLITE_F
166
231
 
167
232
  Per reference-doc rule 248: three approved forms.
168
233
 
169
- - **Shell**: `mythik push <id> --from-file spec.json` and `mythik patch <id> --from-file patch.json` (cross-shell ergonomic for any spec/patch containing `$state`, `$template`, `$auth`, or `$row`)
170
- - **Bulk**: `mythik push --from-dir <folder>` — sequential per-file push of every `*.json`. Continue-on-error: failures don't stop subsequent specs. No rollback. Partial state recovers by fixing failures and re-running.
234
+ - **Shell**: `mythik push <id> --from-file spec.json --author <agent>` and `mythik patch <id> --from-file patch.json --author <agent>` (cross-shell ergonomic for any spec/patch containing `$state`, `$template`, `$auth`, or `$row`)
235
+ - **Bulk**: `mythik push --from-dir <folder> --author <agent>` — sequential per-file push of every `*.json`. Continue-on-error: failures don't stop subsequent specs. No rollback. Partial state recovers by fixing failures and re-running.
171
236
  - **Programmatic**: `import { runPush, runPatch, parsePatchInput, type PushResult } from 'mythik-cli/api'` — same code path as the binary, no shell. Pass `json: true` in options to receive structured command output via `JSON.parse(result.output)`. `runPatch` success output includes `versioned: boolean` and `version?: number` when a versioned store + `author` creates history.
172
237
 
173
238
  `SpecStore.save()` is `@internal` — calling it from application code skips validation and produces runtime errors only at render time. The three forms above are the only validated paths.
174
239
 
240
+ Existing persisted specs are protected by the Agent Protocol: full replacements require `--replace`, and persisted writes require `--author <name>` for versioned stores or explicit `--allow-unversioned` for intentionally unversioned stores.
241
+
175
242
  ### Pre-push linting
176
243
 
177
244
  Run `mythik lint` to detect anti-patterns before pushing. Three approved invocations:
@@ -450,7 +517,7 @@ Any action binding may include `params.skipIf`. Mythik resolves `skipIf` at disp
450
517
  - Sets `/ui/loading` while in flight
451
518
  - On error: sets `/ui/lastError` with status and message
452
519
  - Optional `errorTarget` writes the same structured error to a screen-owned path and clears that path on success. Use it for critical screen-load fetches instead of relying only on global `/ui/lastError`
453
- - Auth headers auto-injected for `authDomains` URLs
520
+ - Auth headers auto-injected for `apiBaseUrl` exact-origin framework URLs and explicit `authDomains` hosts
454
521
 
455
522
  ### Transactions (Optimistic Updates)
456
523
 
@@ -556,6 +623,18 @@ Chain: `$array: "filter"` → `$array: "search"` → `$array: "slice"` for filte
556
623
 
557
624
  Inside repeated elements: `$item`, `$index`, `$bindItem` access current item.
558
625
 
626
+ Repeated rows can opt into horizontal/wrapping layout without changing the child primitive:
627
+
628
+ ```json
629
+ "repeat": {
630
+ "source": { "$state": "/filters" },
631
+ "key": "id",
632
+ "layout": { "direction": "horizontal", "gap": 8, "wrap": true }
633
+ }
634
+ ```
635
+
636
+ For grouped repeats, group headers and footers remain vertical; `repeat.layout` applies only to each group's item row.
637
+
559
638
  ### Grouped Repeat
560
639
 
561
640
  **Client-side** (engine groups flat array): `groupBy: "category"`
@@ -855,7 +934,7 @@ Interceptors:
855
934
  "interceptors": { "logging": true, "timeout": { "ms": 15000 }, "retryOnError": { "maxRetries": 2, "statuses": [502, 503] } }
856
935
  ```
857
936
 
858
- Security: tokens NEVER in state (engine closure only). `$auth` blocks token/password fields. Auth headers only for `authDomains`. Credentials cleared after login. Rate limit: 5/min. Cross-tab sync automatic.
937
+ Security: tokens NEVER in state (engine closure only). `$auth` blocks token/password fields. Auth headers are injected only for the host `apiBaseUrl` exact origin or explicit `authDomains`. Credentials cleared after login. Rate limit: 5/min. Cross-tab sync automatic.
859
938
 
860
939
  Custom JWT provider response mapping: `tokenPath`, `refreshTokenPath`, and `userPath` are dot paths against the full login/refresh response. `rolePath` and `rolesPath` use a compat dual contract: plain keys like `"role"` / `"roles"` resolve inside the extracted `userPath` object; dotted paths like `"user.role"` or `"data.user.role"` resolve against the full response. If no role or roles are found, Mythik uses `defaultRole` (`"user"` by default) and warns in development.
861
940
 
@@ -1167,7 +1246,7 @@ Reference: `"hover": { "$token": "motion.presets.hoverLift" }`
1167
1246
 
1168
1247
  ## Component Variants
1169
1248
 
1170
- Token-driven styling. Define in `tokens.components.{type}.{variant}`:
1249
+ Token-driven styling. **Use this before copying the same style block into multiple elements.** Define in `tokens.components.{type}.{variant}` and reference it from `props.variant`:
1171
1250
 
1172
1251
  ```json
1173
1252
  "tokens": {
@@ -1184,7 +1263,9 @@ Token-driven styling. Define in `tokens.components.{type}.{variant}`:
1184
1263
  ```
1185
1264
  Usage: `{ "type": "button", "props": { "label": "Save", "variant": "primary" } }`
1186
1265
  `$path` references (`$colors.primary`) resolve against active tokens — dark mode works automatically.
1187
- Element-level style/hover overrides variant (variant is base, element is override). Supports: `style`, `hover`, `active`, `focus`, `transition`.
1266
+ Element-level style/hover overrides variant (variant is base, element is override). Supports: `style`, `hover`, `active`, `focus`, `transition`.
1267
+
1268
+ Important boundary: variants are for primitive visual slots. They do **not** carry arbitrary primitive props such as `table.headerStyle`, `table.rowStyle`, `columns`, or action bindings. For prop-bearing reusable patterns, define an AppSpec `templates` entry and use `$prop` to pass through data/columns/actions. Example: use `tokens.components.box.tableCard` for card styling, but use an `ops-table` template when several tables need the same `headerStyle` + `rowStyle` chrome. For composed filter chips (`touchable` + `text`), put static shared styling in variants and keep state/item-specific active colors on the element.
1188
1269
 
1189
1270
  ## Skeleton & Export
1190
1271
 
@@ -1401,8 +1482,8 @@ When the framework changes the schema in a future version, this section will gai
1401
1482
  28. loginScreen always accessible — prevents redirect loops
1402
1483
  29. Login renders fullscreen (no layout) — appears automatically when not authenticated
1403
1484
  30. Login paths: `/screens/login/...` not `/form/...` — clears on logout with statePolicy reset
1404
- 31. Auth headers auto-injected for `authDomains` — no manual headers in fetch
1405
- 32. Use `variant` prop for component styling — don't copy style objects
1485
+ 31. Auth headers auto-injected for `apiBaseUrl` exact-origin framework URLs and explicit `authDomains` hosts — no manual headers in fetch
1486
+ 32. Use `variant` prop for reusable primitive styling — don't copy style objects; put `variant` inside `props`
1406
1487
  33. `$path` references in variants resolve with dark mode automatically
1407
1488
  34. Use `mythik push` for spec creation — no seed scripts
1408
1489
  35. AppSpec patches use `/layout/elements/` not `/elements/`
@@ -1437,11 +1518,11 @@ When the framework changes the schema in a future version, this section will gai
1437
1518
  64. Custom element black-box boundary — consumer's instance-level `animations`, `hover`, `active`, `focus`, `motion`, `style`, `visible`, and `key` apply to the OUTER primitive only. Inner primitives are the author's domain and are not reachable from the consumer. Identity cascade (rule 63 / cascade level 1) still reaches inner primitives; consumer cannot override them directly
1438
1519
  65. `$prop` is nearest-enclosing-custom-element-scoped — inside a custom element's render tree, `$prop` resolves against THAT element's merged props. Nesting a custom element inside another pushes a new prop context; the outer's props are shadowed, not merged. Pass values explicitly via prop declarations when nested access is needed
1439
1520
  66. `$children` marker in custom element render trees — authors write `"$children"` as a string item in their render tree `children` array to mark where consumer children are inserted during expansion. Multiple markers each splice the full consumer children. Same slotting semantics as spec templates
1440
- 67. `variant` is a PROP — place it inside `props`, never as a top-level element field. Applies to built-in primitives and Layer 3 custom elements alike: `{ "type": "stat-card", "props": { "variant": "primary", ... } }`. Top-level `variant` is silently ignored (the renderer reads `props.variant` only). Consumer may drive it dynamically via an expression: `"variant": { "$switch": { "$state": "/filter/recordType" }, "cases": { "1": "active" }, "default": "inactive" }`
1521
+ 67. `variant` is a PROP — place it inside `props`, never as a top-level element field. Applies to built-in primitives and Layer 3 custom elements alike: `{ "type": "stat-card", "props": { "variant": "primary", ... } }`. Top-level `variant` is silently ignored (the renderer reads `props.variant` only). Consumer may drive it dynamically via an expression. Variants are style/interaction slots only; use templates/custom elements when the reusable pattern must carry props like table `headerStyle`/`rowStyle` or composed children.
1441
1522
  68. Custom elements may expose action-chain props — author declares a prop (e.g. `onSelect`, type `"array"`) and the render tree's `on:<event>` references it as `{ "$prop": "onSelect" }`. Consumers supply the full action array as the prop value; inner `$state` / `$template` / `$item` inside those actions resolve at press time with the current state (not at render time). Enables tabs, menu items, and similar patterns where the consumer owns the action chain
1442
1523
  69. Never use `$row` literal — there is no `$row` expression handler. Read row data via `$state: '/ui/selectedRow/<key>'` (framework writes clicked row before column action dispatch).
1443
1524
  70. Never combine `crud: {}` with `endpoint.path` ending in `/:id`. CRUD operations auto-append `/:id` to PUT/DELETE routes. Declaring `path: '/api/x/:id'` produces `/api/x/:id/:id`.
1444
- 71. Never include `:port` in `auth.authDomains[]` entries. The matcher uses `URL.hostname` for comparison ports are silently stripped, leading to apparent matches that don't actually match.
1525
+ 71. Prefer `MythikApp apiBaseUrl` for the app's own backend. Relative runtime URLs (`/api/...`) resolve against that base and inherit auth only for the exact base origin when `authDomains` is omitted or empty. Providing `authDomains` disables the automatic `apiBaseUrl` origin allowlist; list every trusted absolute host explicitly. Never include `:port` in `auth.authDomains[]` entries because that matcher uses hostnames.
1445
1526
  72. `spatial-map` is the generic SVG/data-first primitive for floor plans, seating maps, parking maps, warehouse layouts, hospital beds, and similar spatial workflows. JSON `onItemPress` actions write item context to `/ui/selectedSpatialItem` before lazy dispatch. Compose domain menus/drawers/modals externally; do not encode restaurant-specific behavior into the primitive. Use `{ "$state": "/path" }` expressions for dynamic `items`, `zones`, `mode`, and `statusStyles`; do not invent `*Path` prop aliases.
1446
1527
  73. For `spatial-map` editing, use `editPolicy` for movement rules and `onItemChange` for persistence. Do not use `interactionPolicy` for drag/keyboard movement. In JSON specs, persist moved items with `$array: "replace"` reading from `/ui/spatialItemChange` or the configured `itemChangePath`; use plain `setState` for non-undoable screens and wrap the same value in `editorCommit` for undoable editors.
1447
1528
  74. For JSON-created client-side item ids, use `$uniqueId` with an explicit `source`, `field`, `prefix`, and optional `padding`. It is deterministic and scoped to the source array; use it for local/editor objects, not as a substitute for database primary keys when a backend owns identity.
@@ -1456,12 +1537,12 @@ When the framework changes the schema in a future version, this section will gai
1456
1537
  82. Use `editorSessions.<id>.persistence` plus `editorSave` for generic editor document persistence. The AI should not hand-compose `transaction` + `editorMarkSaved` for normal editor saves. `editorSave` captures the tracked paths snapshot, persists it through the host fetcher with URL-guard checks, and marks only the sent snapshot as saved after success. Failed saves keep `dirty: true` and expose save metadata under `/ui/editorSessions/<id>`.
1457
1538
  83. Use `navigation.editorSessionGuard` for generic unsaved-changes protection in app specs instead of hand-rolled JSON dirty checks. Configure `sessions`, render your own JSON modal from `/ui/navigationGuard/pending`, call `navigationGuardCancel`, `navigationGuardSaveAndProceed`, `navigationGuardProceed`, or `navigationGuardDiscardAndProceed`, and keep `pendingPath` under a consumer-owned `/ui/<segment>` path that does not collide with reserved framework paths. `navigationGuardSaveAndProceed` is the normal save-first UX for persisted editor sessions; `navigationGuardProceed` is a low-level non-destructive retry that only resumes after the pending editor sessions are already clean; use `navigationGuardDiscardAndProceed` when the user explicitly chooses to abandon unsaved tracked-path changes.
1458
1539
  84. In React apps without auth framework fetch, pass `fetcher` to `MythikApp` when editor sessions use `editorSave` or `navigationGuardSaveAndProceed`. Auth-enabled apps continue to use the framework fetch produced by auth interceptors. Do not route save-and-continue through `/ui/lastError` or a hand-composed fetch action; the editor session engine owns save metadata under `/ui/editorSessions/<id>`.
1459
- 85. For existing spec edits, the required AI loop is `mythik manifest` -> `mythik elements` -> `mythik patch --from-file` -> verify. Manifest tells you the structure; elements gives the exact JSON you are changing; patch is the validated write path. Do not skip directly to full-spec `pull`/rewrite/push for a local change, do not mutate DB rows manually, and do not call `SpecStore.save()` from app code. Use `push` for new specs or intentional full replacement only.
1540
+ 85. For existing spec edits, the required AI loop is `mythik manifest` -> `mythik elements` -> `mythik patch --from-file --author` -> `mythik validate` -> verify. Manifest tells you the structure; elements gives the exact JSON you are changing; patch is the validated write path. Do not skip directly to full-spec `pull`/rewrite/push for a local change, do not mutate DB rows manually, and do not call `SpecStore.save()` from app code. Use `push` for new specs or intentional full replacement only; existing specs require explicit `--replace`.
1460
1541
  86. Mythik's AI documentation ships with the `mythik` npm package. Before generating or modifying specs, locate it with `mythik docs path` and start from `docs/llms.txt`, `docs/consumer/ai-context.md`, and `docs/wiki/compiled/README.md`. Use `mythik docs copy ./mythik-docs` when a project-local copy is easier to hand to an AI agent.
1461
1542
  87. DNA numeric seeds (`roundness`, `density`, `depth`, `formality`) are canonical `0–1` values. Generate `0.7`, not `70`. The runtime tolerates legacy `0–100` values by normalizing any numeric seed greater than `1` with `/100` during DNA derivation, including initial AppSpec load and runtime `updateTokens`.
1462
1543
  88. API query endpoints can combine `pagination: "offset"` with `scopeFilter`. For generated counts, Mythik applies the scope filter to the source query before `COUNT(*)`, so paginated totals remain tenant-scoped. Prefer generated counts. If a custom `endpoint.count` is truly needed with `scopeFilter`, include `{{scopeWhere[:alias]}}` or `{{scopeAnd[:alias]}}`; Mythik expands the macro to the correct scope predicate and removes it for bypass roles. Other custom count SQL is left verbatim. Use `:alias` for JOIN/subquery counts, and do not reference internal scope params directly.
1463
1544
  89. Transaction `confirm` failures from `fetch` preserve backend error details for `onError`. Read `/tx/error/message` for the best available message; when the backend returns `{ error: { code, message } }`, Mythik keeps that message, `code`, HTTP `status`, and raw `data` after rollback. Do not parse `/ui/lastError` from transaction specs.
1464
- 90. For SQL-backed stores and servers, use the generic `mythik/server` SQL boundary. Initialize tables with `mythik init-store --dialect <sqlserver|postgres|mysql|sqlite>` or inspect DDL with `--dry-run`; configure the CLI with `--store`, `--url`/`--filename`/SQL Server flags, or `MYTHIK_*` environment variables; and keep spec edits on the required `manifest -> elements -> patch --from-file -> validate` loop. Write custom API SQL in the selected dialect with Mythik named params (`@name`); Mythik does not translate custom SQL between dialects.
1545
+ 90. For SQL-backed stores and servers, use the generic `mythik/server` SQL boundary. Initialize tables with `mythik init-store --dialect <sqlserver|postgres|mysql|sqlite>` or inspect DDL with `--dry-run`; configure the CLI with `--store`, `--url`/`--filename`/SQL Server flags, or `MYTHIK_*` environment variables; and keep spec edits on the required `manifest -> elements -> patch --from-file --author -> validate -> verify` loop. Write custom API SQL in the selected dialect with Mythik named params (`@name`); Mythik does not translate custom SQL between dialects.
1465
1546
  91. Event arrays may mix normal actions and transaction bindings. Mythik executes them sequentially and awaits each transaction before continuing. Use this when a flow needs a small action before or after an optimistic transaction; do not wrap a transaction inside another transaction phase.
1466
1547
  92. `$ref` and `$template` placeholders can read nested values from `$let` object bindings with dot notation, for example `{ "$ref": "user.name" }` or `${user.name}`. If a dotted `$ref` segment is missing, runtime throws an unknown `$ref` error rather than silently returning undefined.
1467
1548
  93. Use `params.skipIf` for a dispatch-time action guard when an action should be skipped but the surrounding action chain should continue. `skipIf` is resolved before other params and is removed before the action handler runs. Do not use `skipIf` as a substitute for form validation or transaction rollback.
@@ -1470,3 +1551,8 @@ When the framework changes the schema in a future version, this section will gai
1470
1551
  96. SQL adapters are optional peer dependencies, not installed-by-default runtime payload. Browser-only apps install `mythik mythik-react` without database drivers. SQL-backed stores/servers must install exactly one selected adapter: `mssql`, `pg`, `mysql2`, or `better-sqlite3`. SQLite uses native `better-sqlite3`; native-build helper warnings from that adapter are not Mythik runtime failures.
1471
1552
  97. Missing SQL adapter errors are actionable. The thrown `SqlDriverError` includes `packageName`, `installCommand`, and a message with the exact `npm install ...` command for the selected dialect.
1472
1553
  98. For native Expo apps, install renderer peer packages with `npx expo install react-native-svg react-native-reanimated react-native-worklets ...` before installing `mythik mythik-react-native`, so Expo selects native module versions that match the app SDK. Keep `react-native-reanimated/plugin` last in the app Babel config. Keep `root` as a stable string element id; use `$platform` only inside localized props, style, children, or values. `$platform` matches exact platform keys first, then falls back from `ios`/`android` to `native`; it has no `default` key. Before using a primitive in native specs, check `REACT_NATIVE_PRIMITIVE_SUPPORT` or `getReactNativePrimitiveSupport` from `mythik-react-native`; native-milestone primitives should fail visibly instead of being treated as supported.
1554
+ 99. Use Mythik Reveal before guessing about running-app behavior. Start `mythik reveal start`, wire the host `reveal` config in development, then inspect with `mythik reveal apps`, `context`, `current`, `screen`, or `element`. Reveal returns live renderer/environment/spec/state/diagnostics/events with redaction, so agents can diagnose runtime behavior from structured context instead of screenshots, stale pulled specs, or source-code assumptions. Never enable Reveal in production, never commit the token, and include/redact state paths deliberately.
1555
+ 100. **Use Mythik Agent Context, Agent Protocol, and Sentinel for fresh AI agents**. Run `mythik agent init codex`, `mythik agent init claude`, or `mythik agent init all` to install project-local agent instructions. `codex` creates/updates `AGENTS.md`, `claude` creates/updates `CLAUDE.md`, and `all` updates both; existing file content outside the Mythik-managed `<!-- mythik-agent-protocol:start -->` / `<!-- mythik-agent-protocol:end -->` block is preserved. Generate live project context with `mythik agent context --app <id> --include-screens --out .mythik/agent/context.md`. Existing stored specs stay on `manifest -> elements -> patch --from-file --author -> validate -> verify`. The active store is the source of truth; local spec files are drafts/snapshots/migrations/fixtures unless a file store is intentionally configured. Use TOON for large reads when supported, and use `mythik lint` for new local drafts, replacement files, bulk imports/migrations, and relevant consumer code changes. Mythik Sentinel runs from the CLI as advisory coaching: first project use reminds the agent to initialize instructions, repeated full replacement intent warns the agent back to patch-first edits, identical `push --replace` writes are skipped as `skipped: true`, and no spec contents or credentials are persisted in the Sentinel cache. Disable only with `MYTHIK_SENTINEL=off` for exceptional automation.
1556
+ 101. `repeat.layout` controls repeated item rows (`direction`, `gap`, `wrap`). In grouped repeats, headers/footers remain vertical and the layout applies only to the group item row.
1557
+ 102. `select` normalizes option identity for display: DB integer IDs and string option values match, `0` is a valid selected value, and null/undefined show the placeholder. Values emitted by existing `onChange` remain string-compatible.
1558
+ 103. `text.on.press` is supported for inline interactive text. Web receives button semantics plus keyboard activation; React Native receives `accessibilityRole="button"`. Use `button` or `touchable` for primary/larger actions.