FABRIC.md — reconcile §1-24, add §25 punch list
Reconciliation. §1-15 were written before any code was read and had drifted from §16-24 in twenty places. Each superseded claim is now marked in place rather than rewritten -- §19.4 and §19.5 quote the original §4 wording directly, and the retracing trail matters more than a tidy read. Two real gaps surfaced during the reread, neither previously recorded: - §3's wire table was missing `mass`. Density is heat / mass, so mass has to live in the entry, and §3 is the table an implementer would work from. It is now an eighth wire. - §8 asserts a per-patron exclusivity primitive -- "one sits in a car, the only exclusive thing in the room, no global lock" -- that nothing in §16-24 defines. §21 covers ISR-vs-mainline concurrency, which is a different question. Marked OPEN at the source and carried as punch list item 1.1. Also corrected: §9's admission table is complete for all five patron kinds with both `?` marks closed; §12's six questions are all resolved; §5's third category is supplied; §15's five-line summary had two wrong lines; §4, §6, §7, §17.4 and §17.6 status markers now match reality. §25 is the punch list, with its operating instructions first: one item at a time, no jumping ahead, no scope increase, no fabricating anything unverified, stop when blocked. Every checked item gets its own commit carrying both the change and this file with that box ticked, so the document is never a claim about work not in the branch. Phase 0 is the substrate -- Hera alone plus real timers and IRQ return paths on all three ISAs. Nothing else can start until it is done. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
7c436351a0
commit
7fe0133d07
@@ -1,8 +1,22 @@
|
||||
# FABRIC.md — DRAFT
|
||||
# FABRIC.md — the Stadium
|
||||
|
||||
**Status:** Draft for review. Captures the design session of 3 August 2026.
|
||||
**Nothing here is committed.** Sections are marked **DECIDED**, **LEANING**, or **OPEN** so
|
||||
you can argue with it rather than inherit it.
|
||||
**Status:** Living working document. Started 3 August 2026 and appended to as work proceeds.
|
||||
Sections are marked **DECIDED**, **LEANING**, or **OPEN** so they can be argued with rather
|
||||
than inherited.
|
||||
|
||||
**How to read it.** §1–15 are the original design argument, written before any code was
|
||||
examined. §16 onward are findings and decisions made against the actual tree, in the order
|
||||
they happened. **Where the two disagree, the later section wins** — earlier text is left
|
||||
standing, with a pointer, because §19.4 and §19.5 quote it directly and because retracing
|
||||
the reasoning matters more than a tidy read.
|
||||
|
||||
**On the name.** The thing described here is the **Stadium**. §1–15 call it "the arena",
|
||||
which was the working name until it collided with `src/starkernel/vm/arena.c` — the
|
||||
PMM-backed VM page allocator, an unrelated structure. Read "arena" as "Stadium" throughout
|
||||
the early sections; the word was not swept, to keep the original text quotable.
|
||||
|
||||
**§25 is the punch list.** It is the authoritative statement of what is done and what is
|
||||
not. Read its instructions before doing any work against this document.
|
||||
|
||||
---
|
||||
|
||||
@@ -23,6 +37,11 @@ message has.
|
||||
|
||||
**DECIDED.** Direction is not optional. The remaining question is effort, not validity.
|
||||
|
||||
> **Updated by §20 and §17.5.** There are five patron kinds, not four — VMs are the fifth
|
||||
> and were already implemented (§20.1). And the Console's patron is the dirty *event*, not
|
||||
> the cell (§17.5); "Console heats dirty cells" above is the reading that §9 flagged as
|
||||
> suspect and §17.5 resolved.
|
||||
|
||||
---
|
||||
|
||||
## 2. The arena
|
||||
@@ -49,17 +68,24 @@ One structure. No variants, no type field, no subclassing.
|
||||
|
||||
| Wire | Meaning |
|
||||
|---|---|
|
||||
| identity | handle or name |
|
||||
| heat | current thermal state |
|
||||
| TTL | remaining lifetime |
|
||||
| identity | handle or name — **never a content hash while resident** (§24.4) |
|
||||
| heat | conserved share of 1.0, moved by traffic (§19.1) |
|
||||
| TTL | remaining lifetime — messages and ACLs only (§17.1) |
|
||||
| pin | invariance flag (opposite of TTL, not an extension of it) |
|
||||
| link | index into the arena, not a pointer |
|
||||
| code field | what to do when this entry is worked |
|
||||
| payload | inline if small, by reference if large |
|
||||
| link | index into the Stadium, not a pointer |
|
||||
| code field | behaviour tag from a closed enumeration (§18.3) |
|
||||
| **mass** | **cells this patron occupies — its footprint (§19.2)** |
|
||||
| payload | carried in the patron's own cells; large patrons are simply heavy (§23.1) |
|
||||
|
||||
Fixed-size cells. Links are indices, so the arena stays an array — no fragmentation,
|
||||
Fixed-size cells. Links are indices, so the Stadium stays an array — no fragmentation,
|
||||
and tractable for Isabelle later.
|
||||
|
||||
> **Amended by §19.2 and §23.1.** `mass` is an eighth wire, added when density was
|
||||
> defined — density is heat ÷ mass, so mass has to live in the entry. And the original
|
||||
> payload rule ("inline if small, by reference if large") was dissolved rather than
|
||||
> answered: a large patron occupies more cells, which is what mass already measures.
|
||||
> By-reference is reserved for things outside the Stadium, which are not patrons.
|
||||
|
||||
**The code field is the entire type system.** A block's code field migrates. A message's
|
||||
delivers. A VM's ticks. The engine never asks what kind of thing it is holding; it
|
||||
heats, ranks, reaps, and calls the code field.
|
||||
@@ -67,7 +93,7 @@ heats, ranks, reaps, and calls the code field.
|
||||
> If you find yourself wanting a type field so the engine can branch on entry kind, the
|
||||
> design has gone wrong. The code field already answers that question.
|
||||
|
||||
**DECIDED**, except payload threshold — see Open Questions.
|
||||
**DECIDED**, including the payload question — dissolved in §23.1.
|
||||
|
||||
---
|
||||
|
||||
@@ -93,7 +119,21 @@ Consequences:
|
||||
TTL expiry stays unconditional: entries leave at their own time, unscheduled, nobody's
|
||||
decision. Pinning remains the separate, opposite mechanism — invariance, not longevity.
|
||||
|
||||
**LEANING.** The causality is right; the density formulation needs a concrete definition.
|
||||
**DECIDED as amended by §19.** The density formulation this section called for is supplied
|
||||
there. Three specific amendments, and the original wording above is left intact because
|
||||
§19.4 and §19.5 quote it:
|
||||
|
||||
- **"Density generates heat" is backwards** (§19.5). Traffic confers heat; density is
|
||||
heat ÷ mass, derived downstream. One word was carrying two meanings.
|
||||
- **The third bullet is struck, not repaired** (§19.4). "A crowded entry is harder to
|
||||
reach" does not translate — a hot entry is *easier* to reach, which is what a cache is
|
||||
for. The conclusion survives via the second bullet: heat is zero-sum, so popularity is
|
||||
self-limiting by conservation.
|
||||
- **The first bullet is now true rather than aspirational.** Ranking reads density, which
|
||||
§19.2 makes a number.
|
||||
|
||||
The line about TTL and pinning is correct but incomplete — §17.1 shows there are three
|
||||
departure mechanisms, not two: TTL, heat decay, and pin.
|
||||
|
||||
---
|
||||
|
||||
@@ -108,7 +148,18 @@ This section exists because forcing everything in is how this design turns into
|
||||
- **Devices are beside the arena.** The framebuffer is the building's lighting, not an
|
||||
occupant. Console's dirty *events* are arena entries; the pixels are not.
|
||||
|
||||
**DECIDED.** Three sharp edges, nothing forced.
|
||||
**DECIDED**, and completed by §17.5, which supplies the third edge this section counted but
|
||||
did not list, and sharpens the second:
|
||||
|
||||
| Category | Relation | Example |
|
||||
|---|---|---|
|
||||
| Warehouse | beneath | Artemis, and the dictionary (§17.3) |
|
||||
| Stadium | the floor | patrons |
|
||||
| Utility | beside | framebuffer, and devices generally |
|
||||
|
||||
"The building's lighting" undersells the framebuffer — it reads as part of the structure.
|
||||
§17.5 calls it **the power company**: external infrastructure the building consumes. Not
|
||||
the Stadium, not the basement of the Stadium, a third thing.
|
||||
|
||||
---
|
||||
|
||||
@@ -123,7 +174,10 @@ The engine cannot be a VM service, because VMs live inside the thing it manages.
|
||||
Structurally the same move as minting Zuse's certificate at first boot: a root that
|
||||
cannot be produced by the mechanism it grounds.
|
||||
|
||||
**LEANING.** Order is right; the allocation mechanism is unspecified.
|
||||
**DECIDED.** The order was right, and the allocation mechanism this section left unspecified
|
||||
is now given: one global array of fixed-size cells, sized at boot from the memory budget,
|
||||
addressed by index (§17.6b, §22.3). Step 1 above allocates that array before any VM exists;
|
||||
step 2 makes Hera the first patron in it (§20).
|
||||
|
||||
---
|
||||
|
||||
@@ -132,10 +186,17 @@ cannot be produced by the mechanism it grounds.
|
||||
Hera's job becomes arena distribution. This is not a new responsibility — allocating a
|
||||
VM's share *is* birthing it, and lifecycle is already what Hera is for.
|
||||
|
||||
**OPEN:** whether a VM's share is a hard bound or an elastic one that can grow and shrink
|
||||
under pressure, with capacity transferring between VMs as a conserved operation Hera
|
||||
arbitrates. Elastic is more powerful and more work. Under elasticity, birth sizes the
|
||||
*rest* volume rather than a cap — a more forgiving thing to have to guess right.
|
||||
~~**OPEN:**~~ **RESOLVED in §22 — elastic.** Whether a VM's share is a hard bound or an
|
||||
elastic one that can grow and shrink under pressure, with capacity transferring between VMs
|
||||
as a conserved operation Hera arbitrates. Elastic is more powerful and more work. Under
|
||||
elasticity, birth sizes the *rest* volume rather than a cap — a more forgiving thing to have
|
||||
to guess right.
|
||||
|
||||
§22 takes the elastic option. §19's density definition turns it into a negative feedback
|
||||
loop that runs itself — capacity flows down the density gradient — so it costs less than
|
||||
this section anticipated. The layout that makes it cheap is a single global cell pool with
|
||||
per-VM quotas held as counts (§22.3), and capacity must move on a slower loop than heat
|
||||
(§22.4).
|
||||
|
||||
---
|
||||
|
||||
@@ -155,6 +216,16 @@ field re-entering through a metaphor. They are one entry shape differing only in
|
||||
code field — a car's lifetime is the show, a person's is a visit; a car's code field is
|
||||
*be attended to*, a person's is *move and attend*.
|
||||
|
||||
> **OPEN — the one thing in this section the design has not addressed.** *"Occasionally one
|
||||
> sits in a car, which is the only exclusive thing in the room, scoped to a single object,
|
||||
> no global lock."* That sentence asserts an exclusivity primitive: per-patron, not global.
|
||||
> Nothing in §16–24 defines it. §21 addresses ISR-versus-mainline concurrency, which is a
|
||||
> different question.
|
||||
>
|
||||
> It matters because a patron reaped while someone is "sitting in" it is a use-after-free
|
||||
> under another name, and §22.3 gave up physical fault containment, which raises the stakes
|
||||
> rather than lowering them. Tracked as a punch-list item in §25.
|
||||
|
||||
---
|
||||
|
||||
## 9. The admission test
|
||||
@@ -165,21 +236,28 @@ both of which must have a non-forced answer:
|
||||
1. **What does heat mean for this thing?**
|
||||
2. **What is its reap event?**
|
||||
|
||||
| Type | Heat means | Reap is | Verdict |
|
||||
|---|---|---|---|
|
||||
| Block | accessed often | migration | passes |
|
||||
| Message | delivery urgency | delivery | passes |
|
||||
| VM | runs often | execution / death by cooling | passes |
|
||||
| ACL | checked often | ? | check |
|
||||
| Screen cell | ? | redraw, which removes nothing | **suspect** |
|
||||
**COMPLETE.** Run against every candidate; all five patron kinds pass, and the two `?` marks
|
||||
are closed:
|
||||
|
||||
Screen cells are the one to resolve first. A cell never expires — it is a fixed grid
|
||||
position always present. If cells are permanent arena entries, most of the arena is inert
|
||||
and permanently pinned. The likely correct read is that the arena entry is the **dirty
|
||||
event**, not the cell: transient, honest TTL, and the grid stays outside where it belongs.
|
||||
| Type | Heat means | Governed by | Reap is | Verdict |
|
||||
|---|---|---|---|---|
|
||||
| Block | accessed often | heat decay | migration back to Artemis | passes |
|
||||
| Message | delivery urgency | TTL | delivery | passes |
|
||||
| VM | runs often | heat decay | death by cooling | passes (§20) |
|
||||
| Word | executed often | heat decay | cooling off the floor | passes (§17.3) |
|
||||
| ACL | checked often | TTL | **expiry** (§17.1) | passes |
|
||||
| ~~Screen cell~~ | — | — | — | **not a patron** (§17.5) |
|
||||
|
||||
Ten minutes on paper. Either it confirms the design or it finds the one case that breaks
|
||||
it, before any code moves.
|
||||
Screen cells were the suspect case and the suspicion was correct. A cell never expires — it
|
||||
is a fixed grid position always present, so cells-as-entries would leave most of the arena
|
||||
inert and permanently pinned. §17.5 confirms the reading anticipated here: **the patron is
|
||||
the dirty event, not the cell.** The grid stays outside, and the event turns out to be a
|
||||
message with a different destination rather than a sixth kind.
|
||||
|
||||
Words were added to the table by §17.3 — the original list omitted them because §1 treated
|
||||
the dictionary as the *reference implementation* rather than as a population of patrons.
|
||||
|
||||
Ten minutes on paper. It confirmed the design and caught one case, which is what it was for.
|
||||
|
||||
---
|
||||
|
||||
@@ -188,6 +266,12 @@ it, before any code moves.
|
||||
**FABRIC.md first, then Hermes native on the fabric, then measure, then Console, then
|
||||
Artemis last.**
|
||||
|
||||
> **Amended by §16.5 and §21.2.** This ordering is still right for the *subsystems*, but it
|
||||
> is not the first work. A substrate floor sits beneath all of it: Hera alone, real timer
|
||||
> interrupts and a real IRQ return path on all three ISAs (§16.1), and compudynamics driven
|
||||
> from that tick. None of the sequencing below can begin until that exists, because the
|
||||
> engine has nothing to run on. §25 carries the actual order.
|
||||
|
||||
Reasoning:
|
||||
|
||||
- Hermes is unfinished, which is lucky. Finishing it the old way and refactoring later
|
||||
@@ -230,7 +314,25 @@ writing code.
|
||||
|
||||
---
|
||||
|
||||
## 12. Open questions
|
||||
## 12. Open questions — all six now closed
|
||||
|
||||
**Status as of §24. Every question in this section has been answered or dissolved.** The
|
||||
original text is kept below because several later sections quote it.
|
||||
|
||||
| | Question | Outcome | Where |
|
||||
|---|---|---|---|
|
||||
| Q1 | Payload threshold | **dissolved** — large patrons are simply heavy | §23.1 |
|
||||
| Q2 | Entry header size | **sized** — 64-byte cell, ~32-byte header (constants to validate) | §23.3 |
|
||||
| Q3 | Screen cell or dirty event | **event**; the grid is not a patron | §17.5 |
|
||||
| Q4 | Per-VM share hard or elastic | **elastic**, via quota over one pool | §22 |
|
||||
| Q5 | Loop coupling / timescales | **partly** — capacity must move slower than heat | §22.4 |
|
||||
| Q6 | One region or nested per VM | **nested**, two levels | §21 |
|
||||
|
||||
Q5 is marked *partly* deliberately: §22.4 fixes the one ordering that matters (capacity
|
||||
slower than heat) but the full eight-loop interference analysis has not been done, and
|
||||
§16.1 notes it cannot be until a real time base exists on all three ISAs.
|
||||
|
||||
---
|
||||
|
||||
1. Payload threshold — what size goes inline versus by reference.
|
||||
2. Arena entry header size. Cardinality spans orders of magnitude (dozens of VMs,
|
||||
@@ -323,19 +425,25 @@ the build, and it is not finished until it is correct.
|
||||
|
||||
## 15. The whole thing in five lines
|
||||
|
||||
- The arena holds the live crowd. Storage is the warehouse. Devices are the building.
|
||||
- The Stadium holds the live crowd. Storage is the warehouse. Devices are the utility.
|
||||
- One entry shape. The code field is the only difference between kinds.
|
||||
- Heat is density, conferred by traffic. Nobody decides.
|
||||
- Departure is unconditional. Pinning is invariance, not longevity.
|
||||
- The kernel opens the hall. Hera walks in first.
|
||||
- Traffic confers heat. Heat is conserved at 1.0. Density is heat per cell. Ranking reads
|
||||
density.
|
||||
- Departure is TTL, or cooling, or never. Pinning is invariance, not longevity.
|
||||
- The kernel opens the hall. Hera walks in first, and cannot be asked to leave.
|
||||
|
||||
*(Amended from the original five by §17.5, §19.5, §17.1 and §20.5 #3. The earlier third
|
||||
line — "heat is density, conferred by traffic" — conflated two quantities; the earlier
|
||||
fourth — "departure is unconditional" — knew only one mechanism.)*
|
||||
|
||||
---
|
||||
|
||||
## 16. Substrate findings — 2026-08-03
|
||||
|
||||
Naming: the arena is now called the **Stadium**, because `src/starkernel/vm/arena.c`
|
||||
already owns "arena" for the PMM-backed VM page allocator — an unrelated concept. Sections
|
||||
1–15 above still say arena and have not been reconciled.
|
||||
already owns "arena" for the PMM-backed VM page allocator — an unrelated concept. §1–15
|
||||
still use the old word; see the note in the header. Their *substance* has since been
|
||||
reconciled against §16–24, with each superseded claim marked in place.
|
||||
|
||||
Four findings from reading the tree. The first three change what step one costs. The
|
||||
fourth changes what the engine is allowed to be.
|
||||
@@ -375,7 +483,9 @@ riscv64 timing numbers before and after that fix are not comparable.
|
||||
### 16.3 The dictionary is already a Stadium
|
||||
|
||||
§1 claims the dictionary is the reference implementation. It is stronger than that.
|
||||
`DictEntry` today carries six of the seven wires in §3:
|
||||
`DictEntry` today carries six of the eight wires in §3 — the two it lacks are `mass`, which
|
||||
§19.2 added later, and a behaviour tag, since its code field is a raw function pointer
|
||||
rather than an enumerated tag (§18.3):
|
||||
|
||||
| §3 wire | Already in `DictEntry` |
|
||||
|---|---|
|
||||
@@ -441,10 +551,14 @@ ground) sits above that floor, not below it.
|
||||
|
||||
## 17. Patrons
|
||||
|
||||
An occupant of the Stadium is a **patron**. Blocks, words, ACLs and messages are all
|
||||
patrons. The word is doing real work: it names the category without implying a class
|
||||
An occupant of the Stadium is a **patron**. Blocks, words, ACLs, messages **and VMs** are
|
||||
all patrons. The word is doing real work: it names the category without implying a class
|
||||
hierarchy, and it keeps the metaphor honest — patrons attend, they are not the building.
|
||||
|
||||
VMs were omitted when this section was written and added by §20, which found they were
|
||||
already implemented as the outer level. **Five kinds, not four** — the counts elsewhere in
|
||||
§17 predate that and should be read accordingly.
|
||||
|
||||
**DECIDED.**
|
||||
|
||||
### 17.1 Four patrons die four different ways — and that is not a type field
|
||||
@@ -479,6 +593,7 @@ Mapped per patron:
|
||||
| ACL | TTL | expiry |
|
||||
| Block | heat decay | **migration back to Artemis** — evicted, not destroyed |
|
||||
| Word | heat decay | cooling off the floor (see §17.3) |
|
||||
| VM | heat decay | death by cooling (see §20); Hera is pinned (§20.5 #3) |
|
||||
|
||||
#### Two measures, one clock
|
||||
|
||||
@@ -580,9 +695,11 @@ direction stated differently earlier the same day, and it deserves longer than a
|
||||
|
||||
### 17.4 Open
|
||||
|
||||
1. **What is a word's TTL, concretely?** Heat decay already cools words, but decay-to-cold
|
||||
and TTL-expiry are not obviously the same clock. Either they unify or §17.3 needs a
|
||||
second mechanism, which would be a bad sign.
|
||||
1. ~~**What is a word's TTL, concretely?**~~ **DISSOLVED — a word has no TTL.** The premise
|
||||
was wrong. §17.1 (as corrected) establishes TTL and heat decay as two distinct
|
||||
mechanisms, not one clock read two ways: words are governed by **heat decay**, and only
|
||||
messages and ACLs carry a TTL. Nothing needed unifying and no second mechanism was
|
||||
required.
|
||||
2. ~~**Is the hot-word set bounded today?**~~ **RESOLVED — yes, hard bounded.**
|
||||
`DictEntry *cache[HOTWORDS_CACHE_SIZE]` (`include/physics_hotwords_cache.h:168`) is a
|
||||
fixed array inside the struct, with `HOTWORDS_CACHE_SIZE = 32` (`:84`). Nothing is
|
||||
@@ -601,12 +718,19 @@ direction stated differently earlier the same day, and it deserves longer than a
|
||||
NULL into `cache[lru_index]`. Unreachable today — every caller passes a non-NULL entry
|
||||
from the bucket search — but the NULL check reads as though it prevents this, and does
|
||||
not.
|
||||
3. **ACL reap** — §9 still marks this `?`. ACL entries carry `acl_ttl` in `DictEntry`
|
||||
already, so this is likely the easiest of the four to close, and it should be closed on
|
||||
paper alongside the others rather than left dangling.
|
||||
4. Does a patron ever change what it is? A block that is written becomes a new block by
|
||||
content-addressing. A word that is redefined is a new word. If nothing mutates in place,
|
||||
that is worth stating explicitly — it removes a whole class of proof obligation in §13.
|
||||
3. ~~**ACL reap**~~ **RESOLVED — TTL expiry.** ACL entries already carry `acl_ttl` in
|
||||
`DictEntry`, so they fall under the TTL mechanism in §17.1 alongside messages. §9's `?`
|
||||
is closed.
|
||||
4. ~~Does a patron ever change what it is?~~ **RESOLVED in §24 — and the question was
|
||||
slightly wrong.** Full immutability is not available: FORTH blocks mutate in place by
|
||||
definition (`BLOCK` / `UPDATE` / `FLUSH`), while words already behave the opposite way,
|
||||
redefinition creating a new entry. The kinds genuinely disagree.
|
||||
|
||||
What actually mattered was never payload but **mass and identity**. §24.2 states the
|
||||
invariant: identity never changes during a residency; mass never changes as a side effect
|
||||
of use; header fields mutate freely; payload contents may mutate provided size and
|
||||
identity do not. That gives §13 the enumerable mass function it needed without demanding
|
||||
immutability nothing could deliver.
|
||||
|
||||
### 17.5 The framebuffer is not a patron — it is a utility
|
||||
|
||||
@@ -644,7 +768,10 @@ That gives three categories, all principled, none of them exceptions:
|
||||
| Stadium | the floor | patrons |
|
||||
| Utility | beside | framebuffer, and devices generally |
|
||||
|
||||
#### What is live is the dirty event — and it is not a fifth patron kind
|
||||
#### What is live is the dirty event — and it is not a new patron kind
|
||||
|
||||
*(Written when the taxonomy had four kinds; §20 has since added VMs as the fifth. The point
|
||||
stands unchanged — the dirty event adds nothing to the taxonomy at all.)*
|
||||
|
||||
Run §9's two questions on it:
|
||||
|
||||
@@ -714,7 +841,10 @@ capacity is determined. Deriving that capacity from available memory rather than
|
||||
constant costs nothing and gives up nothing. Cells stay uniform, links stay indices, the
|
||||
region stays contiguous.
|
||||
|
||||
**LEANING toward (b).**
|
||||
**SUPERSEDED by §22.3 — the answer is (b) with a refinement.** The Stadium is one global
|
||||
array of cells sized at boot, and per-VM shares are **quotas held as counts** rather than
|
||||
separate regions. That keeps (b)'s properties while making (c)'s elasticity trivial, so the
|
||||
two are no longer alternatives.
|
||||
|
||||
#### Why (d) is expensive — by this document's own argument
|
||||
|
||||
@@ -750,9 +880,11 @@ capacity transferring between VMs as a conserved operation Hera arbitrates."* El
|
||||
the Stadium level and elasticity at the per-VM level are the same question asked at two
|
||||
scales, and they should be answered together rather than separately.
|
||||
|
||||
**OPEN**, and coupled to §7 and to §12 Q6 (one Stadium or nested per VM). Note that if Q6
|
||||
resolves to nested-per-VM, (c) becomes considerably more attractive — capacity transfer
|
||||
between VMs is the whole point of that arrangement, and a fixed per-VM bound would waste it.
|
||||
~~**OPEN**~~ **RESOLVED — and the prediction here was right.** Q6 did resolve to nested
|
||||
(§21), and (c) did become the attractive option (§22). But §22.3 found a cheaper route to
|
||||
it than resizing a contiguous region: with quotas held as counts over one shared cell pool,
|
||||
elasticity costs arithmetic on two integers and the denominator never moves at the level
|
||||
that matters. The total stays fixed; only the partition shifts.
|
||||
|
||||
#### The rule this reduces to
|
||||
|
||||
@@ -825,11 +957,15 @@ us to.
|
||||
So a fixed enum with fixed dispatch is mandatory, not a concession to practicality. But
|
||||
there are two things one could enumerate, and only one of them preserves §3:
|
||||
|
||||
| | Enumerate | Engine asks | Cost of a fifth patron |
|
||||
| | Enumerate | Engine asks | Cost of a new patron kind |
|
||||
|---|---|---|---|
|
||||
| ✗ | patron **kinds** — `BLOCK`, `WORD`, `ACL`, `MESSAGE` | "what are you?" | touch the engine |
|
||||
| ✗ | patron **kinds** — `BLOCK`, `WORD`, `ACL`, `MESSAGE`, `VM` | "what are you?" | touch the engine |
|
||||
| ✓ | **behaviours** — `MIGRATE`, `DELIVER`, `EXPIRE`, `COOL` | nothing; calls `dispatch(tag)` | none |
|
||||
|
||||
This was not hypothetical. VMs were added as a patron kind by §20 *after* this section was
|
||||
written, and cost the engine nothing — a VM's behaviour tag is `COOL`, the same tag a word
|
||||
carries. Under the rejected column it would have been an engine change.
|
||||
|
||||
Both are closed, both are fixed at build time, both are equally provable. Only the second
|
||||
keeps the engine ignorant of its contents, which is the property §3 exists to protect. Two
|
||||
patrons may share a tag; a new patron that migrates costs zero engine changes.
|
||||
@@ -1508,3 +1644,375 @@ Firmness of each figure:
|
||||
If identity can be elided for some kinds, 8 bytes of a 32-byte header is a large saving.
|
||||
This must not become a per-kind branch (§18.3), so it is only worth doing if it can be
|
||||
expressed uniformly.
|
||||
|
||||
---
|
||||
|
||||
## 24. Mutation, identity, and mass stability
|
||||
|
||||
§17.4 #4 and §19.6 #2 ask whether patrons mutate in place. The question as posed does not
|
||||
survive contact with the patron kinds, and the version that does is cheaper.
|
||||
|
||||
### 24.1 Full immutability is not available
|
||||
|
||||
FORTH-79 blocks are mutable by definition: `BLOCK` returns a buffer, writes go into it,
|
||||
`UPDATE` marks it dirty, `FLUSH` writes it back. In a block editor that is a mutation per
|
||||
keystroke. A rule that every write produces a new patron would mean a new patron per
|
||||
keystroke.
|
||||
|
||||
Words already behave the opposite way. Redefinition creates a **new** dictionary entry
|
||||
rather than editing the existing one — which is why `vm_dict_resolve_in_bucket()` resolves
|
||||
in reverse-insertion order, newest visible definition winning.
|
||||
|
||||
The kinds genuinely disagree. Forcing them to agree would be §11's exception trap approached
|
||||
from the other side.
|
||||
|
||||
### 24.2 The concern was never payload — it was mass and identity
|
||||
|
||||
§19.6 #2 asks this for density stability: if mass changes underfoot, density changes and
|
||||
ranking is meaningless. §13 asks it because in-place mutation is what makes proofs
|
||||
expensive.
|
||||
|
||||
Neither concern is about payload bytes. A block's contents can change entirely and it is
|
||||
still 1024 bytes at the same handle.
|
||||
|
||||
So the invariant is narrower than immutability and costs almost nothing:
|
||||
|
||||
| Tier | Rule |
|
||||
|---|---|
|
||||
| **Identity** | never changes for the life of the residency |
|
||||
| **Mass** | never changes *as a side effect of use* |
|
||||
| **Header** — heat, TTL, flags, link | mutates freely; this is the engine's work |
|
||||
| **Payload contents** | may mutate in place, provided size and identity do not |
|
||||
|
||||
**DECIDED.**
|
||||
|
||||
### 24.3 VMs appear to violate the mass rule, and do not
|
||||
|
||||
A VM's mass is elastic by §22 — that is the point of elasticity. But it changes only through
|
||||
Hera's arbitrated transfer, on the slow loop of §22.4. So the rule is not that mass is
|
||||
constant:
|
||||
|
||||
> **Mass changes only through an arbitrated transfer, never through traffic.**
|
||||
|
||||
Traffic moves heat and nothing else. Density is therefore stable between transfers, which is
|
||||
what ranking requires, and §13 gets a mass function that changes at known, enumerable points
|
||||
rather than continuously.
|
||||
|
||||
### 24.4 A resident patron's identity is its handle, not its content hash
|
||||
|
||||
This follows from tier 1 and is worth stating because it is easy to get backwards.
|
||||
|
||||
A block's identity while resident is its **handle** — its LBN. Its content hash is computed
|
||||
at **migration**, for the warehouse. If identity were the content hash, editing a resident
|
||||
block would change its identity mid-residency and break tier 1 immediately.
|
||||
|
||||
§3 already permits this: identity is *"handle or name"*. It never said hash.
|
||||
|
||||
Content-addressing therefore stays where it belongs — at the warehouse boundary, which is
|
||||
already how Artemis and the capsule model behave. The Stadium does not do content
|
||||
addressing; the warehouse does.
|
||||
|
||||
### 24.5 The rule this reduces to
|
||||
|
||||
> **No patron may grow or shrink while resident. If it needs to be a different size, it is
|
||||
> a different patron.**
|
||||
|
||||
No per-kind branching, no exception, and it holds for all five patron kinds.
|
||||
|
||||
### 24.6 Open
|
||||
|
||||
1. **What happens to a resident block whose content changes, at migration time?** Its new
|
||||
content hash differs from the one it arrived with. The warehouse sees a new block; the
|
||||
Stadium saw one continuous residency. That is coherent, but the hand-off needs stating —
|
||||
particularly whether the old hash is retained anywhere for audit.
|
||||
2. **Does redefining a word while its old definition is resident leave two patrons?** The
|
||||
dictionary keeps both entries by design. If both are hot, both are on the floor, both
|
||||
have mass. That is probably correct — they are genuinely two different words — but it
|
||||
should be confirmed rather than discovered.
|
||||
|
||||
---
|
||||
|
||||
# 25. The punch list
|
||||
|
||||
**This section is authoritative for what is done and what is not.** Sections 1–24 are the
|
||||
design. This is the work.
|
||||
|
||||
---
|
||||
|
||||
## 25.0 How to implement this punch list
|
||||
|
||||
**Read this subsection every time before touching an item. Do not skip it because it was
|
||||
read earlier in the session.**
|
||||
|
||||
### The rules
|
||||
|
||||
1. **One item at a time.** Take the lowest-numbered unchecked item whose prerequisites are
|
||||
met. Finish it completely. Do not begin a second item while one is in progress.
|
||||
|
||||
2. **Do not jump ahead.** Do not start a later item because it seems easy, related, or
|
||||
convenient. Do not do "while I'm in here" work. If a later item looks like it should be
|
||||
reordered, say so and wait for an answer — do not reorder unilaterally.
|
||||
|
||||
3. **Do not increase scope.** Do exactly what the item says. If the item says "write the
|
||||
trap entry," write the trap entry — not the trap entry plus a refactor of the file it
|
||||
lives in. Anything you notice that is not in the item gets **reported**, not fixed.
|
||||
This includes obvious bugs. Report them; they get their own item if they warrant one.
|
||||
|
||||
4. **Do not fabricate, confabulate, or conflate.** If you do not know how something works,
|
||||
read it. If you cannot determine it by reading, **stop and say so**. Never invent a
|
||||
function, a register name, a constant, a FORTH word, or an API that you have not
|
||||
verified exists in this tree or in the relevant hardware manual. Never guess at a value
|
||||
and present it as known. Never merge two things that are similar into one thing that is
|
||||
neither. A wrong answer stated confidently has cost this project git resets before.
|
||||
|
||||
5. **When blocked, stop.** Report exactly what is blocking, what was tried, and what is
|
||||
needed. Do not work around it silently. Do not substitute a different approach and
|
||||
carry on.
|
||||
|
||||
6. **Acceptance is not optional and not negotiable.** Each item states *Done when*. An item
|
||||
is not done until that exact condition is met and observed. Not "should work," not
|
||||
"compiles cleanly" unless that is what the item says. If acceptance requires the
|
||||
three-architecture QEMU boot, then all three have booted and their logs exist.
|
||||
|
||||
7. **Report failures honestly.** If a test fails, say it failed and show the output. If a
|
||||
step was skipped, say it was skipped and why. Never describe partial work as complete.
|
||||
|
||||
### The commit discipline
|
||||
|
||||
Every checked-off item gets **its own commit**, and that commit contains:
|
||||
|
||||
- the code or document change for that item, and
|
||||
- this file, with that item's checkbox changed from `[ ]` to `[x]`.
|
||||
|
||||
Nothing else. One item, one commit. The punch list and the tree move together, so the
|
||||
document is never a claim about work that is not in the branch.
|
||||
|
||||
Commit message format:
|
||||
|
||||
```
|
||||
<area>: <what the item did> e.g. riscv64: real trap entry with SRET return
|
||||
|
||||
Punch list §25 item <id> complete.
|
||||
<one or two lines on what was actually verified, not what was intended>
|
||||
|
||||
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
||||
```
|
||||
|
||||
### Standing constraints from `.claude/CLAUDE.md`
|
||||
|
||||
These override anything convenient:
|
||||
|
||||
- **Never create a branch without explicit permission.** Work on the branch you are on.
|
||||
- **Never stash.** If the tree is dirty, report it and wait.
|
||||
- **Never apply a fix that was not requested.** Report it instead.
|
||||
- **Acceptance for any kernel change is the three-architecture QEMU boot.** There is no
|
||||
other test. The hosted `make` build is compile-sanity only.
|
||||
- **One QEMU instance at a time, foreground, `clean` before `qemu`.** Concurrent runs
|
||||
corrupt the timing signal.
|
||||
- **Read `experiments/bare_metal/README.md` in full before editing any `.4th` file**, and
|
||||
verify capsule edits with `mkcapsule --lint` rather than counting bytes by hand.
|
||||
|
||||
### When an item is genuinely wrong
|
||||
|
||||
The design is not sacred. If implementing an item shows the design is wrong, **stop, report
|
||||
what the code demonstrated, and propose the amendment.** Amend the relevant section of this
|
||||
document first, get agreement, then continue. Do not implement something you believe is
|
||||
wrong because it is written down, and do not silently implement something different.
|
||||
|
||||
---
|
||||
|
||||
## 25.1 Phase 0 — Substrate
|
||||
|
||||
*Nothing in later phases can start until Phase 0 is complete. The engine has nothing to run
|
||||
on until there is a tick on all three architectures (§16.1, §16.5).*
|
||||
|
||||
- [ ] **0.1 — Prune `capsules/init.4th` to Hera alone.**
|
||||
Remove the Artemis and Hermes births, the `common:msg.4th` and `process.4th` loads, and
|
||||
blocks 2050–2059's fleet test scaffolding. Adjust `BOOT-BANNER`, `VM-TREE`, `VM-CHILDREN`.
|
||||
Leave `capsules/hermes/` and `capsules/artemis/` untouched on disk.
|
||||
*Done when:* all three architectures boot to the prompt with Hera alone, no Hermes or
|
||||
Artemis in the banner, and the three logs exist under `logs/`.
|
||||
*Refs:* the surviving blocks are 2057, 2049, 2050.
|
||||
|
||||
- [ ] **0.2 — riscv64: real trap entry.**
|
||||
Replace the one-way `riscv64_trap_entry` in `arch/riscv64/isr.S` with save / dispatch /
|
||||
restore / `sret`. Route `scause` bit 63 + cause 5 to the timer path; everything else keeps
|
||||
falling through to the existing fatal handler.
|
||||
*Done when:* riscv64 boots to the prompt unchanged, having taken and returned from at
|
||||
least one trap. Exceptions still halt with the same diagnostic as before.
|
||||
|
||||
- [ ] **0.3 — riscv64: SBI timer and real time base.**
|
||||
Arm the timer via the SBI TIME extension. Switch the time base from `rdcycle` to the
|
||||
`time` CSR and take its frequency from the device tree `timebase-frequency`, with a named
|
||||
fallback constant — not a bare magic number (§16.2).
|
||||
*Done when:* `heartbeat_ticks()` advances on riscv64 and the tick interval matches the
|
||||
configured rate within measurement noise. Verify the SBI extension is present before
|
||||
relying on it; if it is absent, stop and report rather than falling back silently.
|
||||
|
||||
- [ ] **0.4 — aarch64: determine the exception level at runtime.**
|
||||
Read `CurrentEL` in `timer_init()` and select the `CNTP_*_EL0` or `CNTHP_*_EL2` register
|
||||
set accordingly. Do not hardcode either.
|
||||
*Done when:* the boot log states which EL was detected, on real QEMU output.
|
||||
|
||||
- [ ] **0.5 — aarch64: IRQ vector split.**
|
||||
Split `irq_spx` out of the shared fatal handler in `arch/aarch64/isr.S`: save `x0`–`x30`,
|
||||
`ELR_EL1`, `SPSR_EL1`, call a C handler, restore, `eret`. The other fifteen vectors are
|
||||
unchanged. Note the 128-byte slot limit — the save sequence will not fit inline and must
|
||||
branch to a trampoline.
|
||||
*Done when:* aarch64 boots to the prompt having taken and returned from at least one IRQ.
|
||||
|
||||
- [ ] **0.6 — aarch64: minimal GICv2.**
|
||||
Enable the distributor and CPU interface, set the priority mask, enable the timer PPI,
|
||||
acknowledge via `IAR` / `EOIR`. **Read the base addresses and the PPI INTID from the
|
||||
device tree — do not take them from memory or from this document.**
|
||||
*Done when:* the timer interrupt is delivered and acknowledged. Scope is one interrupt;
|
||||
a general GIC driver is out of scope and must not be written.
|
||||
|
||||
- [ ] **0.7 — aarch64: arm the generic timer.**
|
||||
`apic_timer_start()` / `apic_timer_stop()` using the register set chosen in 0.4, re-armed
|
||||
each tick.
|
||||
*Done when:* `heartbeat_ticks()` advances on aarch64 at the configured rate.
|
||||
|
||||
- [ ] **0.8 — Converge the three architectures on one tick path.**
|
||||
One `heartbeat_tick()` call site per architecture; ISR does counter and flag only;
|
||||
the engine runs in the mainline off the pending flag. Reuse the existing
|
||||
`adaptive_check_accumulator` / `adaptive_pending` pattern rather than inventing a second.
|
||||
*Done when:* all three architectures drive the same bottom half, and no loop math runs in
|
||||
interrupt context.
|
||||
*Refs:* §18.4, §21.2.
|
||||
|
||||
- [ ] **0.9 — Write the concurrency constraint at the mutex stub.**
|
||||
Add a comment at `src/starkernel/vm/host/shim.c:415` stating that the no-op is correct
|
||||
only while nothing in interrupt context mutates shared structure, and that making it a
|
||||
real spinlock would deadlock a single hart.
|
||||
*Done when:* the comment is in place. This is a documentation item; no behaviour changes.
|
||||
*Refs:* §21.2, §21.5 #1.
|
||||
|
||||
- [ ] **0.10 — Phase 0 acceptance.**
|
||||
Full three-architecture QEMU run. Confirm: boots to prompt on all three; tick count
|
||||
non-zero on all three; TIME-TRUST and variance sane on riscv64 after 0.3; amd64 output
|
||||
unchanged from its pre-branch behaviour.
|
||||
**Then boot one architecture twice and confirm the parity dict hash is identical across
|
||||
runs.** If it drifts, something is firing on wall time and Phase 0 is not complete.
|
||||
*Done when:* all of the above observed, logs committed.
|
||||
*Refs:* §16.4, §18.5.
|
||||
|
||||
---
|
||||
|
||||
## 25.2 Phase 1 — Design questions to settle on paper
|
||||
|
||||
*These need answers, not code. Each one is settled by amending the relevant section of this
|
||||
document and committing that amendment as its own item.*
|
||||
|
||||
- [ ] **1.1 — Exclusive access ("sitting in a car").** §8 asserts a per-patron exclusivity
|
||||
primitive that is not a global lock. Nothing defines it. Decide what it is, what it
|
||||
blocks, and what happens if a patron is selected for reaping while held.
|
||||
*Refs:* §8. **This is the largest unresolved design question.**
|
||||
|
||||
- [ ] **1.2 — The resting floor.** Whether a VM's quota has a floor, and whether it is the
|
||||
mass of its pinned patrons (derived) or a constant (tuned). *Refs:* §22.5 #1.
|
||||
|
||||
- [ ] **1.3 — What triggers a capacity transfer.** Hera arbitrates; on what signal, and how
|
||||
often. Should read the density gradient, not a schedule. *Refs:* §22.5 #2.
|
||||
|
||||
- [ ] **1.4 — The heat/capacity timescale ratio.** The ordering is fixed (capacity slower);
|
||||
the ratio is not. *Refs:* §22.4, §22.5 #3.
|
||||
|
||||
- [ ] **1.5 — The outer bound.** The outer Stadium's capacity, and the behaviour at the
|
||||
bound: birth refused, or coldest VM reaped. *Refs:* §20.5 #1, §22.5 #4.
|
||||
|
||||
- [ ] **1.6 — A VM's mass: allocated share, or one cell.** *Refs:* §20.4, §20.5 #2.
|
||||
|
||||
- [ ] **1.7 — Rule out recursion beyond two levels** — deliberately, not by omission.
|
||||
*Refs:* §20.5 #4.
|
||||
|
||||
- [ ] **1.8 — Block content change at migration.** A resident block whose content changed
|
||||
has a different hash on the way out. State the hand-off. *Refs:* §24.6 #1.
|
||||
|
||||
- [ ] **1.9 — Redefined words as two resident patrons.** Confirm both may be on the floor.
|
||||
*Refs:* §24.6 #2.
|
||||
|
||||
- [ ] **1.10 — Identity elision.** Whether identity can be dropped for some kinds without a
|
||||
per-kind branch. Optimisation; may be closed as "no". *Refs:* §23.4 #3.
|
||||
|
||||
- [ ] **1.11 — Dirty-event granularity.** Leaning region-based. **Deferred to console work**
|
||||
— do not settle speculatively. *Refs:* §17.5, §23.2, §23.4 #1.
|
||||
|
||||
---
|
||||
|
||||
## 25.3 Phase 2 — Prepare the existing physics
|
||||
|
||||
- [ ] **2.1 — Restate heat transfer on tick count.**
|
||||
`vm_physics_touch()` scales transfers by wall-clock elapsed time
|
||||
(`capsule_vm_physics.c:272`). Restate it on tick count.
|
||||
*Done when:* no wall-clock value influences heat, and the double-boot reproducibility
|
||||
check from 0.10 still passes.
|
||||
*Refs:* §18.5, §19.6 #3.
|
||||
|
||||
- [ ] **2.2 — Bound the VM registry.**
|
||||
The registry is a `kmalloc`-backed unbounded list, which makes fleet K an identity that
|
||||
cannot fail (§20.2). Give it the hard bound decided in 1.5.
|
||||
*Done when:* the population is bounded and `VM-CONSERVED?` can, in principle, fail.
|
||||
*Refs:* §2, §20.2.
|
||||
|
||||
---
|
||||
|
||||
## 25.4 Phase 3 — Stadium core
|
||||
|
||||
*Blocked on Phase 0 complete, and on items 1.1–1.7.*
|
||||
|
||||
- [ ] **3.1 — Cell and header.** Define the entry with all eight wires (§3). Validate the
|
||||
64-byte cell by counting real bytes; adjust and record if it does not fit. *Refs:* §23.3.
|
||||
- [ ] **3.2 — Boot-time allocation.** One global cell array, sized from the memory budget,
|
||||
before any VM exists. *Refs:* §6, §17.6, §22.3.
|
||||
- [ ] **3.3 — Behaviour enumeration and dispatch.** Closed tag set fixed at build time.
|
||||
Enumerate behaviours, never patron kinds. *Refs:* §13, §18.3.
|
||||
- [ ] **3.4 — Density ranking.** Heat ÷ mass, read not computed. *Refs:* §19.2, §19.3.
|
||||
- [ ] **3.5 — Admission and eviction.** Admit if denser than the least dense resident.
|
||||
*Refs:* §19.3.
|
||||
- [ ] **3.6 — Hera as patron zero, pinned.** Assert at the eviction site; selecting Hera is
|
||||
a panic, not a filtered candidate. *Refs:* §20.5 #3.
|
||||
|
||||
---
|
||||
|
||||
## 25.5 Phase 4 — Migrate the subsystems
|
||||
|
||||
*One subsystem at a time, converted completely. Never two live heat mechanisms at once
|
||||
(§11).*
|
||||
|
||||
- [ ] **4.1 — Hot words onto the Stadium.** Replaces the round-robin eviction with density
|
||||
ranking. Measurable before and after via `stats.evictions` / `stats.promotions`.
|
||||
*Refs:* §17.3.
|
||||
- [ ] **4.2 — Hermes native on the Stadium.** The proving ground; produces the effort
|
||||
number. *Refs:* §10.
|
||||
- [ ] **4.3 — Console.** Settles 1.11 as part of the work. *Refs:* §17.5.
|
||||
- [ ] **4.4 — Artemis last.** It works today; it is the thing that cannot be broken.
|
||||
*Refs:* §10.
|
||||
|
||||
---
|
||||
|
||||
## 25.6 Phase 5 — Verification and measurement
|
||||
|
||||
- [ ] **5.1 — Re-run the DoE on the new substrate.** A green POST suite is not evidence that
|
||||
K holds; those are different claims. *Refs:* §10.
|
||||
- [ ] **5.2 — Isabelle/HOL.** One datatype, one index space, one conservation theorem.
|
||||
*Refs:* §13, §22.3.
|
||||
- [ ] **5.3 — Shrink the subsystem documents.** `ARTEMIS.md`, `HERMES.md`, `CONSOLE.md`,
|
||||
`TRIPOD.md` should each reduce to roughly three lines. Any that grows is fighting the
|
||||
design. `TRIPOD.md` also needs its Immediate Goal rewritten — it currently requires Hera
|
||||
to spawn Hermes and Artemis at boot, which 0.1 undoes. *Refs:* §11.
|
||||
|
||||
---
|
||||
|
||||
## 25.7 Reported, not scheduled
|
||||
|
||||
*Found while reading. Not fixed, not assigned. They become items only if Captain Bob says
|
||||
so.*
|
||||
|
||||
- `hotwords_cache_promote()` writes NULL into the ring if `word` is NULL and the cache is
|
||||
full (`physics_hotwords_cache.c:363-364`). Unreachable today.
|
||||
- `heartbeat_trust()` is exported and has zero callers.
|
||||
- `m5_time_trust` and `m5_variance` (`include/vm.h:315-316`) are declared and never used.
|
||||
- `src/*.c.bak` files are tracked in git at the `src/` top level.
|
||||
- The `bump-z` / `bump-y` targets in the hosted `Makefile` reference version macros that do
|
||||
not exist in the generated `include/version.h`.
|
||||
|
||||
Reference in New Issue
Block a user