# ==============================================================================
# StarForth Build System - The Fastest Forth in the West!
# ==============================================================================
#
# Quick Start:
#   make                          - Standard optimized build (auto ARCH, TARGET=standard)
#   make TARGET=fastest           - Maximum performance build
#   make ARCH=arm64 TARGET=fastest - Build for arm64 (e.g., Raspberry Pi 4)
#   make help                     - Show all available targets
#
# ==============================================================================

# ==============================================================================
# CONFIGURATION
# ==============================================================================

VERSION ?= 3.2.0
CC = gcc

# Isabelle configuration
# NOTE: Isabelle must be installed separately and available on PATH
# See docs/DEVELOPER.md for installation instructions
ISABELLE ?= isabelle

# Architecture / target selection
ARCH ?= $(shell uname -m)
ARCH := $(strip $(ARCH))
TARGET ?= standard
TARGET := $(strip $(TARGET))

# Friendly guardrail: 32-bit builds are not supported
unsupported_32bit_arches := i386 i486 i586 i686 x86 armv6 armv7 armv7l arm
ifneq ($(filter $(ARCH),$(unsupported_32bit_arches)),)
    $(error StarForth targets 64-bit platforms only. ARCH='$(ARCH)' is unsupported. Please build on amd64/arm64/raspi/riscv64.)
endif

# Canonical architecture normalization (x86_64→amd64, aarch64→arm64, etc.)
canon_arch = $(strip $(if $(filter x86_64,$1),amd64,$(if $(filter amd64,$1),amd64,$(if $(filter aarch64 arm64,$1),arm64,$(if $(filter riscv riscv64,$1),riscv64,$1)))))
ARCH_DIR := $(call canon_arch,$(ARCH))
# Normalize ARCH for consistent ifeq matching
ARCH := $(call canon_arch,$(ARCH))

# Kconfig bridge (menuconfig/xconfig/config/oldconfig/*_defconfig). See
# mk/Kconfig.mk for why KCONFIG_ARCH_DIR must be set here, before include.
# .DEFAULT_GOAL is pinned explicitly because mk/Kconfig.mk's first rule
# would otherwise silently become Make's default goal, since it's included
# before this Makefile's own `all:` target is defined below.
.DEFAULT_GOAL := all
KCONFIG_ARCH_DIR := $(ARCH_DIR)
include mk/Kconfig.mk

# Kconfig platform-mode choice (Phase 5): PLATFORM_DEFAULT/PLATFORM_MINIMAL
# in Kconfig.variant (PLATFORM_L4RE removed -- L4Re support is historical,
# see the "Platform support" section below). Must run this early -- before
# anything below that branches on MINIMAL -- since Make evaluates a Makefile
# top-to-bottom and a variable's value at the point an `ifdef` is reached is
# whatever it resolved to *so far*, not its final value. Only applies when a
# developer hasn't already set MINIMAL explicitly (command line or
# environment) -- `ifdef` in the "Platform support" section below cares
# about definedness, not value, so this can't reuse the kconfig_bool/
# kconfig_int pattern (those always define their target variable once
# active). Checked with $(origin ...) so an explicit `make MINIMAL=1` (or
# unset) always wins over whatever .config says.
ifeq ($(KCONFIG_ACTIVE),1)
ifeq ($(origin MINIMAL),undefined)
ifeq ($(CONFIG_PLATFORM_MINIMAL),y)
MINIMAL := 1
endif
endif
endif

ifeq ($(ARCH),amd64)
    ARCH_NAME = x86_64
    ARCH_FLAGS = -march=native
    ARCH_DEFINES = -DARCH_X86_64=1
    ASM_SYNTAX = -masm=intel
else ifeq ($(ARCH),arm64)
    ARCH_NAME = ARM64
    ARCH_FLAGS = -march=armv8-a+crc+simd -mtune=cortex-a72
    ARCH_DEFINES = -DARCH_ARM64=1
    ASM_SYNTAX =
else ifeq ($(ARCH),raspi)
    ARCH_NAME = Raspberry Pi 4 (ARM64)
    ARCH_FLAGS = -march=armv8-a+crc+simd -mtune=cortex-a72
    ARCH_DEFINES = -DARCH_ARM64=1 -DRASPBERRY_PI_BUILD=1
    ASM_SYNTAX =
else ifeq ($(ARCH),riscv64)
    ARCH_NAME = RISC-V 64 (QEMU)
    ARCH_FLAGS = -march=rv64gc -mabi=lp64d -mcmodel=medany
    ARCH_DEFINES = -DARCH_RISCV64=1
    ASM_SYNTAX =
else ifeq ($(ARCH),riscv)
    ARCH_NAME = RISC-V 64 (QEMU)
    ARCH_FLAGS = -march=rv64gc -mabi=lp64d -mcmodel=medany
    ARCH_DEFINES = -DARCH_RISCV64=1
    ASM_SYNTAX =
else
    $(warning Unknown architecture: $(ARCH), using generic flags)
    ARCH_NAME = generic
    ARCH_FLAGS =
    ARCH_DEFINES =
    ASM_SYNTAX =
endif

# Compiler flags
# Feature switches (default: enabled)
# Physics/SSM knob family: repointed through the Kconfig bridge (see
# mk/Kconfig.mk's kconfig_bool/kconfig_int) as of the Kconfig migration's
# Phase 3. Each call below is a drop-in replacement for a bare `?=`: when
# no build/$(ARCH)/.config exists (KCONFIG_ACTIVE unset), it's exactly
# `VAR ?= default`, identical to before; once a .config exists, the
# Kconfig-fed value takes over. `make VAR=x` on the command line always
# wins regardless of which branch fires.
#
# STRICT_PTR: VM memory bounds checking on every pointer access
#   Default: 1 (enabled). Disable only for raw benchmarking comparisons.
$(eval $(call kconfig_bool,STRICT_PTR,1))

# ENABLE_HOTWORDS_CACHE: Physics-driven hot-words cache (1.78× speedup on dictionary lookups)
#   Default: 1 (enabled - experiments show cache is optimal)
#   Set ENABLE_HOTWORDS_CACHE=0 to disable (for research comparison only)
$(eval $(call kconfig_bool,ENABLE_HOTWORDS_CACHE,0))

# ENABLE_PIPELINING: Speculative execution via word transition prediction
#   Default: 1 (enabled - experiments show pipelining is optimal)
#   Set ENABLE_PIPELINING=0 to disable (for research comparison only)
#   NOTE: As of 2025-11-19, all future experiments use both cache and pipelining enabled
$(eval $(call kconfig_bool,ENABLE_PIPELINING,0))

# EMERGENCY_CONSOLE_ENABLED: 1 = REPL recovers from errors and continues (interactive fallthrough)
#   0 = errors are non-recoverable; REPL exits and vm_fault_handler() is called instead
#   Default: 1 (dev/recovery builds)
#   Set EMERGENCY_CONSOLE_ENABLED=0 for production, embedded, or high-security builds
#   where no interactive fallthrough surface should exist.
# Heartbeat family: repointed through the Kconfig bridge (Phase 4). Same
# drop-in-replacement-for-`?=` behavior as the physics/SSM family (Phase 3).
$(eval $(call kconfig_bool,EMERGENCY_CONSOLE_ENABLED,1))

# Heartbeat configuration
# HEARTBEAT_THREAD_ENABLED: 1 = run vm_tick() in background thread (OPTIMAL), 0 = inline (legacy)
#   Default: 1 (experiments show threaded heartbeat is optimal for adaptive tuning)
#   NOTE: As of 2025-11-19, all future experiments use heartbeat enabled
$(eval $(call kconfig_bool,HEARTBEAT_THREAD_ENABLED,1))

# HEARTBEAT_TICK_NS: Wake frequency for heartbeat thread (default 1ms)
$(eval $(call kconfig_int,HEARTBEAT_TICK_NS,10000))

# HEARTBEAT_CHECK_FREQUENCY / HEARTBEAT_WINDOW_TUNING_FREQUENCY /
# HEARTBEAT_SLOPE_VALIDATION_FREQUENCY: previously unwired in the hosted
# Makefile -- no `?=` and no -D forwarding existed for any of these three
# before Phase 4, so hosted builds always got starforth_config.h's bare
# defaults (256/1000/5000) with no override mechanism at all. The kernel
# Makefile has always had opt-in forwarding for them (VM_FEATURE_FLAG_VARS).
# Promoted here for parity, same treatment as Phase 3's SSM_* knobs.
$(eval $(call kconfig_int,HEARTBEAT_CHECK_FREQUENCY,256))
$(eval $(call kconfig_int,HEARTBEAT_WINDOW_TUNING_FREQUENCY,1000))
$(eval $(call kconfig_int,HEARTBEAT_SLOPE_VALIDATION_FREQUENCY,5000))

# HISTORICAL: L4Re had no pthreads, so this forced HEARTBEAT_THREAD_ENABLED
# off whenever L4RE=1 was set. L4Re support is no longer an active target;
# see "Platform support" below.
# ifeq ($(L4RE),1)
# HEARTBEAT_THREAD_ENABLED := 0
# endif

# Feedback Loop Toggle Switches (for systematic performance measurement)
# ============================================================================
# These allow turning each feedback loop on/off to measure additive performance gains
# EXP_00-BASELINE: all loops OFF (plain FORTH-79)
# EXP_01: Loop #1 ON
# EXP_02: Loops #1-2 ON
# ... etc, building cumulatively to measure each loop's contribution

