Stage 4 verified: reap mechanism proven, real WIREBIND multiuser+multitasking confirmed live (FABRIC-3.md §XXX)

Reap mechanism (increments 2+3): temporary probe using Artemis as a safe
stand-in parked identity, 3/3 checks PASS on all 3 architectures (refuse
on SWITCHED_OUT, correct post-reap state, switch-signal slot released).
Probe reverted, all 3 architectures re-verified clean.

Live multiuser verification (increment 4, no code changes): a real
previously-unattached identity thumbdrive attached via QMP on a running
boot on all 3 architectures. VM-EXEC dispatch into her own live VM
computed correctly, tagged with her own name in console output, full
Tripod fleet unaffected. EJECT cleanly tore down both her VMs and
released the switch-signal slot -- confirms increment 1's per-device fix
under a real live attach/detach.

Found and flagged, not fixed: interactive USE on a freshly-attached
identity halts the kernel outright. Confirmed NOT caused by Stage 4 --
reproduced identically on the commit before any Stage 4 work. Direct
VM-EXEC dispatch into the same identity works correctly; this is
specific to the USE/BINDSTEP codepath, plausibly never caught before
since every prior identity campaign used VM-EXEC, never interactive USE.

Stage 4's original scope is complete: multitasking (Tripod) and
multiuser (WIREBIND) are now genuinely composed, verified against real
hardware-driven identity attach on all 3 architectures.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BWpNjdwPtFLuVLaAq44L9K
This commit is contained in:
Robert Allan James
2026-09-15 03:39:37 -04:00
co-authored by Claude Sonnet 5
parent d9da82b065
commit 597f5a6cd8
15 changed files with 108732 additions and 1 deletions
+94
View File
@@ -3988,3 +3988,97 @@ indicated a larger new initiative is coming that will be introduced during Stage
-- not detailed here since it hasn't been scoped yet, flagged only so this closing note doesn't -- not detailed here since it hasn't been scoped yet, flagged only so this closing note doesn't
read as "nothing more is coming." read as "nothing more is coming."
## XXX. Stage 4 -- WIREBIND-scope extension, built and verified live (2026-09-14/15)
**Bob's call: proceed with Stage 4 immediately rather than wait, folding the two §XXIX-deferred
bugs in only where they turned out to actually block the work** (neither did, in the end --
both stay open, tracked below in their original form). Built in the same staged,
one-commit-per-increment discipline as Stages 0-3.
**Increment 1 (commit `9f0f33d`) -- de-singleton WIREBIND detach tracking.** Found before
writing any Stage 4 code, on advisor review: `capsule_wirebind_unclean_detach()`/`eject()`
tracked "the attached identity" as a single global (`g_wirebind_attached_*`), correct for the
one-physical-console UX but wrong for detach safety once §XV/§XVI's own campaign already proved
multiple identities live simultaneously via this same attach path -- every attach after the
first silently overwrote the singleton, so an unclean detach of any but the *most recently
attached* identity was silently ignored: no console trace, no teardown, the VM and its native
stack leaked forever. Added a per-device live-identity table (`g_wirebind_live[]`, sized off
messaging.4th's own `VM-MAX` minus Tripod's 3 reserved slots -- 13, not an invented number),
kept separate from the console-pairing singleton, which is untouched and still governs the
`(user)` prompt exactly as before. Corrected the stale "single-USB-device constraint (§F.8)"
claim in `capsule_wirebind.h`'s own doc comments, false since §XV/§XVI.
**Increments 2+3 (commit `d9da82b`) -- switch-signal participation + mark-and-defer tombstone
reap.** WIREBIND *user* VMs (the ones that run real FORTH work; console VMs are pure REPL
proxies with nothing for a switch-in to preempt) now register as Stage 3 switch-signal
participants at attach (`sk_vm_switch_signal_register()`), unregister at teardown. Slot table
bumped 8 -> 16 (`VM-MAX`, the same real ceiling increment 1 used). Added
`sk_vm_switch_signal_unregister()` (compaction-based -- Tripod never needed removal, WIREBIND
VMs cycle constantly and would otherwise exhaust the bounded table).
Implements the plan's own ratified option (A) for the async-detach UAF risk named all the way
back when Stage 4 was first scoped (`logical-snuggling-bear.md`): mark-and-defer via a new
`pending_reap` flag on `VMRegistryEntry` -- deliberately a plain flag, not a new `VMState`
(`capsule_vm_kill()` already treats `VM_STATE_DEAD` as idempotent success, which would silently
swallow a reap attempt; `SWITCHED_OUT` still accurately describes a tombstoned-but-not-yet-freed
VM). `unclean_detach()` sets it when `capsule_vm_kill()` refuses a `SWITCHED_OUT` target instead
of just logging and dropping it on the floor; the Stage 3 checkpoint (`vm_core.c`) checks it
*before* ever attempting to resume a pending switch target, and calls the new
`capsule_vm_force_reap()` instead -- the one caller allowed to bypass `capsule_vm_kill()`'s own
refusal, because it runs at the exact safe cooperative point the switcher itself controls, the
same reasoning the original plan named. A new idle-tick sweep
(`capsule_wirebind_reap_idle_check()`) cleans up the stale live-table entry once the reap has
actually happened.
**Reap-mechanism verification (temporary probe, reverted, per this project's write-probe-
capture-revert discipline):** the mechanism has no real hardware timing dependency by design
(it operates purely on registry/switch-signal state), so proving it live against real WIREBIND
hardware timing would have tested the choreography, not the mechanism. Used Artemis as a safe
stand-in "parked identity" instead: force her into `VM_STATE_SWITCHED_OUT`, confirm
`capsule_vm_kill()` refuses, set `pending_reap`, call `capsule_vm_force_reap()` directly, confirm
`state == DEAD`/`vm_ptr == NULL`/`pending_reap == 0`/switch-signal slot released. 3/3 checks PASS
on all 3 architectures (Artemis is deliberately left dead for the remainder of each probe boot --
documented as expected, not a defect). Probe reverted; all 3 architectures re-verified clean at
rest afterward, no `REAP-TEST` output, Artemis alive and normal.
**Increment 4 -- live multiuser verification against real WIREBIND hardware (no code changes,
verification only).** A real, previously-unattached identity thumbdrive (`bob-thumb-ident.img`,
whose on-disk username is actually `rajames` -- confirmed live, not assumed) attached via QMP
`device_add` on a running boot, sequential per §XIV's own established workaround (one identity,
wait for `WIREBIND: rajames attached and ready`, no simultaneous-attach race attempted -- §XIV
itself remains open, sidestepped not fixed, exactly as §XXIX left it). Verified on all 3
architectures: `S" 1 1 + . " S" rajames~user" VM-EXEC` correctly computed `2` inside her own
live VM, tagged `[zuse@rajames~user]` in the console output, with the full Tripod fleet
simultaneously live and unaffected. `EJECT` (amd64) cleanly tore down both the console and user
VM, flushed, and released the switch-signal slot -- no leak, no crash, confirming increment 1's
per-device fix holds under a real live attach/detach, not just the reap probe's synthetic one.
**A real, pre-existing bug found during this verification, confirmed NOT caused by Stage 4:**
interactive `USE <identity>` on a freshly-WIREBIND-attached identity halts the kernel outright
(`VM fault -- emergency console disabled; halting`, the genuine C-level fault handler, not a
graceful FORTH-level error) -- reproduced identically on commit `1c220ad` (the last commit
before any Stage 4 work) via a temporary `git checkout` to that commit, same attach, same `USE
rajames`, same crash. Root cause not chased further this pass (out of Stage 4's own scope) --
plausibly never caught before because every prior identity-correctness campaign (§XII.4, §XV,
§XVI) drove identities via direct `VM-EXEC` dispatch, never through the interactive
`USE`/BINDSTEP console-relay path this specific crash requires. `VM-EXEC` dispatch into the same
identity, immediately before and after, works correctly -- this is specific to the `USE`
codepath (`mama_word_use()`, `mama_forth_words.c`), not a general identity-attach defect.
Flagged for a future pass, not fixed here.
**Verification method note, matching prior sessions' own established practice:** live device
attach used real QMP `blockdev-add`/`device_add` against the running instance's own QMP socket,
not a build-time `QEMU_EXTRA` mechanism -- confirmed identical to the technique §XIV/§XV already
used. Interactive console input used QMP `send-key`, extended this pass to handle uppercase
letters (shift+lowercase) and symbols needed for real command lines (`~`, `"`, `+`) -- the
existing `1 1 + .` probe from §XXVIII.2 only needed digits and `+`.
**Status: Stage 4's original scope (WIREBIND VMs as switch-signal participants, with the async-
detach UAF risk closed per the plan's own ratified design) is complete and verified live on all
3 architectures, real hardware-driven identity attach included.** Multitasking (fixed Tripod
fleet) and multiuser (WIREBIND identities) are now genuinely composed, not just coexisting.
Remaining open items, unchanged in status from §XXIX plus the one new finding: §XIV (concurrent
attach detection gap, still open, sidestepped via sequential attach), the MSG-TICK/Stage-3
dual-ownership rough edge (still open, not observed failing), and the newly-found pre-existing
`USE`/BINDSTEP crash (flagged, not fixed).
+1 -1
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated # Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-09-15T04:46:38Z --> <!-- Generated by mkcapsule --manifest 2026-09-15T07:25:25Z -->
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. --> <!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
<!-- Hand-written justifications and immutability notes live --> <!-- Hand-written justifications and immutability notes live -->
<!-- in MANIFEST.md alongside this auto-generated index. --> <!-- in MANIFEST.md alongside this auto-generated index. -->
BIN
View File
Binary file not shown.
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
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
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
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