Files
LithosAnanake/docs/working/experiments/02-experiments/L8_WIRING_PLAN.md
T

11 KiB
Raw Blame History

L8 Control Wiring Implementation Plan

Status: Infrastructure exists but execution paths are not gated Date: 2025-12-05 Issue: L8 Jacquard mode selector computes 4-bit control signals (L2/L3/L5/L6) but feedback loops ignore them


Executive Summary

The L8 Steady State Machine (SSM) infrastructure is 90% complete but the final 10% is critical:

Complete:

  • L8 state machine (ssm_jacquard.c) computes 4-bit mode (C0-C15) based on entropy/CV/temporal metrics
  • ssm_apply_mode() sets L2/L3/L5/L6 flags in vm->ssm_config
  • vm_heartbeat_update_l8() updates L8 state every heartbeat cycle (lines 993-1045 in vm.c)
  • Mode transitions logged with debug info

Missing:

  • Feedback loop execution paths don't check ssm_config flags before running
  • L2/L3/L5/L6 run unconditionally despite L8 setting mode bits
  • Result: L8 is a "supervisor without authority"

Current Architecture (From DoE Analysis)

From Makefile:112-127 and ssm_jacquard.h:32-51:

Loop Name Status Control
L1 Heat Tracking ALWAYS OFF Disabled (harmful in 86% of configs)
L2 Rolling Window NEEDS GATING L8 bit 3 (entropy-driven)
L3 Linear Decay NEEDS GATING L8 bit 2 (temporal locality)
L4 Pipelining Metrics ALWAYS OFF Disabled (harmful in 100% of configs)
L5 Window Inference NEEDS GATING L8 bit 1 (CV-driven)
L6 Decay Inference NEEDS GATING L8 bit 0 (CV + temporal)
L7 Adaptive Heartrate ALWAYS ON Enabled (beneficial in 71% of configs)
L8 Jacquard Selector IMPLEMENTED 4-bit mode controller (C0-C15)

Key Insight: L1 and L4 are compile-time disabled. L7 is always-on. L2/L3/L5/L6 need runtime gating based on L8 mode bits.


Required Changes (4 Locations)

1. L2: Rolling Window Recording (src/rolling_window_of_truth.c)

Current Code (rolling_window_record_execution() line 181):

int rolling_window_record_execution(RollingWindowOfTruth* window, uint32_t word_id)
{
    if (!window || !window->execution_history)
        return -1;

    window->execution_history[window->window_pos] = word_id;
    // ... always executes
}

Required Change: Add check at function entry or call site. Two options:

Option A (Preferred - Clean separation): Modify call site in src/vm.c:1652:

// Before (line 1652):
rolling_window_record_execution(&vm->rolling_window, word_id);

// After:
if (vm->ssm_config && ((ssm_config_t*)vm->ssm_config)->L2_rolling_window) {
    rolling_window_record_execution(&vm->rolling_window, word_id);
}

Option B (Alternative - Gate inside function): Modify rolling_window_record_execution() to accept VM pointer and check internally.

Files to modify:

  • src/vm.c:1652 - Call site in execute_word()
  • src/rolling_window_of_truth.c:181 - Consider adding VM* parameter for internal check
  • include/rolling_window_of_truth.h - Update function signature if Option B chosen

2. L3: Linear Decay Application (src/physics_metadata.c)

Current Code (line 176):

/* L8 FINAL INTEGRATION: L3 linear decay is always-on */
void physics_metadata_apply_linear_decay(DictEntry *entry, uint64_t elapsed_ns, VM *vm) {
    if (!entry || !vm) {
        return;
    }

    /* Frozen words do not decay */
    if (entry->flags & WORD_FROZEN) {
        return;
    }

    /* Don't bother with insignificant time intervals */
    if (elapsed_ns < DECAY_MIN_INTERVAL) {
        return;
    }

    // ... decay logic always executes
}

Required Change: Add L3 gate check after frozen/interval checks:

/* L8 FINAL INTEGRATION: L3 linear decay - runtime gated by Jacquard */
void physics_metadata_apply_linear_decay(DictEntry *entry, uint64_t elapsed_ns, VM *vm) {
    if (!entry || !vm) {
        return;
    }

    /* Frozen words do not decay */
    if (entry->flags & WORD_FROZEN) {
        return;
    }

    /* Don't bother with insignificant time intervals */
    if (elapsed_ns < DECAY_MIN_INTERVAL) {
        return;
    }

    /* L8 GATE: Check if L3 (linear decay) is enabled by Jacquard mode */
    if (vm->ssm_config) {
        ssm_config_t *config = (ssm_config_t*)vm->ssm_config;
        if (!config->L3_linear_decay) {
            return;  /* L3 disabled by L8 mode selector */
        }
    }

    // ... existing decay logic
}

Files to modify:

  • src/physics_metadata.c:176 - Add L3 gate check (line ~188-193)
  • Comment update at line 175 to reflect runtime gating

Call sites (no changes needed, gate is internal):

  • src/vm.c:737 - vm_tick_apply_background_decay() (heartbeat loop)
  • Other call sites inherit the gate

3. L5: Window Width Inference (src/inference_engine.c)

Current Code (line 538):

/* === PHASE 2C: Window Width Inference === */
/* L8 FINAL INTEGRATION: Loop #5 always-on */
uint32_t inferred_width = find_variance_inflection(
    trajectory,
    traj_len,
    current_variance
);

Required Change: Gate inference execution with conditional block:

/* === PHASE 2C: Window Width Inference === */
/* L8 FINAL INTEGRATION: Loop #5 runtime-gated by Jacquard */
uint32_t inferred_width = outputs->adaptive_window_width;  /* Keep previous value as default */