# ==============================================================================
# SSM (Steady State Machine) Configuration - Data-Driven Architecture
# ==============================================================================
# Based on 2^7 DoE with 300 reps (128 configs, 38,400 runs).
# Top 5% analysis (speed + stability) reveals optimal loop combinations.
#
# Experimentally Validated Architecture:
#   L1 (heat_tracking):       DISABLED (harmful in 86% of top configs)
#   L4 (pipelining_metrics):  DISABLED (harmful in 100% of top configs)
#   L7 (adaptive_heartrate):  ALWAYS ON (beneficial in 71% of top configs)
#   L2, L3, L5, L6:           Runtime-controlled by L8 (workload-dependent)
#   L8 (Jacquard):            4-bit mode selector (16 modes: C0-C15)
#
# L8 dynamically controls L2/L3/L5/L6 based on entropy, CV, and temporal metrics.
# Top 5% validated modes: C4, C7, C9, C11, C12 (see v3/src/ssm_jacquard.c)

# Tuning Knobs (Physics-driven Optimization System)
# ============================================================================
# Knob #7: ROLLING_WINDOW_SIZE (Initial execution history capture)
#   - Default: 4096 (conservative, ensures statistical significance at cold start)
#   - Usage: make ROLLING_WINDOW_SIZE=8192 (larger for complex workloads)
#           or ROLLING_WINDOW_SIZE=2048 (smaller for memory constraints)
#   - System AUTOMATICALLY shrinks during execution if diminishing returns detected
#   - Window starts conservative, self-tunes down if pattern diversity plateaus
#   - Window becomes "warm" (representative) after N executions, then adapts
$(eval $(call kconfig_int,ROLLING_WINDOW_SIZE,4096))

# Knob #6: TRANSITION_WINDOW_SIZE (Pipelining context depth)
#   - Default: 8 (see starforth_config.h for the exact shipped value)
#   - Usage: make TRANSITION_WINDOW_SIZE=1 (or 2, 4 for shallower context)
#   - Empirically tuned via binary chop: 1 vs 2 vs 4 vs 8
$(eval $(call kconfig_int,TRANSITION_WINDOW_SIZE,8))

# SPECULATION_THRESHOLD_Q48 / SPECULATION_DEPTH / MIN_SAMPLES_FOR_SPECULATION /
# MISPREDICTION_COST_Q48 / MINIMUM_PREFETCH_ROI: previously hardcoded,
# non-overridable #define in physics_pipelining_metrics.h -- no Makefile
# knob or -D forwarding existed for any of these five before Phase 5.
# Promoted here for parity, same treatment as Phase 3/4's other
# previously-unwired constants. All five are Q48.16-fixed-point or plain
# counts meaningful only when ENABLE_PIPELINING is on; see Kconfig.physics
# for the exact hex/decimal defaults. MINIMUM_PREFETCH_ROI's default was
# corrected from the long-shipped 0x11999A to 0x1199A (rev y) -- the
# former was ~17.6 in Q48.16, not the documented 1.10.
$(eval $(call kconfig_int,SPECULATION_THRESHOLD_Q48,0x8000))
$(eval $(call kconfig_int,SPECULATION_DEPTH,1))
$(eval $(call kconfig_int,MIN_SAMPLES_FOR_SPECULATION,10))
$(eval $(call kconfig_int,MISPREDICTION_COST_Q48,0x190000))
$(eval $(call kconfig_int,MINIMUM_PREFETCH_ROI,0x1199A))

# Knobs #8-11: Adaptive Window Shrinking Control
# ============================================================================
# These control how aggressively the rolling window learns and adapts
# (NOT the initial window size - that's ROLLING_WINDOW_SIZE above)

# Knob #8: ADAPTIVE_SHRINK_RATE (percentage to retain when shrinking)
#   - Default: 50 (shrink to 50% = discard 50% each cycle)
#   - Range: 50-95 (lower = more aggressive, higher = more conservative)
#   - Usage: make ADAPTIVE_SHRINK_RATE=75 (slower learning) or 90 (slow learning)
$(eval $(call kconfig_int,ADAPTIVE_SHRINK_RATE,50))

# Knob #9: ADAPTIVE_MIN_WINDOW_SIZE (floor to prevent over-shrinking)
#   - Default: 256 (never shrink below 256 word IDs)
#   - Range: 64-1024 (smaller = leaner, larger = safer)
#   - Usage: make ADAPTIVE_MIN_WINDOW_SIZE=512 (conservative) or 128 (lean)
$(eval $(call kconfig_int,ADAPTIVE_MIN_WINDOW_SIZE,256))

# Knob #10: ADAPTIVE_CHECK_FREQUENCY (how often to measure diversity)
#   - Default: 512 (check after every 512 executions)
#   - Range: 32-1024 (more = faster response, less = less overhead)
#   - Usage: make ADAPTIVE_CHECK_FREQUENCY=128 (responsive) or 256 (default of comparable knobs)
$(eval $(call kconfig_int,ADAPTIVE_CHECK_FREQUENCY,512))

# Knob #11: ADAPTIVE_GROWTH_THRESHOLD (growth rate that signals saturation)
#   - Default: 5 (shrink when growth < 5%)
#   - Range: 0-10 (lower = eager shrinking, higher = cautious)
#   - Usage: make ADAPTIVE_GROWTH_THRESHOLD=0 (aggressive) or 1 (less conservative)
$(eval $(call kconfig_int,ADAPTIVE_GROWTH_THRESHOLD,5))

# Knob #11a: INITIAL_DECAY_SLOPE_Q48 (Starting decay slope for inference engine)
#   - Default: 21845 (1/3 in Q48.16 format, = 0.333... starting slope)
#   - Range: 13107-43691 (0.2 to 0.67 in Q48.16)
#   - Usage: make INITIAL_DECAY_SLOPE_Q48=13107 (0.2) or 32768 (0.5) or 43691 (0.67)
#   - DoE: OPP #3 tests 0.2, 0.33, 0.5, 0.67 to find optimal starting point for convergence
#   - Note: Inference engine adapts this value at runtime; this is just the cold-start value
$(eval $(call kconfig_int,INITIAL_DECAY_SLOPE_Q48,21845))

# Knob #11b: DECAY_MIN_INTERVAL (Minimum time before decay applies)
#   - Default: 500 (nanoseconds)
#   - Range: 500-5000 (balance between decay sensitivity and overhead)
#   - Usage: make DECAY_MIN_INTERVAL=1000 (slower decay) or 2000 (slower still)
#   - NOTE: as of the tick-based Loop #3 decay conversion (rev t, see
#     docs/working/architecture/VM-FLEET-ATTRACTOR-DESIGN-20260705.md),
#     Loop #3 no longer consults this value at all -- decay is gated on
#     elapsed heartbeat ticks, not elapsed nanoseconds. Kept only because
#     doe_metrics.c still reports it as a metrics field; informational,
#     not load-bearing.
$(eval $(call kconfig_int,DECAY_MIN_INTERVAL,500))

# Knob #11d: HEARTBEAT_INFERENCE_FREQUENCY (How often to run inference engine)
#   - Default: 1000 (ticks between inference runs)
#   - Set VERY HIGH (999999) to effectively disable inference (static mode)
#   - Set LOW for frequent inference (adaptive mode)
#   - DoE: OPP #3 tests 999999 (static, no Loop #6) vs 1000 (adaptive, with Loop #6)
#   - Measures: Inference engine cost vs benefit in optimized performance
$(eval $(call kconfig_int,HEARTBEAT_INFERENCE_FREQUENCY,1000))

# Knob #12: DECAY_RATE_PER_US_Q16 (Heat decay rate in Q16 fixed-point)
#   - Default: 1 (1/65536 heat units decayed per microsecond)
#   - Range: 0-65536 (0 = no decay, 65536 = 1 heat/µs)
#   - Half-life: ~6.5 seconds at default (100-heat word → 50-heat in 6.5s)
#   - Usage: make DECAY_RATE_PER_US_Q16=2 (faster decay) or 0 (no decay for baseline)
#   - DoE: Vary this to measure impact of heat decay on performance
$(eval $(call kconfig_int,DECAY_RATE_PER_US_Q16,1))

# L8 Jacquard mode selector thresholds (ssm_jacquard.h). Previously unwired --
# no Makefile knob or -D forwarding existed for these before the Kconfig
# migration's Phase 3; ssm_jacquard.h's own #ifndef fallback was the only
# place any of these five values lived. See Kconfig.physics for the
# rationale behind modeling the four float thresholds as Kconfig `string`
# symbols (Kconfig has no native float type).
$(eval $(call kconfig_int,SSM_ENTROPY_HIGH_THRESHOLD,0.75))
$(eval $(call kconfig_int,SSM_CV_HIGH_THRESHOLD,0.15))
$(eval $(call kconfig_int,SSM_TEMPORAL_DECAY_THRESHOLD,0.5))
$(eval $(call kconfig_int,SSM_TEMPORAL_DECAY_LOW_THRESHOLD,0.3))
$(eval $(call kconfig_int,SSM_HYSTERESIS_TICKS,5))

