Revert HEARTBEAT-TICKS@ to vm->heartbeat.tick_count; FABRIC-2.md Section R:

find and fix the real ACL-TTL measurement bug (zuse session never
authenticated, ACL enforcement never active)

Two mistakes corrected in sequence, both documented in full in
FABRIC-2.md Section R:

1. HEARTBEAT-TICKS@ was swapped to read heartbeat_ticks() -- a newer,
   kernel-only ISR hardware-timer counter (src/starkernel/heartbeat.c,
   the M5 TIME-TRUST engine) -- based on a misreading of which counter
   "the one clock" law refers to. Reverted to vm->heartbeat.tick_count,
   Loop #7 "Adaptive Heartrate", the actual year-plus-old counter the
   whole physics runtime is built on. Removed the now-irrelevant
   HEARTBEAT-PERIOD-NS@ accessor added to diagnose the wrong counter's
   adaptive re-arm period. Three-arch QEMU re-acceptance: POST 1012/0/0
   on amd64/aarch64/riscv64, HEARTBEAT-TICKS@ confirmed returning 77
   (matching the original pre-heartbeat_ticks() acceptance) on all three.

2. The real bug, found after the revert: every "ACL enabled" measurement
   in this investigation (Section P's 18-cell campaign, Section Q's
   pilot) loaded ACL.4th and ran EXEC-DOE from the bare `ok>` prompt
   without ever authenticating a zuse session. repl.c:303 keeps
   emergency_console=1 until zuse_session=1; vm_core.c:755 skips the
   entire ACL check block (TTL decrement and acl_recheck()) whenever
   emergency_console is set. ACL was configured but never armed.
   capsules/zuse.4th's pre-existing self-pin bug means the documented
   automatic zuse activation doesn't work either (still flagged, not
   fixed) -- worked around by invoking the directly-registered
   ZUSE-AUTHENTICATE word explicitly.

Validated pilot (amd64, seed 12345, 30 reps, same build, disabled vs.
genuinely zuse-authenticated-enabled): +117 ticks, +0.0448% overhead.
Disabled-arm determinism double-confirmed (261064 ticks, exact repeat
on a fresh boot) -- the 117-tick difference is real signal, not noise.
Reconciles with the original ACL-RWT campaign's own heartbeat-tick
result (+0.0054%-0.0088%, same order of magnitude). Section P's
wall-clock numbers and Section Q's "instrument blind" conclusion are
both marked invalidated/corrected in place, not deleted.

