Guidance for humans and coding agents working in this repository.
Echo is a standalone compiled language implemented in Rust. It is not a
PHP superset. The toolchain entrypoint is xo.
One LLVM backend and one Rust-owned runtime serve both AOT native binaries and in-process JIT. There is no bytecode VM.
Std / runtime (locked): userland imports only / std/… (e.g. io.print).
Privileged std sources may / runtime and call runtime.*; codegen maps those
to echo_runtime_*. No free userland print, no userland / runtime. LSP and
reflection use the same pipeline surface as check — see docs/stdlib.md.
Language surface design was reset. Prefer a small composable core. Do not
treat removed freezes, old Echo PHP-era syntax, or prior session locks as
authority. Track design in docs/roadmap.md; write agreed rules into
docs/syntax.md / docs/lexer.md.
| Kind of fact | Location |
|---|---|
| Crate ownership, pipeline shape | docs/architecture.md |
| SOTA gaps / analysis product | docs/sota-gaps.md, ADR 0012 |
| Shared vocabulary | docs/glossary.md |
| How to run checks and host tools | docs/development-speed.md |
Which test to write (crate / e26 / examples / xo test) |
docs/testing.md |
| Durable decisions | docs/adr/ |
| Layer / domain rules | docs/*.md (syntax, parser, semantics, …) |
| Design/syntax discussion progress | docs/roadmap.md |
| Full pipeline (spine + hosts + order) | docs/pipeline.md |
| Full toolchain vertical checklist | docs/implementation.md |
| User-facing language book / site | www/ |
| Machine-checked behavior | tests and fixtures |
Do not invent a second source of truth in chat, scratch notes, or CLI-only tables. Update the matching doc when a fact becomes durable.
See docs/README.md for the full docs map.
-
Cargo workspace, resolver
2, edition 2024. -
Pipeline (ownership detail in
docs/architecture.md):source → lexer → ast/parser → semantics → hir → mir → codegen (LLVM) → AOT | JIT -
Supporting crates: diagnostics, syntax facts, index, resolver, fingerprint, cache, build, reflection, std, lsp. CLI:
xo.
- Land language features as full vertical slices where applicable: syntax → parse → semantics → IR → codegen → runtime → CLI → proof → docs.
- Behavior belongs in the earliest shared layer that owns it.
xoandecho_lsppresent and orchestrate; they must not redefine language semantics. - Prefer strong types and explicit diagnostics. Fail clearly rather than silently approximate.
- Prefer unit tests for pure logic; file fixtures for end-to-end language
behavior. Fixture conventions live in
docs/fixtures.md.
When you change language, runtime, codegen, std, or CLI behavior, update all three in the same change (or stacked PR). Incomplete = incomplete work.
| Proof | Where | Role |
|---|---|---|
| 1. Crate tests | crates/<name>/ unit/integration tests |
Pure logic of that crate (decode, resolve edge, unify, …) |
| 2. Echo 2026 (echo26 / e26) | echo26/ + e26 |
Executable contract of the Echo 2026 edition via candidate binary |
| 3. Examples | examples/misc/, examples/app/, examples/algos/ as applicable |
Human-runnable demos stay accurate (xo run / xo check) |
Echo 2026 is the language edition and canonical public Language Spec
(ADR 0015). Tooling IDs stay e26 / echo26/. Public law: site /e26 +
Reference /docs. Implementer surface: docs/syntax.md and related layer docs.
Rules:
- Every crate that gains or changes logic must keep its own tests green and current — add tests when adding behavior; do not leave crates testless after non-trivial work.
- Echo 2026 suite (e26) is not optional for user-visible language/runtime behavior (see below).
- Examples are not optional when the change is something users would run or
copy (
xo rundemos underexamples/misc/, app/std samples when relevant). - A crate unit test is not a substitute for e26; e26 is not a substitute for crate tests; examples are not a substitute for either.
- Prove:
cargo test -p <touched crates>,e26 --binary target/debug/xo(orscripts/gate echo26), and smoke the touched examples.
Every language-surface or frontend/runtime behavior change must update the Echo 2026 conformance suite in the same change (or the same PR stack):
- Add or adjust fixtures under
echo26/(small numbered cases per feature). - Extend the candidate protocol in
e26/xoif a new stage or flag is needed (lex,ast,check,run, …). - Refresh expectations (
e26 --binary target/debug/xo --updatewhen intentional). - Prove green:
scripts/gate echo26(orjust e26). - Keep public Spec / Reference (
www) and implementer docs aligned when the user-visible rule changes.
See docs/fixtures.md, docs/adr/0015-echo-2026-canonical-edition.md, and
docs/implementation.md.
Do not invent parser/lexer/semantic special cases for convenience, fixture greenness, or “looks like other languages.”
Before adding a branch, recovery path, extra form, or relaxed rule:
- Cite authority — Echo 2026 public Spec / Reference (
www),docs/syntax.md,docs/lexer.md, an ADR, or an explicit user decision in the session (then write it into the matching doc). - Prefer the general rule — if the design already covers it, implement that; don’t bolt on a one-off.
- Name the edge in code comments only when non-obvious, pointing at the doc.
- Cover it in echo26 with a small fixture (happy path + reject path if the edge is “must fail”).
- Flag unjustified leftovers — when you notice an existing edge with no doc basis, call it out to the user and either remove it, lock it in the spec, or open a short roadmap/ADR note. Do not silently keep expanding it.
Examples of justified edges: dual-use leaders by statement position (syntax);
~ .field = as receiver field write (syntax); no trailing commas (syntax).
Examples of unjustified edges: accepting trailing commas because chumsky’s
allow_trailing is handy; inventing multi-bind syntax only because
examples/app/ has it before the grammar is locked; recovery that changes
program meaning.
- Smallest useful check first:
scripts/gate changed/cargo test -p <crate>. - Language work: also
scripts/gate echo26. - Broad gate:
scripts/gate workspace. - Website:
scripts/gate web. - Host tools:
scripts/gate toolsorjust tools.
Details: docs/development-speed.md.
- Document behavior with at least one concrete snippet when a snippet helps.
- Keep ADRs short and decision-focused. Put operational how-to in
development-speed.mdor layer docs, not in ADRs. - Do not leave empty placeholder docs that claim unfinished behavior is done. Layer specs may say Status: not started until real rules exist.
- Do not reintroduce PHP compatibility as a product goal unless an explicit ADR reverses that.
- Do not copy language implementation or docs from other Echo repositories. Read reference material if needed; synthesize and write for this tree.
- Do not land language behavior without updating Echo 2026 proof
(
echo26+e26, greenscripts/gate echo26) when that stage exists. - Do not introduce language edge cases without doc/user justification (see “Justified edge cases only” above).
When the locked language model and the current compiler diverge:
- Document the gap explicitly in the matching layer doc / roadmap checklist
(see function values in
docs/semantics.mdanddocs/hir.md). - Do not mark Run ✓ or “done” for a design that only works via a shortcut (e.g. static calls only while “functions are values” is incomplete).
- Prefer finishing the model over stacking more shortcuts. When implementing the real path, remove obsolete names/comments/aliases that restate the old story so later sessions are not misled.
- In chat and commits, say partial / gap when that is the truth.