# Build the CFLAGS with tuning knobs
# L8 FINAL INTEGRATION: All experimental loop flags removed.
# L1-L7 are now always-on internal physics layers.
# L8 Jacquard mode selector is the sole policy engine.
BASE_CFLAGS = -std=c99 -Wall -Werror -Iv3/include -Ikernel/include -Iv3/src/word_source -Iv3/src/test_runner/include \
              -DSTRICT_PTR=$(STRICT_PTR) \
              -DENABLE_HOTWORDS_CACHE=$(ENABLE_HOTWORDS_CACHE) \
              -DENABLE_PIPELINING=$(ENABLE_PIPELINING) \
              -DROLLING_WINDOW_SIZE=$(ROLLING_WINDOW_SIZE) \
              -DTRANSITION_WINDOW_SIZE=$(TRANSITION_WINDOW_SIZE) \
              -DSPECULATION_THRESHOLD_Q48=$(SPECULATION_THRESHOLD_Q48) \
              -DSPECULATION_DEPTH=$(SPECULATION_DEPTH) \
              -DMIN_SAMPLES_FOR_SPECULATION=$(MIN_SAMPLES_FOR_SPECULATION) \
              -DMISPREDICTION_COST_Q48=$(MISPREDICTION_COST_Q48) \
              -DMINIMUM_PREFETCH_ROI=$(MINIMUM_PREFETCH_ROI) \
              -DADAPTIVE_SHRINK_RATE=$(ADAPTIVE_SHRINK_RATE) \
              -DADAPTIVE_MIN_WINDOW_SIZE=$(ADAPTIVE_MIN_WINDOW_SIZE) \
              -DADAPTIVE_CHECK_FREQUENCY=$(ADAPTIVE_CHECK_FREQUENCY) \
              -DADAPTIVE_GROWTH_THRESHOLD=$(ADAPTIVE_GROWTH_THRESHOLD) \
              -DINITIAL_DECAY_SLOPE_Q48=$(INITIAL_DECAY_SLOPE_Q48) \
              -DDECAY_RATE_PER_US_Q16=$(DECAY_RATE_PER_US_Q16) \
              -DHEARTBEAT_THREAD_ENABLED=$(HEARTBEAT_THREAD_ENABLED) \
              -DHEARTBEAT_TICK_NS=$(HEARTBEAT_TICK_NS) \
              -DHEARTBEAT_INFERENCE_FREQUENCY=$(HEARTBEAT_INFERENCE_FREQUENCY) \
              -DHEARTBEAT_CHECK_FREQUENCY=$(HEARTBEAT_CHECK_FREQUENCY) \
              -DHEARTBEAT_WINDOW_TUNING_FREQUENCY=$(HEARTBEAT_WINDOW_TUNING_FREQUENCY) \
              -DHEARTBEAT_SLOPE_VALIDATION_FREQUENCY=$(HEARTBEAT_SLOPE_VALIDATION_FREQUENCY) \
              -DEMERGENCY_CONSOLE_ENABLED=$(EMERGENCY_CONSOLE_ENABLED) \
              -DSSM_ENTROPY_HIGH_THRESHOLD=$(SSM_ENTROPY_HIGH_THRESHOLD) \
              -DSSM_CV_HIGH_THRESHOLD=$(SSM_CV_HIGH_THRESHOLD) \
              -DSSM_TEMPORAL_DECAY_THRESHOLD=$(SSM_TEMPORAL_DECAY_THRESHOLD) \
              -DSSM_TEMPORAL_DECAY_LOW_THRESHOLD=$(SSM_TEMPORAL_DECAY_LOW_THRESHOLD) \
              -DSSM_HYSTERESIS_TICKS=$(SSM_HYSTERESIS_TICKS)

ifeq ($(HEARTBEAT_THREAD_ENABLED),1)
# HISTORICAL: this used to branch on L4RE (no pthreads there) --
# THREAD_FLAGS := if L4RE else -pthread. L4Re support is no longer an
# active target; see "Platform support" below.
THREAD_FLAGS := -pthread
else
THREAD_FLAGS :=
endif

BASE_CFLAGS += $(THREAD_FLAGS)

# Build profiles (TARGET values)
SUPPORTED_TARGETS := standard fast fastest turbo asan

TARGET_DESCRIPTION_standard := Standard optimized build
TARGET_CFLAGS_standard := $(BASE_CFLAGS) $(ARCH_FLAGS) $(ARCH_DEFINES) -O2 -flto=auto -fuse-linker-plugin -DNDEBUG \
                           -DUSE_ASM_OPT=1 \
                           -ffunction-sections -fdata-sections -fomit-frame-pointer \
                           -fno-asynchronous-unwind-tables -fno-unwind-tables -fno-strict-aliasing
TARGET_LDFLAGS_standard := -Wl,--gc-sections -s -flto=auto -fuse-linker-plugin -static $(THREAD_FLAGS)

TARGET_DESCRIPTION_fast := Fast optimized build (no LTO, easier debugging)
TARGET_CFLAGS_fast := $(BASE_CFLAGS) $(ARCH_FLAGS) $(ARCH_DEFINES) -O3 -DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1 -DNDEBUG
TARGET_LDFLAGS_fast := -s $(THREAD_FLAGS)

TARGET_DESCRIPTION_fastest := Maximum performance build (ASM + LTO + direct threading)
TARGET_CFLAGS_fastest := $(BASE_CFLAGS) $(ARCH_FLAGS) $(ARCH_DEFINES) -O3 -DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1 -DNDEBUG \
                          -flto=auto -fuse-linker-plugin -funroll-loops -finline-functions -fomit-frame-pointer -fno-plt -fno-semantic-interposition
TARGET_LDFLAGS_fastest := -flto=auto -fuse-linker-plugin -s -Wl,--gc-sections $(THREAD_FLAGS)

TARGET_DESCRIPTION_turbo := Assembly-optimized build (no direct threading)
TARGET_CFLAGS_turbo := $(BASE_CFLAGS) $(ARCH_FLAGS) $(ARCH_DEFINES) -O3 -DUSE_ASM_OPT=1 -DNDEBUG -flto
TARGET_LDFLAGS_turbo := -flto -s $(THREAD_FLAGS)

TARGET_DESCRIPTION_asan := ASan/UBSan instrumented build for memory error detection
TARGET_CFLAGS_asan := $(BASE_CFLAGS) $(ARCH_FLAGS) $(ARCH_DEFINES) -O1 -g \
                      -fsanitize=address,undefined -fno-omit-frame-pointer
TARGET_LDFLAGS_asan := -fsanitize=address,undefined $(THREAD_FLAGS)

define TARGET_POST_fastest
	@echo ""
	@echo "🎯 You've built the FASTEST FORTH IN THE WEST!"
	@echo ""
	@echo "Quick-draw benchmark:"
	@time -f "  ⏱️  Time: %E seconds" ./$(BINARY) -c ": BENCH 1000000 0 DO 1 2 + DROP LOOP ; BENCH BYE" 2>/dev/null || true
endef

TARGET_DESCRIPTION := $(TARGET_DESCRIPTION_$(TARGET))
ifeq ($(strip $(TARGET_DESCRIPTION)),)
    $(error Unknown TARGET '$(TARGET)'. Supported targets: $(SUPPORTED_TARGETS))
endif

TARGET_CFLAGS_SELECTED := $(TARGET_CFLAGS_$(TARGET))
TARGET_LDFLAGS_SELECTED := $(TARGET_LDFLAGS_$(TARGET))
TARGET_POST := $(or $(TARGET_POST_$(TARGET)),@:)

CFLAGS ?= $(TARGET_CFLAGS_SELECTED)
LDFLAGS ?= $(TARGET_LDFLAGS_SELECTED)

CFLAGS += $(EXTRA_CFLAGS)
LDFLAGS += $(EXTRA_LDFLAGS)

USE_ASM_OPT := $(if $(findstring -DUSE_ASM_OPT=1,$(CFLAGS)),1,0)
USE_LTO := $(if $(or $(findstring -flto,$(CFLAGS)),$(findstring -flto,$(LDFLAGS))),1,0)

# Platform support
#
# HISTORICAL: L4Re/Fiasco.OC was a supported platform target (`make L4RE=1`)
# through mid-2026. Removed as an active target; v3/src/platform/l4re/time.c
# is retained for reference but no longer wired into any build. To restore:
# reinstate the `else ifdef L4RE` branch below and the L4RE-conditional
# logic marked HISTORICAL elsewhere in this Makefile (search "L4RE"/"L4Re").
#
# ifdef MINIMAL
# ...
# else ifdef L4RE
# CFLAGS += -D__l4__=1
# PLATFORM_TIME_SRC = v3/src/platform/l4re/time.c v3/src/platform/platform_init.c
# PLATFORM_ALLOC_SRC = v3/src/platform/alloc_kernel.c
# else
# ...
# endif
ifdef MINIMAL
# DISABLED 2026-07-08: this was never a finished feature, not a small bug.
# v3/src/platform/starforth_minimal.c (referenced below) does not exist
# anywhere in the repo, and only 5 of ~90 source files under v3/src/ have any
# STARFORTH_MINIMAL awareness despite -nostdlib -ffreestanding applying to
# the entire tree. Confirmed by trying it: even a throwaway empty stub for
# the missing file lets the build report "success" while linking a binary
# with no _start entry point (this build's specific -nostdlib/-static/
# -pthread/-flto flag combination doesn't actually exclude libc, so most
# calls silently resolve anyway -- the illusion of success is the trap).
# Reviving this needs an actual freestanding platform layer written from
# scratch, not a quick fix -- failing loudly here instead of letting
# anyone hit a confusing compile error or, worse, a binary that "builds"
# but can't run. See .claude/CLAUDE.md's punch list for status.
$(error MINIMAL=1 build is disabled: incomplete freestanding platform layer, not currently supported. See the comment immediately above this line for why)
CFLAGS += -DSTARFORTH_MINIMAL=1 -nostdlib -ffreestanding
LDFLAGS += -nostdlib
PLATFORM_SRC = v3/src/platform/starforth_minimal.c
PLATFORM_ALLOC_SRC = v3/src/platform/alloc_kernel.c
else
PLATFORM_TIME_SRC = v3/src/platform/linux/time.c v3/src/platform/linux/io.c v3/src/platform/platform_init.c
PLATFORM_ALLOC_SRC = v3/src/platform/alloc_host.c
endif

