FENCE ( -- ) exposes the dict_fence_latest/dict_fence_here state FORGET
already honored internally, letting callers (e.g. a future SDK capsule)
raise the boundary after loading their own content -- no new VM fields,
no policy logic beyond exposing existing state.
Writing a direct test for it surfaced a real, severe, pre-existing bug in
FORGET's relink logic, unrelated to FENCE itself and reproducible with the
original boot-time fence alone:
- Forgetting the single newest word incorrectly destroyed every other word
back to the fence too, not just the target.
- Forgetting an older word (correctly cascading to remove newer words too,
per FORTH-79 semantics) crashed with SIGSEGV.
Root cause: the relink code's target_prev pointer was, by construction,
always inside the range the preceding loop had just freed whenever target
wasn't vm->latest -- so writing through it was a use-after-free every time
that branch executed. Fixed by removing the target_prev tracking and both
branches entirely; vm->latest unconditionally becomes target_next (target's
own captured, still-valid link) after the free loop, correct in every case.
Added a FENCE test suite to dictionary_manipulation_words_test.c (Module 14)
including the exact regression case (forgetting the newest word must not
disturb an older one). Verified zero warnings and identical POST/dict_hash
results across all three kernel architectures.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Cluster 4 of the POST-coverage sweep: physics_freeze_words_test.c covers the 6
words proof/StarForth_Physics_Freeze_Words.thy actually gives real lemmas for
(FREEZE-WORD, UNFREEZE-WORD, FROZEN?, HEAT!, HEAT@, DECAY-RATE@), correcting
an earlier fork summary's wrong "5 words" scope.
Writing the tests surfaced two independent, pre-existing bugs in
physics_freeze_words.c, both now fixed:
- Every address-taking word cast the VM's caddr directly to a host pointer
instead of resolving it through vm_ptr() -- caddr is an offset into
vm->memory, not a host pointer. Fixed in all 9 call sites (the 5 in-scope
words plus SHOW-HEAT, which shares the identical pattern).
- Every underflow check used dsp < N (item count) instead of dsp < N-1, since
this VM's dsp is a 0-indexed top-of-stack pointer. Fixed in all 6 checks.
Together these meant every word in this file taking a stack-supplied name has
been broken for any real caller since the file was written. Verified zero
build warnings and a clean three-arch QEMU boot (amd64/aarch64/riscv64), 1009
passed / 0 failed / 0 errors identically on all three, dict_hash matching
across arches.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New module (inference_words_test.c, Module 26) covers exactly the 8
words proof/COVERAGE.md marks proof-covered in inference_words.c (out
of 20 registered): the 5 output accessors (INFER-WINDOW@/DECAY@/
VARIANCE@/FIT@/EARLY-EXIT@), INFER-RUN (populates what they read), and
Q.VARIANCE/INFER-DECAY-SLOPE/INFER-WINDOW-WIDTH (array-based
primitives, using HERE as multi-cell scratch memory). Deliberately not
the L8 Jacquard or Bayesian-posterior words in the same file -- not
proof-covered, out of this cluster's scope.
Caught and fixed a contract-selection mistake before booting: copied
CONTRACT_PHYSICS_TRANSPARENT from the Q48.16 cluster without checking
whether it fit. It doesn't -- these words are specifically about
reading physics state (dictionary heat, rolling window), so asserting
A4' transparency on them would test an invariant they deliberately
don't have. Switched to CONTRACT_NONE with an explanatory comment.
Boot-verified: zero warnings, all 9 suite entries pass, FINAL TEST
SUMMARY 1031->1040 total / 993->1002 passed (+9 exactly), 0 failed,
contract checks (A4'/A1) still report "all passed" -- confirms the
CONTRACT_NONE fix actually avoided the violation, not just silenced it.
Cluster 4 of 4 (final one) left: physics freeze/diagnostic, 5 words.
Full writeup in FABRIC-2.md Section J.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New module (q48_words_test.c, Module 25 -- matches word_registry.c's own
existing numbering for this file's registration) covers all 23 words in
q48_words.c: no test file existed for this file at all before. Standard
WordTestSuite/TestCase tabular format, unlike ACL's hand-rolled style --
these are pure stateless functions, a natural fit. 28 TestCase entries;
values built via Q.FROM-INT/Q.1/Q.0, read back via Q.TO-INT for readable
log output.
Verified q48_16.h's q48_to_u64() sign-extends through a signed int64_t
intermediate before writing the Q.NEG/Q.ABS tests, rather than assuming
negative round-trip works.
Boot-verified: zero build warnings, all 23 words pass individually,
FINAL TEST SUMMARY 1003->1031 total / 965->993 passed (+28 exactly),
0 failed, 0 errors. Noted (pre-existing, not fixed): print_module_summary()
is called with hardcoded (name,0,0,0,0) across every WordTestSuite module
in the tree, including this new one -- decorative, always zero; the real
counts live in each word's own per-suite line and the global summary.
Cluster 3 of 4 in the POST-coverage sequence (code sweeps -> HOL green ->
POST coverage, one proof-covered cluster at a time). Two clusters left:
inference-engine accessors, physics freeze/diagnostic. Full writeup in
FABRIC-2.md Section J.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds interpreter-level POST coverage for six ACL read accessors
(ACL-MODE@/PINNED?/TTL@/ALLOW@/HEAT@/WORD-ID), ACL-INHERIT as an
interpreted word (not just its underlying C function, already tested),
and ACL-INIT-PRIMITIVES -- all proof-covered per proof/COVERAGE.md but
never exercised via vm_interpret() before. Follows acl_words_test.c's
existing hand-rolled ACL_ASSERT style, not the WordTestSuite table
format the rest of the tree uses.
First boot caught a real bug in the new test itself (2/29 assertions
failed): ACL-INHERIT's C implementation pops dst before src, the test
pushed them backwards. Fixed the test, not the word -- ACL-INHERIT's
own dispatch was correct throughout. Re-verified: 29/29 pass, zero
build warnings. Both the failing and fixed boot logs kept as evidence.
Part of the agreed sequence (code sweeps -> HOL green -> POST coverage,
one proof-covered cluster at a time). Three more clusters queued:
Q48.16 math primitives, inference-engine accessors, physics
freeze/diagnostic words. Full writeup in FABRIC-2.md Section J.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Maintainability sweep (prompted by "this is getting hard to maintain"):
fixed the remaining three warning classes after the missing-field-
initializers commit -- 2x -Wsign-compare (control_words.c, cast at the
comparison site rather than changing cf_last_mode's type, which
deliberately holds a -999 sentinel outside vm_mode_t's valid range),
2x -Wstringop-truncation (mkcapsule.c, strncpy+manual-null-terminate
replaced with the idiomatic snprintf equivalent), and 26x
-Wunused-parameter (mostly documented stubs, silenced with the repo's
existing (void)param; idiom).
One of the unused-parameter warnings was not a deliberate stub -- a
real bug. restore_vm_state() (test_common.c) is named, documented, and
called by nine real call sites (acl_words_test.c x8 plus its own
internal use) as "restore saved VM state", but ignored all four of its
parameters and hard-reset to a fixed baseline instead, silently not
restoring what any caller actually saved. Fixed to actually assign the
passed-in dsp/rsp/error/mode. Found while fixing warnings, reported
before touching it, fixed/tested/documented/committed on explicit
instruction.
Verified: all three architectures build with zero C-compiler warnings
(amd64: 3040 -> 0; aarch64's one remaining note is lld-link's own
unrelated linker warning, not a C warning). Full amd64 acceptance boot
post-fix: POST 1003/965/0/0/38 (total/passed/failed/errors/stubs),
"ALL IMPLEMENTED TESTS PASSED!", contract checks (A4'/A1) all passed,
dict_hash=0x24b4279f0670aa3a -- an exact match to this document's own
previously-recorded baseline hash.
.claude/CLAUDE.md corrected to describe the real -Wno-error= exemption
list instead of the "-Wall -Werror" oversimplification. FABRIC-2.md
Section J records the full sweep, including doc-tree staleness findings
flagged but not fixed this pass (docs/lithosananke/ROADMAP.md branch
topology, docs/03-architecture/word-acl/DESIGN.md's Phase 7 claim
contradicting CLAUDE.md, top-level ROADMAP.md's stale StarForth-era
status, the Isabelle pipeline-metrics model mismatch).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
TestCase gained a trailing `contract` field (WordContract) at some point
after all 20 test-module files' compound literals were written -- every
single TestCase/WordTestSuite initializer in the tree (sentinels, real
entries, and per-suite entries) omitted it, producing ~3010 warnings on
every build. CLAUDE.md's own documentation claimed this was isolated to
one file (vocabulary_words_test.c); a full audit found it systemic
across all 20 files.
Fixed mechanically: added the missing `{0}` trailing initializer
everywhere. Semantically a no-op -- C99 already zero-fills unlisted
trailing struct fields, so this only silences the diagnostic, changes
no behavior. Verified: all three architectures (amd64/aarch64/riscv64)
build clean, remaining warning count unchanged (30, matching the other
three known -Wno-error-exempted classes: unused-parameter, sign-compare,
plus mkcapsule.c's stringop-truncation which was never actually gated
by this policy -- it's a separate host tool with no -Werror at all).
.claude/CLAUDE.md corrected to describe the actual -Wno-error= exemption
list (four classes, not "build with -Wall -Werror" unconditionally) and
the real current warning inventory.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>