Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
5842a13
docs: describe the last 56 commits in the release they actually shipp…
Error-Zhang Aug 16, 2026
706b602
docs: say the positioning the repository was already enforcing
Error-Zhang Aug 16, 2026
6ca47ba
docs: open the charter with the positioning its fifth rule already st…
Error-Zhang Aug 16, 2026
3882736
feat: require the tour to cover constructs, not only names
Error-Zhang Aug 16, 2026
c6286b9
docs: two decisions the construct gate surfaced, both printed rather …
Error-Zhang Aug 16, 2026
426902c
docs: correct two charter statements the D71 migration left behind
Error-Zhang Aug 16, 2026
eec380d
docs: the website gate was green against a language that no longer ex…
Error-Zhang Aug 16, 2026
8ba6e95
fix: read CSS as tokens, because a regex was wrong in both directions
Error-Zhang Aug 16, 2026
ef20e66
docs: scope the asset rule by its reason instead of by a function name
Error-Zhang Aug 16, 2026
6939798
fix: discover dependencies structurally, so no container can be missed
Error-Zhang Aug 16, 2026
edff01e
docs: record the audit in the changelog, and what the traversal fix l…
Error-Zhang Aug 16, 2026
7cfdd14
fix: make three gates check what they claim, and derive the fifth ros…
Error-Zhang Aug 16, 2026
ac7fda7
feat(desktop): own durable project changes
Error-Zhang Aug 16, 2026
d5ed85d
build: consume published project transactions
Error-Zhang Aug 16, 2026
4dc9604
ci: authenticate GitHub package installs
Error-Zhang Aug 16, 2026
0076182
ci: keep VelarScript sources on canonical line endings
Error-Zhang Aug 16, 2026
65e5f80
ci: inspect runtime boundaries independent of checkout EOL
Error-Zhang Aug 16, 2026
a5adc73
fix(cli): keep absolute browser entries out of npm resolution
Error-Zhang Aug 16, 2026
09f1b13
fix(ci): run the complete cross-platform toolchain gate
Error-Zhang Aug 16, 2026
0870708
fix(ci): enforce real cross-platform owner boundaries
Error-Zhang Aug 16, 2026
9da8742
fix(node): close terminal input host without IPC noise
Error-Zhang Aug 16, 2026
e17c538
fix(ci): stream generated runtime probes through stdin
Error-Zhang Aug 16, 2026
b891520
fix: make toolchain boundaries portable
Error-Zhang Aug 16, 2026
d55da2e
fix(cli): consume file URL source maps in dev
Error-Zhang Aug 16, 2026
23db8b7
fix(test): make browser readiness explicit
Error-Zhang Aug 16, 2026
b2adc44
fix(node): confirm process groups before stop completes
Error-Zhang Aug 16, 2026
ef42d9c
test(node): verify stopped descendant capabilities
Error-Zhang Aug 16, 2026
ea3e754
fix(cli): keep Windows diagnostics portable
Error-Zhang Aug 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
8 changes: 8 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ on:

permissions:
contents: read
packages: read

jobs:
toolchain:
Expand All @@ -22,6 +23,11 @@ jobs:
node-version: "24.x"
cache: npm
- run: npm ci
env:
NODE_AUTH_TOKEN: ${{ github.token }}
# `npm test` includes Chromium-backed lifecycle and runtime probes. The
# separate browser matrix expands that coverage to all three engines.
- run: npx playwright install --with-deps chromium
- run: npm run check
- run: npm test
- run: npm run test:packages
Expand All @@ -37,5 +43,7 @@ jobs:
node-version: "24.x"
cache: npm
- run: npm ci
env:
NODE_AUTH_TOKEN: ${{ github.token }}
- run: npx playwright install --with-deps chromium firefox webkit
- run: npm run test:browser
3 changes: 3 additions & 0 deletions .github/workflows/external-preview-verification.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ on:

permissions:
contents: read
packages: read
id-token: write
attestations: write
artifact-metadata: write
Expand All @@ -25,6 +26,8 @@ jobs:
node-version: "24.x"
cache: npm
- run: npm ci
env:
NODE_AUTH_TOKEN: ${{ github.token }}
- run: npm run check
- name: Prepare the exact root Netlify preview
run: node scripts/prepare-external-preview.mjs --output-dir release/external-preview/site
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/release-rehearsal.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ on:

permissions:
contents: read
packages: read
id-token: write
attestations: write
artifact-metadata: write
Expand All @@ -26,6 +27,8 @@ jobs:
registry-url: https://registry.npmjs.org
package-manager-cache: false
- run: npm ci
env:
NODE_AUTH_TOKEN: ${{ github.token }}
- run: npm run check
- run: npm test
- name: Create non-publishing rehearsal
Expand Down
2 changes: 2 additions & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
@velaros-ai:registry=https://npm.pkg.github.com
//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN}
220 changes: 220 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,226 @@ language for people who already think in JavaScript and Python, shipped as one
version-locked set of eight packages: compiler, Node runtime, Web and Desktop
frameworks, project creator, CLI, text buffer, and script analysis.

### The reactive grid: `computed` is a declaration, and the function is `cached`

`computed name = expression` now stands beside `state name = value`, and the
`computed(...)` **function is renamed `cached(...)`**. The function is not
retired — it remains a way to cache a value rather than declare one — but a
derived binding is declared, never assigned from a call.

The reason is a defect the owner reported twice, from two directions, without
knowing they were the same defect:

- `const doubled = count * 2` in component setup compiled clean and then never
updated. `const` freezes, which is precisely what `const` means.
- `const total = computed(() => …)` followed by `watch total:` compiled clean,
exited 0, emitted no diagnostic — and the watch body could never run. The
correct spelling was `watch total():`, and the language's own tests had the
parentheses while nothing said so.

Both trace to one asymmetry: `state` was a declaration and `computed` was a
function, so every derived value had to be parked in a `const` — and `const`
is the thing that freezes reactive reads. The grid now has four cells that
each mean one thing: `let`/`const` are not reactive, `state`/`computed` are,
and within each pair one is written and one is derived. Fixing the spelling
removed the diagnostics that would otherwise have been needed to teach around
it, which is the order this project prefers: a design fix beats a diagnostic
fix, because the diagnostic only teaches after the mistake.

Migration is mechanical and `velar fix` carries it, including across module
boundaries: `const x = computed(() => E)` becomes `computed x = E`, and reads
lose their parentheses only when every read in the module is a plain `x()`.

- **VEL5063** answers the three shapes that survive a half-migrated project:
`x()` on a computed value (*"it is read bare like state"*), assignment to a
computed value (which names the `state` spelling that is written instead),
and assignment to a computed value **imported** from another module, which
cannot name a local fix and so names the export that would work.
- **VEL5064** refuses a `watch` subject that can never change, naming the
reactive sources that can: *"This watch subject never changes, so its body
can never run … watch a 'state', a 'computed', a prop, or a resource field,
or move these statements to where they should run."* A bare `5` was already
a compile error (VEL4030); `watch 5:` with a whole block behind it was the
same rule failing to reach one position. The subject is refused only where
the compile can *prove* nothing behind it moves, so a call whose reactivity
lives in another module is left alone.
- The frozen snapshot itself is reported at development time by the runtime,
and it fires **when the source diverges from the snapshot, never when the
snapshot is taken** — taking a snapshot on purpose is legitimate, and a
warning that fires on correct code is one people learn to ignore. The whole
mechanism installs only when the development host publishes its hooks, so a
production build pays nothing: no map, no stack capture.

### Component props are mutable by default

A prop is a handle to reactive state, and refusing to write through it forced
callers into ceremony that fought the way the rest of the language reads.
Props now accept assignment; a component that needs the old guarantee writes
`readonly` on the field, and only those props receive readonly-specific
guidance. This aligns a prop with every other reactive value rather than
making it the one exception, and the choice of which contract to offer now
belongs to the component's author.

### Generic types

`type Box<T>`, `type Pair<A, B>`, `type Sorted<T: Comparable>`, and recursive
forms like `type Tree<T>` are available on records and aliases. A readonly
field is covariant and a mutable one is invariant, so variance follows from
what the field already says rather than from a separate annotation.

**Generic classes are deliberately not in this release.** The evaluation found
that `Stack<number>()` has no spelling in today's grammar — five candidate
paths were measured and all five are closed — and shipping a type that can be
declared but not constructed is worse than shipping nothing. The decision that
unblocks them is recorded, and it is larger than generic classes: annotations
will flow into generic call-site inference, which also fixes
`const names: List<string> = empty()`.

### Inline foreign source

JavaScript and CSS can be written inline against a checked contract:
`extern js(captures)` with a backtick block declares a typed boundary, and
`unsafe js` admits an unchecked one. `unsafe css` does the same for styling
and is owned by the Web extension rather than Core — Core does not know what
CSS is, and putting a `css` keyword in it to save a file would have spent that
boundary for convenience.