PLATFORM_COMMON_SRC = v3/src/platform/threading.c $(PLATFORM_ALLOC_SRC)

# Source and object files
SRC = $(wildcard v3/src/*.c v3/src/word_source/*.c v3/src/test_runner/*.c v3/src/test_runner/modules/*.c) $(PLATFORM_SRC) $(PLATFORM_TIME_SRC) $(PLATFORM_COMMON_SRC)
BUILD_ROOT ?= build
build_dir = $(BUILD_ROOT)/$(call canon_arch,$(or $2,$(ARCH)))/$1
binary_path = $(call build_dir,$1,$2)/starforth
BUILD_DIR = $(call build_dir,$(TARGET),$(ARCH))
BINARY = $(call binary_path,$(TARGET),$(ARCH))
OBJ = $(patsubst v3/src/%.c,$(BUILD_DIR)/%.o,$(SRC))
ASM_FILES = $(patsubst v3/src/%.c,$(BUILD_DIR)/%.s,$(SRC))

# Profiler support for test targets
PROFILE_LEVEL := $(strip $(PROFILE))
ifeq ($(PROFILE_LEVEL),)
PROFILE_ARGS :=
else
PROFILE_ARGS := --profile $(PROFILE_LEVEL)
PROFILE_REPORT ?= 1
ifneq ($(strip $(PROFILE_REPORT)),0)
PROFILE_ARGS += --profile-report
endif
endif

# Installation directories
PREFIX ?= .
BINDIR = $(PREFIX)/bin
MANDIR = $(PREFIX)/share/man/man1
INFODIR = $(PREFIX)/share/info
DOCDIR = $(PREFIX)/share/doc/starforth
CONFDIR = $(PREFIX)/etc/starforth

# ==============================================================================
# PHONY TARGETS
# ==============================================================================

.PHONY: all banner help clean clean-obj clean-docs starkernel mkcapsule capsule-gen capsule-manifest
.PHONY: fastest fast turbo
.PHONY: rpi4 rpi4-cross rpi4-fastest riscv64-clang
.PHONY: minimal debug profile performance
.PHONY: test bench benchmark
.PHONY: asm sbom
.PHONY: docs api-docs docs-latex docs-isabelle isabelle-build isabelle-check info math-companion math-companion-clean
.PHONY: refinement-status refinement-init refinement-phase1 verify-defect refinement-annotate-check refinement-report
.PHONY: install uninstall package deb rpm
.PHONY: quality compile_commands clang_tidy cppcheck gcc_analyzer
.PHONY: FORCE version-info build-manifest

# ==============================================================================
# MAIN TARGETS
# ==============================================================================

all: banner $(BINARY)
	@echo ""
	@echo "✓ Build complete: $(BINARY)"
	@echo "  Architecture: $(ARCH_NAME)"
	@echo "  Profile: $(TARGET_DESCRIPTION) ($(TARGET))"
	@echo "  Output directory: $(BUILD_DIR)"
	@echo "  Optimizations: $(if $(findstring USE_ASM_OPT,$(CFLAGS)),Enabled,Disabled)"
	@echo "  Direct Threading: $(if $(findstring USE_DIRECT_THREADING,$(CFLAGS)),Enabled,Disabled)"
	@mkdir -p lfs/$(ARCH_DIR)
	@cp $(BINARY) lfs/$(ARCH_DIR)/starforth
	@echo "  LFS copy:  lfs/$(ARCH_DIR)/starforth"
	$(TARGET_POST)

banner:
	@echo "════════════════════════════════════════════════════════════"
	@echo "   ⚡ Building the Fastest Forth in the West! ⚡"
	@echo "════════════════════════════════════════════════════════════"
	@echo "  Target Architecture: $(ARCH_NAME) ($(ARCH_DIR))"
	@echo "  Build Profile: $(TARGET_DESCRIPTION) ($(TARGET))"
	@echo ""

# ==============================================================================
# OPTIMIZATION BUILDS - The Fastest in the West
# ==============================================================================

# Maximum performance - no compromises
fastest:
	@echo "🏆 Building FASTEST configuration..."
	@echo "   - Architecture: $(ARCH_NAME)"
	@echo "   - Assembly optimizations: ENABLED"
	@echo "   - Direct threading: ENABLED"
	@echo ""
	@$(MAKE) -f v3/Makefile TARGET=fastest all

# Fast without LTO (easier debugging)
fast:
	@echo "⚡ Building FAST configuration (no LTO for debugging)..."
	@$(MAKE) -f v3/Makefile TARGET=fast all

# Assembly optimizations only (no direct threading)
turbo:
	@echo "🚀 Building TURBO configuration (ASM only)..."
	@$(MAKE) -f v3/Makefile TARGET=turbo all

# ==============================================================================
# PLATFORM-SPECIFIC BUILDS
# ==============================================================================

# Raspberry Pi 4 - native build
rpi4:
	@echo "🥧 Building for Raspberry Pi 4 (native)..."
	@$(MAKE) -f v3/Makefile ARCH=raspi TARGET=fastest CFLAGS="$(BASE_CFLAGS) -march=armv8-a+crc+simd -mtune=cortex-a72 -DARCH_ARM64=1 -O3 -DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1 -DNDEBUG -flto" LDFLAGS="-flto -s" all
	@echo "✓ Raspberry Pi build ready: $(call binary_path,fastest,raspi)"

# Raspberry Pi 4 - cross-compile from x86_64
rpi4-cross:
	@echo "🥧 Cross-compiling for Raspberry Pi 4..."
	@$(MAKE) -f v3/Makefile ARCH=raspi TARGET=fastest CC=aarch64-linux-gnu-gcc \
	        CFLAGS="$(BASE_CFLAGS) -march=armv8-a+crc+simd -mtune=cortex-a72 -DARCH_ARM64=1 -O3 -DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1 -DNDEBUG -flto -static" \
	        LDFLAGS="-flto -s -static" \
	        all
	@echo "✓ Cross-compiled binary ready: $(call binary_path,fastest,raspi)"
	@echo "  Copy to RPi4: scp $(call binary_path,fastest,raspi) pi@raspberrypi.local:~/"

# Raspberry Pi 4 - maximum optimization
rpi4-fastest:
	@echo "🥧⚡ Building FASTEST for Raspberry Pi 4..."
	@$(MAKE) -f v3/Makefile ARCH=raspi TARGET=fastest CC=aarch64-linux-gnu-gcc \
	        CFLAGS="$(BASE_CFLAGS) -march=armv8-a+crc+simd+crypto -mtune=cortex-a72 -DARCH_ARM64=1 -O3 -DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1 -DNDEBUG -flto -funroll-loops -finline-functions" \
	        LDFLAGS="-flto -s -static" \
	        all
	@echo "✓ Raspberry Pi FASTEST build ready: $(call binary_path,fastest,raspi)"

# RISC-V 64 - cross-compile via clang (GCC fails to build this tree under
# -std=c99: a nanosleep visibility failure, not yet root-caused — see
# docs/reference/hosted-acceptance-test/README.md and
# docs/working/archive/session-logs/2026-07-24-punch-list.md item #3).
# CFLAGS deliberately does NOT reuse $(BASE_CFLAGS): BASE_CFLAGS hardcodes
# -std=c99, and clang needs -std=c11 -pthread here. This mirrors the exact
# recipe verified in the hosted-acceptance-test doc, kept in sync with it.
riscv64-clang:
	@echo "🦀 Cross-compiling for RISC-V 64 (clang)..."
	@$(MAKE) -f v3/Makefile ARCH=riscv64 TARGET=fastest \
	        CC="clang-18 --target=riscv64-linux-gnu --sysroot=/usr/riscv64-linux-gnu" \
	        CFLAGS="-std=c11 -pthread -Wall -Werror -Iv3/include -Ikernel/include -Iv3/src/word_source -Iv3/src/test_runner/include -DSTRICT_PTR=1 -march=rv64gc -mabi=lp64d -mcmodel=medany -DARCH_RISCV64=1 -O3 -DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1 -DNDEBUG -flto -static" \
	        LDFLAGS="-flto -s -static -fuse-ld=lld" \
	        all
	@echo "✓ Cross-compiled binary ready: $(call binary_path,fastest,riscv64)"
	@echo "  Run: qemu-riscv64 $(call binary_path,fastest,riscv64)"

# Minimal/embedded build
minimal:
	$(MAKE) -f v3/Makefile MINIMAL=1

# HISTORICAL: L4Re support removed as an active target (see "Platform
# support" above). This target cross-compiled a minimal build with the
# L4Re toolchain (l4-gcc) for smoke-testing without real L4Re headers.
# fake-l4re:
# 	$(MAKE) -f v3/Makefile MINIMAL=1 CC=l4-gcc CFLAGS="$(BASE_CFLAGS) -DL4RE_TARGET=1"

# ==============================================================================
# DEBUG & DEVELOPMENT BUILDS
# ==============================================================================

# Debug build with symbols
debug: banner
	@echo "🐛 Building DEBUG configuration..."
	$(MAKE) -f v3/Makefile CFLAGS="$(BASE_CFLAGS) -O0 -g -DDEBUG" LDFLAGS="" $(BINARY)

# Build with profiler support
profile:
	$(MAKE) -f v3/Makefile CFLAGS="$(BASE_CFLAGS) -g -O1" $(BINARY)

# Performance build (legacy alias - redirects to 'fastest')
performance: fastest

# ==============================================================================
# BUILD RULES
# ==============================================================================

# Generate version header from Makefile VERSION, ARCH, TARGET, and timestamp
v3/include/version.h: FORCE
	@mkdir -p v3/include
	@echo "Generating version header..."
	@BUILD_TS=$$(date -Iseconds 2>/dev/null || echo "unknown"); \
	printf '#ifndef STARFORTH_VERSION_H\n#define STARFORTH_VERSION_H\n\n' > $@; \
	printf '#define STARFORTH_VERSION "%s"\n' "$(VERSION)" >> $@; \
	printf '#define STARFORTH_ARCH "%s"\n' "$(ARCH_NAME)" >> $@; \
	printf '#define STARFORTH_TARGET "%s"\n' "$(TARGET)" >> $@; \
	printf '#define STARFORTH_TIMESTAMP "%s"\n' "$$BUILD_TS" >> $@; \
	printf '#define STARFORTH_VERSION_FULL "StarForth v%s %s %s %s"\n\n' "$(VERSION)" "$(ARCH_NAME)" "$(TARGET)" "$$BUILD_TS" >> $@; \
	printf '#endif /* STARFORTH_VERSION_H */\n' >> $@

# Ensure version.h exists and is up-to-date before compilation
FORCE:

# ==============================================================================
# CAPSULE SUBSYSTEM
# ==============================================================================

CAPSULES_DIR          ?= capsules
MKCAPSULE_SRC          = tools/mkcapsule.c
MKCAPSULE_BIN          = $(BUILD_DIR)/tools/mkcapsule
CAPSULE_GENERATED      = $(BUILD_DIR)/capsule_generated.c
CAPSULE_GENERATED_OBJ  = $(BUILD_DIR)/capsule_generated.o
CAPSULE_MANIFEST       = $(CAPSULES_DIR)/MANIFEST_AUTO.md

# Build the mkcapsule host tool
$(MKCAPSULE_BIN): $(MKCAPSULE_SRC)
	@mkdir -p $(dir $@)
	@echo "  HOSTCC  $<"
	@$(CC) -O2 -Wall -o $@ $<

# Run mkcapsule to generate the capsule directory C source
$(CAPSULE_GENERATED): $(MKCAPSULE_BIN) $(CAPSULES_DIR)
	@mkdir -p $(dir $@)
	@echo "  MKCAP   $(CAPSULES_DIR) -> $@"
	@$(MKCAPSULE_BIN) $(CAPSULES_DIR) $@

# Compile the generated capsule directory
$(CAPSULE_GENERATED_OBJ): $(CAPSULE_GENERATED) | v3/include/version.h
	@mkdir -p $(dir $@)
	@echo "  CC  $<"
	@$(CC) $(CFLAGS) -c $< -o $@

.PHONY: mkcapsule capsule-gen capsule-manifest
mkcapsule: $(MKCAPSULE_BIN)
capsule-manifest: $(MKCAPSULE_BIN)
	@echo "  MANIFEST $(CAPSULES_DIR) -> $(CAPSULE_MANIFEST)"
	@$(MKCAPSULE_BIN) --manifest $(CAPSULES_DIR) $(CAPSULE_MANIFEST)
capsule-gen: $(CAPSULE_GENERATED) capsule-manifest

# starkernel: full capsule-aware build
#   1. Build mkcapsule tool
#   2. Generate capsule_generated.c from capsules/
#   3. Compile StarForth with __STARKERNEL__ and capsule_generated.o linked in
starkernel: $(MKCAPSULE_BIN) $(CAPSULE_GENERATED_OBJ)
	@$(MAKE) -f v3/Makefile $(BINARY) \
		EXTRA_CFLAGS="-D__STARKERNEL__=1 -Iv3/include -Ikernel/include" \
		EXTRA_OBJ="$(CAPSULE_GENERATED_OBJ)"

# ==============================================================================
# Main executable
$(BINARY): $(OBJ) $(EXTRA_OBJ)
	@echo "🔗 Linking $(BINARY)..."
	@mkdir -p $(dir $@)
	$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)

