The project

· a new career in a new town

How to verify that the compiler compiles itself, how language changes are reviewed and the twenty principles printed by heroes this.

How to verify that it compiles itself

The Heroes compiler is written in Heroes. Compiling it requires nested data, name tables, recursion, error handling and programs spread across files. It is a practical test of how the language handles a complex program.

The repository, the project’s archive, also contains the compiler as one generated C file. You can build it on a computer without Heroes installed. Then use the resulting compiler to translate its own source into C and compare the two files. Do it on a release, the version the project has published:

$ git checkout v0.5.0
$ clang -I runtime seed/heroes.c runtime/runtime.c -o heroes
$ ./heroes build selfhost/main.hero --emit-c -o out.c
$ cmp out.c seed/heroes.c
                       (cmp prints nothing: not one byte differs)

  seed/heroes.c   the C the repository ships: the way in
  out.c           what the compiler writes when it compiles itself

  over twenty megabytes of C, and more than 250,000 lines of it

If the files are identical, cmp prints nothing. The comparison verifies that the compiler reproduces the C it was built from. The third command takes under two minutes on a MacBook Air. Anyone can repeat the test: the repository is open and the four commands are the whole of it.

Between two releases the generated C file is an earlier day’s compiler. It builds the current one from the Heroes source, and that one reproduces itself one step later, which the project checks on every change. heroes --version says which one you hold: its second line is a fingerprint of the source it was built from.

Heroes implements every part of the compiler: reading the words and structure of the code, connecting names to declarations, checking types, creating and checking an intermediate representation of the program, managing memory, generating C, checking external libraries, reporting errors, formatting code and handling commands.

Errors found by compiling its own source

The new compiler initially rejected its own source, incorrectly reporting a structure that contained itself. Investigating this found defects in both the new compiler and its predecessor. One involved the underline in an error message: it covered the annotation that the test used to check its position.

Compiling the compiler’s source tests many language forms in one program. Several defects had the same cause: a loop jump inside a match branch skipped the instruction that updated a manually maintained index.

The final difference between the two files was the order of the lines. Some lists were already ordered in the previous compiler; the new one needed explicit sorting, as notes in the source had warned. Maps do not guarantee an order. Adding the missing sorts made the files identical.

How language changes are reviewed · five years

Changes to syntax, language rules, error messages or compiler structure are reviewed by a panel of five seats. Each seat receives different material, and four can veto a proposal. Internal compiler or runtime changes, which affect the code that supports execution, go to two of the five if they leave syntax, error messages and the specification unchanged.

The panel chooses the solution it considers strongest and most complete, then submits it to the author for approval. Work continues while confirmation is pending. If there is a more cautious alternative, it is recorded so the author can consider it. After the five verdicts a sixth reader, the completeness critic, asks what nobody listed, and more than once the solution adopted was a route no seat had proposed.

Each review is recorded in a repository file, including the material examined, the verdicts and any vetoes. There are more than a hundred and fifty reviews. Every language change requires this review process. What the panel refused, and who said no, has a page of its own: what Heroes refused →

Twenty lines

heroes this prints these principles, which are included in the compiler. They are reproduced here as the command prints them.

The Zen of Heroes

 1. This language begins with a tribute to David Bowie.
 2. It is made for language models; people can read it too.
 3. A model may have read everything; each page must still explain itself.
 4. What the eye can see is all there is: sound and vision.
 5. There is one way to say each thing; every program sings in the same voice.
 6. That way may not be obvious at first; the Starman already knows it.
 7. The semantics are familiar; the syntax is new.
 8. Turn and face the strange: every strange choice here is deliberate.
 9. Nothing changes behind your back; every change signs its name.
10. Every plausible mistake must become a compile error before the program runs.
11. Unless you mark the gap with ???: an honest hole beats a confident guess.
12. A compile-time error is an answer; a runtime error is an ambush.
13. An error should tell you how to fix the program. Anything less is a complaint.
14. Repeating a word is cheap; forgetting one can cost the whole program.
15. The specification stays small under pressure; every word must earn its place.
16. Crashes and leaks are bugs. Ashes to ashes: what Heroes takes, it returns.
17. When pretty code and safe code disagree, safe code wins.
18. There is no standard library; C is the man who sold the world.
19. Never crashing in the same car: what C might keep, Heroes assumes it keeps.
20. We can be heroes, just for one day.

