FABRIC-3.5.md §XVIII: the Console component designed in full

Console is three layers and only the middle one is new: the HAL fabric
(~98 KB of C) is unchanged, the per-attach proxies are unchanged, and
Console the VM is a capsule rather than a mechanism.

The finding that shapes the design is that 1,051 lines of Console
vocabulary already exist in the wrong dictionary. fabric.4th's own header
calls it "Console drawing-fabric coordinate machinery" and says outright
that it is the FORTH-side policy over the C pixel primitives -- and it
loads into Hera, along with font.4th, from init.4th block 2049. Moving
both to capsules/console/init.4th is a pure relocation and the clearest
evidence that the fabric wants an owner: it already has the vocabulary,
just attached to the wrong VM. PLOT/FB-WIDTH/FB-HEIGHT registration moves
with it; the implementations do not change.

Records the invariant that constrains everything else. console.h:220-224
states that console.c must have no dependency on capsule or WIREBIND
logic, calling it a real layering violation rather than a style
preference. So Console the VM cannot be built by making console.c
VM-aware: ownership flows one way, and where the HAL genuinely needs
something VM-shaped it takes the shape already established by
console_set_user_prefix_provider() -- a registered callback, never an
include. Console's ownership is therefore by vocabulary and registration,
not enforcement, which is weaker than it sounds and exactly right.

Carries the headless-until-login invariant onto this second path: Console
is born at boot, a console session is not, and Console's birth must not
touch g_wirebind_attached_username or mint a proxy. Same rule §XXXII.2
imposed on unattended birth, cheap to hold and easy to violate with a
well-meaning startup line.

Notes what Console does not own, since a component owning "the bind
point" attracts responsibilities belonging elsewhere, and records that
the three Tripod legs have three different failure characters -- Console
degrades gracefully because the fabric is HAL C and survives its owner,
which is why only Hermes needed the semaphore.