# Pattern rule for all C files (consolidates 5 repetitive rules)
# Order-only dependency: version.h must exist before compilation, but changes don't trigger rebuild
$(BUILD_DIR)/%.o: v3/src/%.c | v3/include/version.h
	@mkdir -p $(dir $@)
	@echo "  CC  $<"
	@$(CC) $(CFLAGS) -c $< -o $@
ifdef ASM
	@$(CC) $(CFLAGS) -S $(ASM_SYNTAX) $< -o $(patsubst %.o,%.s,$@)
endif

# Files that include version.h - rebuild when timestamp changes
$(BUILD_DIR)/main.o: v3/include/version.h
$(BUILD_DIR)/cli.o: v3/include/version.h
$(BUILD_DIR)/word_source/starforth_words.o: v3/include/version.h

# ==============================================================================
# TESTING & BENCHMARKING
# ==============================================================================

# Quick benchmark
bench: $(BINARY)
	@echo ""
	@echo "🏇 Running quick-draw benchmark..."
	@echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
	@echo "Test: 1 million stack operations"
	@time -f "Time: %E seconds" ./$(BINARY) -c ": BENCH 1000000 0 DO 1 2 + DROP LOOP ; BENCH BYE" 2>/dev/null || echo "Benchmark complete"
	@echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

# Full benchmark suite
benchmark: $(BINARY)
	@echo ""
	@echo "🎯 Full Benchmark Suite - Fastest Forth in the West"
	@echo "════════════════════════════════════════════════════════════"
	@echo ""
	@echo "📊 Stack Operations (1M iterations):"
	@time -f "  ⏱️  %E seconds" ./$(BINARY) -c ": BENCH-STACK 1000000 0 DO 1 2 3 DROP DROP DROP LOOP ; BENCH-STACK BYE" 2>/dev/null || true
	@echo ""
	@echo "📊 Arithmetic Operations (1M iterations):"
	@time -f "  ⏱️  %E seconds" ./$(BINARY) -c ": BENCH-MATH 1000000 0 DO 10 20 + 5 * 100 / DROP LOOP ; BENCH-MATH BYE" 2>/dev/null || true
	@echo ""
	@echo "📊 Logic Operations (1M iterations):"
	@time -f "  ⏱️  %E seconds" ./$(BINARY) -c ": BENCH-LOGIC 1000000 0 DO 255 DUP AND DUP OR XOR DROP LOOP ; BENCH-LOGIC BYE" 2>/dev/null || true
	@echo ""
	@echo "════════════════════════════════════════════════════════════"

# ==============================================================================
# ASSEMBLY OUTPUT & ANALYSIS
# ==============================================================================

# Generate assembly files for inspection
asm: banner
	@echo "📝 Generating assembly output..."
	@echo "   Cleaning to force rebuild..."
	@$(MAKE) -f v3/Makefile clean-obj > /dev/null 2>&1
	$(MAKE) -f v3/Makefile ASM=1 CFLAGS="$(BASE_CFLAGS) $(ARCH_FLAGS) $(ARCH_DEFINES) -O3 -DUSE_ASM_OPT=1 -DUSE_DIRECT_THREADING=1" LDFLAGS="" $(BINARY)
	@echo "✓ Assembly files generated in $(BUILD_DIR)/"
	@echo "  Files generated: $$(find $(BUILD_DIR) -name '*.s' | wc -l)"
	@echo "  Example: less $(BUILD_DIR)/stack_management.s"

# Generate Software Bill of Materials (SBOM) in SPDX format
sbom:
	@echo "📋 Generating SBOM (Software Bill of Materials)..."
	@if ! which syft >/dev/null 2>&1; then \
		echo "Error: syft not found. Install from https://github.com/anchore/syft"; \
		exit 1; \
	fi
	@syft dir:. -o spdx-json=sbom.spdx.json -o spdx=sbom.spdx \
		--source-name LithosAnanke \
		--source-version $(VERSION) \
		--exclude './build/**' --exclude './tools/**' --exclude './.git/**'
	@echo "✅ SBOM generated:"
	@echo "   SPDX JSON: sbom.spdx.json"
	@echo "   SPDX:      sbom.spdx"
	@echo "   Version:   $(VERSION)"

# ==============================================================================
# DOCUMENTATION
# ==============================================================================

# Generate API documentation (Doxygen XML → AsciiDoc)
api-docs:
	@echo "📚 Generating API documentation from Doxygen..."
	@if [ ! -f scripts/generate-doxygen-appendix.sh ]; then \
		echo "Error: scripts/generate-doxygen-appendix.sh not found"; \
		exit 1; \
	fi
	@./scripts/generate-doxygen-appendix.sh
	@echo "✅ API documentation generated: docs/src/appendix/"