Line 10 states the thesis, line 18 the initial constraint, line 19 what that constraint costs at the border with C, and line 17 the priority when two principles conflict. Why the language is built this way →

What is built, and what is next

The chain is one table in the repository, a row per milestone, closed first and scheduled after. This page reads it when the site is built: 71 milestones closed of 92, and the open one is M-panic-location.

What comes next

#milestonewhat it delivers
72M-panic-locationan abort a program raises names its file, line, column and function, an assert on any value shows both sides, and no program's text forges a report
73M-generated-programsprograms nobody wrote: a generator that composes valid Heroes and knows the answer before the compiler is asked
74M-compatibility-promisethe paragraph 1.0.0 rests on, and the suite that makes a broken promise red
75M-latency-ceilinga latency the compiler may not lose without a decision: heroes check and clang on the seed timed at every close, the incremental frontend its return condition
76M-core-packagessmall packages that compose, organised as Go's tree, in Heroes or over C
77M-package-managerheroes add/heroes fetch; bindings instead of a standard library
78M-web-frameworkcomposes the core packages, Go/Echo style: explicit routes, records, no magic
79M-doc-generatorheroes doc, the one direction Part 6's literate-source row promises
80M-compared-languagesthe same tasks written by a model in Heroes, Go, Rust, C and Zig, scored on hidden tests, turns and silent failures, the prediction registered before the first run
81M-deployable-binarywhat the machine that RUNS a Heroes program needs, on the three platforms, and which -O a shipped artifact carries
82M-standalone-compilerheroes needing nothing else installed: a C compiler and a linker inside it, and what each platform's C library needs beside it
83M-install-channelsa Homebrew tap, winget, a Nix flake, a Docker image, all built from the seed and pinned to a v* release tag; the version scheme itself was decided ahead, 2026-09-07
84M-online-compilerthe compiler reached without installing anything: the site's visitor writes Heroes and gets its answer
85M-lsp-serverheroes lsp
86M-vscode-extensionthe extension, complete
87M-publication-gatethe last gate before anything goes outward
88M-launch-poststhe first posts, each with the numbers its command produced, after the first hour a stranger spends was tried
89M-microcontroller-verdictthe ruling on a Heroes program running on a microcontroller under an RTOS, RISC-V first; the two facts a 32-bit build refuses today are its brief
90M-qbe-backendPart 7 item 15 — the proof that the IR is not C in disguise
91M-journey-bookthe journey — how this language came to be
92M-guide-bookthe guide, as a book you would find in a shop
The 71 closed milestones, the most recent first
#milestoneclosedwhat it delivered
71M-rechecked-issues2026-10-25every open issue re-checked against the tree: closed with the commit that settled it, closed with the reason it no longer applies, repaired, or re-homed on a row still to come
70M-thesis-harness2026-10-24Part 11's metrics 2 and 4 run for the first time, as a Heroes program: forty held-out tasks sealed and a prediction pushed before the run, the primary inconclusive at +0.0 points with no discordant pair, the strict arm paying at turn 1 and level by turn 3
69M-witnessed-seed2026-10-18the seed witnessed by a second implementation: the archived Rust bootstrap rebuilt and agreeing with the seed at its tag, a gcc leg beside clang, every seed since the archive rebuilt from the one before it, 39 of 39, and every refresh since naming its route for the CI; then the bootstrap out of the tree, kept at its tag
68M-readonly-lend2026-10-18a function reads the caller's place without claiming to change it: lent on a parameter, C's const T * bound read-only, and beside it an @ of the same place and a read-only lend past a variadic prototype refused
67M-bounded-seed2026-10-17the seed bounded in shape and in rhythm: its re-anchoring #lines joined to their anchors, refreshed only when the two-stage build needs it and at each release, every program carrying the key of what it was compiled from, and kept in the history at every tag, the history rewritten once and announced
66M-buildable-structs2026-10-17a C struct with long arrays is built by naming the fields that matter and ending with rest: zero.
65M-typed-inspection2026-10-13a stopped program shows Heroes values: the frame lists the author's bindings alone, the emitter declaring what it invented nodebug, and one fixed formatter, editors/lldb/heroes_lldb.py, which the editor loads, shows a record, a str, a [T], a {K: V}, a T?'s live arm and a variant's case in Heroes' syntax; the names keep their C prefix, refused in the emitted C by panel 213's two vetoes and kept by the author's answer of 2026-10-14 on its issue 2037
64M-issue-files2026-10-12every item this project tracks is one GitHub issue: one file each of issues/ first, a card on each, then 2,029 issues imported in the order they were born with their own days, then GitHub alone and the folder out of the tree.
63M-inferred-cell2026-10-10a mutable cell declared by its own symbol, @=, and typed by its value as a binding is; a cell nothing re-binds refused with a certain fix; the tree rewritten through the compiler's own fix.
62M-agreed-retention2026-09-28one C function, one story about who frees what it is handed: two modules may declare it with contradictory retention marks and the compiler takes both; one declaration cannot carry a contract chosen per call, which is sqlite3_bind_text's three modes; and nothing says a pointer C made is C's to free once.
61M-declared-extents2026-09-21how far C may read through a lent address, and what becomes of the pointer afterwards: counted_by declares the extent and the emitter checks it against the field's own sizeof in C; then the author flipped the default, so a C parameter is taken to KEEP what it is handed and a lend reaches only one declared lent; and where no word can reach — C frees the bytes from a callback, or from a later call with no pointer at all — the runtime names the leases that were live.
60M-readable-bytes2026-09-18a C byte buffer becomes text a program can print: the inbound direction str has never had
59M-arm-platform2026-09-18the fourth real machine: arm64 Linux, where a container runs and where char is unsigned
58M-check-completeness2026-09-17ten defects the hunt turned up, closed — and three were not what their own entry said
57M-marked-acquisition2026-09-14the mark goes where the obligation is CREATED — on the acquiring call — so a C handle nobody releases stops being silent
56M-cleanup-verdict2026-09-14the ruling on a scope-bound release for a C handle: a releaser keyed on the TYPE refused to Part 6, because ownership is a property of the CALL
55M-handle-verdict2026-09-14the ruling on telling one C handle from another, and three defects that were one question
54M-deferral-ledger2026-09-13every Part 7 item with no milestone gets a dated verdict or a return condition, and Part 8's homeless warts with it
53M-declared-thresholds2026-09-12every ceiling and floor says which way it may move, and something notices when it should have moved and did not
52M-reflection-verdict2026-09-12reflection refused at run time and as a general tag; a field's name becomes a name the checker resolves
51M-stated-grammar2026-09-12every syntactic form stated once, in Wirth's notation, inside the specification beside the prose that governs it, and heroes grammar for the half the compiler can print from its own tables
50M-rotated-records2026-09-12the records become directories, one entry per file and one milestone per file, and the file that held each stays behind as a map so every citation still resolves
49M-named-callbacks2026-09-11the role inversion through a generic callback, closed at 7 of 7 against a cheaper reading's 2 of 7: fold's type names its accumulator and its element, and a function handed to it names them the same way — a naming mandate the author chose over the sitting's own recommendation, with both numbers in front of them
48M-labelled-types2026-09-11defect 026 repaired: a function type names the parameters that can be confused, the names are part of its identity, and the classic inversion through a function value goes from 17% to 100% — at 432 lines against a condition of about 150, which is the sitting's own headline
47M-positional-values2026-09-11defect 026 narrowed: a label at a call through a function value is refused, the note stops prescribing the hole in silence, and spec § 3 says the call is positional — a sentence vetoed as false at the ballot that the refusal made true
46M-labelled-builtins2026-09-11defect 025 repaired: the same-typed-argument rule reaches the two built-ins it did not, both halves of it, 1022 call sites relabelled, and the classic argument inversion drops from 367 survivors to 75 with the swapped fail( at zero
45M-anchored-spec2026-09-11the specification takes the shape of a Report: thirteen numbered sections in reference order, one home per rule, the numbers as the citation anchors, two checks that keep the form, 81 citations converted, and the site's page copying the text in one click
44M-interpolated-strings2026-09-09f"line {n}: {word}": one token kind, one AST node, three helper modules, four decided ceilings moved by their measured lines, the spec at +138 real with R6's two sentences, and heroes mutate reaching the holes
43M-interpolation-verdict2026-09-09the ruling on design.md Part 7 item 7: string interpolation ENTERS as f"line {n}: {word}", three counts and one sitting, the spelling the author's; closed with the verdict and not the form, as it was named to
42M-held-bytes2026-09-09§4.19's FOURTH case: x: cstr @ s.lease() is a COPY C may read until end_lease(@x).
41M-cstr-lifetime2026-09-09a cstr may not outlive the str it was lent from: three positional clauses, 0 of 638 files moved.
40M-closures-verdict2026-09-08the ruling on Part 7 items 1 and 12: both refused, closures to Part 6 and inline blocks left unplaced
39M-discard-refusal2026-09-08_ = on a fallible value becomes a compile error, in two positions and not three
38M-open-repository2026-09-08the repository opens, and every page that says it is shut stops saying so
37M-declared-freer2026-09-07owned <C function>: the string C hands you is freed by the name its own declaration gives
36M-thread-stacks2026-09-06the guard speaks on every thread, and a worker's floor is the thread that ran main
35M-isolated-threads2026-09-06Part 7.13 concurrency: three of four measured corruption classes closed, and the door was in the checker
34M-c-callbacks2026-09-05a Heroes function reaches a C callback parameter, and a foreign thread is refused by name rather than left to corrupt
33M-corpus-depth2026-09-04the rung between a program and the compiler: nine programs, and half of every frame
32M-robustness-guards2026-09-03the guards that shut the holes §1.12 named: @ on an immutable, the stack, the C pointer verdict, the harness scratch
31M-documentation-site2026-09-02the site, anchored to programs that run
30M-corpus-coverage2026-09-02every language form has a program that runs it
29M-selfhost-nesting2026-09-02the compiler's own modules move into ten directories
28M-package-layout2026-09-02use paths, the qualifier, as, and where a program's files live
27M-argv-execution2026-08-31the compiler runs programs by argument list, and the shell stops being the boundary
26M-separate-compilation2026-08-26one .c per module, prototypes across TUs, the cache
25M-bootstrap-archive2026-08-19the third language dies
24M-harness-port2026-08-18the net in Heroes
23M-selfhost-fixpoint2026-08-18the fixpoint and the seed
22M-selfhost-port2026-08-17the compiler twice over, and one hash
21M-selfhost-probe2026-08-15the lexer ported, to measure what was missing
20M-complete-structs2026-08-15every field form a C header can write
19M-struct-passing2026-08-15a struct crosses by value
18M-binding-fidelity2026-08-14what an extern accepts
17M-program-corpus2026-08-13nine programs, six compiler defects, a CI
16M-sized-integers2026-08-12eight widths; int stops being a word
15M-literal-bases2026-08-120x 0o 0b, the _ separator
14M-header-constants2026-08-12the number leaves the file
13M-ffi-ladder2026-08-12Heroes calls C; SQLite with no shim
12M-module-namespace2026-08-12a program is many files
11M-generics-library2026-08-11sugar, heroes test, generics, the library
10M-optional-map2026-08-10T? and {K: V} — the last two types
9M-value-aggregates2026-08-10records and variants by value, copy-on-write
8M-strings-ownership2026-08-05str in C, and the ownership pass
7M-scalars-run2026-08-04the first native binary
6M-ir-lowering2026-08-04desugaring, the three-address IR with basic blocks
5M-typed-frontend2026-08-04the frontend complete: names, types, errors as a product
4M-name-resolution2026-08-04scopes, no shadowing, order-free top level
3M-syntax-tree2026-08-04parser, AST, the canonical formatter
2M-token-stream2026-08-04the lexer, and --dump-tokens
1M-day-zero2026-08-03git, workspace, four hand-written C targets

Next: four commands build it yourself, or take the whole language in one file.