@velarscript/cli 0.10.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 (228) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +117 -0
  3. package/dist/application-package-host.d.ts +5 -0
  4. package/dist/application-package-host.d.ts.map +1 -0
  5. package/dist/application-package-host.js +67 -0
  6. package/dist/application-package-host.js.map +1 -0
  7. package/dist/bounded-text.d.ts +2 -0
  8. package/dist/bounded-text.d.ts.map +1 -0
  9. package/dist/bounded-text.js +13 -0
  10. package/dist/bounded-text.js.map +1 -0
  11. package/dist/browser-process-owner.d.ts +15 -0
  12. package/dist/browser-process-owner.d.ts.map +1 -0
  13. package/dist/browser-process-owner.js +151 -0
  14. package/dist/browser-process-owner.js.map +1 -0
  15. package/dist/browser-test-runner.d.ts +11 -0
  16. package/dist/browser-test-runner.d.ts.map +1 -0
  17. package/dist/browser-test-runner.js +1022 -0
  18. package/dist/browser-test-runner.js.map +1 -0
  19. package/dist/build-engine-tool.d.ts +3 -0
  20. package/dist/build-engine-tool.d.ts.map +1 -0
  21. package/dist/build-engine-tool.js +34 -0
  22. package/dist/build-engine-tool.js.map +1 -0
  23. package/dist/build-staging.d.ts +3 -0
  24. package/dist/build-staging.d.ts.map +1 -0
  25. package/dist/build-staging.js +3 -0
  26. package/dist/build-staging.js.map +1 -0
  27. package/dist/bundled-application-package-host-registry.d.ts +10 -0
  28. package/dist/bundled-application-package-host-registry.d.ts.map +1 -0
  29. package/dist/bundled-application-package-host-registry.js +16 -0
  30. package/dist/bundled-application-package-host-registry.js.map +1 -0
  31. package/dist/bundled-extension-registry.d.ts +22 -0
  32. package/dist/bundled-extension-registry.d.ts.map +1 -0
  33. package/dist/bundled-extension-registry.js +10 -0
  34. package/dist/bundled-extension-registry.js.map +1 -0
  35. package/dist/canonical-path.d.ts +3 -0
  36. package/dist/canonical-path.d.ts.map +1 -0
  37. package/dist/canonical-path.js +30 -0
  38. package/dist/canonical-path.js.map +1 -0
  39. package/dist/cli.d.ts +3 -0
  40. package/dist/cli.d.ts.map +1 -0
  41. package/dist/cli.js +1274 -0
  42. package/dist/cli.js.map +1 -0
  43. package/dist/config.d.ts +26 -0
  44. package/dist/config.d.ts.map +1 -0
  45. package/dist/config.js +403 -0
  46. package/dist/config.js.map +1 -0
  47. package/dist/deployment-verifier.d.ts +34 -0
  48. package/dist/deployment-verifier.d.ts.map +1 -0
  49. package/dist/deployment-verifier.js +260 -0
  50. package/dist/deployment-verifier.js.map +1 -0
  51. package/dist/dev-server.d.ts +3 -0
  52. package/dist/dev-server.d.ts.map +1 -0
  53. package/dist/dev-server.js +464 -0
  54. package/dist/dev-server.js.map +1 -0
  55. package/dist/embedded-modules.d.ts +17 -0
  56. package/dist/embedded-modules.d.ts.map +1 -0
  57. package/dist/embedded-modules.js +33 -0
  58. package/dist/embedded-modules.js.map +1 -0
  59. package/dist/extension-metadata.d.ts +23 -0
  60. package/dist/extension-metadata.d.ts.map +1 -0
  61. package/dist/extension-metadata.js +254 -0
  62. package/dist/extension-metadata.js.map +1 -0
  63. package/dist/file-integrity.d.ts +7 -0
  64. package/dist/file-integrity.d.ts.map +1 -0
  65. package/dist/file-integrity.js +14 -0
  66. package/dist/file-integrity.js.map +1 -0
  67. package/dist/framework-host.d.ts +12 -0
  68. package/dist/framework-host.d.ts.map +1 -0
  69. package/dist/framework-host.js +46 -0
  70. package/dist/framework-host.js.map +1 -0
  71. package/dist/host-error.d.ts +6 -0
  72. package/dist/host-error.d.ts.map +1 -0
  73. package/dist/host-error.js +54 -0
  74. package/dist/host-error.js.map +1 -0
  75. package/dist/installed-package.d.ts +9 -0
  76. package/dist/installed-package.d.ts.map +1 -0
  77. package/dist/installed-package.js +23 -0
  78. package/dist/installed-package.js.map +1 -0
  79. package/dist/language-server-bundle-entry.d.ts +2 -0
  80. package/dist/language-server-bundle-entry.d.ts.map +1 -0
  81. package/dist/language-server-bundle-entry.js +7 -0
  82. package/dist/language-server-bundle-entry.js.map +1 -0
  83. package/dist/language-server-tool.d.ts +3 -0
  84. package/dist/language-server-tool.d.ts.map +1 -0
  85. package/dist/language-server-tool.js +30 -0
  86. package/dist/language-server-tool.js.map +1 -0
  87. package/dist/language-server.d.ts +9 -0
  88. package/dist/language-server.d.ts.map +1 -0
  89. package/dist/language-server.js +1733 -0
  90. package/dist/language-server.js.map +1 -0
  91. package/dist/mechanical-fixer.d.ts +30 -0
  92. package/dist/mechanical-fixer.d.ts.map +1 -0
  93. package/dist/mechanical-fixer.js +103 -0
  94. package/dist/mechanical-fixer.js.map +1 -0
  95. package/dist/module-assets.d.ts +11 -0
  96. package/dist/module-assets.d.ts.map +1 -0
  97. package/dist/module-assets.js +90 -0
  98. package/dist/module-assets.js.map +1 -0
  99. package/dist/npm.d.ts +23 -0
  100. package/dist/npm.d.ts.map +1 -0
  101. package/dist/npm.js +456 -0
  102. package/dist/npm.js.map +1 -0
  103. package/dist/official-language-server-extensions.d.ts +2 -0
  104. package/dist/official-language-server-extensions.d.ts.map +1 -0
  105. package/dist/official-language-server-extensions.js +38 -0
  106. package/dist/official-language-server-extensions.js.map +1 -0
  107. package/dist/official-script-language-service.d.ts +2 -0
  108. package/dist/official-script-language-service.d.ts.map +1 -0
  109. package/dist/official-script-language-service.js +10 -0
  110. package/dist/official-script-language-service.js.map +1 -0
  111. package/dist/official-tool-assets.d.ts +3 -0
  112. package/dist/official-tool-assets.d.ts.map +1 -0
  113. package/dist/official-tool-assets.js +41 -0
  114. package/dist/official-tool-assets.js.map +1 -0
  115. package/dist/ownership-graph.d.ts +46 -0
  116. package/dist/ownership-graph.d.ts.map +1 -0
  117. package/dist/ownership-graph.js +255 -0
  118. package/dist/ownership-graph.js.map +1 -0
  119. package/dist/package-manager.d.ts +20 -0
  120. package/dist/package-manager.d.ts.map +1 -0
  121. package/dist/package-manager.js +345 -0
  122. package/dist/package-manager.js.map +1 -0
  123. package/dist/packaged-official-tool.d.ts +8 -0
  124. package/dist/packaged-official-tool.d.ts.map +1 -0
  125. package/dist/packaged-official-tool.js +32 -0
  126. package/dist/packaged-official-tool.js.map +1 -0
  127. package/dist/preview-server.d.ts +11 -0
  128. package/dist/preview-server.d.ts.map +1 -0
  129. package/dist/preview-server.js +163 -0
  130. package/dist/preview-server.js.map +1 -0
  131. package/dist/production-build.d.ts +57 -0
  132. package/dist/production-build.d.ts.map +1 -0
  133. package/dist/production-build.js +332 -0
  134. package/dist/production-build.js.map +1 -0
  135. package/dist/production-verifier.d.ts +9 -0
  136. package/dist/production-verifier.d.ts.map +1 -0
  137. package/dist/production-verifier.js +268 -0
  138. package/dist/production-verifier.js.map +1 -0
  139. package/dist/program-runner.d.ts +7 -0
  140. package/dist/program-runner.d.ts.map +1 -0
  141. package/dist/program-runner.js +98 -0
  142. package/dist/program-runner.js.map +1 -0
  143. package/dist/project-check.d.ts +31 -0
  144. package/dist/project-check.d.ts.map +1 -0
  145. package/dist/project-check.js +95 -0
  146. package/dist/project-check.js.map +1 -0
  147. package/dist/project-format.d.ts +6 -0
  148. package/dist/project-format.d.ts.map +1 -0
  149. package/dist/project-format.js +21 -0
  150. package/dist/project-format.js.map +1 -0
  151. package/dist/project-semantic.d.ts +62 -0
  152. package/dist/project-semantic.d.ts.map +1 -0
  153. package/dist/project-semantic.js +1079 -0
  154. package/dist/project-semantic.js.map +1 -0
  155. package/dist/project-session.d.ts +36 -0
  156. package/dist/project-session.d.ts.map +1 -0
  157. package/dist/project-session.js +339 -0
  158. package/dist/project-session.js.map +1 -0
  159. package/dist/project-task-bundle-entry.d.ts +2 -0
  160. package/dist/project-task-bundle-entry.d.ts.map +1 -0
  161. package/dist/project-task-bundle-entry.js +18 -0
  162. package/dist/project-task-bundle-entry.js.map +1 -0
  163. package/dist/project-task-invocation.d.ts +16 -0
  164. package/dist/project-task-invocation.d.ts.map +1 -0
  165. package/dist/project-task-invocation.js +72 -0
  166. package/dist/project-task-invocation.js.map +1 -0
  167. package/dist/project-task-tool.d.ts +3 -0
  168. package/dist/project-task-tool.d.ts.map +1 -0
  169. package/dist/project-task-tool.js +61 -0
  170. package/dist/project-task-tool.js.map +1 -0
  171. package/dist/project.d.ts +71 -0
  172. package/dist/project.d.ts.map +1 -0
  173. package/dist/project.js +1750 -0
  174. package/dist/project.js.map +1 -0
  175. package/dist/reproduction.d.ts +50 -0
  176. package/dist/reproduction.d.ts.map +1 -0
  177. package/dist/reproduction.js +321 -0
  178. package/dist/reproduction.js.map +1 -0
  179. package/dist/script-language-service.d.ts +82 -0
  180. package/dist/script-language-service.d.ts.map +1 -0
  181. package/dist/script-language-service.js +18 -0
  182. package/dist/script-language-service.js.map +1 -0
  183. package/dist/source-limits.d.ts +5 -0
  184. package/dist/source-limits.d.ts.map +1 -0
  185. package/dist/source-limits.js +19 -0
  186. package/dist/source-limits.js.map +1 -0
  187. package/dist/standalone-build.d.ts +15 -0
  188. package/dist/standalone-build.d.ts.map +1 -0
  189. package/dist/standalone-build.js +76 -0
  190. package/dist/standalone-build.js.map +1 -0
  191. package/dist/standard-modules.d.ts +22 -0
  192. package/dist/standard-modules.d.ts.map +1 -0
  193. package/dist/standard-modules.js +3066 -0
  194. package/dist/standard-modules.js.map +1 -0
  195. package/dist/static-deployment.d.ts +48 -0
  196. package/dist/static-deployment.d.ts.map +1 -0
  197. package/dist/static-deployment.js +142 -0
  198. package/dist/static-deployment.js.map +1 -0
  199. package/dist/test-output.d.ts +20 -0
  200. package/dist/test-output.d.ts.map +1 -0
  201. package/dist/test-output.js +119 -0
  202. package/dist/test-output.js.map +1 -0
  203. package/dist/test-runner.d.ts +13 -0
  204. package/dist/test-runner.d.ts.map +1 -0
  205. package/dist/test-runner.js +226 -0
  206. package/dist/test-runner.js.map +1 -0
  207. package/dist/typescript-declarations.d.ts +20 -0
  208. package/dist/typescript-declarations.d.ts.map +1 -0
  209. package/dist/typescript-declarations.js +1609 -0
  210. package/dist/typescript-declarations.js.map +1 -0
  211. package/dist/uncaught-program-error.d.ts +24 -0
  212. package/dist/uncaught-program-error.d.ts.map +1 -0
  213. package/dist/uncaught-program-error.js +105 -0
  214. package/dist/uncaught-program-error.js.map +1 -0
  215. package/dist/unowned-errors.d.ts +56 -0
  216. package/dist/unowned-errors.d.ts.map +1 -0
  217. package/dist/unowned-errors.js +82 -0
  218. package/dist/unowned-errors.js.map +1 -0
  219. package/dist/version.d.ts +3 -0
  220. package/dist/version.d.ts.map +1 -0
  221. package/dist/version.js +3 -0
  222. package/dist/version.js.map +1 -0
  223. package/dist/workspace-index.d.ts +78 -0
  224. package/dist/workspace-index.d.ts.map +1 -0
  225. package/dist/workspace-index.js +493 -0
  226. package/dist/workspace-index.js.map +1 -0
  227. package/package.json +45 -0
  228. package/skill/ai-skill.md +749 -0