# ----------------------------------------------------------------------------
# Math companion to the SSRN paper.
# Builds docs/SSRN_companion/Math_Companion_SSRN.pdf from the LaTeX source.
# Requires: pdflatex (texlive-latex-base, texlive-latex-extra, texlive-science).
# ----------------------------------------------------------------------------
math-companion:
	@echo "Building SSRN math companion (docs/SSRN_companion/Math_Companion_SSRN.pdf)..."
	@if ! command -v pdflatex >/dev/null 2>&1; then \
		echo "Error: pdflatex not found."; \
		echo "  Install with: apt-get install texlive-latex-base texlive-latex-extra texlive-science"; \
		exit 1; \
	fi
	@cd docs/SSRN_companion && \
		pdflatex -interaction=nonstopmode -halt-on-error Math_Companion_SSRN.tex >/dev/null && \
		pdflatex -interaction=nonstopmode -halt-on-error Math_Companion_SSRN.tex >/dev/null && \
		pdflatex -interaction=nonstopmode -halt-on-error Math_Companion_SSRN.tex >/dev/null
	@echo "Built: docs/SSRN_companion/Math_Companion_SSRN.pdf"
	@cd docs/SSRN_companion && ls -lh Math_Companion_SSRN.pdf | awk '{print "  Size:", $$5, " Pages: see pdfinfo"}'

math-companion-clean:
	@rm -f docs/SSRN_companion/Math_Companion_SSRN.{aux,log,out,toc,lof,lot,nav,snm,vrb}
	@rm -f docs/SSRN_companion/Math_Companion_SSRN.pdf
	@echo "Cleaned math companion build artifacts."

# Convert all AsciiDoc to LaTeX
docs-latex:
	@echo "📄 Converting AsciiDoc to LaTeX..."
	@if [ ! -f scripts/asciidoc-to-latex.sh ]; then \
		echo "Error: scripts/asciidoc-to-latex.sh not found"; \
		exit 1; \
	fi
	@./scripts/asciidoc-to-latex.sh
	@echo "✅ LaTeX files generated: docs/latex/"

# Build and verify Isabelle theories
isabelle-build:
	@echo "🔬 Building Isabelle formal verification theories..."
	@if ! command -v $(ISABELLE) >/dev/null 2>&1; then \
		echo "Error: Isabelle not found: $(ISABELLE)"; \
		echo "  Install Isabelle or set ISABELLE variable"; \
		exit 1; \
	fi
	@echo "   Using: $$(command -v $(ISABELLE))"
	@echo "   Building StarForth_Formal session (VM + Physics)..."
	@cd docs/src/internal/formal && $(ISABELLE) build -v -d . StarForth_Formal
	@echo "✅ Isabelle theories verified successfully"

# Quick check of Isabelle theories (faster than full build)
isabelle-check:
	@echo "🔬 Quick-checking Isabelle theories..."
	@if ! command -v $(ISABELLE) >/dev/null 2>&1; then \
		echo "Error: Isabelle not found: $(ISABELLE)"; \
		exit 1; \
	fi
	@cd docs/src/internal/formal && \
		for thy in *.thy; do \
			echo "  Checking $$thy..."; \
			$(ISABELLE) process -T "$$thy" 2>&1 | head -5; \
		done
	@echo "✅ Quick check complete"

# Generate comprehensive Isabelle documentation (for auditing & CI/CD)
# This generates documentation and verification reports regardless of proof status
# Suitable for Jenkinsfile pipelines - shows complete state including errors
docs-isabelle:
	@echo "📚 Generating comprehensive Isabelle documentation (audit mode)..."
	@mkdir -p docs/src/isabelle
	@echo ""
	@echo "Step 1: Attempting to verify Isabelle theories..."
	@echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
	@cd docs/src/internal/formal && \
		if $(ISABELLE) build -v -d . StarForth_Formal > "$(CURDIR)/docs/src/isabelle/build.log" 2>&1; then \
			echo "✅ All theories verified successfully"; \
		else \
			echo "⚠️  Some theories have incomplete proofs (see build.log for details)"; \
		fi
	@echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
	@echo ""
	@echo "Step 2: Generating documentation from theory source..."
	@if [ ! -f scripts/isabelle-to-adoc.sh ]; then \
		echo "⚠️  Warning: scripts/isabelle-to-adoc.sh not found"; \
		echo "   Creating default script..."; \
		mkdir -p scripts; \
		echo '#!/bin/bash' > scripts/isabelle-to-adoc.sh; \
		echo 'echo "Generating Isabelle theory reports to AsciiDoc..."' >> scripts/isabelle-to-adoc.sh; \
		echo 'cd docs/src/internal/formal' >> scripts/isabelle-to-adoc.sh; \
		echo 'for thy in *.thy; do' >> scripts/isabelle-to-adoc.sh; \
		echo '  base=$$(basename "$$thy" .thy)' >> scripts/isabelle-to-adoc.sh; \
		echo '  echo "= $$base Theory" > "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \
		echo '  echo "" >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \
		echo '  echo "Formal verification theory for StarForth VM." >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \
		echo '  echo "" >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \
		echo '  echo "== Theory Source" >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \
		echo '  echo "" >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \
		echo '  echo "[source,isabelle]" >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \
		echo '  echo "----" >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \
		echo '  cat "$$thy" >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \
		echo '  echo "----" >> "../../../isabelle/$$base.adoc"' >> scripts/isabelle-to-adoc.sh; \
		echo 'done' >> scripts/isabelle-to-adoc.sh; \
		chmod +x scripts/isabelle-to-adoc.sh; \
	fi
	@bash scripts/isabelle-to-adoc.sh
	@echo ""
	@echo "Step 3: Creating audit summary..."
	@echo "✅ Comprehensive Isabelle documentation generated: docs/src/isabelle/"
	@echo ""
	@echo "📂 Documentation includes:"
	@echo "   • index.adoc - Master index of all theories"
	@echo "   • VERIFICATION_REPORT.adoc - Audit report"
	@echo "   • <Theory>.adoc - Individual theory documentation with source"
	@echo "   • build.log - Isabelle build output (verification status)"

# ==============================================================================
# REFINEMENT VERIFICATION (C ⊑ Isabelle)
# ==============================================================================

# Refinement status dashboard
refinement-status:
	@echo "════════════════════════════════════════════════════════════"
	@echo "  StarForth C ⊑ Isabelle Refinement Status"
	@echo "════════════════════════════════════════════════════════════"
	@echo ""
	@if [ ! -f docs/REFINEMENT_CAPA.adoc ]; then \
		echo "⚠️  No REFINEMENT_CAPA.adoc found. Initialize with: make refinement-init"; \
		echo ""; \
		echo "Refinement infrastructure status: UNINITIALIZED"; \
	else \
		echo "📋 Defect Summary:"; \
		echo ""; \
		echo -n "  Total Defects:    "; grep -c "^== DEFECT-" docs/REFINEMENT_CAPA.adoc || echo "0"; \
		echo -n "  OPEN:             "; grep "| OPEN" docs/REFINEMENT_CAPA.adoc | wc -l; \
		echo -n "  IN-PROGRESS:      "; grep "| IN-PROGRESS" docs/REFINEMENT_CAPA.adoc | wc -l; \
		echo -n "  CLOSED:           "; grep "| CLOSED" docs/REFINEMENT_CAPA.adoc | wc -l; \
	fi
	@echo ""
	@echo "🏆 Phase Status:"
	@echo "  Phase 1: vm.c core .............. NOT STARTED"
	@echo "  Phase 2: Root utilities ......... NOT STARTED"
	@echo "  Phase 3: Test harness .......... NOT STARTED"
	@echo "  Phase 4: Primitive dictionary .. NOT STARTED"
	@echo "  Phase 5: VM expansion .......... NOT STARTED"
	@echo ""
	@echo "📂 Documentation:"
	@echo "   • docs/REFINEMENT_CAPA.adoc - Defect tracking"
	@echo "   • docs/REFINEMENT_ANNOTATIONS.adoc - Code annotation guide"
	@echo ""
	@echo "Next steps: make refinement-phase1 (to start Phase 1 proofs)"
	@echo "════════════════════════════════════════════════════════════"

# Initialize refinement tracking
refinement-init:
	@echo "Initializing refinement tracking..."
	@if [ -f docs/REFINEMENT_CAPA.adoc ]; then \
		echo "✅ docs/REFINEMENT_CAPA.adoc already exists"; \
	else \
		echo "📋 Creating REFINEMENT_CAPA.adoc"; \
	fi
	@if [ -f docs/REFINEMENT_ANNOTATIONS.adoc ]; then \
		echo "✅ docs/REFINEMENT_ANNOTATIONS.adoc already exists"; \
	else \
		echo "📋 Creating REFINEMENT_ANNOTATIONS.adoc"; \
	fi
	@echo "✅ Refinement infrastructure ready"
	@echo ""
	@echo "Next: Review docs/REFINEMENT_ANNOTATIONS.adoc"
	@echo "      Then: make refinement-phase1"

