FABRIC-3.md §I.1: (user) console prompt segment, closing the 4.4s->4.3->1.11 chain

Extends the REPL prompt to "[VM name] (user) ok>" (e.g. "[Hera]
(zuse) ok>") per the locked FABRIC.md §4.4s spec, unblocked by this
session's own §I.3/§I.8 identity-tracking work. Adds
capsule_wirebind_attached_username() alongside the existing tracked
VMUuid, and a new sk_print_prompt() helper (repl.c) that all three
prompt call sites now go through -- checks Zuse first, then a WIREBIND
user, prints nothing when neither is attached.

Closes 4.4s, 4.3 (console umbrella), and formally settles 1.11
(dirty-event granularity) as region-based per FABRIC-2.md's own "no
independent path" ruling -- a decision closure only, not an
implementation, so §17.4 (framebuffer heat/decay physics) stays open,
re-scoped precisely: blocked on the dirty-region-tracking mechanism
existing, not on 1.11's decision.

Documents a reported-but-unreproduced terminal defect (§I.9) as a new
punch-list item -- investigated the readline/keyboard-bridge code
paths, found nothing conclusive, needs a live repro with a serial log
before it's actionable.

Verified 3-arch boot to ok> (amd64/aarch64/riscv64, each in the
foreground); logs and DoE CSVs from this session's verification runs
included per this repo's own audit-artifact convention.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019YcT3H2PQeyujrzjqS3Var
This commit is contained in:
Robert Allan James
2026-09-04 09:42:26 -04:00
co-authored by Claude Sonnet 5
parent 60d9c2520e
commit 1d468a65b1
12 changed files with 27532 additions and 24 deletions
+76 -15
View File
@@ -31,17 +31,32 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR
### From FABRIC-2.md §A — Blocked or scoped, not started
- [ ] **1.11 — Dirty-event granularity.** Leaning region-based. Blocked on item 4.3 — settled
- [x] **1.11 — Dirty-event granularity.** Leaning region-based. Blocked on item 4.3 — settled
as part of the console migration, not speculatively before it. *Refs (FABRIC.md):* §17.5,
§23.2, §23.4 #1.
§23.2, §23.4 #1. **DONE 2026-09-04**: 4.3's sole blocker (4.4s) closed, and per
`FABRIC-2.md`'s own ruling ("both close only when 4.4s closes; no independent path"), this
formally settles the leaning as the decision: **region-based**, no further ruling needed.
This closes the *design decision* only — the dirty-region-tracking mechanism itself is not
built, which is exactly what §17.4 stays blocked on below.
- [ ] **4.3 — Console.** Umbrella item; settles 1.11 as part of the work. Nearly everything
- [x] **4.3 — Console.** Umbrella item; settles 1.11 as part of the work. Nearly everything
under it (4.3.14.3.7f, 4.44.4ac) is done — the parent stays open only because 4.4s below
is still blocked and nothing has formally closed the umbrella. *Refs (FABRIC.md):* §17.5,
§27.
§27. **DONE 2026-09-04**: 4.4s closed (see below); every sub-item was already done; nothing
else stands between this umbrella and closing.
- [ ] **4.4s — `(user)` prompt segment.** Scoped, blocked, not started. Extends 4.4's prompt
format. *Refs (FABRIC.md):* §27.8, 4.4.
- [x] **4.4s — `(user)` prompt segment.** Scoped, blocked, not started. Extends 4.4's prompt
format. *Refs (FABRIC.md):* §27.8, 4.4. **DONE 2026-09-04**: the blocker (`FABRIC.md` §4.4s'
own text — "`zuse_session` is a boolean only, no username/identity string exists anywhere")
was resolved by this session's own §I.3/§I.8 work, which gave both identities a real,
readable form (`capsule_wirebind_attached_username()` for a regular user;
`mama_vm->zuse_session` alone suffices for Zuse, since there is only ever one). Built exactly
to the locked spec — `[VM name] (user) ok>`, e.g. `[Hera] (zuse) ok>` — via a new
`sk_print_prompt()` helper (`repl.c`) that all three existing prompt call sites now go
through, printing nothing (today's bare `ok> `, unchanged) when no identity is attached.
Verified live in this session's own 3-arch boot logs (amd64/riscv64 showed `(zuse) ok> `
with Zuse's thumbdrive attached; aarch64 showed bare `ok> ` headless, no drive attached —
both are the correct behavior, not two different bugs).
- [x] **5.1 — Re-run the DoE on the new substrate.** A green POST suite is not evidence that
determinism holds under the Stadium migration — needs its own campaign. **RETROACTIVELY
@@ -60,6 +75,11 @@ decisions get added here, not to `FABRIC-2.md`. Follow the same discipline `FABR
internally was never designed. Not blocking anything. Blocked on item 1.11 specifically
(dirty-event granularity), not "the framebuffer work" in general — see `FABRIC-2.md` §D's
own 2026-08-13 refinement of this item before assuming it's ripe.
**Sharpened, not closed, 2026-09-04**: 1.11 itself closed above, but only as a *decision*
(region-based) — no dirty-region-tracking mechanism was built. `FABRIC-2.md`'s own framing
of this blocker is exact: "no dirty-region tracking exists yet for a heat/decay scheme to
attach to." This item stays open until that mechanism exists, not until 1.11's decision is
made — the decision alone gives this nothing to design against yet.
### From FABRIC-2.md §E — Documentation debt
@@ -4299,18 +4319,26 @@ item here closes, check it here *and* at its original location with a matching n
`RETROACTIVELY CHECKED`-style discipline the 2026-09-03 sweep used, so this list never drifts
stale the way the original carry-forwards did.
### I.1 — Console prompt chain (one real blocker, three downstream)
### I.1 — Console prompt chain — CLOSED 2026-09-04, except §17.4
- [ ] **4.4s — `(user)` prompt segment.** Not started; the REPL prompt is still the fixed
- [x] **4.4s — `(user)` prompt segment.** Not started; the REPL prompt is still the fixed
`ok> `. This is the actual blocker for the three items below — nothing else stands between
them and closing. *(Full entry: §A, "From FABRIC-2.md §A — Blocked or scoped, not started.")*
- [ ] **4.3 — Console umbrella.** Open only because 4.4s is; everything else under it
(4.3.14.3.7f, 4.44.4ac) is already done. *(Same location as above.)*
- [ ] **1.11 — Dirty-event granularity.** Blocked on 4.3 by design (settled as part of the
console migration, not speculatively before it). *(Same location as above.)*
them and closing. **DONE 2026-09-04** — built to the locked `FABRIC.md` §4.4s spec exactly
(`[VM name] (user) ok>`), verified 3-arch. *(Full entry: §A, "From FABRIC-2.md §A — Blocked
or scoped, not started.")*
- [x] **4.3 — Console umbrella.** Open only because 4.4s is; everything else under it
(4.3.14.3.7f, 4.44.4ac) is already done. **DONE 2026-09-04** — sole blocker cleared, all
sub-items already done. *(Same location as above.)*
- [x] **1.11 — Dirty-event granularity.** Blocked on 4.3 by design (settled as part of the
console migration, not speculatively before it). **DONE 2026-09-04** — the leaning
(region-based) is now the formal decision, per `FABRIC-2.md`'s own "no independent path"
ruling. Decision only, not an implementation — see §17.4 below, still open for exactly that
reason. *(Same location as above.)*
- [ ] **§17.4 — framebuffer utility's internal heat/decay dynamics.** Undesigned, blocked on
1.11 specifically (not "the framebuffer work" generally). *(Full entry: §A, "From
FABRIC-2.md §D — Design questions still genuinely open.")*
1.11 specifically (not "the framebuffer work" generally). **Still open 2026-09-04**: 1.11's
*decision* closed, but no dirty-region-tracking mechanism exists yet for this item's physics
to attach to — that mechanism, not a further ruling, is the real remaining blocker.
*(Full entry: §A, "From FABRIC-2.md §D — Design questions still genuinely open.")*
### I.2 — Block subsystem
@@ -4418,3 +4446,36 @@ FABRIC-2.md §X, Milestone 8 — Bare-metal boot from physical USB.")*
"already logged in this session," not "has ever been minted." Verified 3-arch boot to `ok>`
(amd64/aarch64/riscv64, each in the foreground). *(Full entry: §B, "From FABRIC-2.md §... —
EXPIRE (ACL).")*
### I.9 — Terminal defect: first keypress behaves as though CR+LF was emitted (reported
2026-09-04, NOT reproduced, NOT fixed)
Captain Bob observed, live in a QEMU session's own terminal: pressing the first key after a
prompt appears causes what looks like a CR+LF being emitted (i.e., the line behaves as though
Enter was pressed) rather than the character itself echoing normally. Not yet pinned to a
specific architecture, input path (serial vs. keyboard-event bridge), or exact repro steps —
reported in passing during Step 3's own verification runs, not isolated on purpose.
**Investigated, not reproduced** (no ability to observe a live QEMU GTK window directly):
traced `sk_console_readline()`'s normal-character path (`repl.c` — echoes via `console_putc()`,
appends to `buf`, no newline emitted) and both raw input sources it polls,
`sk_console_getc_raw()`: serial `console_getc()` and the keyboard-event bridge
`sk_kbd_getc()` (`repl.c`, translating `sk_key_event_poll()`'s converged keycode stream from
`keyboard_words.c` — i8042 scancodes on amd64, virtio-input on aarch64/riscv64). Nothing in
either path stood out as a source of a spurious `'\n'` specifically on a *first* keypress —
`sk_kbd_getc()`'s modifier-key handling (shift/alt tracked as persistent state,
zero-initialized BSS) looked correct, and `SK_KEY_ENTER`/backspace are the only two codes that
can produce a control character at all, both explicit, keycode-gated branches (28 and 14
respectively) rather than a fallthrough default that a garbage/uninitialized first event could
accidentally hit.
**Not ruled out, for lack of a way to check from here:** a stale/synthetic event already
sitting in the i8042 or virtio-input ring buffer at boot (before any real keypress), decoded as
`SK_KEY_ENTER` on the very first `sk_key_event_poll()` call; a GTK-level artifact (e.g. window
focus-in event) that QEMU's virtual keyboard forwards as a keycode; or something in the local
terminal emulator rendering the serial log stream, unrelated to kernel code at all.
**Needs, before this can be actioned:** a captured serial log (`logs/`) spanning the exact
moment of the first keypress after a fresh boot, ideally with the specific key pressed and
architecture noted, so the byte sequence that actually reached the console can be read back
directly rather than inferred from code review alone.
+1 -1
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-09-04T12:45:58Z -->
<!-- Generated by mkcapsule --manifest 2026-09-04T13:31:03Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. -->
BIN
View File
Binary file not shown.
+10
View File
@@ -112,6 +112,16 @@ int capsule_wirebind_eject(void);
*/
void capsule_wirebind_unclean_detach(void);
/**
* capsule_wirebind_attached_username - The plain username (no "~user"
* registry-name suffix) of whichever identity is currently tracked as
* attached, or NULL if none is (FABRIC-3.md §I.1/4.4s -- the `(user)`
* console prompt segment reads this). Points into WIREBIND's own
* internal storage; valid only until the next attach/eject/detach, same
* caveat as console_get_vm_name().
*/
const char *capsule_wirebind_attached_username(void);
#endif /* __STARKERNEL__ */
#endif /* STARKERNEL_CAPSULE_WIREBIND_H */
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+17 -3
View File
@@ -34,6 +34,11 @@
* candidate, so one tracked id is enough -- no set/list. */
static VMUuid g_wirebind_attached_vm_id;
static int g_wirebind_attached_valid = 0;
/* Plain username (no "~user" registry-name suffix), tracked separately
* from g_wirebind_attached_vm_id -- the registry entry's own name is
* user_vm_name ("<username>~user"), not the bare form the (user) prompt
* segment wants (FABRIC-3.md §I.1/4.4s). */
static char g_wirebind_attached_username[USER_IDENTITY_USERNAME_MAX] = {0};
/* WIREBIND_CERT_MAX_DEVBLOCKS: a sane upper bound on how much cert
* content this reads, independent of whatever sig->cert_devblocks
@@ -179,6 +184,7 @@ void capsule_wirebind_try_attach(struct blkio_dev *dev,
* actually owns block-subsystem state). */
g_wirebind_attached_vm_id = user_id;
g_wirebind_attached_valid = 1;
memcpy(g_wirebind_attached_username, username, sizeof(g_wirebind_attached_username));
/* Register the pairing in the console's own routing table, index 3
* -- the fixed convention sk_repl_dispatch_line() (repl.c) uses. */
@@ -203,7 +209,8 @@ static int wirebind_resolve_attached(VMRegistryEntry *out) {
if (!g_wirebind_attached_valid) return -1;
if (capsule_vm_registry_get(g_wirebind_attached_vm_id, out) != 0 ||
out->state != VM_STATE_LIVE) {
g_wirebind_attached_valid = 0;
g_wirebind_attached_valid = 0;
g_wirebind_attached_username[0] = '\0';
return -1;
}
return 0;
@@ -225,7 +232,8 @@ int capsule_wirebind_eject(void) {
}
capsule_vm_kill(entry.name);
g_wirebind_attached_valid = 0;
g_wirebind_attached_valid = 0;
g_wirebind_attached_username[0] = '\0';
console_puts("EJECT: ");
console_puts(entry.name);
@@ -245,9 +253,15 @@ void capsule_wirebind_unclean_detach(void) {
}
capsule_vm_kill(entry.name);
g_wirebind_attached_valid = 0;
g_wirebind_attached_valid = 0;
g_wirebind_attached_username[0] = '\0';
console_puts("WIREBIND: ");
console_puts(entry.name);
console_println(" detached without flush (device already gone)");
}
const char *capsule_wirebind_attached_username(void) {
if (!g_wirebind_attached_valid) return (const char *)0;
return g_wirebind_attached_username;
}
+33 -5
View File
@@ -57,6 +57,31 @@
const char lithos_version[64] = LITHOS_VERSION_STR;
/* FABRIC.md §27.8/4.4s, unblocked 2026-09-04: extends the prompt to
* "[VM name] (user) ok>" (e.g. "[Hera] (zuse) ok>") whenever an
* identity is currently attached -- Zuse (mama_vm->zuse_session; there
* is only ever one, so no username lookup needed) or a regular WIREBIND
* user (capsule_wirebind_attached_username()). Checked independently of
* which VM's own bracket console.c is currently showing: both identities
* are console-level attach state, not per-VM dictionary state, so the
* segment reflects "who is at the console" the same way regardless of
* which VM you've USE'd into. Prints nothing (bare "ok> ", today's
* existing format, unchanged) when neither is attached -- Hera's own
* documented normal steady state (FABRIC-3.md §D.2). */
static void sk_print_prompt(void) {
VM *mama_vm = (VM *)sk_get_mama_vm();
const char *username;
if (mama_vm && mama_vm->zuse_session) {
console_puts("(zuse) ");
} else if ((username = capsule_wirebind_attached_username()) != (const char *)0) {
console_puts("(");
console_puts(username);
console_puts(") ");
}
console_puts(SK_PROMPT_TEXT);
}
/*===========================================================================
* USE-word dispatch: which VM receives REPL input.
@@ -597,7 +622,7 @@ int sk_console_readline(char* buf, int size, VM* active_vm, int reanchor_prompt)
if (reanchor_prompt && n == 0 &&
console_tx_count() != prompt_tx_mark)
{
console_puts(SK_PROMPT_TEXT);
sk_print_prompt();
console_fb_draw_cursor();
prompt_tx_mark = console_tx_count();
}
@@ -787,8 +812,10 @@ int sk_repl_step(VM *vm)
* only by the genuine C-level fault handler now (vm.c's own
* emergency-fault-recovery use, EMERGENCY_CONSOLE_ENABLED). Every
* word run from this REPL, Hera's bare prompt included, goes
* through ordinary ACL enforcement. */
console_puts(SK_PROMPT_TEXT);
* through ordinary ACL enforcement. FABRIC.md 4.4s (2026-09-04):
* sk_print_prompt() extends this with a "(user)" segment when an
* identity is attached -- see its own doc comment. */
sk_print_prompt();
}
sk_console_readline(input, sizeof(input), vm, 1);
@@ -831,8 +858,9 @@ void sk_repl_run(VM *vm)
* bracket -- print only "ok> " here, don't build a second one.
* emergency_console is no longer set from here (FABRIC-3.md §F.20/
* §F.21: the emergency-CLI ACL bypass is retired) -- see sk_repl_
* step()'s matching comment above. */
console_puts(SK_PROMPT_TEXT);
* step()'s matching comment above. FABRIC.md 4.4s (2026-09-04):
* sk_print_prompt() extends this with a "(user)" segment. */
sk_print_prompt();
sk_console_readline(input, sizeof(input), active, 1);