-O2 over -O1/-Og: no optimization level has a verified track record here,
so pick the one that actually solves the motivating problem (4.4g's
fb_scroll_rows() stall needs real loop batching -Og doesn't reliably do)
and matches what both sibling Makefiles already default to.
No LTO yet: deliberately deferred, not rejected -- stacking a second risky
change (LTO, which CLAUDE.md already documents has broken this exact
codebase before) on top of a first-ever optimization pass would make any
4.5e failure ambiguous between two causes. Isolate them.
No -DNDEBUG: checked, not assumed. Zero runtime assert() calls exist
anywhere in the kernel build -- the two "assert(" hits found are a
_Static_assert pair (compile-time, ungated regardless) and a comment. The
freestanding assert.h shim is itself unconditional and ignores NDEBUG too.
Nothing for the flag to affect; copying it by habit would have been exactly
the cargo-culting this item warned against.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Marks 4.5b done with the verification record (three log dirs, all reaching
[Hera] ok> at unchanged -O0). Routine artifacts from this session's
three-arch runs: capsules/BLOCK_MAP.md, disk/artemis.img, DOE CSVs, QEMU
serial logs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Written directly in ISR context on all three architectures
(heartbeat_tick(), heartbeat.c:163) and read directly by mainline
(heartbeat_ticks(), including the busy-wait at kernel_main.c:880) without
being volatile -- worked by accident at -O0, would be a real bug once the
kernel builds with optimization (item 4.5). Every other field in this
struct is mainline-only (heartbeat_service()'s deferred window/variance/
trust processing), so only this one field needed the qualifier.
Punch list item 4.5b complete.
Three-arch acceptance boot clean at unchanged -O0 (no behavior change
intended yet -- this is prep for enabling optimization, not the switch
itself).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Read every interrupt/exception vector handler on amd64, aarch64, and
riscv64, and every global or static variable each one touches directly or
through a called function, checked against its volatile declaration.
One confirmed hazard, matching what 4.5 already reported: TimeTrustState.
ticks is written in ISR context on all three architectures and read by
mainline (including a busy-wait) without being volatile.
Everything else checked out for one of three reasons, each verified by
reading the actual read/write sites rather than assumed: already correctly
volatile (g_sk_fault_word, g_spurious_count, g_plic_claim_count, the
heartbeat.c top/bottom-half handoff, i8042.c's ring buffer, virtio_input.c's
diagnostic counters); write-once during init then single-context for the
rest of boot, so never actually concurrent (each arch's timer-calibration
state, virtio_input.c's device-routing globals); or ISR-reachable only on
the fatal exception path, which halts the core permanently afterward so
there's no return to mainline to race with (console/framebuffer state).
Full findings recorded in FABRIC.md as 4.5a's inventory. No code changed --
investigation only, per the item's own scope.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Captain Bob 2026-08-11: no other item gets worked until 4.5a-4.5f (the
-O0-kernel fix) are done.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Captain Bob: the -O0-kernel finding (with its 4.5a-4.5f scoping) takes
priority over Artemis-last, so it moves up to 4.5 and Artemis moves down
to 4.6. Updated the three other places in this document that referenced
"the 4.5 Artemis boundary" by number to point at 4.6 instead, with a note
on the renumbering history for anyone reading those passages later.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Scoping only, per Captain Bob's explicit instruction -- nothing implemented.
Breaks the -O0-kernel finding into six sequential, individually-committable
items matching this document's §25.0 rule-1 discipline (same treatment 4.4
itself got before 4.4a onward):
4.6a full ISR-global volatile audit (investigation only), 4.6b fix whatever
4.6a finds (starting from the one already-confirmed TimeTrustState.ticks
hazard), 4.6c decide the actual -O flags and record the reasoning before
touching the Makefile, 4.6d apply them and get a clean three-arch build,
4.6e the real three-arch acceptance boot (a kernel that has only ever run
at -O0 has no track record at any other level), 4.6f retry 4.4g's reorder
now that optimization exists to make it viable.
Also flagged, not scoped: the ACL-RWT DoE campaign's overhead numbers were
all measured at -O0; nobody has asked whether they still hold once the
kernel builds differently.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Attempted 4.4g's console_fb_init() reorder twice this session: once bare,
once with the fb_scroll_rows() volatile fix applied. Both stalled boot
indefinitely (12,000+ lines logged, still running after 3 minutes vs. a
normal few-second boot) instead of completing. Root cause traced past the
scroll fix to Makefile.starkernel building the kernel at -O0 -- no
optimization flag has ever been configured there, verified against the
file's full git history (17 commits, only ever one unrelated host-tool -O2
line). Both the kernel's own vendored hosted Makefile and the standalone
StarForth repo's Makefile default to -O2 (up to -O3/-flto on faster
targets); Makefile.starkernel was written fresh for the bare-metal target
and never got that ladder.
Documented as new item 4.6: enabling optimization is not a safe drop-in
change on its own. Found one confirmed, isolated correctness hazard first --
TimeTrustState.ticks (timer.h:90) is written directly in ISR context on all
three architectures (heartbeat.c:163) and read directly by mainline
(heartbeat.c:200-202, including a busy-wait in kernel_main.c:880) without
being volatile, unlike every other ISR-shared global checked
(g_spurious_count, g_plic_claim_count, g_pending_counter/g_pending_valid/
g_adaptive_period_ns are all correctly volatile already). Reverted the
console_fb_init() reorder itself (uncommitted, so a plain git restore) --
4.4g stays open pending 4.6.
Both the reorder attempts' logs (stalled, never reached ok>) and this
session's routine three-arch artifacts (capsules/BLOCK_MAP.md, disk/
artemis.img, DOE CSV) are committed as audit trail per CLAUDE.md.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Found while investigating item 4.4g's boot stall (moving console_fb_init()
earlier caused a >3-minute hang scrolling the fleet-birth transcript). The
GOP framebuffer is mapped write-back RAM (vmm.c:350-363), not
cache-disabled MMIO with side effects, so there's no correctness reason for
the scroll/fill loops to force one un-batchable volatile access per pixel.
g_fb.base stays volatile for other call sites; this function now casts to a
plain pointer for its bulk copy only.
Confirmed this alone does not fix the 4.4g stall -- the kernel builds at
-O0 (no optimization flag anywhere in Makefile.starkernel), so nothing
here gets vectorized regardless of the qualifier. That's now documented as
its own item, FABRIC.md 4.6. Keeping this fix regardless: it's correct on
its own terms independent of 4.4g's outcome.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Three-arch acceptance boot (amd64/aarch64/riscv64) clean, all reaching
[Hera] ok>, plus amd64 screendump evidence for each item -- checking off
4.4c (console_fb_init wiring), 4.4d ([VMName] prefix reaching the
framebuffer), 4.4e (\r/\n fix), and 4.4f (glyph-flip fix, previous commit).
Screendump verification itself stays amd64-only pending future
aarch64/riscv64 screendump tooling; Captain Bob accepted that gap as
non-blocking for these checkboxes.
4.4g reframed from "anomaly, not root-caused" to a design decision: traced
the missing hundreds of lines of boot output to console_fb_init() running
after capsule_birth_mama() (which births the whole Tripod fleet and their
self-tests) rather than before it, so that output is serial-only by
construction -- not a scroll/CSI-parser bug. Left unchecked pending a call
on whether to reorder console_fb_init() earlier in boot.
Also commits routine artifacts from this session's three-arch runs:
capsules/BLOCK_MAP.md (regenerated manifest), disk/artemis.img (Artemis
writes on every boot), DOE CSVs, and the corresponding QEMU serial logs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
font_8x16_data stores each glyph's 16 scanline bytes bottom-to-top, not
top-to-bottom -- confirmed by hand-decoding 'A' (0x41) and 'T' (0x54): both
only produce their correct letterform when the row order is reversed.
fb_draw_glyph() mapped glyph row 0 to the cell's top pixel row, which was
wrong for this table. Flip the row-to-pixel mapping instead of touching the
4096-byte font data; every glyph now renders right-side up.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
fb_draw_orientation_test() was a one-time diagnostic for 4.3.1 (raw GOP
framebuffer wiring), already [x] done. With 4.4c's vt100 console now live,
the corner blocks just obscure real console output in every screendump.
Function definition left in framebuffer.c/framebuffer.h for future reuse;
only the boot-time call site is removed.
Also commits routine log/manifest housekeeping: capsules/BLOCK_MAP.md
(regenerated by mkcapsule on each build), the amd64 probe logs from before
today's outage, and the screendump logs + evidence screenshot from this
session's 4.4c/4.4d/4.4e verification.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
4.4c/4.4d's fix was screendump-verified on amd64: the framebuffer console
mechanism works and [VMName] genuinely reaches the screen for the first
time. Verifying that screendump surfaced three more issues, each given its
own item per SS25.0 rule 3 rather than folded in silently:
- 4.4e (fixed this session, see prior commit): console_putc() forwarded raw
'\n' to vt100_putc() without '\r', corrupting line-start columns.
- 4.4f (open, not root-caused): character glyphs render vertically flipped.
- 4.4g (open, not root-caused): most of the framebuffer stays blank despite
hundreds of lines of boot output that should have scrolled through it.
Confirmed via a second screendump after the 4.4e fix that 4.4f and 4.4g are
both independent of the \r/\n bug -- unchanged before and after.
Added evidence/ as a new tracked location for durable proof screenshots,
distinct from the existing gitignored fb/ (disposable local eyeballing,
per the 2026-08-07 decision to stop tracking it). Linked from the relevant
4.4c/4.4d/4.4e/4.4f/4.4g entries.
4.4c and 4.4d remain unchecked -- their Done-when criteria require a
three-arch screendump match against the serial log, not yet attempted on
aarch64/riscv64.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
console_putc() mirrored raw '\n' to vt100_putc() while raw_putc() (serial
path) auto-injects '\r' before '\n'. vt100_putc()'s '\n' handler only
increments the row, never resets the column, so every line after the first
started at whatever column the previous line ended on instead of column 0 --
confirmed by screendump as diagonal text scatter across the framebuffer.
Latent since this code was written; invisible until 4.4c turned the
framebuffer console on for the first time.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
console_fb_init() (which calls vt100_init()) was never called anywhere in the
boot sequence -- only raw fb_init() ran, so vt100_putc() no-op'd on every
call and no console output (REPL, POST, boot logs) ever reached the
framebuffer, only the corner orientation-test blocks. Replace the raw
fb_init() call in kernel_main_deep() with console_fb_init() so the
framebuffer console actually comes up (4.4c).
emit_prefix() also wrote the "[VMName] " bracket text via raw_putc() only
(serial), never vt100_putc(), so the bracketed VM name never reached the
screen even once the framebuffer console was live. Mirror console_putc()'s
existing serial/framebuffer split there too (4.4d).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Found while screendump-verifying the prefix fix (now 4.4d): kernel_main.c
deliberately never calls console_fb_init(), so vt100_init() never runs and
vt100_putc() no-ops on every call -- no console output has ever reached the
framebuffer, only fb_init()'s own corner self-test blocks. ROADMAP.md's
obsolete M8 section claimed this path was already live; that was never
actually screendump-verified.
Records Captain Bob's stated end goal (2026-08-11): framebuffer console and
serial console must be functional identically. 4.4c wires that in.
Only uncommitted items were renumbered; 4.4/4.4a/4.4b keep their IDs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Found while scoping color application (now 4.4d): console.c's emit_prefix()
writes [VMName] directly via raw_putc(), serial-only -- it never calls
vt100_putc(), so the bracketed VM name has never actually reached the
framebuffer despite 4.4a's serial-verified "regression: clean" note. Given
its own item (4.4c) per §25.0 rule 3 rather than folded in silently.
Only uncommitted items (old 4.4c-4.4n, none yet [x]) were renumbered;
4.4/4.4a/4.4b keep their IDs since they're already committed. Also fixed one
stale reference in §27.8's narrative that still pointed at the old
CANVAS-geometry item by its pre-breakdown ID (4.4b -> 4.4k).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.4b complete.
Investigation only, no code. draw_cursor_glyph() (vt100.c:125) already
draws through g_vt.fg/g_vt.bg via fb_draw_glyph(), and apply_sgr() already
parses true 24-bit-color SGR (ESC[38;2;R;G;Bm) -- color reaches the screen
today independent of which font renders the glyph. 4.4c can proceed without
waiting on 4.4d/4.4e.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.4a complete.
Removed the manually-built <Name>)ok>/zuse)ok> prompt suffix from
sk_repl_step()/sk_repl_run() -- console.c's console_putc() already emits a
"[VMName] " prefix at line-start, so the old code was double-printing the
name for non-Hera VMs ("[Hermes] Hermes)ok> "). Now prints only "ok> " and
lets the existing prefix supply the bracket. emergency_console/zuse_session
security semantics unchanged, display-only. Verified: all three
architectures boot live to "[Hera] ok>" (logs/20260811-073408 amd64,
logs/20260811-073448 aarch64, logs/20260811-073542 riscv64).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.4 complete.
Design-only, no code. Format [VM name] ok>; ok> reuses existing
FB_ANSI_PALETTE[14] bright cyan (0x55FFFF); [VM name] gets a fresh literal
orange (0xFFA500), since no orange exists in the ANSI palette. Verified: no
implementation, just literal values checked against the existing codebase
constants and written into FABRIC.md for 4.4a/4.4c to consume.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Expands the single 4.4 REPL item into a granular punch list matching the
4.3.5/4.3.6-series style: prompt format+color (4.4-4.4c), VT100 ANSI/SGR
engine retargeted onto TrueType (4.4d-4.4f), CANVAS geometry (4.4g-4.4k),
scrollback+toggle (4.4l-4.4m), and the blocked (user) prompt segment (4.4n).
Corrects the font_8x16/TTF handoff boundary: the embedded VT100 path renders
everything through and including POST (which runs after the capsule/VM
bootstrap, per CLAUDE.md), not just pre-bootstrap M1-M6 messages as
originally scoped -- TTF-TEXT takes over only once POST completes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
FABRIC.md: renumber Artemis-last boundary 4.4 -> 4.5, add new 4.4 REPL item
(console visual design scoped this session -- CANVAS is one shared region
above the REPL strip, 640x480 scroll box centered within it, stroke font,
ANSI color, VM-identity + ACL-identity prompt segments, ~1000-line
scrollback), linked to repl-mockup.png.
ROADMAP.md: mark M8 section OBSOLETE -- replaced by FABRIC.md 4.4, kept for
history only.
ttf_words.c: TTF-TEXT's y argument now Cartesian (origin bottom-left of the
physical framebuffer, y increasing upward) instead of raw framebuffer
coordinates, translated once via fb_height() - y. Verified via amd64
screendump (logs/20260811-070418/), text renders right-side-up with
increasing y moving up the screen.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Not implementation -- documentation-only. M8 is tracked outside Stadium
(FABRIC.md §27.5), so this scoping lives in docs/lithosananke/ROADMAP.md
rather than as new FABRIC.md 4.3.x items.
Read the code before scoping, not assumed: the REPL already works
interactively over serial (sk_repl_run(), repl.c) with working
backspace/echo, and its on-screen text already renders via the existing
VT100 console (vt100_init()/font_8x16.c) independent of both the stroke
font and TrueType work -- neither was ever a REPL-strip prerequisite.
What's actually missing: sk_readline()'s console_getc() is
serial-UART-only, never touches KEY-EVENT (4.3.5f); KEY/?TERMINAL
(io_words.c) are dead stubs (KEY calls a getchar() hardcoded to EOF in
shim.c, ?TERMINAL always returns false).
Resolved design decisions: minimal US-QWERTY scancode->ASCII layout
with shift-state tracking (KEY-EVENT carries no modifier state today);
merge keyboard into console_getc()'s poll with serial staying a
co-equal source, not replaced (the entire acceptance/DoE harness
injects over the serial socket -- breaking that breaks make qemu,
DOE_INJECT, and every screendump technique used throughout 4.3.x); wire
KEY/?TERMINAL to the same merged source for FORTH-79 compliance;
CANVAS viewport sizing last, since it's presentation not input
plumbing. Extended scancodes, autorepeat-as-character-repeat, arrow-key
history explicitly out of scope for the "minimal" bar.
FABRIC.md's existing "not yet scoped" notes (4.3.5f's landing-point
note, 4.3.7f's closing note) updated to point here rather than
duplicating the design content.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.7f complete. No code changes -- ttf.c/ttf.h/
ttf_words.c unchanged since 4.3.7e; this is verification only, same
posture as 4.3.6g.
Three-arch acceptance boot clean (standard make ARCH=<arch> clean qemu,
run in order, one at a time): all three reached [Hera] ok> cleanly,
logs committed under logs/20260810-231553(amd64)/231640(aarch64)/
231739(riscv64).
One screendump per architecture, identical TTF-TEXT command sequence
(S" Hi 4.3.7e!" 200 200 28 16777215 TTF-TEXT) via monitor-socket +
serial-injection, same pattern as 4.3.7c/e. All three show the string
rendered legibly with correct mixed-case/digit/punctuation glyphs and
identical monospace spacing.
TrueType rendering (4.3.7-4.3.7f) is now complete, adjunct to the
stroke font per §27.7 decision #4. Stopping here before scoping REPL
wiring (M8), per this item's own posture matching 4.3.6g.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.7e complete. New src/word_source/ttf_words.c
registers TTF-TEXT ( c-addr u x y size color -- ): lazily loads the v1
default font capsule + raster cache once, decodes UTF-8 (C
reimplementation mirroring capsules/fabric.4th's DECODE-UTF8 exactly),
looks up each glyph's cached bitmap, blits via fb_put_pixel, advances
the pen by the glyph's real hmtx advance width scaled to pixels.
Necessary plumbing: ttf_parse() now also locates hhea/hmtx, and
ttf_glyph_advance_width() reads a glyph's advance width -- required for
this item's own "proportional spacing correct" acceptance clause, no
advance-width data existed anywhere else in the parser. Verified in
tools/ttftest.c: A/a/0/space all read advance_width=600, correctly
uniform since JetBrainsMono-Regular.ttf is monospace.
(x,y) is raster pixel space (top-left origin, Y-down), deliberately not
the stroke font TEXT's Cartesian Y-up convention -- recorded explicitly
in ttf_words.h, not conflated.
Verified live, amd64, screendump: injected
S" Hi 4.3.7e!" 200 200 28 16777215 TTF-TEXT over a serial socket after
boot, no error, captured a screendump showing the string rendered
legibly with correct mixed-case/digit/punctuation glyphs and even
spacing. TTF-TEXT is this item's permanent deliverable, not a
throwaway probe. Compile-checked clean on all three architectures.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.7d complete. ttf_raster_cache_get() looks up
(font, codepoint, size_px) in a caller-owned fixed slot array, evicting
round-robin once full, rasterizing into a slot on a miss.
Verified live in tools/ttftest.c: an identical (font, 'A', 24px) call
made twice returns was_hit=0 then was_hit=1, and the slot's own hits
counter reads exactly 1 afterward -- checked programmatically. A
different-codepoint call misses again, proving the key actually
discriminates. Wall-clock timing (miss 0.040ms vs hit 0.001ms) is
printed as informational corroboration only, not the load-bearing
check.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.7c complete. ttf_rasterize_glyph() flattens
quadratic-Bezier contours (fixed 8-segment subdivision, matching
CIRCLE/ELLIPSE's fixed-segment precedent) and fills them into a
caller-supplied bitmap via even-odd scanline fill, no AA. A local
signed Q48.16 multiply (q48_smul) handles negative outline coordinates,
since the shared q48_mul/q48_div are unsigned-only.
Verified two ways: tools/ttftest.c's ASCII-art dump + structural checks
for 'A'/'.'/'a', all recognizable and passing; and a live amd64
screendump via a throwaway TTF-PROBE word (loaded the font capsule,
rasterized 'A', blit via fb_put_pixel), showing a clearly legible 'A'
on the CANVAS -- probe reverted immediately after capture, only the
permanent ttf.c/ttf.h rasterizer remains. Compile-checked clean on all
three architectures (hal/*.c wildcard); this item's own acceptance is
the amd64 screendump, not a three-arch boot (that's 4.3.7f).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
mkcapsule.c already ingests arbitrary non-.4th files as raw byte blobs
(validate_forth_blocks only applies to .4th filenames), so no hex/base64
text-encoding or tool changes are needed -- corrects the design premise
in FABRIC.md's 4.3.7b item text (see the FABRIC.md correction note this
commit carries). ttf_load_from_capsule() resolves the font capsule by
name, validates its content hash, and points ttf_font_t at the capsule
arena bytes directly, zero-copy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
ttf_glyph_outline() decodes simple-glyph flag/coordinate runs and
recursively resolves composite components into a caller-supplied point/
contour-end buffer, in Q48.16. Composite scale/rotation/skew transforms
are rejected with TTF_ERR_UNSUPPORTED rather than mis-rendered, since the
shared q48_mul/q48_div are unsigned-only; translation-only composites
(the only kind the v1 glyph repertoire uses) apply cleanly via q48_add.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Freestanding C module resolving a Unicode codepoint (cmap format 4) to a
glyph index and its outline header (contour count, bounding box), verified
against an independent from-scratch Python reference reader via
tools/ttftest.c. Not yet wired into the boot path or capsule system.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.6g complete. Fixed the cross-arch bug found
while first attempting this checkpoint: aarch64 and riscv64 both boot
an 800x600 ramfb device (confirmed empirically for both, not assumed),
against amd64's 1280x800 GOP framebuffer -- the original verification
grid's Y coordinates ran off the top of the smaller screens. Rebuilt
the grid to fit 800x600, safe on amd64's larger screen too.
All 113 glyphs confirmed legible and correctly positioned on all three
architectures, one screendump each, from the identical FORTH command
sequence. No code changes -- font.4th/fabric.4th are unchanged since
4.3.6f; this is verification only.
Three-arch acceptance boot clean, Stadium conservation unchanged.
Stroke font (4.3.6-4.3.6g) is now complete. Per this item's own
posture, stopping here before scoping REPL wiring (M8) -- 4.3.7
(TrueType, adjunct) is the scoped-but-not-started next work.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Not implementation -- documentation-only. Reviewing the 4.3.6 stroke
font's full 113-glyph repertoire (mid-4.3.6g checkpoint) prompted the
decision to add TrueType rendering as an adjunct path rather than
continue refining strokes alone. Stroke font stays; nothing reverted.
Adds 4.3.7-4.3.7f (parser core, outline extraction, font data
ingestion, rasterization, glyph cache, TTF-TEXT entry point,
three-arch checkpoint) plus §27.7 documenting four resolved design
decisions: C implementation layer, Q48.16 fixed-point (not float --
confirmed real via Makefile.starkernel grep that GAP-B2's hypothesized
FP-register-clobber-on-interrupt risk is real on all three
architectures, not just hypothetical), capsule-block-encoded font
data, and TrueType as the primary text path once built.
4.3.6g itself stays open -- paused mid-verification when a real
cross-arch framebuffer-resolution difference (aarch64 800x600 ramfb
vs amd64 1280x800 GOP) surfaced; not yet re-run with corrected
coordinates.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.6f complete. TEXT ( c-addr u x y size color -- )
walks a UTF-8 byte string, decoding one codepoint at a time via
DECODE-UTF8, drawing each via DRAW-GLYPH, and accumulating the cursor
X by the glyph's em-advance scaled to pixels. Uses 2>R/2R> to stash
the DECODE-UTF8 remainder off the data stack while DRAW-GLYPH's args
are pushed.
Defined in font.4th, not fabric.4th, so its compiled DRAW-GLYPH call
binds to the real definition, not fabric.4th's 4.3.6b placeholder --
same early-binding constraint as DISPATCH-GLYPH itself.
Verified live: a string mixing ASCII, Latin-1 (degree sign), and
General Punctuation (em dash) renders correctly in one TEXT call with
proportional spacing, no overlap.
Three-arch acceptance boot clean, Stadium conservation unchanged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.6e complete. Optional capsule, not wired into
the boot chain, overriding DISPATCH-UPPER's codepoint 65 (G-A) with a
marked alternate glyph while every other letter falls through to
font.4th's originals unchanged.
Corrects §27.6.5: redefining the DISPATCH-* bucket word alone is not
enough, since DISPATCH-GLYPH/DRAW-GLYPH are themselves compiled early
against font.4th's own dispatch words (same early-binding finding as
4.3.6c, one layer up). Verified both ways: the partial override (bucket
word only) left DRAW-GLYPH still drawing the original glyph; redefining
DISPATCH-GLYPH/DRAW-GLYPH too made the override take effect.
Three-arch acceptance boot clean, Stadium conservation unchanged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.6d complete. Both capsules now EXEC via
Hera's Mama IDENTITY capsule (init.4th block 2049), loading on every
normal boot instead of manual serial injection. Verified by booting
with zero injected commands and calling DRAW-GLYPH directly.
Three-arch acceptance boot clean, Stadium conservation unchanged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.6c complete. Digits, upper/lowercase, ASCII
punctuation, Latin-1 Supplement, and General Punctuation, each a
stroke-drawing word in the 4.3.6 em-square convention.
Corrected mid-build: the first pass used G-LINE only, missing that
CIRCLE/ARC/ELLIPSE already existed (4.3.3b). Reworked all round shapes
to use them via new em-square-aware wrappers (G-CIRCLE/G-ARC/G-ELLIPSE/
EM-R, fabric.4th 5000-5002), bounded to quarter/half/three-quarter/
full sweeps to avoid unbounded per-glyph angle tuning. font.4th also
redefines DISPATCH-GLYPH/DRAW-GLYPH themselves, not just the six
DISPATCH-* bucket words, since fabric.4th's originals were compiled
early against the 4.3.6b placeholders (CASE early-binding, §27.6.5).
Three-arch acceptance boot clean, Stadium conservation unchanged.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.6b complete.
capsules/fabric.4th blocks 4921-4924: DISPATCH-GLYPH routing via
WITHIN to six bucket words (DISPATCH-DIGIT/-UPPER/-LOWER/
-ASCII-PUNCT/-LATIN1/-GENPUNCT), TOFU fallback, DRAW-GLYPH. Buckets
carry one placeholder stroke word each (G-TEST-*), not the real
113-glyph set -- that's item 4.3.6c's scope, deliberately deferred.
Also: merged two lines in block 4920 (DECODE-UTF8) to fit mkcapsule's
real 64-char x 16-line block limit once a trailing blank separator
line is counted against it -- mechanical reformat, re-verified via a
DECODE-UTF8 regression check (65/176/8212, unchanged).
Verified live on amd64: one representative codepoint per bucket plus
one out-of-range codepoint, all seven DRAW-GLYPH results matched
expected exactly (400/600/450/250/550/700/500-TOFU). Three-arch
acceptance boot clean, Stadium conservation unaffected.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.6a complete.
capsules/fabric.4th blocks 4918-4920: UTF8-SEQ-LEN/UTF8-CONT? verbatim
from §27.6.2, DECODE-UTF8 dispatching to four UTF8-ASSEMBLE-N helpers
(1/2/3/4-byte). Factored into helpers after the single-word draft hit
mkcapsule's real 64-char x 16-line block limit. Verified live on
amd64: a 6-byte test buffer (ASCII + deg-sign + em-dash) decoded to
65/176/8212 across three sequential calls, exact match against
hand-computed codepoints, buffer fully consumed. Three-arch acceptance
boot clean, Stadium conservation unaffected.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.6 complete.
capsules/fabric.4th blocks 4916-4917: EM-UNITS, GOX/GOY/GSIZE/GCOLOR,
EM-X/EM-Y/G-LINE per §27.6.1, scaling/translating em-square strokes
into CART-PLOT screen coordinates via */. Verified live on amd64 via
a temporary probe (built, run once, reverted): two G-LINE test shapes
at GSIZE 100 and GSIZE 50, one leg each exercising a negative em-y
value chosen to hit */'s truncate-toward-zero behaviour, not a
multiple of EM-UNITS. Screendump pixel-bbox extraction matched
hand-calculated raster coordinates exactly on both shapes. Three-arch
acceptance boot clean, Stadium conservation unaffected
(resident_sum=43691 reservoir=21845 sum=65536 on all three).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CaptainBob.png: untracked repo-root image, added per Captain Bob's
direction.
logs/20260808-002843/riscv64/: a riscv64 boot log from ~00:28, minutes
after the committed 4.3.5 acceptance boots. Not cited anywhere in
FABRIC.md. It ends in the scause=0xF store-page-fault at 0x0c201000
that item 4.3.5a later formally traced to riscv64 having no
software-controlled paging -- an earlier, informal hit of the same
bug, predating 4.3.5a's own cited diagnostic logs (10:19am+).
Committed for the audit record per this repo's "logs are committed
artifacts" convention, superseded by but not duplicating 4.3.5a's
own logs.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Expands the previously sparse \xc2\xa727.6 to match the derivation depth of
\xc2\xa727.3/\xc2\xa727.5.1/\xc2\xa727.5.2 -- worked line items, not just prose scoping.
Every primitive used (*/, WITHIN, CASE/OF/ENDOF, CREATE, EXECUTE, ',
LSHIFT/RSHIFT/AND/OR) was confirmed against src/word_source/*.c before
being used in a formula; no $/0x hex literal syntax exists in this tree,
so all formulas use decimal only.
Concrete design landed, in order derived:
- EM-UNITS 1000, standard Type1/OpenType em-square proportions; EM-X/
EM-Y/G-LINE scale-and-translate into CART-PLOT space via */ (noting
*/'s "avoid overflow" rationale is vestigial at this VM's 64-bit
cell_t width -- not a bug, just an observation)
- UTF-8 decoder: full bit-level 1/2/3/4-byte assembly formulas. Found
mid-derivation that the 3-byte path is v1-required, not future-i18n-
only -- smart quotes/dashes/ellipsis are Unicode General Punctuation
(U+2000-206F), NOT Latin-1 Supplement despite the original framing
lumping them together
- Dispatch: bucketed CASE/OF/ENDOF chains (DISPATCH-DIGIT/-UPPER/-LOWER/
-ASCII-PUNCT/-LATIN1/-GENPUNCT) routed by WITHIN range checks, chosen
over a flat 256-entry xt-table despite the table composing more
cleanly with override -- explicit tradeoff, not an oversight
- Glyph-word contract ( -- em-advance ), TOFU placeholder for misses,
~8-10 block budget estimate for dispatch alone
- Character list corrected and finalized: 113 glyphs (95 ASCII + 11
Latin-1 Supplement + 7 General Punctuation), itemized
- User font override: found CASE's early binding means redefining a
single glyph word does NOT retroactively change an already-compiled
DISPATCH-* caller (same binding behavior 4.3.3b found for CART-PLOT).
Resolved: override replaces a whole DISPATCH-* bucket word, not
individual glyphs -- explicit granularity tradeoff for keeping CASE
- TEXT entry point sketched at algorithm level
DECODE-UTF8's exact byte-consumption stack mechanics and TEXT's loop
are deliberately left as algorithm-level spec, not final FORTH -- this
project's history (VARIABLE alignment, Q.TO-INT sign bug) shows exactly
this class of stack/precision bug surfaces only under live testing, not
design review; presenting untested stack-juggling code as settled would
be the "confidently wrong" mistake \xc2\xa725.0 rule 4 warns against.
Punch-list items 4.3.6-4.3.6g (\xc2\xa725.5) updated to match: concrete Done-
when criteria replacing several that were previously vague or TBD
(4.3.6b now names the six buckets; 4.3.6c names the 113-glyph list;
4.3.6e goes from "TBD" to a real, testable override-mechanism
criterion; 4.3.6f requires proportional-width verification). Still no
implementation -- scoping and derivation only, per \xc2\xa725.0 rule 2.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list \xc2\xa725.5 additions (4.3.6-4.3.6g, unchecked) and new \xc2\xa727.6
rationale section, scoped per the 2026-08-07 sequencing note. Captures
requirements dictated this session: UTF-8-aware decoding, ASCII + Latin-1
typographic repertoire for v1, i18n-ready codepoint-indexed design, one
capsule per font-set (not per glyph), a default system font wired into
init.4th's boot chain as early as the capsule/VM bootstrap allows, with
user-font-override support -- additive to, not replacing, the existing
font_8x16.c VT100 console path. Flags two open items found while scoping
rather than deciding them unilaterally: an unused encoding field on
block_subsystem.h, and the open call on which capsule file eventually
houses the glyph data. No implementation yet -- scoping only, per \xc2\xa725.0
rule 2.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Background-session edits were being forced into an isolated git worktree,
defaulting to a branch off origin/master -- which has none of this
branch's FABRIC.md content -- conflicting with this repo's subversion-
style workflow (commit directly to the working branch, no side branches
without explicit permission). Sets worktree.bgIsolation: none so
background-session edits land directly in the shared checkout, consistent
with the existing CLAUDE.md policy.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.5f complete. Keyboard-input slice done.
New KEY-EVENT ( -- keycode pressed -1 | 0 ) converges amd64's i8042,
riscv64's and aarch64's virtio-keyboard-pci behind one shape. Translation
needed is minimal: XT Set-1 make codes and Linux input keycodes are
numerically identical across the standard 84-key block (a documented
historical property of the Linux input subsystem, confirmed against this
host's own headers and this tree's own prior live observations), so amd64
needs two lines, riscv64/aarch64 need none. Verified live with a real
keypress on all three architectures: identical "-1 1 30" output everywhere.
Three-arch acceptance boot clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.5e complete.
Extended virtio_input.c with a GIC-routed interrupt path alongside 4.3.5c's
PLIC one -- same capability walk, feature negotiation, eventq handling
(confirming §27.5.1's prediction that these items would share most of the
driver). aarch64_irq_handler() dispatches to virtio_input_isr() before its
EOIR write, same claim-dispatch-complete ordering riscv64 uses. Verified
with a real QEMU sendkey keypress: exact KEY_A/press match, two real
interrupts serviced, identical result to riscv64. Found (not fixed) an
unrelated pre-existing bug: BYE's cold-reset path faults on aarch64,
discovered incidentally since nobody had exercised it from a monitored
session before. Three-arch acceptance boot clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.5d complete.
New apic_spi_enable(intid) generalizes item 0.6's PPI-only sequence to one
explicit SPI (IPRIORITYR/ISENABLER/ITARGETSR, ICFGR read-checked not
written). Verified via a software-pended SPI (GICD_ISPENDR, no device
needed) through the existing generic IRQ dispatch, which needed no changes.
Found and fixed a real bug during verification: PSTATE.I is still set at
apic_init()'s point in boot, so the first self-test run correctly latched
but never took the interrupt. Self-test code reverted after capturing
evidence -- interrupts.c has zero net diff, only apic_spi_enable() remains,
unused until 4.3.5e. Three-arch acceptance boot clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.5c complete.
Amended from a nonexistent MMIO transport to PCI (matching the board's
actual virtio-blk-pci precedent). New virtio-input driver: eventq with
pre-posted buffers, PLIC source computed at runtime from PCI slot/pin
(derived live from this host's QEMU riscv64 DTB), mandatory ISR-status
read, PCI interrupt-disable-bit check. New VKBD-EVENT/VKBD-DEBUG FORTH
words. Verified with a real QEMU sendkey keypress: exact KEY_A/press
match, two real interrupts serviced, zero exceptions. Three-arch
acceptance boot clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.5b complete.
sie.SEIE enabled, PLIC threshold/claim/complete wired into the trap
handler. Verified with a UART-loopback synthetic interrupt (PLIC has no
software set-pending register, unlike GICv2): claim_count=1, last_irq=10,
IIR confirms genuine receive-data cause, byte matched exactly. Self-test
code run once for evidence then fully reverted, per Captain Bob's ruling;
only the permanent substrate remains, no source enabled by default.
Three-arch acceptance boot clean, zero exceptions.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Punch list §25 item 4.3.5a complete.
Firmware (EDK2 RISC-V) leaves Sv57 paging active at kernel entry with an
identity mapping that has at least one confirmed hole (PLIC threshold
register). Verified live before acting: satp.MODE/PPN and __kernel_start's
address confirmed identity mapping; switched to Bare mode (csrw satp, x0 +
sfence.vma) in arch_early_init(), ahead of pmm_init()/vmm_init().
Three-arch acceptance boot clean, zero exceptions; riscv64's PLIC_THRESHOLD
write now survives (PLIC driver code itself lands separately with 4.3.5b).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Live-probed GICD_ITARGETSR before starting 4.3.5d/e; survived cleanly,
unlike riscv64's PLIC (4.3.5a). Decoded QEMU's own virt DTB for the PCI
slot->SPI interrupt-map routing table and closed-form formula. Amended
4.3.5d's circular acceptance criterion to a synthetic GICD_ISPENDR probe
(mirrors 4.3.5b's fix for the same problem), and wrote the full 4.3.5e
virtio-input execution plan: device ID 0x1052, the real virtio_input_event
struct, the mandatory ISR-status read, and the pci_enable() interrupt-
disable-bit gap.
Log: logs/20260808-093228/aarch64/.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>