An inline block is **more checked** than the `import js unsafe` it replaces,
not less: the contract sits three lines below the source, where an entire
module going implicitly `any` is invisible. The data-URL spelling that used to
be the only way to write a module inline now gets a mechanical rewrite to the
block form, which is source-mapped.

A block with captures does not support top-level `await`. That is a decision,
not a gap, and the conditions under which it would be revisited are recorded.

### Classes can say what iterating them means

`@iterate:` gives a class a definition for iteration, so a user type takes part
in the same loops the built-in collections do instead of exposing an internal
list to be iterated on its behalf.

### Web surface

- Element geometry and computed styles are readable through a controlled
surface — `measure(panel)` and its siblings — rather than through an escape
hatch. The framing that settled it: this is a library surface, not the
language, so it does not carry the language's strictness burden.
- `aria-*` booleans render as literal `true`/`false`, which is what ARIA
actually specifies; the previous rendering was silently wrong for assistive
technology.
- **Every Look property that accepts keywords now carries its own closed set.**
A diagnostic used to say *"use one of the closed fontWeight keywords"* when
`fontWeight` had no closed set to point at — publishing an unreachable table
is the same defect as publishing an unreachable name. A refused Look value
now names the values that property really takes, leading with the property's
own vocabulary and keeping the five CSS-wide keywords separate. The
invariant is enforced for every value kind that closes over strings, not
only the one kind where it was first found, and `transitionProperty`'s
vocabulary is now derived from the Look property table rather than listed by
hand — it previously accepted only the generic defaults, so no property name
was writable.
- **VEL5065**: a Web module publishes its own type names, and declaring one
used to be accepted at the declaration and then lose at every use — `type
Event:` compiled, and the first use was told it could not assign to `Event`,
naming a type the author had just written. The refusal now lands on the
declaration, which is the only place a rename is cheap.
- A module no longer emits type checks against bindings it does not have.

### Spelling

- The `Look.` prefix is retired; `Math` joins the permanent namespaces, which
are vocabulary rather than values and are legal only as the head of a member
access.
- Single quotes are retired in JSX attributes.
- Imports return to the named form.
- `-> null` is written only where it cannot be inferred; a body-backed
declaration that returns nothing omits it.
- A type position that receives `=>` is taught `->` instead of being told the
syntax is wrong.
- `velar fix` withholds a mechanical rewrite when the body returns a value,
because the rewrite would not be provably equivalent there.

### Tests

`toBe` and `toContain` use the language's own equality rather than
JavaScript's, so a test agrees with the `==` the author would have written.

### What an adversarial audit found

A ten-track audit was run against the language before this release, briefed to
attack rather than assess and to record for each finding *which gate should
have caught it*. It produced 26 entries, five of them the highest-value kind:
compiled clean, no diagnostic, wrong answer.

- **A dynamic import inside `try`, `using`, a test body, a class getter,
`@dispose`, or `@iterate` was left out of the module graph.**
`try await import("./dep.vel")` answered `false` for a module that exists,
with a clean check and exit 0; the other positions surfaced as a runtime
module-not-found. Dependency discovery had a hand-written second walk over
node kinds, which had already drifted past `@iterate` on the day it shipped.
It is now a structural descent that never asks what a node is, the same
duplicated walk is gone from the extension protocol, and the regression is a
matrix of every expression position in the corpus rather than one example.
- **A CSS filename containing a legal `)` walked past the relative-address
gate** and shipped a stylesheet whose address resolves one directory from the
asset — measured as a live 404. The same regex refused `content:
"url(./x.svg)"`, which is text. Both came from a regex standing in for CSS
grammar; the scanner now tokenizes, which surfaced twenty more wrong shapes
in both directions, and the `@import` check — a second scanner in the same
file with lower coverage — reads the same token stream.
- **Three gates claimed more coverage than they had.** The documentation gate's
fence extractor was a regex rather than CommonMark, so legal fences were
silently skipped under a report that every block had compiled; the tour
coverage gate counted a named import as usage, so deleting the only call to a
name still reported full coverage; and the package gate derived its pack list
while leaving the content check and clean-install hand-written. A gate that
overstates its own reach is worse than a missing gate, because it is trusted.

### Corpus, gates, and documentation