n=1 per arm, one architecture -- not yet a full campaign. Scoped as
next step, not undertaken in this pass.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-08-21 11:22:56 -04:00
co-authored by Claude Sonnet 5
parent 4076a01c35
commit 70c259dad0
12 changed files with 110178 additions and 14 deletions
+120
View File
@@ -2127,6 +2127,17 @@ describe nothing real going forward.
## P. ACL-TTL overhead — paired 18-cell measurement, 2026-08-20
> **INVALIDATED by Section R, 2026-08-21 — not a methodology-noise problem, ACL enforcement
> was never active in any of the 18 cells.** Every "enabled" cell here loaded `ACL.4th` at the
> bare `ok>` prompt and ran `EXEC-DOE` without ever authenticating a `zuse` session. Per
> `src/starkernel/repl.c:303`, `vm->emergency_console` stays `1` whenever `zuse_session` is `0`
> (the documented bare-`ok>`-only bypass) — and `src/starkernel/vm/vm_core.c:755`'s
> `if (w && !vm->emergency_console)` skips the entire ACL check block, hot-path TTL decrement
> included, whenever `emergency_console` is set. So none of these 18 "enabled" cells ever
> actually enforced ACL. What P measured was the cost of *having loaded* `ACL.4th` (a longer
> dictionary, boot-time `ACL-INIT-PRIMITIVES` stamping) plus wall-clock noise — not enforcement.
> Left below as historical record per repo convention; do not cite its numbers as ACL overhead.
Full paired campaign: 9 (arch, seed) cells × {ACL disabled, ACL enabled} = 18 cells, all in
one continuous sitting, fully randomized order (not blocked by ACL state or architecture —
same rigor as Sections M/O, learned the hard way earlier this session), independent `clean`
@@ -2192,6 +2203,19 @@ attempted here; scoped as a natural next step, not undertaken in this pass.
## Q. ACL-TTL overhead via heartbeat ticks — instrument found blind to the effect, 2026-08-20/21
> **CORRECTED by Section R, 2026-08-21 — this section's diagnosis of the instrument was wrong,
> and its choice of instrument was also wrong.** Two separate mistakes, found in sequence:
> (1) this section swapped `HEARTBEAT-TICKS@` to read `heartbeat_ticks()` — a newer, kernel-only
> ISR hardware-timer counter in `src/starkernel/heartbeat.c` (the M5 TIME-TRUST engine) — instead
> of `vm->heartbeat.tick_count`, the actual year-plus-old "Adaptive Heartrate" Loop #7 this
> project's "one clock" law refers to; that swap was reverted. (2) Once reverted, the pilot still
> showed byte-identical tick deltas — not because the counter is blind to per-dispatch cost (the
> "root cause" below is wrong), but because the pilot inherited Section P's exact bug: ACL was
> never actually enforced (see Section P's invalidation note and Section R). Left below as
> historical record of the (wrong) reasoning at the time; see Section R for the real fix and the
> real result. `HEARTBEAT-TICKS@` itself is fine as committed after the revert (`vm->heartbeat.
> tick_count`, three-arch accepted in Section R) — only this section's *conclusions* are wrong.
Section P's wall-clock methodology was flagged by Captain Bob as a direct violation of
established project law: **the adaptive heartbeat tick counter is the one and only clock**,
not host wall-clock. Correcting for this required a new read-only accessor and a re-run —
@@ -2267,3 +2291,99 @@ Candidates for a follow-up, not undertaken here: (a) a separate counter incremen
`acl_recheck()` itself (would directly count ACL work, trivially showing 0 when disabled), or
(b) instrumenting at the CPU-cycle level (`rdtsc`/equivalent) around the word-dispatch
primitive rather than at the colon-word-entry granularity.
---
## R. ACL-TTL overhead — the real bug, the real fix, the real (small) result, 2026-08-21
Two prior sections (P, Q) both measured zero-or-noise for ACL enforcement cost. Neither
correctly measured ACL enforcement cost, because neither ever actually turned it on. This
section found and fixed the real bug, corrected the instrument back to the right counter, and
produced the first result in this whole investigation where ACL enforcement was genuinely
active during the measurement window.
**Captain Bob's correction, restated precisely.** "The adaptive heartbeat is the one clock" —
confirmed to mean `vm->heartbeat.tick_count`, Loop #7 "Adaptive Heartrate" in `HeartbeatState`
(`vm_runtime.c`), the year-plus-old, foundational counter the whole physics runtime (Loops
#17, L8 Jacquard) is built on. Section Q's swap to `heartbeat_ticks()` (a different, newer,
kernel-only ISR hardware-timer counter in `src/starkernel/heartbeat.c`, the M5 TIME-TRUST
engine — unrelated subsystem, unrelated age) was a real mistake, made without confirming which
counter the law actually referred to. Reverted: `starforth_word_heartbeat_ticks` now pushes
`vm->heartbeat.tick_count` again, unconditionally (no `__STARKERNEL__` split needed — this
field exists identically on both builds). The now-irrelevant `HEARTBEAT-PERIOD-NS@` accessor
and its `starkernel/timer.h` include were removed. Three-arch QEMU re-acceptance (amd64/
aarch64/riscv64): POST 1012/0/0 on each, `HEARTBEAT-TICKS@` live-tested returning the correct
`vm->heartbeat.tick_count` value on all three. (`4076a01`, the commit that introduced the
`heartbeat_ticks()` swap, is itself superseded by this section's revert commit.)
**The real bug.** `src/starkernel/repl.c:303`: `vm->emergency_console = is_hera ?
(vm->zuse_session ? 0 : 1) : 0;` — at the bare `ok>` prompt, `emergency_console` stays `1`
until a `zuse` session is authenticated. `src/starkernel/vm/vm_core.c:755`:
`if (w && !vm->emergency_console) { ... }` — the entire ACL check block (hot-path TTL
decrement *and* cold-path `acl_recheck()`) is skipped whenever `emergency_console` is set.
Every "ACL enabled" measurement in this whole session (Section P's 18 cells, Section Q's
pilot) loaded `ACL.4th`, confirmed `EXEC` was pinned STRICT via `ACL-MODE@` (a passive read of
static state), and then ran `EXEC-DOE` straight from the bare `ok>` prompt — never
authenticating `zuse`. ACL was configured but never armed. This is documented, intentional
behavior (the emergency-console bypass exists so a locked-out operator can always recover) —
the bug was in the test methodology, not the ACL subsystem.
**Why the documented self-boot didn't save this.** `capsules/zuse.4th`'s `ACL-ZUSE-BOOT` (the
capsule meant to call `ZUSE-AUTHENTICATE` automatically at ACL.4th load time) has the
pre-existing, previously-flagged bug where its self-pin line sits inside its own
still-compiling colon-definition body, so `ACL-ZUSE-BOOT` never successfully compiles and its
self-activation (`capsules/zuse.4th` block 4018) fails with `UNKNOWN WORD: 'ACL-ZUSE-BOOT'`.
Even a correctly-written test that trusted the capsule's automatic zuse activation would have
silently gotten the bare-console bypass. Not fixed here (still flagged only, per "report don't
fix without being asked" — this is a capsule bug, not something blocking the measurement).
**Workaround used:** `ZUSE-AUTHENTICATE` is directly registered as its own callable word
(`src/word_source/starforth_words.c`), so invoking it directly at the REPL — bypassing the
broken `ACL-ZUSE-BOOT` wrapper — sets `vm->zuse_session = 1` correctly. Confirmed this is a
real fix, not just a state flag with no effect: a 2-rep sanity check pre/post showed the tick
delta was no longer byte-identical to the no-ACL-loaded baseline (17565 vs. 17523 for
different seeds — not yet the clean paired comparison, but proof of a live, non-zero effect).
**Note on the console prompt.** `repl.c`'s own header comment (line 263) claims the prompt
switches to `"zuse)ok> "` when `zuse_session=1`; the actual prompt-printing code (`SK_PROMPT_TEXT`
at line 45, used unconditionally at lines 304 and 351) is a single fixed string with no
zuse-conditional variant anywhere in the file. This is a real, secondary, cosmetic-only doc/code
mismatch — the *security* gate (`emergency_console` assignment, line 303) works correctly
regardless; only the visual indicator is stale/unimplemented. Flagged, not fixed.
**Validated pilot: amd64, seed 12345, 30 reps (480/480 rows, 0 errors), same build, disabled
vs. genuinely-enabled** (`ACL.4th` loaded + `ZUSE-AUTHENTICATE` invoked directly), `HB-OFF`-
bracketed, tick deltas via the reverted `HEARTBEAT-TICKS@`:
| ACL state | before | after | delta |
|-----------|--------|-------|-------|
| disabled | 17600 | 278664 | **261064** |
| enabled (zuse-authenticated) | 17645 | 278826 | **261181** |
**Overhead: +117 ticks, +0.0448%.**
**Determinism check — the discriminator for whether this number means anything.** This
project's stated design property is 0.000% CV. Re-ran the disabled arm on a fresh boot of the
same build, identical warm-up shape (2-rep warm-up seed 99999, then the seed-12345/30-rep
measurement window): before-tick **17600** (exact match), after-tick **278664** (exact match),
delta **261064** (exact match, to the tick). The counter is fully deterministic on repeat.
**Therefore the 117-tick difference between arms is real, unambiguous signal, not run-to-run
scatter** — with the caveat that this is still n=1 per arm (one seed, one architecture); the
disabled arm's own reproducibility is now double-confirmed, the enabled arm's is not yet.
**Reconciliation.** +0.0448% is the same order of magnitude as the original ACL-RWT DoE
campaign recorded in `.claude/CLAUDE.md`: **+0.0054%+0.0088% across 9 cells, CV = 0.000%** —
also heartbeat-tick-based. Section P's +5.30%/+4.42% wall-clock numbers were never the right
thing to reconcile against (see P's invalidation note); they measured something else (dictionary
load + boot-time stamping cost + host jitter), not enforcement. `tick_count` only advances on
colon-word entry (`execute_colon_word`, gated at `HEARTBEAT_CHECK_FREQUENCY`); the hot-path TTL
decrement (`w->acl_ttl--`) adds real instructions but zero colon-word entries, contributing
exactly zero ticks by construction — so the 117 measured ticks are attributable to the handful
of cold-path `acl_recheck()` calls that fired when TTL hit 0, each running `ACL-RECHECK`'s body
through a real `execute_colon_word` pass. Not independently attributed via `' DOE-WORK ACL-TTL@`
in this pass (identified as a useful follow-up, not done).
**Status: validated methodology, one real data point, not yet a campaign.** n=1 per arm, one
architecture, one seed. Determinism confirmed for the disabled arm only. Before treating
+0.0448% as a project-wide ACL-TTL overhead figure, the same 9-(arch,seed)-cell × 2-state design
as Section P should be re-run with the zuse-authentication fix included throughout — this was
not undertaken in this pass; scoped as the natural next step.
+1 -1
View File
@@ -1,5 +1,5 @@
# Capsule Block Manifest — Auto-generated
<!-- Generated by mkcapsule --manifest 2026-08-21T13:31:45Z -->
<!-- Generated by mkcapsule --manifest 2026-08-21T15:21:26Z -->
<!-- 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.
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
+3 -13
View File
@@ -47,7 +47,6 @@
#include "../../include/vm.h"
#ifdef __STARKERNEL__
#include "starkernel/vm/arena.h"
#include "starkernel/timer.h"
#endif
#include "include/vocabulary_words.h"
#include "../../include/version.h"
@@ -798,23 +797,14 @@ static void starforth_word_zuse_authenticate(VM *vm)
* @brief Read-only accessor for the canonical heartbeat tick counter
*
* Stack effect: ( -- n )
* On the kernel build, pushes heartbeat_ticks() -- the ISR-driven hardware
* timer tick count (src/starkernel/heartbeat.c), the one real clock in this
* project. NOT vm->heartbeat.tick_count, which is a FORTH colon-word-dispatch
* counter gated at a fixed cadence -- frozen during idle time and blind to
* any cost that doesn't change control flow (see FABRIC-2.md Section Q).
* On the hosted build (no ISR timer), falls back to vm->heartbeat.tick_count
* since that's the only tick source available there.
* Read-only: no corresponding store word exists or should exist.
* Pushes vm->heartbeat.tick_count -- Loop #7 "Adaptive Heartrate", the one
* clock this project's timing measurements are supposed to read, not host
* wall-clock. Read-only: no corresponding store word exists or should exist.
* @param vm Pointer to the VM instance
*/
static void starforth_word_heartbeat_ticks(VM* vm)
{
#ifdef __STARKERNEL__
vm_push(vm, (cell_t)heartbeat_ticks());
#else
vm_push(vm, (cell_t)vm->heartbeat.tick_count);
#endif
}
/**