Read this brief before writing VelarScript. It explains how to choose among the
language's capabilities, including decisions the compiler cannot infer from
local syntax. velar skill core prints this file verbatim. Projects also load
the web, node, server, or desktop brief required by their manifest.
Run velar skill core topics to discover the installed, version-matched Core
reference. Read the relevant topic before inventing syntax, adding a helper,
or deciding that an API is missing. For example, velar skill core collections
prints collection contracts and velar skill core types prints data modeling,
generics, optional values, and readonly rules. These references work offline.
Choose a feature because it expresses the task's meaning. A shorter expression that hides state changes, ordering, ownership, or field meanings is a worse result. A feature does not need to occur in every project to be useful.
| Task | Core capability and decision | Reference topic |
|---|---|---|
| Write ordinary computation | const for a fixed binding, let for reassignment; def for functions; indentation for suites, // for comments, /// for declaration documentation |
syntax |
| Select or compose values | condition ? value : fallback, ??, optional member/index/call access, minimum <= value < maximum, and/or/not, in/not in |
syntax |
| Produce text | f"{value}" for interpolation; layout strings for indented multiline text; str for primitive text, Json.stringify for data, print for inspection |
syntax, api |
| Name a data contract | type Name: for fields; a type alias for an existing contract; single record inheritance for shared fields; use field names for multiple business results |
types |
| Describe finite states | enum members, optional values, and record unions with an enum-member discriminator; map enum members to external wire strings or safe integers when needed | types, control |
| Protect writes | readonly type for all record slots, a field modifier for selected slots, readonly List<T> for list structure; each nested type retains its own permissions |
types |
| Reuse a typed algorithm or shape | Generic functions, records, and classes; constraints describe required capabilities; calls infer type arguments from their arguments/context | types, functions, classes |
| Define a clear call | Parameter names, defaults, named arguments, function types, arrow functions, and explicit return types at public boundaries; source argument order owns evaluation order | functions |
| Read or transform collections | map, filter, flatMap, find, some, every, sum, min, max, sorted, groupBy, keyBy, countBy, partition, zip, chunk, unique, compact, flatten |
collections |
| Build or adapt a collection | Typed empty List/Map/Set, compiler-owned conversions, .copy(), spread, record/list destructuring; Pair supplies named first/second results from zip |
collections |
| Perform ordered work | for, optional second loop binding, while, break and continue; preserve explicit loops for mutation, multiple outputs, or state carried between items |
control |
| Classify or unpack input | Exhaustive match, enum/type/record/List patterns, alternatives, guards and as; case _: pass explicitly handles a remaining no-op case |
control |
| Validate a boundary | is/Type.is for shape checks, Type.parse for checked data copies, Type.from for a shallow projection of typed data, semantic validation for domain rules |
types, validation |
| Represent absence or failure | T?, explicit presence checks, ??, try expression for an expected recoverable failure, typed catch when the error matters, assertions for invariants |
control |
| Own behavior and resources | Classes with explicit constructors, implicit self, methods/getters, inheritance and overrides; using and @dispose for scope-owned resources |
classes, control |
| Own asynchronous work | async def, await, detach, Promise combinators, duration values such as 250ms, async for for checked pull contracts |
functions, control, api |
| Organize and cross boundaries | Named imports/re-exports, module @main, literal dynamic imports, checked JSON imports, one extern module adapter per foreign contract; validated unknown for unsafe entry |
modules |
| Verify the program | test "description":, assertions and velar/test expectations; compiler checks, formatter, and the project's affected tests |
modules, api |
For the standard's promises and limits, read velar skill core contract.
The map above is an index; the topic reference supplies exact spellings,
signatures, evaluation behavior, failures, and examples.
- Return a named record when results have distinct meanings. Four numbers such as gridX, gridZ, amountX and amountZ are a shape, not a numeric sequence whose positions a caller should memorize. Use List for sequence operations and Pair for generic two-item pairing.
- Use enum-based unions for state-dependent fields instead of one record full of unrelated nullable fields or open strings checked throughout the program.
- Use a target record's
.from(source, {overrides})when it expresses an exact typed projection. A handwritten literal is appropriate when it combines different sources or has meaningful evaluation order. - Use collection queries for a complete stateless operation. Do not force a
stateful loop into
reduceor split one ordered pass into unrelated pipelines. - Check the library before writing conversion, grouping, counting, lazy Map initialization, validation, numeric-boundary, or resource-cleanup helpers.
- A public API's field names, optionality, mutation, errors, and ownership are part of its contract. Do not hide those decisions behind convenience wrappers.
- Before a project-wide change, run
velar graph; narrow large graphs withvelar graph --focus <symbol> --depth 2. Treat this compiler-owned graph as the source of module, call, state, derivation, ownership, and capability relationships. - Make the smallest change that preserves those boundaries.
- Run
velar fix, thenvelar check. The compiler owns syntax migration and locally provable canonical forms, so follow its diagnostics instead of copying correction rules into prompts. Resolve every advisory too, or keep the intentional spelling with// velar-allow <CODE>: <reason>. - Run the affected tests and
velar format. Use the repository's broader gates when the change crosses packages, targets, generated artifacts, or public contracts.
Core owns target-neutral computation. Web, Node, Server, and Desktop extensions own their host syntax, modules, manifest keys, and runtime capabilities. Do not move a host operation into Core or recreate an extension API in application code. Load only the briefs for the project's declared owners.
Use the permanent namespaces for their established Core operations and named imports for chosen toolboxes and external-resource capabilities. Core ownership and a permanent name do not promise purity: random values, task scheduling and diagnostic output have observable behavior. Judge effects by the operation's contract. Keep project-specific codecs, storage, protocols, and algorithms in project-owned modules or dependencies. Use the installed reference and compiler completion for the current API surface.
Let the toolchain create projects and manifests. velar create chooses the
owner and writes the gates. A declared surfaces map is an exact compatibility
receipt, not a version range: copy its values from velar --version, and update
them only after reviewing the named surface changes. A Core library declares
the Core target and publishes its .vel source with the frozen artifact made by
velar build-library; host-specific targets remain exact.
//always starts a comment. The compiler can recognize arithmetic-looking comment text, butconst ratio = total // divisorsilently bindstotalbecausedivisorlooks like prose. Floor division is(total / divisor).floor().- Collection
==compares identity. Useequals(left, right)for deep data equality. This matters most when both operands are bindings, where no literal gives the compiler evidence that content comparison was intended. Type.parse(value)validates untrusted data and returns a copy. Later writes to the input and parsed result do not meet again, except at deliberately opaque positions such asunknownfields and class instances.
Use collection APIs when they express the whole stateless operation:
filter, map, flatMap, some, every, find, sum, min, max, or a
pure reduce with an explicit initial value. When the answer is a different
collection, the member says so: groupBy, keyBy, countBy, partition,
chunk, zip, unique, compact, flatten, repeat, min(by=),
max(by=), and sorted(by=, descending=). Keep an explicit for when work
mutates state, has custom exits, writes multiple outputs, depends on ordered
effects, or carries state between items. The compiler corrects the narrow forms
it can prove; this rule covers larger designs it cannot prove locally.
List callback operations read a stable checked snapshot. A for loop observes
the List's live length, so appending during iteration extends that loop. Choose
between them deliberately; do not refactor a mutating loop into a callback
pipeline merely because the result looks similar.
For Maps, use getOrSet for a cheap missing value and getOrSetWith when
creation must be lazy. Use a pull iterator when only the first entry is needed;
copying keys() or values() is appropriate only when a stable List snapshot
is actually required.
NaN is a valid number, and equality follows SameValueZero, so NaN == NaN.
Operations that promise ordering or an aggregate answer reject NaN rather than
silently ordering it: Math.min, Math.max, Math.clamp, collection sorting,
min, max, and sum. Filter or reject NaN before those operations when input
is not already constrained.
Model finite application states as enums and stable shapes as named records.
Validate unknown once at the boundary with Type.parse, then keep the inside
typed. Use Type.from for a shallow, target-owned projection from one already
typed record; surplus source fields never enter the result.
JSON resources and unsafe JavaScript imports produce unknown. Do not spread
validation across consumers: adapt and validate in the module that owns the
boundary, then export a checked application type.
Use Type.parse alone for structural shape. When already typed data also has
domain constraints—integer ranges, non-blank identifiers, cross-field rules,
or bounded collections—compose them with velar/validation in the module that
owns those constraints. Reuse that validator at construction and parsing
boundaries instead of repeating assertions in configuration loaders.
An extern module is the preferred boundary for a third-party package with a
contract. Declare it once in an adapter module and export an application-owned
surface. Use import js unsafe only when there is no honest contract to write;
validate its unknown result before typed code reads it.
Extern arguments cross by raw identity and are read-only from VelarScript's point of view. If foreign code mutates one, VelarScript reactivity does not see that write. Prefer a foreign operation that returns new data, then assign the result on the VelarScript side.
Place a resource's using binding in the scope that truly owns its lifetime.
@dispose: is cleanup, not an ordinary method: scope exit runs it in reverse
ownership order. Return data read from an owned handle, not the handle itself.
For a foreign handle, wrap it in an application class whose @dispose: releases
it.
Await work whose result or completion matters. Detach only genuinely independent work whose failure has an owner. Prefer optionals for expected absence, typed errors for failures callers distinguish, and assertions for broken invariants. Validate external data before side effects so a rejected value cannot leave partial state behind.
Deep reactive state observes VelarScript-owned field and collection mutations. Mutate that state directly when identity should survive; rebuild a value only when replacement itself is the intended event. Calls, getters, awaits, and foreign mutations are semantic boundaries—do not assume a previous narrowing or derived value stays valid across them unless the compiler proves it.
Use this order:
- Look for a checked standard or installed-package contract.
- Add one project-owned adapter with an
extern modulecontract. - If no contract is possible, isolate
import js unsafe, validate immediately, and keepunknownfrom escaping the adapter. - If the compiler itself blocks a valid design, reproduce and report it rather than distributing a workaround.
velar build --mode readable --source-maps is the inspection exit when emitted
JavaScript must be understood. Ordinary builds should retain the project's
production mode.
For a diagnostic that cannot be satisfied, incorrect emitted behavior, or a check that contradicts the installed surface:
- Run
velar repro. It writes a local, self-contained reproduction and uploads nothing. - Complete the generated README sections What I wrote (or wanted to write)
and How I resolved it. Use
blockedwhen there is no workaround. Keep What the compiler said verbatim. - File it with the repository's
.github/ISSUE_TEMPLATE/defect template.
For language detail not decided here, inspect the compiling tour under
examples/tour/ or the relevant owner documentation. The installed compiler is
authoritative when this brief and the current toolchain differ.
readonly protects one layer. readonly List<State> protects list slots and
keeps mutable State values; List<readonly State> protects State slots and
keeps a mutable list. Declare readonly type State: when every State field
slot is readonly and use State directly. A readonly field prevents replacing
that field and preserves the value's own type. Apply qualifiers to nested
collections explicitly when their contents must not be replaced. Follow A20
and A21 for equivalent declaration and qualifier simplifications.