if (inputs->vm && inputs->vm->ssm_config) {
    ssm_config_t *config = (ssm_config_t*)inputs->vm->ssm_config;
    if (config->L5_window_inference) {
        /* L5 enabled - run inference */
        inferred_width = find_variance_inflection(
            trajectory,
            traj_len,
            current_variance
        );
    }
    /* else: L5 disabled, keep previous adaptive_window_width */
}

Files to modify:

  • src/inference_engine.c:538-544 - Add L5 conditional gate
  • Comment update at line 539 to reflect runtime gating

4. L6: Decay Slope Inference (src/inference_engine.c)

Current Code (line 546):

/* === PHASE 2D: Decay Slope Inference === */
/* L8 FINAL INTEGRATION: Loop #6 always-on */
uint64_t inferred_slope = infer_decay_slope_q48(trajectory, traj_len);

Required Change: Gate inference execution with conditional block:

/* === PHASE 2D: Decay Slope Inference === */
/* L8 FINAL INTEGRATION: Loop #6 runtime-gated by Jacquard */
uint64_t inferred_slope = outputs->adaptive_decay_slope;  /* Keep previous value as default */

if (inputs->vm && inputs->vm->ssm_config) {
    ssm_config_t *config = (ssm_config_t*)inputs->vm->ssm_config;
    if (config->L6_decay_inference) {
        /* L6 enabled - run inference */
        inferred_slope = infer_decay_slope_q48(trajectory, traj_len);
    }
    /* else: L6 disabled, keep previous adaptive_decay_slope */
}

Files to modify:

  • src/inference_engine.c:546-548 - Add L6 conditional gate
  • Comment update at line 547 to reflect runtime gating

  1. L3 (Decay) - Simplest, internal to one function

    • Modify src/physics_metadata.c:176-220
    • Add gate check after line 188
    • Test with mode C0 (all off) vs C4 (L3 on)
  2. L5 (Window Inference) - Medium complexity

    • Modify src/inference_engine.c:538-544
    • Preserve previous value when disabled
    • Test early-exit behavior with mode C0 vs C2
  3. L6 (Decay Inference) - Similar to L5

    • Modify src/inference_engine.c:546-548
    • Preserve previous value when disabled
    • Test with mode C0 vs C1
  4. L2 (Rolling Window) - Most complex (data structure)

    • Decide on Option A (call-site gate) vs Option B (internal gate)
    • Modify call site src/vm.c:1652 OR function signature
    • Test with mode C0 vs C8 (high entropy)

Testing Strategy

Unit Tests

For each loop, verify:

  1. Mode C0 (0000): All loops disabled → function returns early or skips logic
  2. Single-bit modes: Enable one loop at a time (C1, C2, C4, C8)
  3. Top 5% modes: C4, C7, C9, C11, C12 work correctly

Integration Tests

  1. Mode transitions: L8 changes mode → loops respond correctly
  2. Hysteresis: Mode doesn't flap rapidly (5-tick delay)
  3. Metrics: Verify entropy/CV/temporal drive correct mode selection

Regression Tests

  1. Run existing 936+ test suite with different L8 modes
  2. Verify deterministic behavior (no variance across runs)
  3. Check performance matches DoE predictions

Validation Checklist

After implementation, verify:

  • L2: rolling_window_record_execution() only executes when L2 bit set
  • L3: physics_metadata_apply_linear_decay() returns early when L3 bit clear
  • L5: find_variance_inflection() only called when L5 bit set
  • L6: infer_decay_slope_q48() only called when L6 bit set
  • L8: Mode transitions logged in debug output
  • All existing tests pass with default mode (C0 or C12)
  • DoE experiment can reproduce 128 configurations via L8 mode forcing

Impact Assessment

Lines of code changed: ~40-60 lines across 3 files Risk: Low (adding guards, not changing logic) Performance: Neutral to positive (skips work when disabled) Correctness: High confidence (L8 already validated via DoE)


Open Questions

  1. Default mode: Should VM initialize with C0 (minimal) or C12 (DoE optimal)?

    • Current: ssm_l8_init(..., SSM_MODE_C0) in vm.c:387
    • Top 5%: C4, C7, C9, C11, C12
    • Recommendation: Start C0, let L8 adapt
  2. Mode override: Should FORTH words exist to force L8 mode?

    • L8-MODE! to set mode manually
    • L8-MODE@ to query current mode
    • Useful for debugging/benchmarking
  3. Metrics exposure: Should heartbeat CSV export include L8 mode?

    • Currently exports: executions, heat, variance, slope
    • Add: l8_mode column (0-15 integer)

Success Criteria

Definition of Done:

  1. All 4 feedback loops check ssm_config flags before executing
  2. L8 mode selector controls L2/L3/L5/L6 runtime behavior
  3. Mode transitions visible in debug logs
  4. Existing test suite passes with default L8 mode
  5. Can reproduce DoE configurations by forcing L8 modes 0-15

Verification:

# Build with L8 control wired
make clean && make fastest

# Test mode C0 (all off) - should be fastest, least adaptive
./build/amd64/fastest/starforth --benchmark 1000

# Test mode C15 (all on) - should be slowest, most adaptive
# (requires word to set L8 mode or environment variable)

# Run full test suite
make test

Next Steps

Before coding:

  1. Review this plan with stakeholders
  2. Decide on L2 implementation strategy (Option A vs B)
  3. Choose default L8 mode (C0 vs C12)

During implementation:

  1. Follow order: L3 → L5 → L6 → L2
  2. Test each loop individually before moving to next
  3. Update comments to reflect runtime gating

After completion:

  1. Run full DoE validation (128 configs × 300 reps)
  2. Verify 0% algorithmic variance maintained
  3. Document L8 control API for future developers