@@ -0,0 +1,749 @@
1
+ # The VelarScript AI skill brief
2
+
3
+ This is the language brief for AI agents working in a VelarScript project. It
4
+ is agent-agnostic markdown, it ships inside the toolchain, and `velar skill`
5
+ prints it verbatim — no network, no external documentation. Every code fence
6
+ in this file compiles against the current compiler; the brief cannot drift
7
+ from the language.
8
+
9
+ ## What VelarScript is
10
+
11
+ VelarScript (Vel) is an extensible programming language for the AI era where the
12
+ framework is the language: `component`, `state`, and `look` are keywords rather
13
+ than imports, so one checked language covers markup, styling, state, and tests.
14
+ You write the Vel and every later change; the owner reads the result; the
15
+ compiler guards each change. Vel's parents are JavaScript and Python: ask the
16
+ mother about behavior — the program runs inside her — and the father about spelling.
17
+ **Write on your priors, and the compiler will teach you the rest**: where JS
18
+ offers five spellings Vel keeps one, and every removed or mistaken spelling
19
+ gets a diagnostic naming the one current spelling. Expect to self-correct in
20
+ one round.
21
+
22
+ The working loop:
23
+
24
+ 1. Write ordinary code the way a fluent JS/Python author would.
25
+ 2. Run `velar check`. Do exactly what each diagnostic says.
26
+ 3. Run `velar fix` to apply every rewrite the diagnostics already named
27
+ (retired spellings with one successor, line-ending semicolons, and the rest
28
+ of that family); it never rewrites anything that needs a decision, so what
29
+ is left after it is the real work.
30
+ 4. Run `velar test`. Run `velar format` to settle layout.
31
+
32
+ Do not invent workarounds for a diagnostic; it is the language telling you
33
+ the canonical spelling.
34
+
35
+ What a program can compute needs no import; what reaches outside the program
36
+ must be imported. A prefix is permanent only when it mirrors a namespace-shaped
37
+ JavaScript global, so there are exactly four and the list is closed:
38
+ `Json.` (`parse`, `tryParse`, `stringify`, `stableStringify`, `clone`,
39
+ `isSerializable`), `Promise.` (`all`, `race`, `sleep`, `timeout`, `retry`,
40
+ `map`, `series`), `Math.` (`pi`, `e`, `tau`, `infinity`, `min`, `max`, `clamp`,
41
+ `sign`, `trunc`, `sqrt`, `cbrt`, `pow`, `exp`, `log`, `log2`, `log10`, `sin`,
42
+ `cos`, `tan`, `asin`, `acos`, `atan`, `atan2`, `degrees`, `radians`, `hypot`,
43
+ `random`, `randomInt`, `gcd`, `lcm`), and `Text.` (`trimStart`, `trimEnd`,
44
+ `capitalize`, `title`, `lines`, `lineStarts`, `chunks`, `words`, `slug`,
45
+ `normalize`, `truncate`, `indent`, `dedent`, `normalizeWhitespace`, `utf8Size`,
46
+ `escapeHtml`, `codePoint`, `fromCodePoint`, `matches`, `findMatch`,
47
+ `findMatches`, `replaceMatches`, `splitPattern`). A string method is a core
48
+ operation; `Text.*` is the extension toolbox, and nothing moves between them.
49
+ Web visual builders are named imports from `velar/look` — there is no `Look`
50
+ global in JavaScript, so there is no `Look.` prefix here either. These names
51
+ need no import and cannot be shadowed: `const Text = 1` is rejected. `print`,
52
+ `str`, `number`, `equals`, and `range` are likewise in the Core prelude —
53
+ `equals(a, b)` is the one content-comparison spelling. One roster grants and
54
+ protects these names. Capabilities stay explicit imports. Durations use `ms` or
55
+ `s`, so write `await Promise.sleep(250ms)`, not a bare number.
56
+
57
+ Use checked binary/random/task, manifest Workers, pull WebSockets, Node SQLite,
58
+ Web binary IndexedDB, and official adapters. A direct `for index in range(...):` is a native counter; range as a value is a List. Use `UInt16Buffer` for 16-bit numeric state,
59
+ `UInt8Buffer` for compact data, and bounded `UInt32Builder`/`Float32Builder` values for variable-size numeric output.
60
+
61
+ ## Project setup
62
+
63
+ A VelarScript project is a directory containing a `velar.json` manifest. Let
64
+ the toolchain write it — `velar create my-app` scaffolds the Web template, and
65
+ `--template node|desktop|docs|library|component` picks another. Each writes
66
+ `velar.json`, a `package.json` whose scripts are the gates, a `src/` tree, a
67
+ passing test, and an `AGENTS.md`.
68
+
69
+ Writing the manifest yourself: `formatVersion` is required, `extensions` may be
70
+ omitted by a project that loads none, `entry` defaults to `src/main.vel`,
71
+ `outDir` to `dist`, and `publicDir` to `public`.
72
+
73
+ A Core project (CLI, library, Node) loads no extensions:
74
+
75
+ ```json
76
+ {
77
+ "formatVersion": 2,
78
+ "entry": "src/main.vel",
79
+ "extensions": []
80
+ }
81
+ ```
82
+
83
+ A Web project activates the Web extension **by package name**. That one line is
84
+ what turns on `component`, JSX, `state`, `computed`, `resource`, `action`,
85
+ `watch`, `look`, and `mount`; without it `component` is an unknown declaration
86
+ keyword and every JSX token is a parse error:
87
+
88
+ ```json
89
+ {
90
+ "formatVersion": 2,
91
+ "entry": "src/main.vel",
92
+ "extensions": ["@velarscript/web"]
93
+ }
94
+ ```
95
+
96
+ An extension owns its own manifest key — `"web": {"title": "My App"}` sets the
97
+ document title. Anything else in `velar.json` is rejected by name.
98
+
99
+ **Files the toolchain owns.** `dist/` is build output, and in a Web build the
100
+ toolchain writes `dist/index.html` itself — the title comes from `web.title`, the
101
+ favicon from `web.icon` (a `publicDir`-relative `.svg`/`.png`/`.ico` path; the
102
+ build fails if no such file exists), the
103
+ mount host is `<div id="app"></div>`, and assets are content-hashed. Never author
104
+ that file; a `public/index.html` is overwritten by the generated one, while
105
+ everything else in `public/` is copied through. `.velar/` is scratch; both belong in `.gitignore`.
106
+
107
+ **Tests.** `velar test` finds every `*.test.vel` file under the project (skipping
108
+ `outDir` and `publicDir`) and runs its `test "name":` blocks. The name is a
109
+ sentence about the code, quoted verbatim by the reporter and unique in its
110
+ module; the body may `await` directly and needs no `export`. A file that declares no tests is a failure rather than a skip.
111
+ `velar test --browser` runs `*.browser.test.vel` in a real browser — and bare
112
+ `--browser` is **Chromium only**. Three engines is `--browser=all` (or
113
+ `--browser all`), which is what any cross-browser claim costs;
114
+ `--browser=firefox` and `--browser=webkit` pick one.
115
+
116
+ **Separate the mounted entrypoint from testable code.** A test runs in Node with
117
+ no DOM, so a headless test that imports the module calling `mount` fails on
118
+ `document`. Keep the entry trivial and put everything worth testing in modules it
119
+ imports:
120
+
121
+ ```velar fragment
122
+ // src/main.vel — the mounted entrypoint; no test imports this file
123
+ import {App} from "./app.vel"
124
+
125
+ mount(<App />, "#app")
126
+ ```
127
+
128
+ Components, functions, and types live in `src/app.vel` and its neighbours;
129
+ `src/app.test.vel` tests those exports headlessly and
130
+ `src/app.browser.test.vel` drives the mounted application.
131
+
132
+ **A browser test drives the page; it does not build one.** Its body runs in the
133
+ test process against a page already running the production build, so `mount`,
134
+ JSX, and `document` are unavailable there — the driving surface is
135
+ `velar/web-test`, and selectors are ordinary CSS:
136
+
137
+ ```velar fragment
138
+ import {expect} from "velar/test"
139
+ import {browser, localStorage} from "velar/web-test"
140
+
141
+ test "adding a link shows it in the list":
142
+ await browser.open("/")
143
+ await browser.fill("#title", "Vel")
144
+ await browser.click("#add")
145
+ await browser.waitForText("[data-item]", "Vel")
146
+ expect(await browser.text("[data-count]")).toBe("1")
147
+ expect(await localStorage.get("reading")).toBe(`[{"title":"Vel"}]`)
148
+ ```
149
+
150
+ `browser` also carries `reload`, `press`, `select`, `attribute`, `count`,
151
+ `visible`, `waitFor`, `currentPath`, and `viewport`; `localStorage` and
152
+ `sessionStorage` read and write the page's raw storage. Any unhandled error —
153
+ in the page or in the test process — fails the test.
154
+
155
+ `velar/web-test` may be imported **only** from a `*.browser.test.vel` module.
156
+ Reaching for it from application code, from a plain `*.test.vel`, through
157
+ `import js`, or through a re-export is rejected on the import line — rename the
158
+ module or move the browser test into one of its own. Application code that
159
+ needs the page reaches it through `velar/browser` instead.
160
+
161
+ ## The traps your reflexes will hit
162
+
163
+ Everything in this table was hit by real models writing Vel blind. All but
164
+ the first two rows produce a teaching diagnostic, so `velar check` will catch
165
+ them; the first two are the **silent traps** in the list — read them twice.
166
+
167
+ | Your reflex | Write instead |
168
+ | --- | --- |
169
+ | `"${value}"` or `` `${value}` `` template interpolation | `f"{value}"` or `` f`{value}` ``. Only the `f` prefix interpolates, in either delimiter; `${...}` is legal literal text in every string — including a backtick one — so nothing warns you. |
170
+ | `a // b` floor division | `//` starts a comment, so the rest of the line disappears and `const c = a // b` silently binds `a`. Write `(a / b).floor()`. |
171
+ | `# comment` | `// comment` (`///` documents the following declaration). |
172
+ | `function f(...)`, `fn f(...)` | `def f(...) -> Result:` |
173
+ | `interface X:`, `record X:`, `struct X:` | `type X:` — one keyword for record shapes and aliases. |
174
+ | `items.length` | `items.size` (also on strings, Sets, Maps). |
175
+ | `items.push(x)` | `items.append(x)`. There is no `splice`/`shift`/`unshift`/mutating `sort`; use `insert`, `pop`, `remove`, `extend`, and the copying `sorted()`/`reversed()`. |
176
+ | `if value:` truthiness | Conditions accept only `bool`/`bool?`. Test presence explicitly: `if value != null:`. |
177
+ | `value is null` | `value == null` / `value != null` — `is` tests runtime types, `null` is a value. |
178
+ | `switch`, or an `if`/`else if` ladder over an enum | `match` with `case _:` as the only fallback. |
179
+ | Renaming a binding away from `type`, `state`, `from`, `match`, `as`, `action`, `resource`, `watch`, `look`, `component` | Don't. Declaration words are contextual: each declares only in its own shape, so `const {type, from} = event` and `const state = "ready"` are ordinary code in Core and Web alike. `enum` and `case` are the exceptions — `enum` is a real VelarScript keyword, `case` is reserved by JavaScript — so neither can be a binding name; both stay fine as record fields, member names, and `match` branches. |
180
+ | A component's `mounted:` / `cleanup:` block | `@mounted:` / `@cleanup:`. Lifecycle hooks live in the language's `@` namespace, which is why a component can also declare its own `def mounted()`. |
181
+ | Two statements on one line | One statement per line; there are no semicolons. A line starting with `.` or `?.` continues the previous line, so method chains format normally. |
182
+ | `count++` | `count += 1` |
183
+ | `call(name: value)` named argument | `call(name=value)` |
184
+ | Importing `range` | `range(...)` is a Core prelude function and needs no import. |
185
+ | `import {sqrt} from "velar/math"` | `Math.sqrt(x)`; `Math.` is permanent. `velar fix` performs the rewrite. |
186
+ | `Look.spacing(16px)` | `spacing(16px)` with `import {spacing} from "velar/look"`; the `Look.` prefix is retired. `velar fix` performs the rewrite. |
187
+ | `"""triple-quoted"""` for a block of text | A layout string: a double quote followed immediately by a newline opens it; a quote back at the opening line's indentation closes it. Backtick strings are real, but always single-line. |
188
+ | Escaping `\"` through a JSON, HTML, or selector string | Use backticks: `` `{"name":"Nova"}` `` is the same `string` value, with `"` as ordinary text. Prefixes are orthogonal (`` f` ``, `` r` ``, `` rf` ``), and `velar format` picks the delimiter for you (`"` by default, backticks when the text contains `"`), so write whichever is convenient. |
189
+ | `007`, `.5` | Write `7`, `0.5`. Explicit `0xFF`, `0b1010`, and `0o17` integers are supported; legacy leading-zero octal is not. Group long digits with `_` — `1_000_000`. `Infinity` and `NaN` are not literals: write `1 / 0` and `0 / 0`. |
190
+ | `a == b == c` | Equality never chains: `a == b and b == c`. Ordered chains work but must point one way — `0 < index <= size` is fine, `a < b > c` is not. An `in` or `is` test inside a comparison needs parentheses. |
191
+ | A line that is only a value — `x == 5`, `items[0]`, `"a note"` | A statement must do something: call, assign, `await`, or `async`. A computed-and-discarded value is a compile error, and a bare string is not a docstring — use `//`. |
192
+ | A block comment that starts or ends beside code on a multi-line span | `/* */` exists and nests — commenting out a region that already holds a comment works — but a multi-line one takes whole lines: only `/*` on its opening line, only `*/` on its closing line. Within a single line it can sit anywhere: `call(/* why */ value)`. |
193
+ | `x if cond else y` | `cond ? x : y` |
194
+ | `&&`, `\|\|`, `!`, `===`, `var`, `elif`, `None`, `undefined` | `and`, `or`, `not`, `==`, `let`/`const`, `else if`, `null`, `null`. |
195
+ | `f"{user}"` or `str(user)` on a record | Text conversion accepts strings, numbers, bools, enums, and `null` only. `print(user)` inspects a value; permanent `Json.stringify(user)` builds data text without an import. |
196
+ | Calling an async function and moving on | A dropped Promise is a compile error. `await task()` to wait; `async task()` to run it detached. |
197
+ | `flag or name ?? fallback` | Parenthesize — `??` never shares an unparenthesized chain with `and`/`or`. |
198
+ | `onClick={handler}` | `on:click={handler}`; form binding is `bind:value={state}`. |
199
+ | Implicit `{props.children}` | Declare it: a `children: WebNode` prop receives the JSX tag body. |
200
+ | `map[key]` reads | `map.get(key)` returns `T?`. On Lists, `[index]` throws on a bug; `.get(index)` returns `null` when absence is an expected answer. |
201
+ | `[...text]` or `list(text)` for characters | `text.split("")` — the empty separator splits per Unicode code point. |
202
+ | `x !== x` or `Number.isNaN(x)` | Number predicates are members: `x.isNaN()`, `x.isFinite()`, `x.isInteger()`. `NaN == NaN` is `true` — equality is SameValueZero. |
203
+ | `text.trim().size == 0` blank test | `text.isBlank()` — `true` for empty or whitespace-only text. |
204
+ | `while true:` plus a `pop()` null check to drain a List | `pop(index=-1)` returns `T` and throws `IndexError` when empty or out of range, so drain with `while items.size > 0:`. |
205
+ | `1 == "1"`, `user == "a"`, `A.member == B.member`, `raw == Kind.member` | `==`/`!=` require the operand types to intersect. Compare enums with `Kind.parse(raw) == Kind.member` when the text must name a member — `parse` throws otherwise — or `str(Kind.member) == raw` when unknown values must be ignored, as on an open wire protocol. `value == null` on an optional is always fine. |
206
+ | `[1, 2] == [1, 2]` content comparison | Collection `==` is identity; `equals(a, b)` compares data deeply (Lists ordered, Sets/Maps by members, SameValueZero leaves) with no import. |
207
+ | Iterating or spreading an enum object | `Status.values()` returns the members in declaration order as a fresh `List<Status>`. |
208
+ | `sorted()`, `min()`, or `sorted(by=)` over enums | Only `number`, `string`, and single-category unions are ordered. Give the order explicitly with `sorted(by=row => row.rank)` or a string-backed enum (`low = "1-low"`). |
209
+
210
+ The long tail is deliberately not in this table: the diagnostic will name
211
+ the current spelling when you hit it.
212
+
213
+ ## Declarations at a glance
214
+
215
+ Functions are `def`, with typed parameters, defaults, and `name=value` calls:
216
+
217
+ ```velar
218
+ def formatName(name: string, prefix: string = "@") -> string:
219
+ return f"{prefix}{name}"
220
+
221
+ print(formatName("ada"))
222
+ print(formatName("ada", prefix="#"))
223
+ ```
224
+
225
+ A generic body that must order, interpolate, or serialize its type parameter
226
+ names a bound — `def label<T: Text>(value: T)`; the diagnostic names the one
227
+ you need.
228
+
229
+ **There are two arrows and they are not interchangeable.** `=>` is the
230
+ value-level arrow: it introduces a lambda body. `->` is the type-level arrow:
231
+ it names a result. They stand next to each other most often on a callback prop
232
+ — the type is written with `->`, the value handed to it with `=>`:
233
+
234
+ ```velar
235
+ type Transform = (value: number) -> number
236
+
237
+ const double: Transform = value => value * 2
238
+
239
+ component Stepper(step: number, onChange: (next: number) -> null):
240
+ return <button type="button" on:click={() => onChange(step + 1)}>+1</button>
241
+
242
+ mount(<Stepper step={double(1)} onChange={next => print(next)} />, "#app")
243
+ ```
244
+
245
+ Parameter names in a function type are optional but worth writing:
246
+ `(title: string, author: string) -> Promise<null>` says at the call site what
247
+ `(string, string) -> Promise<null>` makes you guess. An async callback's
248
+ **type** names the Promise, while an `async def` **declaration** annotates the
249
+ resolved value — `async def loadUser(id: string) -> User` satisfies
250
+ `(id: string) -> Promise<User>`.
251
+
252
+ `type` declares record shapes and aliases, `T?` is optional, and every record
253
+ type carries a runtime validator for untrusted data:
254
+
255
+ ```velar
256
+ type User:
257
+ id: string
258
+ name: string
259
+ avatar: string?
260
+
261
+ type UserId = string
262
+
263
+ def load(untrusted: unknown) -> User:
264
+ return User.parse(untrusted)
265
+ ```
266
+
267
+ `enum` declares finite string-backed states; a member may map an external
268
+ wire spelling without losing its nominal identity:
269
+
270
+ ```velar
271
+ enum Status:
272
+ pending
273
+ active
274
+ done
275
+
276
+ enum ProviderEventKind:
277
+ textDelta = "response.output_text.delta"
278
+ completed = "response.completed"
279
+
280
+ const status: Status = Status.active
281
+ print(ProviderEventKind.textDelta)
282
+ ```
283
+
284
+ Classes use typed body fields, one explicit constructor, and explicit `self`;
285
+ instances are called directly, without `new`:
286
+
287
+ ```velar
288
+ class Session:
289
+ let active: bool = true
290
+
291
+ constructor(const id: string):
292
+ pass
293
+
294
+ def close():
295
+ self.active = false
296
+
297
+ @dispose:
298
+ self.close()
299
+
300
+ const session = Session("session-1")
301
+ ```
302
+
303
+ `@name` members belong to the language and can never collide with yours.
304
+ `@dispose:` is the release contract — never called directly — that
305
+ `using name = expression` runs on every exit from the owning scope (block end,
306
+ `return`, `break`, `continue`, throw), in reverse declaration order. A derived
307
+ `@dispose:` adds to its base's; the compiler runs derived first, then base.
308
+ Standard handles already have it, so `using watcher = await watchFiles(path)`
309
+ above an `async for` needs no `try`/`finally`. An owned value may not leave its
310
+ scope: `return handle`, storing it outside, or capturing it in a closure that
311
+ escapes are rejected — return the data you read from it, or move the `using` up
312
+ to the scope that really owns it. A JavaScript handle is owned by composition:
313
+ hold it in a field of a class whose `@dispose:` releases it.
314
+
315
+ Components (Web extension) return JSX directly — there is no `render` block.
316
+ `state` holds a fact, `computed name = ...` derives and is read bare, `action`
317
+ performs a user operation with reactive `pending`/`error`:
318
+
319
+ ```velar
320
+ component Counter(label: string):
321
+ state count = 0
322
+ computed caption = f"{label}: {count}"
323
+
324
+ action reset():
325
+ count = 0
326
+
327
+ def bump():
328
+ count += 1
329
+
330
+ return <section>
331
+ <button type="button" on:click={bump}>{caption}</button>
332
+ <button type="button" disabled={reset.pending} on:click={reset}>Reset</button>
333
+ </section>
334
+
335
+ mount(<Counter label="Clicks" />, "#app")
336
+ ```
337
+
338
+ `look` is the checked visual language — a value, composed per element with
339
+ `look={...}`. CSS keywords are quoted strings; property names are real DOM
340
+ camelCase; units are literal:
341
+
342
+ ```velar
343
+ import {border, rgb, spacing} from "velar/look"
344
+
345
+ const buttonLook = look:
346
+ border = border(0px, rgb(220, 224, 235))
347
+ borderRadius = 10px
348
+ padding = spacing(10px, 14px)
349
+ cursor = "pointer"
350
+
351
+ if @hover:
352
+ background = rgb(235, 240, 255)
353
+
354
+ component SaveButton(children: WebNode):
355
+ return <button look={buttonLook} type="button">{children}</button>
356
+ ```
357
+
358
+ A `look:` literal is built once, so its conditions and values cannot read state; put a reactive visual on the element with `look={active ? a : b}` or `look:color={...}`. Declare checked motion as a module-level `keyframes:` value and pass it to `animate` from `velar/look`; disable nonessential motion at the CSS layer:
359
+
360
+ ```velar
361
+ import {animate} from "velar/look"
362
+
363
+ const spin = keyframes:
364
+ from:
365
+ rotate = 0deg
366
+ to:
367
+ rotate = 1turn
368
+
369
+ const rotatingLook = look:
370
+ if not motion.reduced:
371
+ animation = animate(spin, 1s, easing="linear", loop=true)
372
+ ```
373
+
374
+ The `animation` property accepts only `Animation`, `List<Animation>`, or `null`; a CSS animation string is rejected. Bind a changing animation on the element with `look:animation={active ? animate(spin, 1s) : null}`. Native animation longhands remain outside Look because `animate` owns the checked contract.
375
+
376
+ Form state binds with `bind:value={name}` (also a writable path such as `bind:value={form.email}`), `bind:checked={flag}`, and `bind:group={choice}` — radio state holds the selected input's `value`, checkbox `List<string>` state holds the checked values; the event object has no `target`.
377
+
378
+ ## The idioms
379
+
380
+ These are the canonical shapes, distilled from the project's best-practices
381
+ canon. When two spellings both work, use the one shown here.
382
+
383
+ ### Model the data first
384
+
385
+ Give every finite state an enum, every shape a `type`, and return multiple
386
+ values as a named record with shorthand fields — never an out-parameter:
387
+
388
+ ```velar
389
+ type TextMeasure:
390
+ lines: number
391
+ words: number
392
+
393
+ def measure(text: string) -> TextMeasure:
394
+ const lines = text.split("\n").size
395
+ const words = text.split(" ").size
396
+ return {lines, words}
397
+ ```
398
+
399
+ ### Null discipline
400
+
401
+ One spelling per job: test presence with `!= null`, default with `??`,
402
+ reach through maybes with `?.`, and narrow once — then use the value
403
+ directly:
404
+
405
+ ```velar
406
+ type Ticket:
407
+ title: string
408
+ assignee: string?
409
+
410
+ def notifyLine(ticket: Ticket) -> string:
411
+ if ticket.assignee != null:
412
+ return f"notify {ticket.assignee}"
413
+ return f"unassigned: {ticket.title}"
414
+
415
+ def owner(ticket: Ticket) -> string:
416
+ return ticket.assignee ?? "unassigned"
417
+ ```
418
+
419
+ ### Chains over cursors
420
+
421
+ Collection work is method chains; a hand-advanced index loop is the
422
+ exception for genuine cursor algorithms. Need the position? Use the
423
+ two-slot loop, never a shadow counter. Three or more `or value == ...`
424
+ comparisons want a `Set`:
425
+
426
+ ```velar
427
+ type Task:
428
+ title: string
429
+ priority: number
430
+ done: bool
431
+
432
+ const tasks: List<Task> = []
433
+ const titles = tasks
434
+ .filter(task => not task.done)
435
+ .sorted(by=task => task.priority)
436
+ .map(task => task.title)
437
+
438
+ for title, index in titles:
439
+ print(f"{index}: {title}")
440
+
441
+ const closingWords = Set(["done", "closed", "resolved"])
442
+
443
+ def isClosing(word: string) -> bool:
444
+ return word in closingWords
445
+ ```
446
+
447
+ One-liners worth knowing: `values.flatMap(x => x.parts)` maps then flattens
448
+ one level; `values.filter(x => x != null)` — exactly that predicate — drops
449
+ absences and narrows `List<T?>` to `List<T>`; Sets combine with the copying
450
+ algebra `a.union(b)`, `a.intersection(b)`, `a.difference(b)`.
451
+
452
+ Mutate state directly — deep reactivity is the default, so rebuild-the-list
453
+ spellings are noise: `tasks.append(task)`, `tasks[0].done = true`,
454
+ `panel.open = not panel.open`.
455
+
456
+ ### Dispatch with match
457
+
458
+ A finite state fans out through one `match` with `case _:` as the only
459
+ fallback:
460
+
461
+ ```velar
462
+ enum Phase:
463
+ todo
464
+ doing
465
+ done
466
+
467
+ def advance(phase: Phase) -> Phase:
468
+ match phase:
469
+ case Phase.todo:
470
+ return Phase.doing
471
+ case Phase.doing:
472
+ return Phase.done
473
+ case _:
474
+ return Phase.todo
475
+ ```
476
+
477
+ ### Small functions, guard first
478
+
479
+ Handle empty and missing cases with early returns, then write the happy
480
+ path unindented. Contracts are one `assert condition else "message"` per
481
+ rule at the top:
482
+
483
+ ```velar
484
+ def firstLine(text: string) -> string:
485
+ assert text.size <= 1000000 else "Text is beyond the supported size"
486
+ if text == "":
487
+ return ""
488
+ return text.split("\n")[0]
489
+ ```
490
+
491
+ Callbacks stay arrows while they are one expression; promote two-statement
492
+ logic to a named `def`. Name arguments where a bare value would read as a
493
+ mystery: `buttonLook(dangerous=true)`, never `buttonLook(true)`.
494
+
495
+ ### Strings
496
+
497
+ Build text with f-strings — numbers, bools, enums, and Web unit values with a declared text form interpolate directly.
498
+ Data becomes text through permanent `Json.stringify`. Multi-line text is
499
+ a layout string, not a stack of `\n` escapes. Text that contains `"` — a JSON
500
+ fixture, a quoted selector — goes in backticks instead of being escaped:
501
+
502
+ ```velar
503
+ const count = 3
504
+ const gap = 16px
505
+ const summary = f"{count} open tickets"
506
+ const fixture = `{"open":3,"state":"ready"}`
507
+ const gapLabel = f"gap: {gap}"
508
+ const usage = "
509
+ velar check
510
+ velar test
511
+ "
512
+ print(summary)
513
+ print(fixture)
514
+ print(gapLabel)
515
+ print(usage)
516
+ print(Json.stringify({open: count}))
517
+ ```
518
+
519
+ ### Components: four cells, one job each
520
+
521
+ `state` holds a fact. `computed` derives from facts. `resource` loads async
522
+ data. `action` performs a user operation. Read a resource as
523
+ `value != null`; render nothing with `null`; key dynamic children:
524
+
525
+ ```velar fragment
526
+ component TicketPanel(id: string):
527
+ state draft = ""
528
+ resource ticket: Ticket = loadTicket(id)
529
+ computed heading = ticket.value?.title ?? "Loading"
530
+
531
+ action save():
532
+ await saveDraft(id, draft)
533
+
534
+ watch id:
535
+ async ticket.reload()
536
+
537
+ return <section>
538
+ <h2>{heading}</h2>
539
+ <textarea bind:value={draft}></textarea>
540
+ <button disabled={save.pending} on:click={save}>Save</button>
541
+ </section>
542
+ ```
543
+
544
+ Lifecycle is two sibling blocks in the language's own `@` namespace —
545
+ `@mounted:` runs once after the DOM is inserted and may `await`; `@cleanup:`
546
+ runs once before the component is destroyed and is synchronous:
547
+
548
+ ```velar fragment
549
+ component Chart(points: List<number>):
550
+ let canvas: CanvasElement? = null
551
+
552
+ @mounted:
553
+ if canvas != null:
554
+ drawChart(canvas, points)
555
+
556
+ @cleanup:
557
+ releaseChart()
558
+
559
+ return <canvas ref={canvas}></canvas>
560
+ ```
561
+
562
+ **A resource loads once, at mount, and does not refetch when its inputs
563
+ change** — a new `id` prop leaves the old data on screen. "Refetch when the
564
+ input changes" is the `watch` above: watch the input, and start `reload()` with
565
+ the detached `async` statement, because a watch body is synchronous. `reload()`
566
+ re-evaluates the initializer against the current inputs, keeps the last value if
567
+ it fails, and puts the failure in `error`. Actions do not queue either: two
568
+ clicks run two calls, `pending` means any call is active, so guard with
569
+ `disabled={save.pending}`.
570
+
571
+ Conditional rendering is an ordinary expression — there are no magic JSX
572
+ control-flow attributes:
573
+
574
+ ```velar fragment
575
+ component Panel:
576
+ return <section>
577
+ {loading ? <p aria-busy="true">Loading…</p> : <Results items={items} />}
578
+ <ul>
579
+ {items.map(item => <li key={item.id}>{item.title}</li>)}
580
+ </ul>
581
+ </section>
582
+ ```
583
+
584
+ Component props are live reactive inputs and their data is mutable by default.
585
+ Writing `item.title = next` through a prop publishes through the same deep
586
+ reactive path as writing the source state. Write `items: readonly List<Item>`
587
+ when the component author deliberately wants a read-only contract; that
588
+ explicit view travels into helpers and nested data without copying or freezing.
589
+
590
+ `velar/storage` persists JSON and validates on the way back in, so a read needs
591
+ a **named** runtime type — a primitive or generic spelling is a type, not a
592
+ value:
593
+
594
+ ```velar fragment
595
+ import {storage} from "velar/storage"
596
+
597
+ type SavedItem:
598
+ title: string
599
+
600
+ type SavedItems = List<SavedItem>
601
+
602
+ const items = storage.get("reading", SavedItems, [])
603
+ storage.set("reading", items)
604
+ ```
605
+
606
+ The third argument is the fallback for missing or invalid data; `session` and
607
+ `database(name)` from the same module carry the same shape.
608
+
609
+ ### Errors and async
610
+
611
+ Throw `Error` (or a subclass) with a message that names the broken rule.
612
+ An error's class is its only classification — discriminate with
613
+ `if error is FileNotFoundError:` and read `error.code` (the declared class
614
+ name) only when the identity must survive a log or JSON boundary. The nameable
615
+ capability classes need no import: `FileNotFoundError`, `PermissionError`,
616
+ `NotADirectoryError`, `FileExistsError`, `AddressInUseError`, plus
617
+ `ValidationError`, `NarrowingError`, and `IndexError`.
618
+ Validate untrusted data at the boundary with `Type.parse`, then trust the
619
+ types inward. `await` every call whose result or completion you depend on:
620
+
621
+ ```velar
622
+ type Config:
623
+ baseUrl: string
624
+
625
+ def parseConfig(raw: unknown) -> Config:
626
+ const config = Config.parse(raw)
627
+ if config.baseUrl == "":
628
+ throw Error("Config requires a non-empty baseUrl")
629
+ return config
630
+ ```
631
+
632
+ An expected failure is an optional, not a block: `try expression` produces
633
+ `null` when anything in the chain throws, and its result must be consumed —
634
+ `const settings = try Settings.parse(raw) ?? defaults`. Use `try`/`catch` when the details matter.
635
+
636
+ ### Modules
637
+
638
+ Export and import by name; a package's public face is a barrel of explicit
639
+ re-exports — `export {measure, firstLine} from "./text.vel"`. There is no
640
+ `import type`: Vel does not erase types, so a type carries its runtime
641
+ validator and a type import is an ordinary import.
642
+
643
+ ## When Vel is in your way
644
+
645
+ Vel maintains checked exits so a missing capability never strands the
646
+ project. In order:
647
+
648
+ 1. **Missing stdlib capability or a third-party npm package** — declare a
649
+ checked boundary with `extern module`; it is the first choice:
650
+
651
+ ```velar
652
+ type Payload:
653
+ id: string
654
+
655
+ extern module "some-sdk":
656
+ export def load() -> unknown
657
+
658
+ import js {load} from "some-sdk"
659
+
660
+ print(Payload.parse(load()).id)
661
+ ```
662
+
663
+ An `extern module` block governs only the file that contains it, so **declare it
664
+ once in an adapter module** and re-export a checked surface. Do not paste extern
665
+ blocks across consumers:
666
+
667
+ ```velar
668
+ extern module "text-tools":
669
+ export def format(value: string) -> string
670
+
671
+ export class Formatter:
672
+ constructor(prefix: string)
673
+ def format(value: number) -> string
674
+
675
+ import js {Formatter, format} from "text-tools"
676
+
677
+ export const formatText = format
678
+ export type TextFormatter = Formatter
679
+
680
+ export def formatter(prefix: string) -> Formatter:
681
+ return Formatter(prefix)
682
+ ```
683
+
684
+ Consumers then write ordinary Vel — `import {TextFormatter, formatText,
685
+ formatter} from "./text-tools.vel"` — and every call, construction, and
686
+ annotation is checked. A function export re-exports directly as a value; a class
687
+ needs a factory `def` (a class name is not a value) plus an exported `type`
688
+ alias so consumers can annotate. Validate anything declared `-> unknown` inside
689
+ the adapter, so only checked types leave it.
690
+
691
+ **Extern arguments are read-only.** What crosses is the raw identity, so a
692
+ package that writes into what you passed changes the real data while nothing on
693
+ the Vel side observes it — no recompute, no re-render, no refreshed flow fact —
694
+ until some unrelated Vel assignment invalidates. Have the package **return** the
695
+ data and assign the result on the Vel side.
696
+
697
+ 2. **Quick raw access** — `import js unsafe` admits the value as `any`.
698
+ Operations on an `any` are raw JavaScript with no runtime check anywhere: it
699
+ is assignable to every type without validation, and an `any` holding
700
+ `undefined` even answers `false` to `== null`. The import statement is the
701
+ only correctness boundary — validate with `Type.parse` there, before the
702
+ value touches typed code.
703
+ 3. **Styling beyond Look** — `import css unsafe "./file.css" before look`
704
+ (or `after look`); trusted markup renders through `unsafe:html`.
705
+ 4. **A suspected compiler defect blocking you** — run `velar repro` (below),
706
+ then take the final exit: `velar build` output is readable, source-mapped
707
+ JavaScript that runs without the toolchain.
708
+
709
+ The full decision tree, including the honest limits of each hatch, is
710
+ [docs/escape-hatches.md](https://github.com/VelarOS-AI/VelarScript/blob/main/docs/escape-hatches.md).
711
+
712
+ ## When you hit a compiler wall
713
+
714
+ A diagnostic that cannot be satisfied, an emit that misbehaves, a check that
715
+ contradicts this brief: you are this language's reporter as much as its author,
716
+ and the channel is the one a human uses. Do not quietly work around a defect and
717
+ leave the next reader to rediscover it.
718
+
719
+ 1. **Run `velar repro`.** It writes a self-contained minimal reproduction to
720
+ disk and prints the path — the source the diagnostic touches, `velar.json`,
721
+ the verbatim output, and the versions. It uploads nothing and collects
722
+ nothing about the machine; whether to send it is the human's decision.
723
+ 2. **Fill in the produced `README.md`.** Two of its three sections are blanks:
724
+ *What I wrote (or wanted to write)* and *How I resolved it* — the workaround,
725
+ or the single word `blocked`. *What the compiler said* is already filled in
726
+ verbatim; do not trim it.
727
+ 3. **File it** with the repository's defect template
728
+ (`.github/ISSUE_TEMPLATE/`), which asks for exactly those three sections.
729
+ Paste the README.
730
+
731
+ A word that reads wrong is worth the same trip. The spelling-objection template
732
+ exists for it, no alternative word is required, and while there is no
733
+ compatibility promise, changing a word costs nothing yet.
734
+
735
+ ## Where to look up what this brief leaves out
736
+
737
+ The repository carries a **tour** that shows every spelling exactly once, as
738
+ compiling projects you can run: `examples/tour/core/` (17 numbered chapters,
739
+ values through testing), `examples/tour/web/` (13 — components, Look, routing,
740
+ both kinds of test), `examples/tour/desktop/` (4). When you are about to guess
741
+ at a spelling, open the chapter instead. `examples/app/` is the companion — one
742
+ real application, showing how the pieces are put together.
743
+
744
+ ## The meta-rule
745
+
746
+ Above everything in this brief: **run `velar check` and do what the
747
+ diagnostic says.** Diagnostics name the one current spelling; they are the
748
+ canon's enforcement arm, and they outrank any memory of this page. If the
749
+ compiler and this brief ever appear to disagree, the compiler is right.