Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
fb8addd
add one-shot memory spec fix workflow
TheLazyCat00 Jul 25, 2026
78a700d
clarify host overwrite slot reuse
github-actions[bot] Jul 25, 2026
4da40a7
Add one-shot memory layout updater
TheLazyCat00 Jul 25, 2026
905cc5b
Specify arena regions and dynamic backing-store reuse
github-actions[bot] Jul 25, 2026
aac419e
Add one-shot review fix updater
TheLazyCat00 Jul 25, 2026
ef78cde
Remove inactive review updater
TheLazyCat00 Jul 25, 2026
85f2931
Use global recyclable anchor pool
TheLazyCat00 Jul 25, 2026
4eeedb4
Align memory story with global anchors
TheLazyCat00 Jul 25, 2026
7b2de03
Clarify anchor layout and rehosting conflicts
TheLazyCat00 Jul 25, 2026
cf1e533
Align memory story with anchor rules
TheLazyCat00 Jul 25, 2026
c7c3bdc
Merge main into memory arena specification
TheLazyCat00 Jul 27, 2026
2feefae
Preserve memory story and append allocator revision
TheLazyCat00 Jul 27, 2026
b5cab00
docs: anchor pool and move-guest liveness rules (#149)
TheLazyCat00 Jul 28, 2026
6140d1a
Clarify rehosting storage relocation
TheLazyCat00 Jul 28, 2026
a717afc
Add temporary forwarding-anchor update workflow
TheLazyCat00 Jul 29, 2026
ff46f0f
Specify forwarding anchors for merged host identities
TheLazyCat00 Jul 29, 2026
b10eaed
docs: bare symbols are no longer guest sources; add the 'T borrow mode
claude Jul 30, 2026
00de7e8
docs: story chapters for the guest-source restriction and the three m…
claude Jul 30, 2026
a9b2a8b
docs: address CodeRabbit review on #151
claude Jul 30, 2026
fe62f1a
docs: second CodeRabbit round on #151
claude Jul 30, 2026
84c3505
docs: third CodeRabbit round on #151
claude Jul 30, 2026
6b0c909
docs(meta): make the glossary a required stop when a rule changes
claude Jul 30, 2026
99e143b
docs: the borrow is the non-swallowing way to pass a bare symbol
claude Jul 30, 2026
1e4dcd5
docs: tighten two guest-source summaries
claude Jul 30, 2026
dba89ba
docs: never write ' on this; add a forward pointer in the anchor story
claude Jul 31, 2026
19806f0
docs(stories): keep stories/memory.md strictly append-only against main
claude Jul 31, 2026
2905461
docs(meta): state the append-only story rule literally, with a check
claude Jul 31, 2026
5b7616a
docs: the receiver is never a swallow position, and say why properly
claude Jul 31, 2026
ad39224
docs: say which sense of "receiver" each rule means
claude Jul 31, 2026
97e5bf2
docs: rename receiver to subject
claude Jul 31, 2026
dd1cbcb
docs: tell the receiver-to-subject story
claude Jul 31, 2026
042a7bd
docs(meta): make the stories guide the source of truth for append-only
claude Jul 31, 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
76 changes: 74 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ next agent gets up to speed — keep it to durable, agent-facing facts.
internal contradictions on this codebase before: re-read the *un-updated*
spec files and `bench/zane_bench.c` against the new design before opening a
PR, not just the file you changed.
3. **A rule correction is not done until `glossary.md` carries it.** The
glossary summarizes rules it does not own, so fixing a rule in its canonical
home and leaving the entry paraphrasing the superseded version produces a
spec that contradicts itself — this has happened twice, both times caught in
review rather than by the author. After editing any normative rule, grep
`spec/glossary.md` for the concept and update the entry in the same commit.

## The `bench/` harness
`bench/` is a reference **C** harness for runtime experiments — **not** Zane
Expand All @@ -43,13 +49,51 @@ The generics system was unified into a `<>`-header / `()`-call model (canonical
home `spec/generics.md`, casing rules `spec/lexical.md`). Several pre-redesign
forms are now illegal and must never reappear. Grep for them — none should hit:

```sh
grep -RIn -E "Array\[|\[size\]|Array[0-9]+|Matrix10|\[rows\]|\[cols\]|inferred type generic|type-parameter symbol|root form" spec/
```
grep -nE "Array\[|\[size\]|Array[0-9]+|Matrix10|\[rows\]|\[cols\]|'[A-Z]|inferred type generic|type-parameter symbol|root form" spec/*.md
```

`'[A-Z]` used to be on that list — it is **not** any more. A leading `'` is now
the **borrow** type marker (`'Node`), canonical home `spec/memory.md` §2.9,
surface form `spec/syntax.md` §2.3. Do not re-add it to the retired-forms grep.

The only legitimate stray `<...>` is `Result<T, E>` in `spec/error-handling.md`
— Rust's type named as a comparison, not Zane's.

A second guard covers the memory model. A **bare symbol is not a guest source**
(`spec/memory.md` §2.8.1), so a spec example that mints an `&` from one is a
bug. Eyeball every hit of:

```sh
grep -RIn -E "&[A-Z][A-Za-z0-9]*[[:space:]]*=[[:space:]]*_?[a-z][A-Za-z0-9]*[[:space:]]*(//.*)?[[:space:]]*$" spec/
```

The pattern matches a **bare-symbol** right-hand side. Only one legal source is
excluded syntactically: a field access (`= car.engine`) never matches, because
`.` is outside the character class. The other legal source **does** match — an
`&T` parameter is written bare, so `r &Node = source` inside a callee is a hit
even though it is correct. Read every hit and keep it if any of these hold:

- the right-hand side is an `&T` parameter of the enclosing verb (check the
signature, not the line);
- it is a deliberate `// ILLEGAL:` example;
- it is a grammar metavariable, as in `syntax.md`.

Anything else is a real one to fix.

Two details are load-bearing. The trailing `(//.*)?[[:space:]]*$` is what makes
the guard see the `// ILLEGAL: ...` examples; without it the end anchor skipped
every commented line, which is most of them. The `_?` catches a private
lowercase name (`_engine`) — Zane allows `_` only as a leading character, never
inside a name (`lexical.md` §4.1–4.2), so nothing more is needed there.

Run both with `-R` on the directory, not a `spec/*.md` glob plus a bare
directory argument: `grep` prints `bench/: Is a directory` and silently skips
it otherwise.

Stories are exempt from both greps: `stories/` records the language as it was
at each turn and is never rewritten to match the present spec.

If the grep hits an old form, stop and rewrite it in the unified system. If a
cross-reference target moved (renumbered `§`), fix the reference in every doc
that uses it, then re-grep for the old numbers. If the change conflicts with
Expand All @@ -66,6 +110,34 @@ quality bar — dense, opinionated, long-form prose. Writing a story is two
halves: write the narrative, then integrate it into the spec. Don't skip the
second half.

### Append-only: run the check, and read the rule where it lives
**Story guide §5 owns this rule** — what may be edited, what the PR-versus-commit
distinction means, and the rare consolidation exception. Read it there; it is
the source of truth for contributors and agents alike, and this section adds
only what a session keeps getting wrong.

Nothing enforces it automatically — no CI, no hook. Run the check yourself
before every commit that touches `stories/`:

```sh
git diff origin/main -- stories/<topic>.md | grep -E "^-[^-]"
```

Additions only is the passing result.

Two failure modes, both from real sessions on this repo:

- **Too loose.** Editing a merged chapter to fix a retired claim, or bolting a
forward pointer onto one. Say what stopped being true from the *new* chapter
instead, naming the older chapter's claim. Caught in review, not by the author.
- **Too strict.** Refusing to touch chapters *your own branch* added, because
they were already written. They are drafts until the PR merges — rewrite,
reorder, and insert among them freely; a decision reached late in review often
belongs before them. The grep is quiet through all of that by design.

If the grep is clean, you have not violated the rule, whatever your instinct
says.

### Interview the maintainer — you cannot reconstruct the real reasoning
The actual thread — which roads were tried and rejected, in what order the
realizations came, what pressure forced each turn — lives only in the
Expand Down
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,13 @@ The spec states *what* the language does; the **why** lives in a parallel set of
| [`stories/adt.md`](stories/adt.md) | [`spec/adt.md`](spec/adt.md) — splitting `enum` from `variant` against the hype, the shared struct body, escaping the matcher machine with case overloads and the turn to a central `match` block, matching variants rather than patterns, keeping enum data outside the members, reducing a match group to sugar for one arm per case, and building a variant by naming a case rather than calling a constructor |
| [`stories/generics.md`](stories/generics.md) | [`spec/generics.md`](spec/generics.md) — the parameter model, the `<>`/`()` split, size-in-the-type, and the deferred features |
| [`stories/dependencies.md`](stories/dependencies.md) | [`spec/dependencies.md`](spec/dependencies.md) — URL identity, the manifest/resolution split, prebuilt distribution, symbol-rewriting, the browsable global cache, the package-graph acyclicity rule, opt-in remapping, and why `core` became a bundled implementation package |
| [`stories/memory.md`](stories/memory.md) | [`spec/memory.md`](spec/memory.md) — the no-GC-no-lifetimes goal, the move problem and the anchor, lazy backpointer creation, the indexed heap table, the rooted-guest rules and the host/guest terminology split, the collapse to one value/reference axis with a borrowed receiver, and the shift to segmented chunked bump arenas |
| [`stories/lifetimes.md`](stories/lifetimes.md) | [`spec/lifetimes.md`](spec/lifetimes.md) — lexical scope in place of a borrow checker, what may be moved, the declaration-block rule that kills flow analysis, downgrade instead of use-after-move, parameter-rooted returned guests, and why each strict rule is the minimal guard against one specific memory corruption |
| [`stories/effects.md`](stories/effects.md) | [`spec/effects.md`](spec/effects.md) — inferring effects instead of annotating them, receiver-scoped `mut`, capabilities in place of ambient I/O, the four-level ladder and the Total-Pure/Pure split, what deliberately is not an effect, and mutation through a borrowed receiver |
| [`stories/memory.md`](stories/memory.md) | [`spec/memory.md`](spec/memory.md) — the no-GC-no-lifetimes goal, the move problem and the anchor, lazy backpointer creation, the indexed heap table, the rooted-guest rules and the host/guest terminology split, the collapse to one value/reference axis with a borrowed subject, the shift to segmented chunked bump arenas, the split into fixed-size and dynamic regions with anchors moved to a runtime-global recyclable pool, taking the bare symbol away as a guest source, and the three passing modes that split out of it |
| [`stories/lifetimes.md`](stories/lifetimes.md) | [`spec/lifetimes.md`](spec/lifetimes.md) — lexical scope in place of a borrow checker, what may be moved, the declaration-block rule that kills flow analysis, downgrade instead of use-after-move, parameter-rooted returned guests, why each strict rule is the minimal guard against one specific memory corruption, and narrowing a returned guest's root to a guest parameter once borrows arrived |
| [`stories/effects.md`](stories/effects.md) | [`spec/effects.md`](spec/effects.md) — inferring effects instead of annotating them, subject-scoped `mut`, capabilities in place of ambient I/O, the four-level ladder and the Total-Pure/Pure split, what deliberately is not an effect, and mutation through a borrowed subject |
| [`stories/concurrency.md`](stories/concurrency.md) | [`spec/concurrency.md`](spec/concurrency.md) — the parallelism/concurrency split and the refusal of `async` coloring, why `spawn` marks only a call, water-tower lifetimes, signature-based safety without locks, and value-typed mutation closing the aliased-write gap |
| [`stories/error-handling.md`](stories/error-handling.md) | [`spec/error-handling.md`](spec/error-handling.md) — the two-doors model and why failure is control flow rather than a `Result` value, `resolve` as expression-substitution rather than assignment, typed abort paths and the deliberately-absent propagate operator, keeping abortability orthogonal to effects, and explicit path values through `Unit` |
| [`stories/control-flow.md`](stories/control-flow.md) | [`spec/control-flow.md`](spec/control-flow.md) — `guard` as an active exit that opens no scope of its own, doing without `while` behind a written loop bound, one-based counting after the loop that forced the question, and why control-flow contracts use fundamental semantic types |
| [`stories/functions.md`](stories/functions.md) | [`spec/functions.md`](spec/functions.md) — pulling methods out of the type body and the verb model that revealed, mutation made visible with `:`/`!`, overloading on parameter shape alone, why callables are call-only while self-typed lambdas are values, and why every return carries an explicit value |
| [`stories/functions.md`](stories/functions.md) | [`spec/functions.md`](spec/functions.md) — pulling methods out of the type body and the verb model that revealed, mutation made visible with `:`/`!`, overloading on parameter shape alone, why callables are call-only while self-typed lambdas are values, why every return carries an explicit value, and dropping the inherited word "receiver" for `subject` |
| [`stories/operators.md`](stories/operators.md) | [`spec/operators.md`](spec/operators.md) — the fixed vocabulary worth overloading, `~` as the universal flip, laws enforced through derived operators, grammar-only grouping, and home-package coherence |
| [`stories/packages.md`](stories/packages.md) | [`spec/packages.md`](spec/packages.md) — the directory as namespace and compilation unit, declarations as move checks, explicit qualified access through `$`, and keeping mutable state inside values so the effect model can see it |

Expand Down
7 changes: 5 additions & 2 deletions contributing/naming-terms.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This guide describes how the spec chooses the coined terms it reuses — the named
concepts recorded in [`glossary.md`](../spec/glossary.md), such as `verb`,
`mould`, `borrow`, `host`, `guest`, `anchor`, and `tether`. It governs the *terms of art* the
`subject`, `mould`, `borrow`, `host`, `guest`, `anchor`, and `tether`. It governs the *terms of art* the
documentation leans on, not the surface keywords of the language itself.

Terminology is worth naming deliberately because a good term is used on nearly
Expand All @@ -20,6 +20,9 @@ does the teaching before the definition is even read.

- **`verb`** — a function, method, operator, constructor, or lambda. In grammar
a verb is the word that *acts*; a callable is the construct that *does* work.
- **`subject`** — the object a method is called on. Grammar again, and the same
sentence: the subject is what the verb acts from, so `player!setScale(...)`
reads subject–verb–object down the line.
- **`mould`** — a `struct`/`variant`/`enum` form. A mould gives shapeless
material a fixed form; these forms give a type its shape, and the type is what
is cast from them.
Expand Down Expand Up @@ -99,7 +102,7 @@ seen rarely and gains its meaning slowly, so an oblique reference like *Ariadne*
(the thread through the labyrinth) is a strength.

A **term** is the opposite case: read constantly, and needed to teach on contact.
Terms therefore lean plain and everyday — `verb`, `mould`, `borrow`, `host`,
Terms therefore lean plain and everyday — `verb`, `subject`, `mould`, `borrow`, `host`,
`guest`, `anchor`, `tether` — even when the underlying instinct (name by metaphor, keep the link
oblique) is the same. When in doubt for a term, choose the ordinary word over the exotic one.

Expand Down
19 changes: 18 additions & 1 deletion contributing/writing-stories-docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,24 @@ The href ends in the chapter's heading **anchor** so the link scrolls straight t

This is the discipline that makes the folder a *history* rather than a stale snapshot.

**Append, don't overwrite.** When the design changes, the old reasoning did not become false — it became *the previous chapter*. So when the spec moves, add to the story: open a new chapter (or extend the relevant one) that names the cause and what it forced — *"The shift to X meant the old Y no longer held, so we…"* — and pin its spec references to the new commit (§4.2). The discarded path stays on the page as the record of why the design used to be one way and is now another; that causal trail is often the most illuminating thing in the file, and rewriting it away destroys it.
**Append, don't overwrite.** When the design changes, the old reasoning did not become false — it became *the previous chapter*. So when the spec moves, add to the story: open a **new chapter at the end of the file** — after everything already published, see the publication note below — that names the cause and what it forced — *"The shift to X meant the old Y no longer held, so we…"* — and pin its spec references to the new commit (§4.2). The discarded path stays on the page as the record of why the design used to be one way and is now another; that causal trail is often the most illuminating thing in the file, and rewriting it away destroys it.

"Append" is meant literally, and it has two teeth:

- **Do not touch a published chapter — at all.** Not to correct a claim the design has since retired, and not to bolt a forward pointer onto the end of it. A chapter records what was true when it was written; a later chapter is where you say what stopped being true and why. Naming the superseded claim explicitly *from the new chapter* — "the segmented-offset chapter had promotion rewrite the one anchor cell; that holds only while…" — does the same job for the reader without editing history.
- **A new chapter goes after every published one**, never slotted between chapters that already exist. Chapter order is the order the thinking moved, and the file's tail is the present. Inserting into the middle rewrites the sequence even when no existing character changes.

**The unit of publication is the pull request, not the commit.** "Published" means merged — what is on the default branch. The chapters a PR is *itself* adding are still draft until it lands, so within that PR they may be rewritten, reordered, or have a new chapter inserted among them, however many commits it takes. A design decision reached late in review often belongs *before* the chapters already drafted on the branch, and putting it there is not a violation. What must not move is anything that was already merged.

**Verify it by diffing.** Before committing a story change, check it against the branch you are merging into:

```sh
git diff origin/main -- stories/<topic>.md | grep -E "^-[^-]"
```

Any output is a violation: a removed or rewritten line means a published chapter was edited, and a `-` next to a chapter heading means a chapter was inserted ahead of one that had already merged. The clean result is additions only — which is also why the check is the right one to run: it compares against what is published, so it stays silent while you rearrange your own branch's new chapters and speaks up the moment you disturb a merged one.

Nothing runs this for you. There is no CI job and no hook; the rule is enforced by the author running the diff before committing and by the reviewer running it again on the branch. That is deliberate — the "consolidate dead threads" exception below is a judgement call no check could make, so a green check would have to be overridable anyway — but it does mean a violation reaches `main` if both people skip it. Treat the command as part of the commit, not as an optional audit.

**Consolidate dead threads, sparingly.** Appending forever would bury the present under history. So a chapter *may* be rewritten or folded down — but only when its narrative has become pure dead weight: it no longer illuminates the present design *and* is not interesting as history. That is a high bar. The default is to append; consolidation is the rare exception, not routine cleanup, and when in doubt you keep the history.

Expand Down
Loading