Scope new initiative: unattended identity model, USE fail-closed-halt root cause, primitive error-handling audit, logging cleanup (FABRIC-3.md §XXXII)
USE crash fully root-caused against current source: mama_word_use() has seven guards, two of which set vm->error silently (stack underflow, bad VM address) while the other five print diagnostics; sk_repl_step() halts the whole kernel only when the faulting VM is Hera; Zuse's console runs directly on Hera's own VM (confirmed in capsule_zuse_boot.c), making a benign typo at her prompt the one deterministic path to the fail-closed halt. Three fix options named, none applied yet. Human-vs-unattended identity/console-birth model scoped: console/user VM pairing is pure name-convention + a per-console-VM VM-NAME-REG call, so after-the-fact console attach needs no new mechanism. Named the invariant an unattended birth path must not violate (never touch the global driving the "no thumbdrive, no prompt" gate). Error-handling audit scoped kernel-only (mama_forth_words.c + 5 kernel-only word_source files + vm_core.c/repl.c): 107 vm->error=1 sites across 8 files, counted directly. The vendored word_source sweep is explicitly out of scope here, spun off as future FABRIC-4.md work. Logging cleanup sequenced ahead of the audit's own fixes, with the two open §XXVII gaps (LOG-FLUSH doesn't exist; FIFO eviction is level-blind) named for an explicit in/out-of-scope call. Staged A-E plan proposed; nothing implemented this pass. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01BWpNjdwPtFLuVLaAq44L9K
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
16cc74243c
commit
05159c9f9e
+219
@@ -4128,3 +4128,222 @@ own value here is what it has always been: a large-N cross-identity, cross-archi
|
||||
correctness regression check, now re-confirmed clean after Stage 4's registry/switch-signal
|
||||
plumbing changes -- not a preemption-specific test.
|
||||
|
||||
## XXXII. New initiative: human-vs-unattended identity model, `USE` fail-closed-halt root cause,
|
||||
primitive error-handling audit, logging cleanup -- scoped, staged plan written up before any code
|
||||
(2026-09-15)
|
||||
|
||||
**Origin.** Closing out §XXX/§XXXI left one flagged, deferred item: the `USE`/BINDSTEP crash
|
||||
found during Stage 4 verification. Asked directly whether multitasking/multiuser is fully
|
||||
functional, told to close that gap -- then, before any fix was attempted, redirected: the real
|
||||
gap is a functional OS, and every WIREBIND identity currently births both a console VM and a user
|
||||
VM together; the alternative is a "human" identity (gets a console) versus an "unattended
|
||||
instance" (no console by default, ACL-gated attach later). Refined further: an unattended
|
||||
identity should carry the *same* real cryptographic identity format a thumbdrive does, baked
|
||||
directly into a birth capsule instead of requiring a physical/virtual attach event -- and the
|
||||
`USE` crash stays in scope, to be root-caused independently of the architecture question. Then,
|
||||
before any code: plan this like every other FABRIC feature -- scope it, stage it into testable
|
||||
chunks, read all four FABRIC files first, and fold in two more concerns: auditing every primitive
|
||||
for proper error handling, and the standing logging cleanup already flagged in
|
||||
`project_production_logging_cleanup_needed.md`.
|
||||
|
||||
**All four FABRIC files read** (0/1/2 archival, 3 living) before writing this section, per that
|
||||
instruction -- FABRIC-0 directly for its Stadium/patron model (§1-17: heat/TTL/pin as three
|
||||
distinct departure mechanisms, VMs as patrons, the admission test), FABRIC-1/2's identity and
|
||||
error-handling history via a background pass, cross-checked against source rather than trusted
|
||||
at face value (see the correction in §XXXII.1 below, where a first-pass count was itself found
|
||||
wrong on inspection). Every code claim below was re-verified directly against current source
|
||||
before being written down here, not carried over from any prior draft.
|
||||
|
||||
### XXXII.1 -- `USE` fail-closed-halt: root cause found, not a Stage 4 regression, not a mystery
|
||||
|
||||
**Root cause, confirmed by reading the actual code path:**
|
||||
|
||||
`mama_word_use()` (`src/starkernel/capsule/mama_forth_words.c:443-546`) has **seven** guards, not
|
||||
four -- verified by reading the whole function, not sampling it. Two set `vm->error` with **zero
|
||||
console output**; five print a diagnostic and return without touching `vm->error`:
|
||||
|
||||
| Line | Guard | Sets `vm->error`? |
|
||||
|---|---|---|
|
||||
| 451-454 | `dsp < 1` (stack underflow -- nothing pushed via `S"`) | **yes, silent** |
|
||||
| 459-462 | name too long or empty | no -- prints |
|
||||
| 467 | `vm_ptr()` returns NULL (bad VM address on stack) | **yes, silent** |
|
||||
| 473-478 | target not found | no -- prints |
|
||||
| 480-485 | target dead/stillborn | no -- prints |
|
||||
| 496-501 | target not ready (interpreter not yet enabled) | no -- prints |
|
||||
| 526-531 | live identity re-verify fails (BINDSTEP) | no -- prints |
|
||||
|
||||
Typing the bare, syntactically-wrong `USE rajames` (two tokens, nothing pushed via `S"`) hits the
|
||||
451-454 guard on whatever VM is currently interpreting the typed line.
|
||||
|
||||
`sk_repl_step()` (`src/starkernel/repl.c:1317-1338`) has a Hera-vs-guest fault-scoping split, added
|
||||
2026-09-09 (§XI.4 above): `if (vm == (VM *)sk_get_mama_vm()) { sk_fault_handler(vm); } else {
|
||||
/* recover, don't halt */ }` -- confirmed by reading both branches directly. **Zuse's own console
|
||||
session runs on Hera's own VM, not a separate one**: `capsule_zuse_boot.c`'s
|
||||
`install_and_activate(VM *mama_vm, ...)` calls `vm_zuse_cert_install(mama_vm, ...)` and sets
|
||||
`mama_vm->zuse_session` directly on the passed-in Hera VM -- no VM is birthed for her (matching
|
||||
§X.4's "Zuse costs nothing extra" finding, re-confirmed here by reading the actual install path
|
||||
rather than citing the earlier finding secondhand).
|
||||
|
||||
So a benign syntax mistake typed at Zuse's own prompt is the one case in this system that
|
||||
deterministically hits the fail-closed halt -- not a WIREBIND/Stage-4 defect, not intermittent, a
|
||||
direct consequence of three separately-correct decisions (§VIII.1's fail-closed default, Zuse
|
||||
running on Hera's own VM, and two of `mama_word_use()`'s seven guards breaking its own five-guard
|
||||
pattern) composing badly. Already confirmed NOT a Stage 4 regression (§XXX, reproduced identically
|
||||
on the pre-Stage-4 commit) -- this section supersedes that entry's "root cause not chased further"
|
||||
note with the actual mechanism.
|
||||
|
||||
**Three fixes are possible, named so Bob can pick, not pre-decided:**
|
||||
|
||||
- **(a) Per-word fix, smallest, precedent-exact.** Make both silent guards (line 451-454 and 467)
|
||||
match their own five siblings: print a diagnostic and `return;` **without** setting `vm->error`.
|
||||
Two lines changed in one function, no ripple, closes the reported crash exactly. *Recommended
|
||||
as the immediate fix.*
|
||||
- **(b) Per-word fix, keep the fault.** Same diagnostic, but still set `vm->error` -- treats a
|
||||
stack-convention violation as a genuine FORTH-79 ABORT-worthy fault. Diverges from this same
|
||||
function's own five-guard precedent.
|
||||
- **(c) Upstream, bigger and more consequential.** Narrow `sk_repl_step()`'s Hera-branch so an
|
||||
ordinary *interactive* typo at Zuse's console recovers like any other session, while a genuine
|
||||
boot/capsule-load fault on Hera still halts. Fixes the entire class (any silent-error site, hit
|
||||
interactively as Zuse, currently halts the machine) in one place -- but directly amends
|
||||
§VIII.1's deliberate, named fail-closed "no fallthrough surface" ruling. **A gate decision for
|
||||
Bob, not an implementer's call.**
|
||||
|
||||
**Verification trap, named explicitly** (matches `feedback_confirm_specific_vm_before_concluding`):
|
||||
whichever fix lands, it must be verified by typing bare `USE <name>` at **Zuse's own console** (on
|
||||
Hera). A WIREBIND identity's own guest session already recovers from this exact error today,
|
||||
pre-fix (§XI.4's fix already covers that path) -- testing there would be a false pass.
|
||||
|
||||
### XXXII.2 -- Human vs. unattended identity/console-birth model
|
||||
|
||||
**Current model, confirmed by reading `capsule_wirebind_try_attach()` end to end
|
||||
(`capsule_wirebind.c:174-342`):** every WIREBIND attach unconditionally births two VMs --
|
||||
`capsule_console_birth()` (a minimal relay proxy, registered under the bare username) and
|
||||
`capsule_runcap_birth()` (the real identity, `<username>~user`). Pairing is pure name convention
|
||||
plus one `VM-NAME-REG` call, executed **inside the console VM's own dictionary**
|
||||
(`vm_interpret((VM *)console_ctx, "S\" <user_vm_name>\" 3 VM-NAME-REG")`, `capsule_wirebind.c:331-337`)
|
||||
-- confirmed this is scoped per-console-VM, not a global singleton, since `VM-NAME-REG` mutates
|
||||
whichever VM interprets it. **This means after-the-fact console pairing already works
|
||||
structurally**: a console VM named `X` can be birthed later and paired to an already-live
|
||||
`X~user` by the identical call, with no new pairing mechanism needed.
|
||||
|
||||
**Proposal, as refined by direct instruction:** distinguish two identity *origins*, not two
|
||||
identity *kinds*:
|
||||
|
||||
- **Human (WIREBIND, thumbdrive-attach-triggered, unchanged):** births console + user VM
|
||||
together, exactly as today. A physically-present human gets an immediate console.
|
||||
- **Unattended (capsule-embedded, new):** the same real cryptographic identity format `MINT`
|
||||
already produces (cert + seed + personality) is baked directly into a birth capsule instead of
|
||||
requiring a physical/virtual thumbdrive attach event. Births the user VM only -- no console VM,
|
||||
and does not touch the console-pairing mechanism. A human can attach a console to an
|
||||
already-live unattended instance later (ACL-gated), by birthing a console VM under its name and
|
||||
running the same `VM-NAME-REG` call -- the mechanism already confirmed above to work
|
||||
after-the-fact, not a new capability to invent.
|
||||
|
||||
**A real invariant this must not violate**, confirmed by reading `capsule_wirebind_try_attach()`
|
||||
against §VIII.1's own ratified policy: `g_wirebind_attached_username` is what
|
||||
`sk_console_identity_present()` reads (its own comment says so directly,
|
||||
`capsule_wirebind.c:321-327`), and that function is §VIII.1's live "no thumbdrive, no prompt"
|
||||
gate. **An unattended birth path must never set this global or otherwise touch the
|
||||
console-pairing mechanism** -- doing so would bring up a console with no human present, directly
|
||||
reopening the headless-until-login gate §VIII.1 ratified. State this as an explicit invariant in
|
||||
whatever code implements unattended birth.
|
||||
|
||||
**Genuinely open, to settle on paper before any code:**
|
||||
1. Where does an unattended identity's capsule-embedded cert/seed/personality actually get
|
||||
produced and packaged -- a new `mkcapsule` mode, a new `MINT` variant that writes to a capsule
|
||||
instead of a device, or something else? `mkcapsule --resolve` already touches capsule-content
|
||||
generation and was separately flagged (§XXIV) for block-number-collision risk on the same
|
||||
path -- check whether that's still open before adding a second capsule-content producer.
|
||||
2. What births an unattended identity, and when -- at boot (like Hermes/Artemis today), on demand
|
||||
via a new FORTH word, or something else? Determines whether it needs its own
|
||||
`capsule_*_birth()` entry point or can reuse `capsule_runcap_birth()` with a capsule-sourced
|
||||
byte buffer instead of a `blkio_dev*`.
|
||||
3. What does "ACL-gated console attachment later" concretely check -- a capability bit on the
|
||||
attaching human's own identity, a fixed allowlist, something else? Not designed here.
|
||||
4. Confirm §VII's per-device xHCI/live-table machinery genuinely has nothing to do with an
|
||||
unattended identity's own lifecycle (no `blkio_dev`, no device to attach/detach) before
|
||||
assuming the two paths share no state.
|
||||
|
||||
### XXXII.3 -- Primitive error-handling audit: kernel-only scope, real surface counted
|
||||
|
||||
**Scope, decided explicitly:** this audit covers kernel-side primitives only --
|
||||
`mama_forth_words.c`, the five kernel-only `word_source/` additions (`defer_words.c`,
|
||||
`inference_words.c`, `lifecycle_words_hosted.c`, `log_words.c`, `q48_words.c`), and the
|
||||
error-propagation machinery itself (`vm_core.c`, `repl.c`). This is where WIREBIND/ACL/Stadium/
|
||||
switch-signal logic lives, where both the `USE` bug and the §XXVIII.3 `SWITCH-MARK-WORK`
|
||||
off-by-one came from, and where a fix carries no dual-target (hosted + kernel) compileability
|
||||
risk. **The 26 vendored, shared `word_source/` files (arithmetic, stack, string, control, etc.)
|
||||
are explicitly out of scope for this initiative** -- they are StarForth's own vendored source,
|
||||
must stay correct in the hosted build too, and any sweep of them is real, separable work: spun
|
||||
off as its own future `FABRIC-4.md`, not bundled here.
|
||||
|
||||
**Surface, counted directly, not guessed:** `grep -rn "error = 1"` across the kernel-only scope
|
||||
above finds **107 sites across 8 files** -- `mama_forth_words.c` (51), `defer_words.c` (15),
|
||||
`vm_core.c` (24), `log_words.c` (13), `inference_words.c` (3), `repl.c` (1),
|
||||
`lifecycle_words_hosted.c`/`q48_words.c` (0 each). (For reference: the same grep across the full
|
||||
vendored+kernel tree returns 659 sites/35 files -- that larger number is `FABRIC-4.md`'s scope,
|
||||
not this one's.) `USE`'s own two sites (§XXXII.1) are two of the 51 in `mama_forth_words.c`.
|
||||
|
||||
**Not scoped as one pass** -- 107 sites still spans 8 files with different owners (word
|
||||
primitives vs. the interpreter core vs. the REPL dispatch loop). Proposed shape:
|
||||
|
||||
- **Triage first, fix later.** A read-only pass across the 8 files classifying each site as
|
||||
(i) already prints a diagnostic before/without setting `vm->error` (fine as-is, matches the
|
||||
pattern `USE`'s five correct guards already follow), (ii) sets `vm->error` with no diagnostic
|
||||
at all (the `USE` defect's exact shape, worth a message), or (iii) something else worth
|
||||
flagging individually (a genuine ABORT-worthy fault correctly reported some other way). One
|
||||
pass, its own commit, a document, no code changes.
|
||||
- **Fix in file-sized batches afterward**, each its own commit + 3-arch boot -- not 107
|
||||
individual commits, not one 107-site commit.
|
||||
- `USE`'s own fix (§XXXII.1) is exempt from waiting on this -- already fully diagnosed, lands on
|
||||
its own as the worked example the triage pass can point back to.
|
||||
|
||||
### XXXII.4 -- Logging cleanup: two already-open gaps decided before the audit's fixes land
|
||||
|
||||
**Standing, already-flagged item** (`project_production_logging_cleanup_needed.md`):
|
||||
`console_println()` overuse where much INFO-level output should route through `log_message()`
|
||||
instead. Sequenced *ahead* of §XXXII.3's fix stage, not merely adjacent to it: that audit's own
|
||||
fixes will add diagnostic output at newly-found silent sites, and the policy for where that
|
||||
output goes needs to be decided first, or the audit's fixes get redone once the cleanup lands.
|
||||
Precedent already exists and should be the default: `capsule_mint.c`'s `verify_mint()` (§IX.5)
|
||||
logs entirely via `log_message()` (`LOG_ERROR`/`LOG_INFO`), explicitly to avoid unconditional
|
||||
console/serial noise. `USE`'s own fix is the deliberate exception -- an interactive typo's
|
||||
diagnostic belongs on the console the human is looking at, matching its five siblings, not routed
|
||||
to a log region nobody's reading in the moment.
|
||||
|
||||
**Two already-open logging gaps from §XXVII, named here so this pass explicitly decides whether
|
||||
they're in scope rather than silently colliding with them:**
|
||||
1. **`LOG-FLUSH` does not exist.** Both `init.4th`'s and `log_region.c`'s own doc comments
|
||||
describe it as the normal path into `LOG-APPEND`; grep matches only those two comments. The
|
||||
only entry points today are the raw primitive or a manual console call.
|
||||
2. **FIFO eviction is level-blind.** `log_region.c`'s ring buffer evicts strictly oldest-first
|
||||
once its 32-devblock ceiling is reached, with no awareness of `LOG_ERROR` vs `LOG_DEBUG` -- a
|
||||
flood of low-level noise can still evict real error history before it's read.
|
||||
|
||||
**Decision needed at this stage's own gate:** fix both alongside the console-vs-log-level policy
|
||||
pass, or explicitly defer them again as separate follow-on work. Either is fine; leaving it
|
||||
unstated is not, per this document's own "never silently drop a stale claim" rule.
|
||||
|
||||
### XXXII.5 -- Proposed staging and sequencing
|
||||
|
||||
Matching §XXVIII's own gated-stage shape (each stage its own commit, its own full 3-architecture
|
||||
`clean qemu` acceptance boot, no bundling):
|
||||
|
||||
1. **Stage A -- `USE` fix.** Land option (a) from §XXXII.1. Verify per the named trap (test at
|
||||
Zuse's own console, not a WIREBIND guest session). Ratify or reject option (c) (the upstream
|
||||
Hera-fault-scoping amendment) as a separate, explicitly-flagged follow-on decision -- not
|
||||
bundled into this stage.
|
||||
2. **Stage B -- logging policy pass.** Decide and document the `console_println` vs `log_message`
|
||||
split (§XXXII.4), and rule on the two open §XXVII gaps (`LOG-FLUSH`, level-aware eviction)
|
||||
before Stage C begins.
|
||||
3. **Stage C -- error-handling audit, triage only.** The read-only 107-site classification pass
|
||||
(§XXXII.3), its own commit, no code changes.
|
||||
4. **Stage D -- error-handling audit, fixes.** File-sized batches applying Stage B's logging
|
||||
policy to whatever Stage C's triage found in category (ii), each its own commit + 3-arch
|
||||
acceptance.
|
||||
5. **Stage E -- unattended identity design.** Settle §XXXII.2's four open questions on paper,
|
||||
amend this section with the ratified answers, *then* scope an implementation punch list.
|
||||
|
||||
**Nothing in this section has been implemented.** This write-up is the deliverable for this
|
||||
pass -- Stage A begins only once reviewed.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user