# Phase 1: VM Core refinement
refinement-phase1:
	@echo "════════════════════════════════════════════════════════════"
	@echo "  ⚙️  PHASE 1: VM Core Refinement (C ⊑ Isabelle)"
	@echo "════════════════════════════════════════════════════════════"
	@echo ""
	@echo "Building refinement proofs for:"
	@echo "  • stack_push / stack_pop (DEFECT-001, DEFECT-002)"
	@echo "  • execute_call / execute_return (DEFECT-002)"
	@echo "  • instruction_dispatch (DEFECT-003)"
	@echo ""
	@echo "Proof target: StarForth_Refinement (Isabelle theory)"
	@if ! command -v $(ISABELLE) >/dev/null 2>&1; then \
		echo "Error: Isabelle not found. Install or set ISABELLE variable"; \
		exit 1; \
	fi
	@echo ""
	@echo "Step 1: Creating StarForth_Refinement.thy if needed..."
	@if [ ! -f docs/src/internal/formal/StarForth_Refinement.thy ]; then \
		echo "Creating template..."; \
		mkdir -p docs/src/internal/formal; \
		touch docs/src/internal/formal/StarForth_Refinement.thy; \
		echo "TODO: Add refinement proofs here" > docs/src/internal/formal/StarForth_Refinement.thy; \
	fi
	@echo ""
	@echo "Step 2: Attempting to build refinement proofs..."
	@echo "(This will fail until proofs are written)"
	@cd docs/src/internal/formal && \
		$(ISABELLE) build -v -d . StarForth_Formal 2>&1 | tail -20
	@echo ""
	@echo "📋 Next steps:"
	@echo "  1. Review open defects: grep 'OPEN' docs/REFINEMENT_CAPA.adoc"
	@echo "  2. Start working on DEFECT-001 (stack_push)"
	@echo "  3. Add refinement proofs to StarForth_Refinement.thy"
	@echo "  4. Run: make verify-defect DEFECT=001"

# Verify specific defect resolution
verify-defect:
	@if [ -z "$(DEFECT)" ]; then \
		echo "Usage: make verify-defect DEFECT=001"; \
		echo ""; \
		echo "Available defects:"; \
		grep "== DEFECT-" docs/REFINEMENT_CAPA.adoc | sed 's/== /  /'; \
		exit 1; \
	fi
	@echo "════════════════════════════════════════════════════════════"
	@echo "  🔍 Verifying Resolution: $(DEFECT)"
	@echo "════════════════════════════════════════════════════════════"
	@echo ""
	@if ! grep -q "== DEFECT-$(DEFECT):" docs/REFINEMENT_CAPA.adoc; then \
		echo "❌ DEFECT-$(DEFECT) not found in REFINEMENT_CAPA.adoc"; \
		exit 1; \
	fi
	@echo "Defect Details:"
	@grep -A 30 "== DEFECT-$(DEFECT):" docs/REFINEMENT_CAPA.adoc | head -35
	@echo ""
	@echo "Next: Implement corrective action, then run this again"

# Check code annotation coverage
refinement-annotate-check:
	@echo "════════════════════════════════════════════════════════════"
	@echo "  📝 Checking Code Annotation Coverage"
	@echo "════════════════════════════════════════════════════════════"
	@echo ""
	@echo "Phase 1 target functions:"
	@echo ""
	@for func in stack_push stack_pop execute_instruction execute_call execute_return; do \
		if grep -q "REFINEMENT.*$$func\|void $$func" v3/src/vm.c; then \
			if grep -q "REFINEMENT:" v3/src/vm.c | grep -q "$$func"; then \
				echo "  ✅ $$func - ANNOTATED"; \
			else \
				echo "  ❌ $$func - MISSING ANNOTATION"; \
			fi; \
		else \
			echo "  ⚠️  $$func - NOT FOUND"; \
		fi; \
	done
	@echo ""
	@echo "Guide: docs/REFINEMENT_ANNOTATIONS.adoc"

# Generate refinement report
refinement-report:
	@echo "🔄 Generating refinement status report..."
	@mkdir -p docs/reports
	@echo "# StarForth Refinement Status" > docs/reports/refinement-status.md
	@echo "" >> docs/reports/refinement-status.md
	@echo "Generated: $$(date)" >> docs/reports/refinement-status.md
	@echo "" >> docs/reports/refinement-status.md
	@echo "## Summary" >> docs/reports/refinement-status.md
	@echo "" >> docs/reports/refinement-status.md
	@grep -c "== DEFECT-" docs/REFINEMENT_CAPA.adoc | xargs -I {} echo "Total Defects: {}" >> docs/reports/refinement-status.md
	@echo "" >> docs/reports/refinement-status.md
	@echo "See docs/REFINEMENT_CAPA.adoc for details" >> docs/reports/refinement-status.md
	@echo "✅ Report: docs/reports/refinement-status.md"

# Generate GNU info documentation
docs/starforth.info:
	@if [ ! -f docs/starforth.texi ]; then \
		echo "⚠️  Skipping info docs: docs/starforth.texi not present"; \
		touch docs/starforth.info; \
	else \
		echo "📖 Building GNU info documentation..."; \
		if ! command -v makeinfo >/dev/null 2>&1; then \
			echo "Warning: makeinfo not found. Install with: sudo apt-get install texinfo"; \
			touch docs/starforth.info; \
		else \
			makeinfo docs/starforth.texi -o docs/starforth.info && \
			echo "✅ Info documentation: docs/starforth.info"; \
		fi; \
	fi

# Generate all documentation
docs: api-docs docs-isabelle
	@echo "✅ All documentation generated!"
	@echo ""
	@echo "📂 Documentation locations:"
	@echo "  • API docs:      docs/src/appendix/"
	@echo "  • Isabelle docs: docs/src/isabelle/"
	@echo "  • LaTeX:         docs/latex/"

# Clean generated documentation
clean-docs:
	@echo "🗑️  Cleaning generated documentation..."
	@rm -rf docs/src/appendix/ docs/latex/ docs/src/isabelle/
	@echo "✅ Documentation cleaned"

# ==============================================================================
# INSTALLATION
# ==============================================================================