One genuinely open design question remains: whether agent-VM binding
widens the ~user convention or takes a sibling word.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VkM1zHGvBerLF6aqkHPweP
This commit is contained in:
Claude
2026-09-18 22:18:34 +00:00
parent 887b34d0a7
commit 38735e52c2
+197
View File
@@ -1378,3 +1378,200 @@ first bullet is a placement question; item 16 is a build-time check. Every load-
architectural question this document opened — what Hermes becomes, what the Tripod is, what
`BIRTH` means, who owns death, who owns turn order, what survives, what shutdown is — is now
ruled.
---
## XVIII. The Console component, designed in full (2026-09-18)
§XVII settled *what* the Console leg is. This section designs the component. Everything below
traced 2026-09-18 against `e56974e`; decisions are marked, proposals are marked, and open
questions are marked.
### XVIII.1 — Console is three layers, and only the middle one is new
| Layer | What | Where it lives today | Change |
|---|---|---|---|
| **0 — the fabric** | Raw output: framebuffer, VT100, glyph raster, UART | `hal/console.c`, `framebuffer.c`, `vt100.c`, `font_8x16.c` (~98 KB C) | **None.** Stays kernel HAL. |
| **1 — Console the VM** | Tripod leg. Owns fabric *policy*, is the bind point | **Does not exist** | **New — and it is a capsule, not a mechanism.** |
| **2 — console proxies** | Per-attach relay VMs, one per bound user/agent | `capsule_console_birth()` | **None**, except their birther becomes Console. |
The design is almost entirely layer 1, and layer 1 is mostly a *relocation* of vocabulary that
already exists in the wrong dictionary (§XVIII.3).
### XVIII.2 — The constraint that shapes everything: the HAL must never know a VM exists
**This is not a preference, it is a defended invariant with a documented rationale**
(`include/starkernel/console.h:220-224`, verbatim):
> `console.c` is a clean HAL module with no dependency on capsule/WIREBIND logic
> (`zuse_session`, `capsule_wirebind_attached_username()`) — pulling either in directly here
> would be a **real layering violation, not just a style preference.**
So **Console-the-VM may not be implemented by making `console.c` VM-aware.** Ownership flows one
way only: Console reaches *down* into the HAL; the HAL never reaches *up*.
**The sanctioned pattern for the cases where the HAL does need something VM-shaped already
exists and should be reused rather than reinvented:** `console_set_user_prefix_provider()`
(`console.h:231`) — a callback that lets `repl.c` supply the "user" half of the `[user@VMName]`
prompt "without `console.c` knowing anything about VMs, sessions, or WIREBIND." Any further
HAL→Console coupling this design needs takes that shape: a registered callback, never an
include.
**Consequence, stated plainly:** Console's "ownership" of the fabric is by *convention,
vocabulary and registration* — it is the VM that holds the drawing words — **not** by any
enforcement inside `console.c`. That is weaker than it sounds and is exactly right: it keeps
the HAL reusable and the layering intact.
### XVIII.3 — Console's dictionary: 1,051 lines of Console vocabulary currently live in Hera
**The most concrete finding in this section.** `capsules/fabric.4th`'s own header reads:
> `fabric.4th` — **Console drawing-fabric coordinate machinery.** `FABRIC-0.md` item 4.3.3.
> 45-degree cavalier orthographic projection... Raw pixel write (`PLOT`/`FB-WIDTH`/`FB-HEIGHT`)
> is C; **this capsule is the FORTH-side policy on top of it.**
It is named Console's fabric, it *is* the FORTH-side fabric policy — and it loads into **Hera's**
dictionary (`capsules/init.4th` block 2049: `S" fabric.4th" EXEC`, `S" font.4th" EXEC`).
| Capsule | Lines | Loads into today | Should load into |
|---|---|---|---|
| `fabric.4th` | 319 | **Hera** (`init.4th` b2049) | **Console** |
| `font.4th` | 733 | **Hera** (`init.4th` b2049) | **Console** |
| `turtle.4th` | 99 | `sdk.4th` (opt-in, not boot) | unchanged — it is SDK, not fabric |
**Plus the C primitives underneath:** `PLOT`, `FB-WIDTH`, `FB-HEIGHT`
(`src/word_source/framebuffer_words.c:62-64`). Their *registration* moves to Console's word
table; the implementations do not change.
**DECIDED (proposal — Captain Bob's call, but this is the direct consequence of §XVII.2):**
`fabric.4th` and `font.4th` move from `init.4th` to `capsules/console/init.4th`. This is a pure
relocation of 1,051 lines — the single most obviously-correct edit in the whole reshuffle, and
the clearest evidence that the fabric wants an owner: it already *has* the vocabulary, just
attached to the wrong VM.
**Parity consequence** (§III.5): Hera's `dict_hash` will shrink, Console's is new. Per §XX's
standard, the property that matters is cross-architecture identity, not an unchanging absolute.
### XVIII.4 — Console's capsule personality
`capsules/console/init.4th` — a new directory beside `hermes/` and `artemis/`, and **the one
genuinely new artifact this reshuffle creates.**
Contents, in load order:
1. `S" common:messaging.4th" EXEC` + `MSG-CD-INIT` — Console is a Tripod leg and a full
messaging participant. **Note this differs from the proxies**, which deliberately skip
`COMMON-CH` (`capsule_console.c:23-26`: "a console's own traffic is direct 1:1 with its
paired user VM"). The *leg* subscribes; the *proxies* still do not.
2. `S" fabric.4th" EXEC` and `S" font.4th" EXEC` — per §XVIII.3.
3. Console's own bind vocabulary (§XVIII.5).
4. A `WELCOME`/banner word, matching `hermes/init.4th`'s and `artemis/init.4th`'s shape.
**Block allocation is a real to-do, not a formality.** `mkcapsule`'s actual constraints, per
§XXXII.2's correction: block range **`[2048, 5120)`** and a hard **16-content-line-per-block**
cap (*not* the 1024-byte framing `.claude/CLAUDE.md` still describes — that doc error is
outstanding). **Block 4997 is already taken** by the proxy's own C string literal
(`capsule_console.c:28-31`), so Console's range must avoid it. Allocate against
`capsule-reserved.txt` and verify with `mkcapsule --lint capsules/` (§XXIV's collision
machinery covers this; it is blind to content, only to numbers).
### XVIII.5 — The bind point: flow and vocabulary
**Binding, end to end, after the reshuffle:**
```
human attaches thumbdrive agent VM needs a console
│ │
▼ ▼
WIREBIND path CONSOLE-ATTACH
│ │
└──────────► Console ◄───────────┘
│
BIRTH a proxy (§XV.1 makes this legal:
│ any VM may birth)
▼
proxy paired to target
(VM-NAME-REG, unchanged)
```
- **`CONSOLE-ATTACH ( name-c name-u -- ok? )` already exists and is live-verified** (§I.6,
closed 2026-09-16). It resolves the target's liveness *before* birthing, so a typo refuses
cleanly with no orphaned VM. **Keep it exactly as is.** The change is *who runs it*: today
the three `capsule_console_birth()` call sites run in Hera's or the caller's context
(`capsule_wirebind.c:245`, `mama_forth_words.c:1591`, `:2015`). Under this design Console is
the birther.
- **This is why Console needs `BIRTH` registered** (§XIII.6) — binding *mints a proxy*, and
minting is `BIRTH`. Already legal per §XV.1.
- **Mechanically this already works:** two of the three call sites pass `vm->stadium_vm_id` —
whichever VM invoked — so parentage is already generic (§I.5, §XIII.6). Only registration
changes.
**OPEN — agent-VM binding and the `~user` convention** (§V.3.1, still unresolved). The pairing
check in `sk_repl_dispatch_line()` reconstructs the target as `console_get_vm_name() + "~user"`
— a convention built for *human at a console → identity VM*. A GPIO VM (`FABRIC-4.md` §2) is
not a `~user` identity. §XXXII.2 records that a console named anything else "silently falls
back to direct interpretation with no error," caught live. **Two shapes, neither chosen:**
(a) widen the convention so the suffix is a property of the *binding*, not hardcoded; (b) give
agent binding its own word alongside `CONSOLE-ATTACH`. **(a) is preferable on
no-bespoke-gate grounds but touches a live, bug-prone path; flagged rather than decided.**
### XVIII.6 — Headless-until-login survives, and the invariant that makes it survive
**Console the leg is born at boot. A console session is not.** These must not be conflated, or
§VIII.1's ratified headless-until-login policy reopens — the exact failure §XXXII.2 warned
about for unattended birth.
**INVARIANT, to be stated in whatever code implements Console:**
> Console's birth at boot must not set `g_wirebind_attached_username`, must not cause
> `sk_console_identity_present()` (`repl.c:122`) to report an identity, and must not mint a
> proxy. Console owns the fabric from boot; it presents nothing until something binds.
This is the same invariant §XXXII.2 imposed on unattended identity birth, applied to a second
path. It is cheap to hold — Console births no proxy until asked — but it is exactly the kind of
thing that gets violated by a well-meaning "initialise the console at startup" line.
### XVIII.7 — What Console does *not* own
Stated because a component that owns "the bind point" attracts responsibilities that belong
elsewhere, and this document has already had to defend against exactly that (§XV.3):
- **Not turn order.** Nothing owns it (§XV.3).
- **Not routing.** That is kernel-Hermes (§III), which sits *below* Console.
- **Not the lifecycle of anything but its own proxies.** Console births proxies; it does not
birth, kill, or supervise identity VMs. Authority flows down the birth graph only (§IX.2).
- **Not the prompt's user half.** That stays `repl.c`'s, supplied to the HAL via the existing
provider callback (§XVIII.2). Moving it into Console would be the layering violation
`console.h` explicitly names.
### XVIII.8 — Console's own failure behaviour
Console is a Stadium-floor VM **above** the routing layer, so §VIII.2's rule applies without an
exception: **Console emits `SOS`**; only Hermes uses the semaphore. Its clean shutdown is
§XVI's: forced `blk_flush(0)`, then `BYE`.
**A pleasant property worth recording: Console's death degrades gracefully by construction.**
Because layer 0 is HAL C (§XVIII.1) and ownership is by convention rather than enforcement
(§XVIII.2), a dead Console leaves the framebuffer, VT100 and UART fully functional — the fabric
simply becomes ownerless again, which is precisely today's status quo. The kernel can still
print. **This is the opposite of Hermes**, whose death takes the transport with it. So the
Tripod's three legs have three genuinely different failure characters: Artemis's death costs
storage arbitration, Console's costs only fabric *policy*, Hermes's costs the transport itself —
which is why only Hermes needed the semaphore.
### XVIII.9 — Punch list for Console
Design items, in dependency order. **No code authorized.**
1. ⬜ Ratify §XVIII.3's relocation: `fabric.4th` + `font.4th` move from `init.4th` to
`capsules/console/init.4th`; `PLOT`/`FB-WIDTH`/`FB-HEIGHT` registration moves to Console.
2. ⬜ Allocate Console's block range against `capsule-reserved.txt`, avoiding 4997; verify with
`mkcapsule --lint` (§XVIII.4).
3. ⬜ Decide §XVIII.5's agent-binding shape: widen the `~user` convention, or a sibling word.
**The only genuinely open design question in this section.**
4. ⬜ Confirm Console in the `is_fleet_foundation` triple (`capsule_birth.c:793-796`) and its
birth at `kernel_main.c:865`, replacing Hermes's (§XVII.4).
5. ⬜ Handle the `doe_log.c` CSV schema change (§XIII.3) — still the expensive consequence.
6. ⬜ State §XVIII.6's headless invariant in the implementation.
7. ⬜ **Not in this reshuffle:** the `console_set_vm_name()` ownerless-global fix (§XVII.3) is
now *available* once Console exists, but remains out of scope. Flagged, not scheduled.