- **`examples/` is retired.** Seven legacy directories and four loose files
are replaced by a 38-chapter usage tour (20 Core, 14 Web, 4 Desktop) and one
real application. The tour is not illustration: it is **gate corpus**, and
writing the Web and Desktop chapters alone produced ten findings — two of
them a class above the rest, because they are the tools breaking what the
tools promise. `velar format` turned compiling source into source that no
longer compiled, on the one-line reflow path the charter documents by name;
the regression now pins the invariant rather than the shape, so source that
compiles must still compile after formatting. And an exported
`type X = Component<Signature>` passed `velar check` on two modules and then
failed `velar build`, because the emitter wrote Vel type syntax into
JavaScript — a clean check is a contract, and passing check then failing
build is worse than failing check, because by then the author has believed
it.
- A gate now requires the tour to cover the language surface, reading the
compiler's own tables at run time rather than a list maintained beside them.
- A gate audits what each standard module's runtime actually exports against
what it declares. It audits **one extension at a time**, because merging
them let Desktop's `velar/fs` mask Node's — the merged form reported 30
surfaces where there were 36.
- The publishable package set is derived from `packages/*`, reading each
manifest's own private flag, so a new package joins the toolchain the day it
exists. The two literal copies it replaces were both correct — the failure
happened outside the repository, where a brief transcribed a truncated view
of one of them and shipped six names instead of eight. A list somebody has
to read and retype is a list somebody eventually gets wrong. Wiring it up
also revealed that `test:packages` had been red since `velar/collections`
stopped exporting `range`, because that gate is in CI but was not in the
three-command sequence being run by hand.
- `check:docs` now compiles every root README rather than only the English
one, and **prints what it could not fully check** instead of reporting a
clean pass over a partial scan.
- Ten instances of one defect family — a name list maintained by hand beside
the authority it was copied from — were replaced by derivation. Core's
contextual keywords had four copies; they now have one original.
- The user-facing documentation is rewritten for someone arriving rather than
for the marathon: a shorter README, a new getting-started guide, a new
language reference in reading order, a rewritten best-practices guide with
one complete program per rule, and a CLI reference grouped by task.
- `docs/handoff/` becomes `docs/decisions/`, with process artifacts moved to
an archive and an index that says what each ruling settled.

### Final hardening and language surface

- `IndexError` was unreachable from every CLI path — the class existed but
Expand Down
35 changes: 28 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,36 @@

[![VelarScript CI](https://github.com/VelarOS-AI/VelarScript/actions/workflows/ci.yml/badge.svg)](https://github.com/VelarOS-AI/VelarScript/actions/workflows/ci.yml)

**A language AI writes and maintains, and humans read and own.**

You supply the intent and read the result. The model writes the VelarScript
and every later change to it. The compiler guards each change.
**An extensible programming language for the AI era, where the framework is the
language.**

A model can now write code faster than anyone can check it, so the bottleneck
has moved from writing to trusting. Every existing stack was built for the era
when a person wrote each line and held the whole context, where a silent
mistake was affordable because the author knew what they had meant. That
assumption is gone and the stacks have not moved. Vel answers with two things
that are one thing: it shrinks what has to be verified down to a single
language, and it makes the compiler the thing that verifies it. A wrong CSS
value, a misspelled `aria-*`, a missed reactive dependency, a coercion, an
unowned failure — silent everywhere else, compile errors here.

`component`, `state`, `computed`, `watch`, `look`, and `keyframes` are
keywords, not imports — there is no framework layered on top of the language,
because the framework *is* the language. And Core itself knows none of those
words: it does not know what a DOM, a stylesheet, a filesystem, or a window
is. Every capability arrives as an extension that adds real syntax through a
compiler protocol — `@velarscript/web` brings the words above and JSX,
`@velarscript/desktop` runs that same source model on a system WebView with
permission-scoped capabilities, and `@velarscript/node` adds the server side.
Extensions add syntax, not only libraries, which is what makes the language
extensible rather than merely configurable.

Vel is built from the bones of JavaScript and Python — the two languages every
model already knows best — so a model writes it on prior knowledge alone. And
the language keeps **one obvious spelling per idea**, so model output stays
uniform and any Vel codebase reads like any other.
model already knows best — so a model writes it on prior knowledge alone, and
the language keeps **one obvious spelling per idea** so that output stays
uniform and any Vel codebase reads like any other. You supply the intent and
read the result; the model writes the VelarScript and every later change to
it; the compiler guards each change.

## Start

Expand Down
Loading