17 KiB
StarForth Developer Guide
Development Environment Setup
Required Tools
Core Build Tools
sudo apt-get update
sudo apt-get install build-essential gcc make git
Optional Tools
- Doxygen - For API documentation generation
- syft - For SBOM generation
- fpm - For package building (DEB/RPM)
# Doxygen
sudo apt-get install doxygen
# syft
wget https://github.com/anchore/syft/releases/latest/download/syft_linux_amd64.tar.gz
tar -xzf syft_linux_amd64.tar.gz
sudo mv syft /usr/local/bin/
# fpm (for packaging)
sudo apt-get install ruby-dev build-essential
sudo gem install fpm
Isabelle/HOL Installation
Isabelle is required for formal verification documentation generation.
Download & Install
- Download Isabelle2025 from https://isabelle.in.tum.de/
# Example installation to ~/bin/
cd ~/bin
wget https://isabelle.in.tum.de/dist/Isabelle2025_linux.tar.gz
tar -xzf Isabelle2025_linux.tar.gz
- Add Isabelle to your PATH in
~/.bashrc:
# Add Isabelle2025 binaries to PATH
export PATH="$PATH:$HOME/bin/Isabelle2025/bin"
- Reload your shell:
source ~/.bashrc
- Verify installation:
isabelle version
# Should output: Isabelle2025
Project Integration
The StarForth build system expects isabelle to be available on the PATH. The Makefile will automatically detect it:
# From Makefile line 24:
ISABELLE ?= isabelle
Do NOT commit Isabelle to the repository. It's excluded via .gitignore:
/tools/Isabelle2025/
Isabelle Make Targets
Once Isabelle is installed, you can use these targets:
# Verify all formal theories
make isabelle-build
# Quick syntax check (faster)
make isabelle-check
# Generate audit-ready documentation
make docs-isabelle
Build Configuration (Kconfig)
StarForth has a Linux-kernel-style Kconfig system for its ~40 build-time
tuning knobs (physics/SSM engine, adaptive heartbeat, pipelining
speculation, architecture, build profile, platform mode, kernel-only
flags). It is entirely optional. Nothing changes about a plain make
or make -f Makefile.starkernel until you explicitly generate a
.config — until then, every knob uses the same bare defaults it always
has, and the two Makefiles behave exactly as if Kconfig didn't exist.
Configuring
Config lives per architecture, under build/$(ARCH)/.config — not one
root .config — because building all three kernel architectures
back-to-back in one script is this project's normal QEMU acceptance
workflow, and a single shared config would desync from whichever arch is
actually being built.
# Interactive, curses-based menu (recommended)
make menuconfig # hosted build, current ARCH
make -f Makefile.starkernel ARCH=amd64 menuconfig
# Interactive, Qt-based GUI menu
make xconfig
# Line-by-line interactive prompts (every symbol, in order)
make config
# Re-prompt only for symbols that are new or changed since last time
make oldconfig
# Apply one of the example configs below non-interactively
make hosted_standard_defconfig
make -f Makefile.starkernel ARCH=amd64 kernel_amd64_defconfig
Four example configs ship in configs/: hosted_standard_defconfig,
kernel_amd64_defconfig, kernel_aarch64_defconfig,
kernel_riscv64_defconfig. Each just selects the architecture/variant
choice; every other symbol takes its Kconfig default.
Once a .config exists for an architecture, a plain make (or
make -f Makefile.starkernel ARCH=...) picks it up automatically — no
extra flag needed. Editing the .config by hand or re-running
menuconfig and rebuilding does not force recompilation of unrelated
object files (the same is true of today's bare ?= knobs); run
make clean first if you want a config change to take full effect.
Command-line always wins. make ROLLING_WINDOW_SIZE=8192 (or any
knob, or MINIMAL=1 for platform mode) overrides whatever the
active .config says, whether or not Kconfig is in use at all. This is
true for every symbol below without exception.
Symbol reference
Architecture & build variant (Kconfig.arch, Kconfig.variant)
| Symbol | Type | Default | Effect |
|---|---|---|---|
ARCH (choice: AMD64/AARCH64/RISCV64) |
choice | AMD64 | Selects the ARCH= value passed to whichever Makefile is driven from this config. |
STARFORTH_VARIANT (choice: HOSTED/KERNEL) |
choice | HOSTED | Which Makefile this config targets. Gates the choices below — a kernel-variant config never offers TARGET/PLATFORM, and Kconfig.kernel's symbols only exist for a kernel-variant config. |
TARGET (choice: standard/fast/fastest/turbo/pgo/asan) — hosted only |
choice | standard | Hosted TARGET= build profile: optimization level, LTO, direct threading, ASan/UBSan instrumentation. pgo only selects which multi-stage make recipe runs, not a flag set. |
PLATFORM (choice: default/minimal) — hosted only |
choice | default | minimal swaps in freestanding platform sources (-nostdlib -ffreestanding, no libc dependency surface). HISTORICAL: a third mode, l4re (L4Re/Fiasco.OC, -D__l4__=1, forced HEARTBEAT_THREAD_ENABLED off), existed here through mid-2026; removed as an active target. |
Physics / SSM engine tuning (Kconfig.physics)
| Symbol | Type | Default | Effect |
|---|---|---|---|
STRICT_PTR |
bool | y | VM memory bounds checking on every pointer access. Disable only for raw benchmarking comparisons. |
ENABLE_HOTWORDS_CACHE |
bool | n | Physics-driven hot-words cache — 1.78× speedup on dictionary lookups when on. Also gates L8 Jacquard's L1_heat_tracking bit. |
ENABLE_PIPELINING |
bool | n | Speculative execution via word-transition prediction. Also gates L8 Jacquard's L4_pipelining bit, and gates every symbol below down to MINIMUM_PREFETCH_ROI. |
TRANSITION_WINDOW_SIZE |
int | 8 | Word-transition lookahead depth for pipelining prediction. Range 1–8. |
SPECULATION_THRESHOLD_Q48 |
hex (Q48.16) | 0x8000 (0.50) | Minimum transition probability before pipelining will speculate on a word. Range 0.10 (aggressive) to 0.95 (conservative). |
SPECULATION_DEPTH |
int | 1 | How many words ahead to prefetch speculatively. Range 1–4. |
MIN_SAMPLES_FOR_SPECULATION |
int | 10 | Transitions observed before pipelining makes a speculation decision for a word. |
MISPREDICTION_COST_Q48 |
hex (Q48.16) | 0x190000 (25 ns) | Estimated cost of recovering from a wrong speculation. |
MINIMUM_PREFETCH_ROI |
hex (Q48.16) | 0x1199A (1.10) | Minimum expected return-on-investment ratio for speculation to be worthwhile. (Corrected 2026-07-08 from a long-shipped 0x11999A, which was ~17.6 in Q48.16 — an order of magnitude off from the documented 1.10 intent and effectively disabling speculation whenever pipelining was on.) |
ROLLING_WINDOW_SIZE |
int | 4096 | Initial execution-history capture size (rolling window of truth). Automatically shrinks at runtime if pattern diversity plateaus. |
ADAPTIVE_SHRINK_RATE |
int | 50 | Percentage of the rolling window retained when an adaptive shrink fires. Range 50–95; lower = more aggressive. |
ADAPTIVE_MIN_WINDOW_SIZE |
int | 256 | Floor — the rolling window never shrinks below this many word IDs. |
ADAPTIVE_CHECK_FREQUENCY |
int | 512 | How often (in executions) to measure pattern diversity for the shrink decision. |
ADAPTIVE_GROWTH_THRESHOLD |
int | 5 | Growth-rate percentage below which the window is considered saturated and a shrink is considered. |
INITIAL_DECAY_SLOPE_Q48 |
int (Q48.16) | 21845 (≈0.333) | Cold-start seed for the adaptively-inferred heat-decay slope (Loop #6 retunes it at runtime; this only matters at boot). |
DECAY_MIN_INTERVAL |
int | 500 | Historical minimum-interval knob. No longer load-bearing since decay went tick-based (2026-07); kept only because doe_metrics.c still reports it. |
DECAY_RATE_PER_US_Q16 |
int (Q16) | 1 | Heat decay rate. Half-life ≈6.5s at the default; 0 disables decay entirely. |
HEARTBEAT_INFERENCE_FREQUENCY |
int | 1000 | Heartbeat ticks between inference-engine runs (window-width + decay-slope re-inference). Set very high (e.g. 999999) to effectively disable inference for a static-configuration run. |
SSM_ENTROPY_HIGH_THRESHOLD |
string (float) | "0.75" | Rolling-window entropy above this votes to enable L8 Jacquard's Loop #2 (diversity). |
SSM_CV_HIGH_THRESHOLD |
string (float) | "0.15" | Short-term volatility above this votes to enable Loops #5/#6 (inference). |
SSM_TEMPORAL_DECAY_THRESHOLD |
string (float) | "0.5" | Temporal-locality strength above this votes to enable Loop #3 (decay). |
SSM_TEMPORAL_DECAY_LOW_THRESHOLD |
string (float) | "0.3" | Lower temporal-locality bound that, combined with a high-CV vote, also enables Loop #6. |
SSM_HYSTERESIS_TICKS |
int | 5 | Consecutive ticks required before an L8 mode change actually commits (prevents thrashing). |
Heartbeat / adaptive tick coordinator (Kconfig.heartbeat)
| Symbol | Type | Default | Effect |
|---|---|---|---|
HEARTBEAT_THREAD_ENABLED — hosted only |
bool | y | vm_tick() runs in a dedicated background thread (optimal) vs. inline from the interpreter loop (legacy). Invisible/off for a kernel-variant config — LithosAnanke has no pthreads — and the kernel Makefile keeps an unconditional -D...=0 override regardless, as a second, independent guarantee of the same fact. |
HEARTBEAT_TICK_NS |
int | 10000 | Background heartbeat thread wake period, nanoseconds. Only meaningful when HEARTBEAT_THREAD_ENABLED is on. |
HEARTBEAT_CHECK_FREQUENCY |
int | 256 | Word executions between heartbeat checks — the counter that makes vm->heartbeat.tick_count a purely execution-driven virtual clock, which is also what Loop #3 heat decay reads (kept architecture-invariant on purpose). |
HEARTBEAT_WINDOW_TUNING_FREQUENCY |
int | 1000 | Heartbeat ticks between Loop #5 (window-width inference) runs. |
HEARTBEAT_SLOPE_VALIDATION_FREQUENCY |
int | 5000 | Heartbeat ticks between Loop #6 (decay-slope inference) revalidation runs. |
EMERGENCY_CONSOLE_ENABLED |
bool | y | REPL recovers from errors and continues (interactive fallthrough), vs. exiting to vm_fault_handler(). Set n for production/embedded/high-security builds with no interactive fallthrough surface. Applies to both builds; on the kernel side this only bypasses the bare ok> REPL — zuse sessions are always subject to ACL regardless. |
Kernel-only options (Kconfig.kernel) — kernel variant only
| Symbol | Type | Default | Effect |
|---|---|---|---|
STARFORTH_ENABLE_VM |
bool | y | Compiles the full StarForth VM source tree into the kernel image and enables capsule birth/execution (M7 milestone). Off builds a kernel that only reaches M0–M6 (console, PMM, VMM, interrupts, timers, kmalloc) — no interpreter, no capsules, no ok> REPL. |
PARITY_MODE |
bool | n | Enables the parity/determinism verification harness used to compare dict_hash/capsule state across independent runs. |
SK_PARITY_DEBUG |
bool | n | Extra diagnostic logging in the parity/vocabulary code paths. |
HEARTBEAT_DOE_LOG |
bool | y | Per-heartbeat-tick DoE observation logging. The bare-metal DoE tooling (experiments/bare_metal/) expects this on. |
Out of scope for Kconfig, by design: compudynamics.h's CDTuning
values (C function return values, not macros); runtime-only parameters
like PROFILE, DOE_INJECT/DOE_REPS/DOE_SEED, KERNEL_ARGS (these
govern a specific run, not a compiled configuration).
CI/CD Considerations
GitHub Actions / GitLab CI
For CI/CD pipelines, you'll need to install Isabelle as part of the build process:
GitHub Actions Example
name: Build & Verify
on: [push, pull_request]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install build dependencies
run: |
sudo apt-get update
sudo apt-get install -y build-essential gcc make
- name: Install Isabelle
run: |
wget -q https://isabelle.in.tum.de/dist/Isabelle2025_linux.tar.gz
tar -xzf Isabelle2025_linux.tar.gz
echo "$GITHUB_WORKSPACE/Isabelle2025/bin" >> $GITHUB_PATH
- name: Verify Isabelle installation
run: isabelle version
- name: Build StarForth
run: make fastest
- name: Run tests
run: make test
- name: Build formal verification docs
run: make docs-isabelle
- name: Upload documentation artifacts
uses: actions/upload-artifact@v3
with:
name: isabelle-docs
path: docs/src/isabelle/
GitLab CI Example
variables:
ISABELLE_VERSION: "2025"
ISABELLE_URL: "https://isabelle.in.tum.de/dist/Isabelle2025_linux.tar.gz"
before_script:
- apt-get update -qq
- apt-get install -y -qq build-essential gcc make wget
- wget -q $ISABELLE_URL
- tar -xzf Isabelle2025_linux.tar.gz
- export PATH="$PATH:$PWD/Isabelle2025/bin"
- isabelle version
build:
stage: build
script:
- make fastest
- make test
docs:
stage: deploy
script:
- make docs-isabelle
artifacts:
paths:
- docs/src/isabelle/
Docker Considerations
If building in Docker, Isabelle requires Java (JDK 17+):
FROM ubuntu:22.04
# Install build tools
RUN apt-get update && apt-get install -y \
build-essential \
gcc \
make \
wget \
openjdk-17-jdk \
&& rm -rf /var/lib/apt/lists/*
# Install Isabelle
RUN wget -q https://isabelle.in.tum.de/dist/Isabelle2025_linux.tar.gz && \
tar -xzf Isabelle2025_linux.tar.gz && \
rm Isabelle2025_linux.tar.gz
ENV PATH="/Isabelle2025/bin:${PATH}"
# Verify
RUN isabelle version
CI/CD Performance Notes
- Isabelle build takes 5-10 minutes for all theory verification
- Consider caching Isabelle installation between builds
- Use
isabelle-checkfor quick syntax checking in pre-commit hooks - Only run full
isabelle-buildon main branch or release tags
GitHub Actions Caching Example
- name: Cache Isabelle
uses: actions/cache@v3
with:
path: Isabelle2025
key: isabelle-2025-${{ runner.os }}
- name: Install Isabelle (if not cached)
if: steps.cache-isabelle.outputs.cache-hit != 'true'
run: |
wget -q https://isabelle.in.tum.de/dist/Isabelle2025_linux.tar.gz
tar -xzf Isabelle2025_linux.tar.gz
Quick Start
# Clone the repository
git clone <repo-url>
cd StarForth
# Install dependencies (including Isabelle - see above)
# ...
# Build the fastest version
make fastest
# Run tests
make test
# Generate documentation (requires Isabelle)
make docs-isabelle
# View generated docs
ls -la docs/src/isabelle/
Project Structure
StarForth/
├── src/ # C source code
├── include/ # Header files
├── docs/
│ ├── src/
│ │ ├── internal/formal/ # Isabelle theory files
│ │ └── isabelle/ # Generated Isabelle docs (gitignored)
│ └── DEVELOPER.md # This file
├── scripts/
│ └── isabelle-to-adoc.sh # Isabelle doc generator
├── build/ # Build artifacts (gitignored)
├── tools/
│ └── Isabelle2025/ # Local Isabelle (gitignored)
└── Makefile # Build system
Formal Verification
StarForth includes machine-checked correctness proofs using Isabelle/HOL:
- Location:
docs/src/internal/formal/*.thy - Sessions:
VM_Formal,Physics_Formal - Generated docs:
docs/src/isabelle/(aftermake docs-isabelle)
Theory Files
VM_Core.thy- Core VM semanticsVM_Stacks.thy- Stack operationsVM_StackRuntime.thy- Runtime stack managementVM_Words.thy- Word definitionsVM_DataStack_Words.thy- Data stack operationsVM_ReturnStack_Words.thy- Return stack operationsVM_Register.thy- Register operationsPhysics_StateMachine.thy- Physics state machinePhysics_Observation.thy- Observation model
All theories are fully verified with machine-checked proofs.
Contributing
When contributing code that affects the VM semantics:
- Update relevant Isabelle theories if needed
- Run
make isabelle-buildto verify proofs - Run
make docs-isabelleto regenerate documentation - Include Isabelle docs in your pull request
Support
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Isabelle Help: https://isabelle.in.tum.de/
- Formal Verification: See
docs/src/isabelle/VERIFICATION_REPORT.adoc
Last Updated: 2025-10-30 Isabelle Version: 2025 Build System: GNU Make