# SPDX-License-Identifier: LSL-1.0 # Copyright (c) 2026 Markus Maiwald # Stewardship: Self Sovereign Society Foundation # # This file is part of the Nexus Sovereign Core. # See legal/LICENSE_SOVEREIGN.md for license terms. ## Rumpk Layer 1: The Reactive Dispatcher (The Tyrant) ## ## Implements the Silence Doctrine (SPEC-102). ## - No Tick. ## - No Policy. ## - Only Physics. {.push stackTrace: off, lineTrace: off.} import fiber # We might need to access the Fiber globals from fiber.nim # fiber.nim exports current_fiber, but we need to iterate them. # Currently kernel.nim manages the specific fibers variables (fiber_ion, fiber_ui, etc.) # We need a centralized registry or a way to iterate. # # For the first pass, we can replicate the logic in kernel.nim which explicitly checks # the known fibers, but structured as the Spectrum loop. # Or we can make kernel.nim pass the fibers to us. # # Let's keep it simple and stateless in sched.nim if possible, or have it manage the queue. # Since kernel.nim holds the variables, sched.nim should probably define the *Strategy* # and kernel.nim calls it, OR sched.nim should import kernel (circular!). # # Better: fiber.nim holds a linked list of fibers? # Or sched.nim is just a helper module that kernel.nim uses. # Let's define the Strategy here. # To avoid circular imports, kernel.nim will likely INCLUDE sched.nim or sched.nim # will act on a passed context. # BUT, SPEC-102 implies sched.nim *is* the logic. # # Let's define the Harmonic logic. # We need access to `current_fiber` (from fiber.nim) and `get_now_ns` (helper). proc sched_get_now_ns*(): uint64 {.importc: "rumpk_timer_now_ns", cdecl.} proc console_write(p: pointer, len: csize_t) {.importc: "hal_console_write", cdecl.} proc uart_print_hex(v: uint64) {.importc: "uart_print_hex", cdecl.} proc uart_print_hex8(v: uint8) {.importc: "uart_print_hex8", cdecl.} # M4.5: STL emission for budget violations (The Ratchet) proc emit_policy_violation*(fiber_id, budget_ns, actual_ns: uint64, violation_count: uint32, cause_id: uint64): uint64 {.importc, cdecl.} # Forward declaration — implementation is in THE RATCHET section below proc sched_analyze_burst*(f: ptr FiberObject, burst_ns: uint64) var photon_idx, matter_idx, gravity_idx, void_idx: int # Forward declaration for channel data check (provided by kernel/channels) proc fiber_can_run_on_channels*(id: uint64, mask: uint64): bool {.importc, cdecl.} proc is_runnable(f: ptr FiberObject, now: uint64): bool = if f == nil or f.state.sp == 0: return false # Can only run initialized fibers if now < f.sleep_until: return false if f.is_blocked: if fiber_can_run_on_channels(f.id, f.blocked_on_mask): f.is_blocked = false # Latched unblock return true return false return true proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool = let now = sched_get_now_ns() # ========================================================= # Phase 1: PHOTON (Hard Real-Time / Hardware Driven) # ========================================================= var run_photon = false for i in 0.. now: if f.sleep_until < min_wakeup: min_wakeup = f.sleep_until return min_wakeup # ========================================================= # M4.5: Budget Defaults Per Spectrum Tier # ========================================================= proc default_budget_for_spectrum*(s: Spectrum): uint64 = ## Return the default budget_ns for a given Spectrum tier case s of Spectrum.Photon: return 2_000_000'u64 # 2ms — hard real-time of Spectrum.Matter: return 10_000_000'u64 # 10ms — interactive of Spectrum.Gravity: return 50_000_000'u64 # 50ms — batch of Spectrum.Void: return 0'u64 # unlimited — scavenger # ========================================================= # THE RATCHET (Post-Execution Analysis) # ========================================================= proc sched_log(msg: string) = if msg.len > 0: console_write(unsafeAddr msg[0], csize_t(msg.len)) proc sched_demote(f: ptr FiberObject) = ## Demote a fiber to a lower Spectrum tier let current = f.getSpectrum() case current: of Spectrum.Photon: f.setSpectrum(Spectrum.Matter) console_write(cstring("[Ratchet] DEMOTED fiber to Matter\n"), 34) of Spectrum.Matter: f.setSpectrum(Spectrum.Gravity) console_write(cstring("[Ratchet] DEMOTED fiber to Gravity\n"), 35) of Spectrum.Gravity: f.setSpectrum(Spectrum.Void) console_write(cstring("[Ratchet] DEMOTED fiber to Void\n"), 32) of Spectrum.Void: discard # Already at the bottom proc sched_analyze_burst*(f: ptr FiberObject, burst_ns: uint64) = ## Analyze the burst duration of a fiber after it yields/switches ## Implements the 3-strike rule for budget violations if f == nil: return f.last_burst_ns = burst_ns # Update moving average (exponential: 75% old, 25% new) if f.avg_burst == 0: f.avg_burst = burst_ns else: f.avg_burst = (f.avg_burst * 3 + burst_ns) div 4 # Budget enforcement if f.budget_ns > 0 and burst_ns > f.budget_ns: f.violations += 1 discard emit_policy_violation(f.id, f.budget_ns, burst_ns, f.violations, 0) console_write(cstring("[Ratchet] Budget violation fiber=0x"), 33) uart_print_hex(f.id) console_write(cstring(" burst="), 7) uart_print_hex(burst_ns) console_write(cstring(" budget="), 8) uart_print_hex(f.budget_ns) console_write(cstring(" strikes="), 9) uart_print_hex(uint64(f.violations)) console_write(cstring("\n"), 1) if f.violations >= 3: sched_demote(f) f.violations = 0 # Reset after demotion {.pop.}