install: $(BINARY)
	@echo "📦 Installing StarForth to $(PREFIX)..."
	@install -d $(BINDIR)
	@install -m 755 $(BINARY) $(BINDIR)/starforth
	@echo "   ✓ Binary installed: $(BINDIR)/starforth"
	@if [ -f capsules/init.4th ]; then \
		install -d $(CONFDIR); \
		install -m 644 capsules/init.4th $(CONFDIR)/init.4th; \
		echo "   ✓ Config installed: $(CONFDIR)/init.4th"; \
	fi
	@if [ -f man/starforth.1 ]; then \
		install -d $(MANDIR); \
		install -m 644 man/starforth.1 $(MANDIR)/starforth.1; \
		echo "   ✓ Man page installed: $(MANDIR)/starforth.1"; \
	fi
	@if [ -f docs/starforth.info ]; then \
		install -d $(INFODIR); \
		install -m 644 docs/starforth.info $(INFODIR)/starforth.info; \
		install-info --info-dir=$(INFODIR) $(INFODIR)/starforth.info 2>/dev/null || true; \
		echo "   ✓ Info docs installed: $(INFODIR)/starforth.info"; \
	fi
	@install -d $(DOCDIR)
	@if [ -f README.md ]; then install -m 644 README.md $(DOCDIR)/; fi
	@if [ -f QUICKSTART.md ]; then install -m 644 QUICKSTART.md $(DOCDIR)/; fi
	@if ls docs/*.md >/dev/null 2>&1; then install -m 644 docs/*.md $(DOCDIR)/ 2>/dev/null || true; fi
	@echo "   ✓ Documentation installed: $(DOCDIR)/"
	@echo "✅ Installation complete!"

uninstall:
	@echo "🗑️  Uninstalling StarForth from $(PREFIX)..."
	@rm -f $(BINDIR)/starforth
	@rm -f $(MANDIR)/starforth.1
	@install-info --delete --info-dir=$(INFODIR) $(INFODIR)/starforth.info 2>/dev/null || true
	@rm -f $(INFODIR)/starforth.info
	@rm -rf $(DOCDIR)
	@rm -rf $(CONFDIR)
	@echo "✅ Uninstall complete!"

# ==============================================================================
# PACKAGING
# ==============================================================================

# Build Debian package
deb: $(BINARY)
	@echo "📦 Building Debian package..."
	@if ! command -v fpm >/dev/null 2>&1; then \
		echo "Error: fpm not found. Install with: gem install fpm"; \
		echo "  or: sudo apt-get install ruby-dev && gem install fpm"; \
		exit 1; \
	fi
	@mkdir -p package/deb
	@fpm -s dir -t deb \
		-n starforth \
		-v $(VERSION) \
		-a native \
		--description "StarForth - The Fastest Forth in the West" \
		--url "https://github.com/yourusername/starforth" \
		--maintainer "StarForth Team" \
		--license "MIT" \
		--category "devel" \
		--package package/deb/ \
		--deb-compression xz \
		$(BINARY)=/usr/bin/starforth \
		capsules/init.4th=/etc/starforth/init.4th \
		README.md=/usr/share/doc/starforth/README.md
	@echo "✅ Debian package created: package/deb/starforth_$(VERSION)_*.deb"

# Build RPM package
rpm: $(BINARY)
	@echo "📦 Building RPM package..."
	@if ! command -v fpm >/dev/null 2>&1; then \
		echo "Error: fpm not found. Install with: gem install fpm"; \
		exit 1; \
	fi
	@mkdir -p package/rpm
	@fpm -s dir -t rpm \
		-n starforth \
		-v $(VERSION) \
		-a native \
		--description "StarForth - The Fastest Forth in the West" \
		--url "https://github.com/yourusername/starforth" \
		--maintainer "StarForth Team" \
		--license "MIT" \
		--category "Development/Languages" \
		--package package/rpm/ \
		$(BINARY)=/usr/bin/starforth \
		capsules/init.4th=/etc/starforth/init.4th \
		README.md=/usr/share/doc/starforth/README.md
	@echo "✅ RPM package created: package/rpm/starforth-$(VERSION)-*.rpm"

# Build all packages
package: deb rpm
	@echo ""
	@echo "✅ All packages built successfully!"
	@echo "  Debian: package/deb/"
	@echo "  RPM:    package/rpm/"

# ==============================================================================
# VERSIONING & BUILD MANIFEST (Workflow/Governance Integration)
# ==============================================================================

# Parse version from v3/include/version.h
GET_VERSION = $(shell grep "define STARFORTH_VERSION" v3/include/version.h | grep -o '[0-9]\+\.[0-9]\+\.[0-9]\+' | head -1)

.PHONY: version-info
version-info:
	@echo "Current Version: $(GET_VERSION)"
	@grep "STARFORTH_VERSION" v3/include/version.h | grep define

.PHONY: build-manifest
build-manifest: $(BINARY)
	@echo "📋 Generating BUILD_MANIFEST.json..."
	@mkdir -p builds/manifest
	@VERSION=$$(grep "STARFORTH_VERSION_STRING" v3/include/version.h | grep -o '"[^"]*"' | tr -d '"'); \
	GIT_HASH=$$(git rev-parse --short HEAD 2>/dev/null || echo "0000000"); \
	GIT_BRANCH=$$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown"); \
	BUILD_TIME=$$(date -Iseconds); \
	BINARY_PATH="$(BINARY)"; \
	if [ -f "$$BINARY_PATH" ]; then \
		BINARY_SHA256=$$(sha256sum "$$BINARY_PATH" | awk '{print $$1}'); \
	else \
		BINARY_SHA256="not-built"; \
	fi; \
	cat > builds/manifest/BUILD_MANIFEST.json << EOF; \
{ \
  "version": "$$VERSION", \
  "timestamp": "$$BUILD_TIME", \
  "git": { \
    "commit_hash": "$$GIT_HASH", \
    "branch": "$$GIT_BRANCH", \
    "tag": "v$$VERSION" \
  }, \
  "build": { \
    "timestamp": "$$BUILD_TIME", \
    "compiler": "$$(gcc --version | head -1)", \
    "compiler_flags": "$(CFLAGS) $(ARCH_FLAGS)", \
    "target_architecture": "$(ARCH_NAME)", \
    "use_asm_opt": $(USE_ASM_OPT), \
    "use_lto": $(USE_LTO) \
  }, \
  "testing": { \
    "total_tests": 936, \
    "passed_tests": 0, \
    "failed_tests": 0, \
    "code_coverage_percent": 0, \
    "smoke_test_result": "N/A" \
  }, \
  "binary": { \
    "path": "$$BINARY_PATH", \
    "sha256": "$$BINARY_SHA256" \
  }, \
  "signatures": { \
    "builder": "jenkins-agent", \
    "approval_timestamp": "$(shell date -Iseconds)" \
  } \
} \
EOF
	@echo "✓ BUILD_MANIFEST created: builds/manifest/BUILD_MANIFEST.json"
	@cat builds/manifest/BUILD_MANIFEST.json | head -20

# ==============================================================================
# CLEANUP
# ==============================================================================

clean:
	@echo "🧹 Cleaning build artifacts..."
	@rm -rfv build/*
	@rm -f v3/src/*.gcda v3/src/word_source/*.gcda v3/src/*.gcno v3/src/word_source/*.gcno
	@echo "✓ Clean complete"

clean-obj:
	@echo "🧹 Cleaning object files..."
	@find build -name "*.o" -type f -delete 2>/dev/null || true
	@echo "✓ Object files cleaned"

# ==============================================================================
# HELP
# ==============================================================================

help:
	@echo "════════════════════════════════════════════════════════════"
	@echo "   ⚡ StarForth Build System - Fastest in the West! ⚡"
	@echo "════════════════════════════════════════════════════════════"
	@echo ""
	@echo "🛠️  Variables:"
	@echo "  ARCH=<arch>      (default: $(ARCH); canonical directory: $(ARCH_DIR))"
	@echo "  TARGET=<profile> (standard | fast | fastest | turbo)"
	@echo ""
	@echo "🏆 OPTIMIZATION BUILDS:"
	@echo "  fastest         - Maximum performance (ASM + direct threading + LTO)"
	@echo "  fast            - Fast without LTO (easier debugging)"
	@echo "  turbo           - Assembly optimizations only"
	@echo ""
	@echo "🥧 PLATFORM-SPECIFIC BUILDS:"
	@echo "  rpi4            - Native build on Raspberry Pi 4"
	@echo "  rpi4-cross      - Cross-compile from x86_64"
	@echo "  rpi4-fastest    - Maximum optimization for RPi4"
	@echo "  riscv64-clang   - Cross-compile for RISC-V 64 via clang (GCC fails here)"
	@echo "  minimal         - Minimal/embedded build"
	@echo ""
	@echo "🔧 DEBUG & DEVELOPMENT:"
	@echo "  all             - Standard optimized build (default)"
	@echo "  debug           - Debug build with symbols (-g -O0)"
	@echo "  profile         - Build with profiling support"
	@echo "  performance     - Legacy performance build (use 'fastest')"
	@echo ""
	@echo "🧪 TESTING & BENCHMARKING:"
	@echo "  test            - Run full test suite"
	@echo "  bench           - Quick benchmark"
	@echo "  benchmark       - Full benchmark suite"
	@echo ""
	@echo "📝 ANALYSIS & DOCUMENTATION:"
	@echo "  asm             - Generate assembly output"
	@echo "  sbom            - Generate SBOM (Software Bill of Materials, SPDX)"
	@echo "  docs            - Generate all documentation"
	@echo "  api-docs        - Generate API docs (Doxygen → AsciiDoc)"
	@echo "  docs-latex      - Convert AsciiDoc to LaTeX"
	@echo "  docs-isabelle   - Generate Isabelle documentation for auditing (includes build log)"
	@echo "  isabelle-build  - Build and verify Isabelle formal theories (strict verification)"
	@echo "  isabelle-check  - Quick-check Isabelle theories"
	@echo "  info            - Build GNU info documentation"
	@echo ""
	@echo "🔬 REFINEMENT VERIFICATION (C ⊑ Isabelle):"
	@echo "  refinement-init - Initialize refinement tracking (CAPA + annotations)"
	@echo "  refinement-status - Show refinement defect summary and phase status"
	@echo "  refinement-phase1 - Build Phase 1 refinement proofs (vm.c core)"
	@echo "  verify-defect   - Verify specific defect (Usage: make verify-defect DEFECT=001)"
	@echo "  refinement-annotate-check - Check code annotation coverage"
	@echo "  refinement-report - Generate refinement status report"
	@echo ""
	@echo "📦 INSTALLATION & PACKAGING:"
	@echo "  install         - Install to PREFIX (default: .)"
	@echo "  uninstall       - Uninstall from PREFIX"
	@echo "  deb             - Build Debian package"
	@echo "  rpm             - Build RPM package"
	@echo "  package         - Build all packages"
	@echo ""
	@echo "🧹 CLEANUP:"
	@echo "  clean           - Remove all build artifacts"
	@echo "  clean-obj       - Remove object files only"
	@echo "  clean-docs      - Remove generated documentation"
	@echo ""
	@echo "⚙️  CONFIGURATION VARIABLES:"
	@echo "  CC              - Compiler (default: gcc)"
	@echo "  CFLAGS          - Compiler flags (override defaults)"
	@echo "  LDFLAGS         - Linker flags (override defaults)"
	@echo "  PREFIX          - Install prefix (default: .)"
	@echo "  MINIMAL=1       - Minimal build mode (DISABLED -- incomplete, errors out)"
	@echo "  ASM=1           - Generate assembly files during build"
	@echo ""
	@echo "📋 EXAMPLES:"
	@echo "  make fastest                    # Fastest build for current platform"
	@echo "  make rpi4-cross                 # Cross-compile for Raspberry Pi 4"
	@echo "  make riscv64-clang              # Cross-compile for RISC-V 64 (clang)"
	@echo "  make asm                        # Generate assembly for inspection"
	@echo "  make debug                      # Debug build"
	@echo "  make PREFIX=/usr/local install  # Install system-wide"
	@echo ""
	@echo "🌟 CURRENT PLATFORM: $(ARCH_NAME)"
	@echo "════════════════════════════════════════════════════════════"

quality: compile_commands clang_tidy cppcheck gcc_analyzer

compile_commands:
	bear --output build/compile_commands.json -- make -j$(nproc)

clang_tidy:
	clang-tidy -p build $(shell find src -name '*.c' -o -name '*.h') | tee build/clang-tidy.txt

cppcheck:
	cppcheck --enable=all --inconclusive --std=c99 src 2> build/cppcheck.txt || true

gcc_analyzer:
	$(MAKE) -f v3/Makefile clean
	CFLAGS="$(CFLAGS) -fanalyzer -Wall -Wextra" $(MAKE) -f v3/Makefile all 2>&1 | tee build/gcc-analyzer.txt
