From 453a7f2a12f4517f4c108ab5566d3294435acb98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20K=C3=B6lnberger?= <159939812+ProfRandom92@users.noreply.github.com> Date: Fri, 15 May 2026 12:05:42 -0700 Subject: [PATCH] Add compression signal engine --- .../src/core/foundation/compressionSignals.ts | 346 ++++++++++++++++++ dashboard/app/src/core/foundation/index.ts | 1 + .../app/src/core/foundation/sampleData.ts | 63 ++++ docs/compression_signal_engine.md | 166 +++++++++ tests/test_compression_signals_ts.py | 183 +++++++++ 5 files changed, 759 insertions(+) create mode 100644 dashboard/app/src/core/foundation/compressionSignals.ts create mode 100644 docs/compression_signal_engine.md create mode 100644 tests/test_compression_signals_ts.py diff --git a/dashboard/app/src/core/foundation/compressionSignals.ts b/dashboard/app/src/core/foundation/compressionSignals.ts new file mode 100644 index 0000000..56c8482 --- /dev/null +++ b/dashboard/app/src/core/foundation/compressionSignals.ts @@ -0,0 +1,346 @@ +import type { JsonValue } from './shared'; + +export type CognitiveMode = 'habit' | 'monitor' | 'deliberation' | 'rollback_required'; + +export interface CompressionSignalInput { + executionId: string; + windowId: string; + timestamp: string; + profileId: string; + compressionRatio: number; + baselineCompressionRatio: number; + sparseFrameRate: number; + baselineSparseFrameRate: number; + unseenSignatureRate: number; + tokenEstimate?: number; + baselineTokenEstimate?: number; + replayConsistency?: number; + baselineReplayConsistency?: number; + metadata?: Record; +} + +export interface CompressionSignalProfile { + profileId: string; + medianCompressionRatio: number; + medianSparseFrameRate: number; + medianUnseenSignatureRate: number; + medianTokenEstimate?: number; + medianReplayConsistency?: number; + sampleCount: number; + updatedAt: string; +} + +export interface CompressionSignalWeights { + compressionRatioDrop: number; + sparseFrameSpike: number; + unseenSignatureRate: number; + tokenCostSpike?: number; + replayConsistencyDrop?: number; +} + +export interface CompressionSignalThresholds { + habitMax: number; + monitorMax: number; + deliberationMin: number; + rollbackRequiredMin: number; +} + +export interface CompressionSignalHysteresis { + enterDeliberation: number; + exitDeliberation: number; +} + +export interface CompressionSignalDebounce { + requiredWindows: number; + ofLast: number; +} + +export interface CompressionSignalWindow { + input: CompressionSignalInput; + compressionRatioDrop: number; + sparseFrameSpike: number; + unseenSignatureSignal: number; + tokenCostSpike?: number; + replayConsistencyDrop?: number; + predictionError: number; +} + +export type CompressionSignalReason = + | 'compression_ratio_drop exceeded baseline' + | 'sparse_frame_rate increased' + | 'unseen_signature_rate elevated' + | 'unseen_signature_rate remained elevated across debounce window' + | 'token_cost increased' + | 'replay_consistency dropped' + | 'deliberation debounce pending' + | 'deliberation debounce satisfied' + | 'prediction_error below exit threshold' + | 'prediction_error reached rollback threshold' + | 'prediction_error within habit band' + | 'prediction_error within monitor band'; + +export interface CompressionSignalResult { + executionId: string; + windowId: string; + timestamp: string; + profileId: string; + mode: CognitiveMode; + predictionError: number; + triggered: boolean; + window: CompressionSignalWindow; + reasons: readonly CompressionSignalReason[]; +} + +export const defaultCompressionSignalWeights: CompressionSignalWeights = Object.freeze({ + compressionRatioDrop: 0.35, + sparseFrameSpike: 0.35, + unseenSignatureRate: 0.3, +}); + +export const defaultCompressionSignalThresholds: CompressionSignalThresholds = Object.freeze({ + habitMax: 0.3, + monitorMax: 0.55, + deliberationMin: 0.62, + rollbackRequiredMin: 0.85, +}); + +export const defaultCompressionSignalHysteresis: CompressionSignalHysteresis = Object.freeze({ + enterDeliberation: 0.62, + exitDeliberation: 0.42, +}); + +export const defaultCompressionSignalDebounce: CompressionSignalDebounce = Object.freeze({ + requiredWindows: 2, + ofLast: 3, +}); + +function clamp01(value: number): number { + if (!Number.isFinite(value)) { + return 0; + } + return Math.max(0, Math.min(1, value)); +} + +function roundSignal(value: number): number { + return Math.round((clamp01(value) + Number.EPSILON) * 1000) / 1000; +} + +export function calculateCompressionRatioDrop(compressionRatio: number, baselineCompressionRatio: number): number { + if (!Number.isFinite(baselineCompressionRatio) || baselineCompressionRatio <= 0) { + return 0; + } + return roundSignal((baselineCompressionRatio - compressionRatio) / baselineCompressionRatio); +} + +export function calculateSparseFrameSpike(sparseFrameRate: number, baselineSparseFrameRate: number): number { + if (!Number.isFinite(sparseFrameRate)) { + return 0; + } + if (!Number.isFinite(baselineSparseFrameRate) || baselineSparseFrameRate <= 0) { + return roundSignal(sparseFrameRate); + } + return roundSignal((sparseFrameRate - baselineSparseFrameRate) / baselineSparseFrameRate); +} + +export function calculateUnseenSignatureSignal(unseenSignatureRate: number): number { + return roundSignal(unseenSignatureRate); +} + +export function calculateTokenCostSpike(tokenEstimate?: number, baselineTokenEstimate?: number): number { + if (tokenEstimate === undefined || baselineTokenEstimate === undefined || !Number.isFinite(baselineTokenEstimate) || baselineTokenEstimate <= 0) { + return 0; + } + return roundSignal((tokenEstimate - baselineTokenEstimate) / baselineTokenEstimate); +} + +export function calculateReplayConsistencyDrop(replayConsistency?: number, baselineReplayConsistency?: number): number { + if (replayConsistency === undefined || baselineReplayConsistency === undefined || !Number.isFinite(baselineReplayConsistency) || baselineReplayConsistency <= 0) { + return 0; + } + return roundSignal((baselineReplayConsistency - replayConsistency) / baselineReplayConsistency); +} + +export function calculatePredictionError(input: CompressionSignalInput, weights: CompressionSignalWeights = defaultCompressionSignalWeights): number { + const compressionRatioDrop = calculateCompressionRatioDrop(input.compressionRatio, input.baselineCompressionRatio); + const sparseFrameSpike = calculateSparseFrameSpike(input.sparseFrameRate, input.baselineSparseFrameRate); + const unseenSignatureSignal = calculateUnseenSignatureSignal(input.unseenSignatureRate); + const tokenCostSpike = calculateTokenCostSpike(input.tokenEstimate, input.baselineTokenEstimate); + const replayConsistencyDrop = calculateReplayConsistencyDrop(input.replayConsistency, input.baselineReplayConsistency); + + return roundSignal( + weights.compressionRatioDrop * compressionRatioDrop + + weights.sparseFrameSpike * sparseFrameSpike + + weights.unseenSignatureRate * unseenSignatureSignal + + (weights.tokenCostSpike ?? 0) * tokenCostSpike + + (weights.replayConsistencyDrop ?? 0) * replayConsistencyDrop, + ); +} + +export function classifyCognitiveMode(predictionError: number, thresholds: CompressionSignalThresholds = defaultCompressionSignalThresholds): CognitiveMode { + const score = clamp01(predictionError); + if (score >= thresholds.rollbackRequiredMin) { + return 'rollback_required'; + } + if (score < thresholds.habitMax) { + return 'habit'; + } + if (score <= thresholds.monitorMax) { + return 'monitor'; + } + if (score >= thresholds.deliberationMin) { + return 'deliberation'; + } + return 'monitor'; +} + +function buildSignalWindow(input: CompressionSignalInput, weights: CompressionSignalWeights): CompressionSignalWindow { + const tokenCostSpike = calculateTokenCostSpike(input.tokenEstimate, input.baselineTokenEstimate); + const replayConsistencyDrop = calculateReplayConsistencyDrop(input.replayConsistency, input.baselineReplayConsistency); + return Object.freeze({ + input: Object.freeze({ ...input, metadata: input.metadata === undefined ? undefined : Object.freeze({ ...input.metadata }) }), + compressionRatioDrop: calculateCompressionRatioDrop(input.compressionRatio, input.baselineCompressionRatio), + sparseFrameSpike: calculateSparseFrameSpike(input.sparseFrameRate, input.baselineSparseFrameRate), + unseenSignatureSignal: calculateUnseenSignatureSignal(input.unseenSignatureRate), + tokenCostSpike: input.tokenEstimate === undefined || input.baselineTokenEstimate === undefined ? undefined : tokenCostSpike, + replayConsistencyDrop: input.replayConsistency === undefined || input.baselineReplayConsistency === undefined ? undefined : replayConsistencyDrop, + predictionError: calculatePredictionError(input, weights), + }); +} + +export function shouldEnterDeliberation( + windows: readonly Pick[], + hysteresis: CompressionSignalHysteresis = defaultCompressionSignalHysteresis, + debounce: CompressionSignalDebounce = defaultCompressionSignalDebounce, +): boolean { + const recent = windows.slice(-debounce.ofLast); + return recent.filter((window) => window.predictionError >= hysteresis.enterDeliberation).length >= debounce.requiredWindows; +} + +export function shouldExitDeliberation( + currentPredictionError: number, + hysteresis: CompressionSignalHysteresis = defaultCompressionSignalHysteresis, +): boolean { + return clamp01(currentPredictionError) <= hysteresis.exitDeliberation; +} + +function uniqueReasons(reasons: CompressionSignalReason[]): CompressionSignalReason[] { + return Array.from(new Set(reasons)); +} + +function buildReasons( + window: CompressionSignalWindow, + mode: CognitiveMode, + enteredDeliberation: boolean, + exitedDeliberation: boolean, + hysteresis: CompressionSignalHysteresis, +): CompressionSignalReason[] { + const reasons: CompressionSignalReason[] = []; + if (window.compressionRatioDrop > 0) { + reasons.push('compression_ratio_drop exceeded baseline'); + } + if (window.sparseFrameSpike > 0) { + reasons.push('sparse_frame_rate increased'); + } + if (window.unseenSignatureSignal > 0) { + reasons.push(mode === 'deliberation' ? 'unseen_signature_rate remained elevated across debounce window' : 'unseen_signature_rate elevated'); + } + if ((window.tokenCostSpike ?? 0) > 0) { + reasons.push('token_cost increased'); + } + if ((window.replayConsistencyDrop ?? 0) > 0) { + reasons.push('replay_consistency dropped'); + } + if (mode === 'rollback_required') { + reasons.push('prediction_error reached rollback threshold'); + } else if (enteredDeliberation) { + reasons.push('deliberation debounce satisfied'); + } else if (window.predictionError >= hysteresis.enterDeliberation) { + reasons.push('deliberation debounce pending'); + } else if (exitedDeliberation) { + reasons.push('prediction_error below exit threshold'); + } else if (mode === 'habit') { + reasons.push('prediction_error within habit band'); + } else if (mode === 'monitor') { + reasons.push('prediction_error within monitor band'); + } + return uniqueReasons(reasons); +} + +export interface CompressionSignalEngineOptions { + weights?: CompressionSignalWeights; + thresholds?: CompressionSignalThresholds; + hysteresis?: CompressionSignalHysteresis; + debounce?: CompressionSignalDebounce; +} + +export class CompressionSignalEngine { + private readonly weights: CompressionSignalWeights; + private readonly thresholds: CompressionSignalThresholds; + private readonly hysteresis: CompressionSignalHysteresis; + private readonly debounce: CompressionSignalDebounce; + + constructor(options: CompressionSignalEngineOptions = {}) { + this.weights = Object.freeze({ ...defaultCompressionSignalWeights, ...options.weights }); + this.thresholds = Object.freeze({ ...defaultCompressionSignalThresholds, ...options.thresholds }); + this.hysteresis = Object.freeze({ ...defaultCompressionSignalHysteresis, ...options.hysteresis }); + this.debounce = Object.freeze({ ...defaultCompressionSignalDebounce, ...options.debounce }); + } + + evaluateSignalWindow(input: CompressionSignalInput, priorWindows: readonly CompressionSignalWindow[] = [], priorMode: CognitiveMode = 'habit'): CompressionSignalResult { + const window = buildSignalWindow(input, this.weights); + const windows = [...priorWindows, window]; + const rollback = window.predictionError >= this.thresholds.rollbackRequiredMin; + const enterDeliberation = !rollback && shouldEnterDeliberation(windows, this.hysteresis, this.debounce); + const exitDeliberation = priorMode === 'deliberation' && shouldExitDeliberation(window.predictionError, this.hysteresis); + + let mode: CognitiveMode; + if (rollback) { + mode = 'rollback_required'; + } else if (priorMode === 'deliberation' && !exitDeliberation) { + mode = 'deliberation'; + } else if (enterDeliberation) { + mode = 'deliberation'; + } else if (window.predictionError < this.thresholds.habitMax) { + mode = 'habit'; + } else { + mode = 'monitor'; + } + + return Object.freeze({ + executionId: input.executionId, + windowId: input.windowId, + timestamp: input.timestamp, + profileId: input.profileId, + mode, + predictionError: window.predictionError, + triggered: mode === 'deliberation' || mode === 'rollback_required', + window, + reasons: Object.freeze(buildReasons(window, mode, enterDeliberation, exitDeliberation, this.hysteresis)), + }); + } + + evaluateSignalSequence(inputs: readonly CompressionSignalInput[], initialMode: CognitiveMode = 'habit'): CompressionSignalResult[] { + const windows: CompressionSignalWindow[] = []; + let priorMode = initialMode; + return inputs.map((input) => { + const result = this.evaluateSignalWindow(input, windows, priorMode); + windows.push(result.window); + priorMode = result.mode; + return result; + }); + } +} + +const defaultEngine = new CompressionSignalEngine(); + +export function evaluateSignalWindow( + input: CompressionSignalInput, + priorWindows: readonly CompressionSignalWindow[] = [], + priorMode: CognitiveMode = 'habit', +): CompressionSignalResult { + return defaultEngine.evaluateSignalWindow(input, priorWindows, priorMode); +} + +export function evaluateSignalSequence(inputs: readonly CompressionSignalInput[], initialMode: CognitiveMode = 'habit'): CompressionSignalResult[] { + return defaultEngine.evaluateSignalSequence(inputs, initialMode); +} diff --git a/dashboard/app/src/core/foundation/index.ts b/dashboard/app/src/core/foundation/index.ts index 98b3369..983b009 100644 --- a/dashboard/app/src/core/foundation/index.ts +++ b/dashboard/app/src/core/foundation/index.ts @@ -1,3 +1,4 @@ +export * from './compressionSignals'; export * from './decisionQuality'; export * from './executionEventLog'; export * from './replaySnapshot'; diff --git a/dashboard/app/src/core/foundation/sampleData.ts b/dashboard/app/src/core/foundation/sampleData.ts index 9f57ded..5b995d4 100644 --- a/dashboard/app/src/core/foundation/sampleData.ts +++ b/dashboard/app/src/core/foundation/sampleData.ts @@ -1,3 +1,4 @@ +import { CompressionSignalEngine, type CompressionSignalInput } from './compressionSignals'; import { DecisionQualityEngine } from './decisionQuality'; import { InMemoryExecutionEventStore, appendExecutionEvent, getExecutionTimeline, summarizeExecutionEvents } from './executionEventLog'; import { createReplaySnapshot } from './replaySnapshot'; @@ -30,6 +31,66 @@ const event = appendExecutionEvent(eventStore, { compactPayload: { manifestId: manifest.manifestId }, }); +const compressionSignalWindows: CompressionSignalInput[] = [ + { + executionId: 'exec-sample', + windowId: 'signal-stable-known-family', + timestamp: '2026-05-15T00:00:03.000Z', + profileId: 'known-family', + compressionRatio: 0.79, + baselineCompressionRatio: 0.8, + sparseFrameRate: 0.11, + baselineSparseFrameRate: 0.1, + unseenSignatureRate: 0.02, + }, + { + executionId: 'exec-sample', + windowId: 'signal-minor-fluctuation', + timestamp: '2026-05-15T00:00:04.000Z', + profileId: 'known-family', + compressionRatio: 0.72, + baselineCompressionRatio: 0.8, + sparseFrameRate: 0.18, + baselineSparseFrameRate: 0.1, + unseenSignatureRate: 0.08, + }, + { + executionId: 'exec-sample', + windowId: 'signal-sparse-signature-spike-a', + timestamp: '2026-05-15T00:00:05.000Z', + profileId: 'known-family', + compressionRatio: 0.42, + baselineCompressionRatio: 0.8, + sparseFrameRate: 0.26, + baselineSparseFrameRate: 0.1, + unseenSignatureRate: 0.72, + }, + { + executionId: 'exec-sample', + windowId: 'signal-sparse-signature-spike-b', + timestamp: '2026-05-15T00:00:06.000Z', + profileId: 'known-family', + compressionRatio: 0.44, + baselineCompressionRatio: 0.8, + sparseFrameRate: 0.25, + baselineSparseFrameRate: 0.1, + unseenSignatureRate: 0.7, + }, + { + executionId: 'exec-sample', + windowId: 'signal-severe-unknown-pattern', + timestamp: '2026-05-15T00:00:07.000Z', + profileId: 'known-family', + compressionRatio: 0.08, + baselineCompressionRatio: 0.8, + sparseFrameRate: 0.31, + baselineSparseFrameRate: 0.1, + unseenSignatureRate: 0.95, + }, +]; + +const compressionSignalResults = new CompressionSignalEngine().evaluateSignalSequence(compressionSignalWindows); + export const coreFoundationSample = Object.freeze({ reference, manifest, @@ -52,4 +113,6 @@ export const coreFoundationSample = Object.freeze({ replaySnapshotIds: [], tokenUsed: manifest.totalTokenEstimate, }), + compressionSignalWindows: Object.freeze(compressionSignalWindows), + compressionSignalResults: Object.freeze(compressionSignalResults), }); diff --git a/docs/compression_signal_engine.md b/docs/compression_signal_engine.md new file mode 100644 index 0000000..57b1b63 --- /dev/null +++ b/docs/compression_signal_engine.md @@ -0,0 +1,166 @@ +# Compression Signal Engine + +## Purpose + +The Compression Signal Engine is a deterministic core telemetry layer for CompTextv7. It treats compression instability as an early warning signal that compacted context is no longer matching the known semantic family for the current execution window. + +The engine does not run agents, call models, or make subjective judgments. It converts numeric telemetry into a compact prediction error score and a cognitive mode that downstream systems can use as a replanning gate. + +## Core claim: Compression is perception + +CompTextv7 uses compression behavior as a perception surface. When known work continues to compress like the stable baseline, the system can remain in `habit` or `monitor` mode. When compression becomes unstable across deterministic windows, the system has evidence that the current execution may be seeing a new pattern and should deliberate before continuing. + +## Why this is not LLM judging + +The engine is pure TypeScript and deterministic: + +- no LLM-as-judge calls; +- no external API calls; +- no graph store dependency; +- no swarm orchestration; +- no active inference runtime; +- no MCP integration; +- no random sampling. + +Every output is derived from caller-provided numeric input fields and fixed thresholds, weights, hysteresis, and debounce rules. + +## Input data shape + +Each telemetry window provides a `CompressionSignalInput`: + +```ts +{ + executionId: string; + windowId: string; + timestamp: string; + profileId: string; + compressionRatio: number; + baselineCompressionRatio: number; + sparseFrameRate: number; + baselineSparseFrameRate: number; + unseenSignatureRate: number; + tokenEstimate?: number; + baselineTokenEstimate?: number; + replayConsistency?: number; + baselineReplayConsistency?: number; + metadata?: Record; +} +``` + +Optional token and replay consistency fields are computed as helper signals when present, but they do not affect the default MVP score unless their weights are explicitly configured. + +## MVP formula + +All component signals are clamped to the range `0.0` through `1.0`. + +```text +prediction_error = + 0.35 * compression_ratio_drop ++ 0.35 * sparse_frame_spike ++ 0.30 * unseen_signature_rate +``` + +Where: + +- `compression_ratio_drop` is the normalized drop from the baseline compression ratio. +- `sparse_frame_spike` is the normalized increase from the baseline sparse frame rate. +- `unseen_signature_rate` is already normalized telemetry and is clamped before scoring. + +## Default thresholds + +```json +{ + "habitMax": 0.30, + "monitorMax": 0.55, + "deliberationMin": 0.62, + "rollbackRequiredMin": 0.85 +} +``` + +## Hysteresis and debounce + +Default hysteresis: + +```json +{ + "enterDeliberation": 0.62, + "exitDeliberation": 0.42 +} +``` + +Default debounce: + +```json +{ + "requiredWindows": 2, + "ofLast": 3 +} +``` + +Rules: + +- A single noisy window does not enter `deliberation` unless it reaches `rollback_required` severity. +- The engine enters `deliberation` only when prediction error is at or above `0.62` in at least 2 of the last 3 windows. +- The engine exits `deliberation` only when prediction error is at or below `0.42`. +- `rollback_required` triggers immediately when prediction error is at or above `0.85`. + +## Cognitive modes + +- `habit`: prediction error is below `0.30`; known compression behavior is stable. +- `monitor`: prediction error is elevated but deliberation debounce has not been satisfied. +- `deliberation`: persistent compression instability has crossed the debounce rule. +- `rollback_required`: severe compression instability crossed the immediate rollback threshold. + +## Examples + +Stable known family: + +```json +{ + "mode": "habit", + "predictionError": 0.045, + "triggered": false, + "reasons": ["compression_ratio_drop exceeded baseline", "sparse_frame_rate increased", "unseen_signature_rate elevated", "prediction_error within habit band"] +} +``` + +Persistent sparse signature spike: + +```json +{ + "mode": "deliberation", + "predictionError": 0.725, + "triggered": true, + "reasons": [ + "compression_ratio_drop exceeded baseline", + "sparse_frame_rate increased", + "unseen_signature_rate remained elevated across debounce window", + "deliberation debounce satisfied" + ] +} +``` + +Severe unknown pattern: + +```json +{ + "mode": "rollback_required", + "predictionError": 0.95, + "triggered": true, + "reasons": ["prediction_error reached rollback threshold"] +} +``` + +## Limitations + +- Thresholds are initial defaults and require calibration against real telemetry. +- The MVP score intentionally ignores optional token and replay helper signals unless weights are configured. +- The engine emits telemetry decisions only; it does not execute rollback or start an agent runtime. +- The engine assumes callers provide deterministic timestamps and stable window ordering. + +## Next integration points + +- Attach compression signal results to replay artifacts. +- Feed operational invariants with signal windows and compact reasons. +- Use the engine as a future IAIF planner gate without adding active inference runtime in this layer. +- Add a future dashboard panel after the core behavior is stable. diff --git a/tests/test_compression_signals_ts.py b/tests/test_compression_signals_ts.py new file mode 100644 index 0000000..7c9a1b3 --- /dev/null +++ b/tests/test_compression_signals_ts.py @@ -0,0 +1,183 @@ +import json +import subprocess +import textwrap +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[1] +DASHBOARD_APP = REPO_ROOT / "dashboard" / "app" +TSC = DASHBOARD_APP / "node_modules" / ".bin" / "tsc" + + +def run_compression_script(tmp_path: Path, script: str): + out_dir = tmp_path / "compiled" + source_files = sorted((DASHBOARD_APP / "src" / "core" / "foundation").glob("*.ts")) + subprocess.run( + [ + str(TSC), + "--target", + "ES2022", + "--module", + "CommonJS", + "--moduleResolution", + "Node", + "--rootDir", + str(DASHBOARD_APP / "src"), + "--outDir", + str(out_dir), + "--strict", + "--skipLibCheck", + *[str(path) for path in source_files], + ], + cwd=REPO_ROOT, + check=True, + ) + completed = subprocess.run( + ["node", "-e", script], + cwd=out_dir, + text=True, + capture_output=True, + check=True, + ) + return json.loads(completed.stdout) + + +def test_signal_calculations_and_prediction_error(tmp_path): + result = run_compression_script( + tmp_path, + textwrap.dedent( + """ + const assert = require('node:assert/strict'); + const { + calculateCompressionRatioDrop, + calculateSparseFrameSpike, + calculateUnseenSignatureSignal, + calculatePredictionError, + } = require('./core/foundation'); + assert.equal(calculateCompressionRatioDrop(0.6, 0.8), 0.25); + assert.equal(calculateCompressionRatioDrop(0.9, 0.8), 0); + assert.equal(calculateSparseFrameSpike(0.3, 0.2), 0.5); + assert.equal(calculateSparseFrameSpike(0.1, 0.2), 0); + assert.equal(calculateUnseenSignatureSignal(1.5), 1); + assert.equal(calculateUnseenSignatureSignal(-0.2), 0); + const input = { + executionId: 'exec-1', windowId: 'w-1', timestamp: '2026-05-15T00:00:00.000Z', profileId: 'p-1', + compressionRatio: 0.6, baselineCompressionRatio: 0.8, + sparseFrameRate: 0.3, baselineSparseFrameRate: 0.2, + unseenSignatureRate: 0.4, + tokenEstimate: 300, baselineTokenEstimate: 100, + replayConsistency: 0.5, baselineReplayConsistency: 1, + }; + assert.equal(calculatePredictionError(input), 0.383); + console.log(JSON.stringify({ predictionError: calculatePredictionError(input) })); + """ + ), + ) + assert result["predictionError"] == 0.383 + + +def test_classification_and_debounce_rules(tmp_path): + result = run_compression_script( + tmp_path, + textwrap.dedent( + """ + const assert = require('node:assert/strict'); + const { CompressionSignalEngine, classifyCognitiveMode } = require('./core/foundation'); + assert.equal(classifyCognitiveMode(0.2), 'habit'); + assert.equal(classifyCognitiveMode(0.4), 'monitor'); + assert.equal(classifyCognitiveMode(0.7), 'deliberation'); + assert.equal(classifyCognitiveMode(0.9), 'rollback_required'); + const makeInput = (windowId, sparseFrameRate, unseenSignatureRate, compressionRatio = 0.8) => ({ + executionId: 'exec-2', windowId, timestamp: `2026-05-15T00:00:0${windowId}.000Z`, profileId: 'p-1', + compressionRatio, baselineCompressionRatio: 0.8, + sparseFrameRate, baselineSparseFrameRate: 0.1, + unseenSignatureRate, + }); + const engine = new CompressionSignalEngine(); + const sequence = engine.evaluateSignalSequence([ + makeInput(1, 0.1, 0.01), + makeInput(2, 0.26, 0.7, 0.42), + makeInput(3, 0.1, 0.05), + makeInput(4, 0.25, 0.7, 0.44), + ]); + assert.deepEqual(sequence.map((result) => result.mode), ['habit', 'monitor', 'habit', 'deliberation']); + assert.equal(sequence[1].triggered, false); + assert.equal(sequence[3].triggered, true); + assert(sequence[3].reasons.includes('deliberation debounce satisfied')); + console.log(JSON.stringify(sequence.map((result) => ({ mode: result.mode, error: result.predictionError, reasons: result.reasons })))); + """ + ), + ) + assert [entry["mode"] for entry in result] == ["habit", "monitor", "habit", "deliberation"] + + +def test_rollback_and_hysteresis_exit(tmp_path): + result = run_compression_script( + tmp_path, + textwrap.dedent( + """ + const assert = require('node:assert/strict'); + const { CompressionSignalEngine, shouldExitDeliberation } = require('./core/foundation'); + const engine = new CompressionSignalEngine(); + const base = { executionId: 'exec-3', profileId: 'p-1', baselineCompressionRatio: 0.8, baselineSparseFrameRate: 0.1 }; + const rollback = engine.evaluateSignalWindow({ + ...base, windowId: 'rollback', timestamp: '2026-05-15T00:00:01.000Z', + compressionRatio: 0.08, sparseFrameRate: 0.31, unseenSignatureRate: 0.95, + }); + const stayDeliberation = engine.evaluateSignalWindow({ + ...base, windowId: 'stay', timestamp: '2026-05-15T00:00:02.000Z', + compressionRatio: 0.8, sparseFrameRate: 0.22, unseenSignatureRate: 0.25, + }, [], 'deliberation'); + const exitDeliberation = engine.evaluateSignalWindow({ + ...base, windowId: 'exit', timestamp: '2026-05-15T00:00:03.000Z', + compressionRatio: 0.8, sparseFrameRate: 0.1, unseenSignatureRate: 0.01, + }, [], 'deliberation'); + assert.equal(rollback.mode, 'rollback_required'); + assert.equal(rollback.triggered, true); + assert(rollback.reasons.includes('prediction_error reached rollback threshold')); + assert.equal(shouldExitDeliberation(0.42), true); + assert.equal(shouldExitDeliberation(0.43), false); + assert.equal(stayDeliberation.mode, 'deliberation'); + assert.equal(exitDeliberation.mode, 'habit'); + console.log(JSON.stringify({ rollback, stayDeliberation, exitDeliberation })); + """ + ), + ) + assert result["rollback"]["mode"] == "rollback_required" + assert result["exitDeliberation"]["mode"] == "habit" + + +def test_deterministic_reasons_and_sample_data_validity(tmp_path): + result = run_compression_script( + tmp_path, + textwrap.dedent( + """ + const assert = require('node:assert/strict'); + const { CompressionSignalEngine } = require('./core/foundation'); + const { coreFoundationSample } = require('./core/foundation/sampleData'); + const input = { + executionId: 'exec-4', windowId: 'w-1', timestamp: '2026-05-15T00:00:01.000Z', profileId: 'p-1', + compressionRatio: 0.42, baselineCompressionRatio: 0.8, + sparseFrameRate: 0.26, baselineSparseFrameRate: 0.1, + unseenSignatureRate: 0.72, + }; + const engine = new CompressionSignalEngine(); + const first = engine.evaluateSignalSequence([input, { ...input, windowId: 'w-2', timestamp: '2026-05-15T00:00:02.000Z' }]); + const second = engine.evaluateSignalSequence([input, { ...input, windowId: 'w-2', timestamp: '2026-05-15T00:00:02.000Z' }]); + assert.deepEqual(first.map((result) => result.reasons), second.map((result) => result.reasons)); + assert.deepEqual(first.map((result) => result.mode), ['monitor', 'deliberation']); + assert.equal(coreFoundationSample.compressionSignalWindows.length, 5); + assert.deepEqual( + coreFoundationSample.compressionSignalResults.map((result) => result.mode), + ['habit', 'monitor', 'monitor', 'deliberation', 'rollback_required'], + ); + console.log(JSON.stringify({ reasons: first[1].reasons, sampleModes: coreFoundationSample.compressionSignalResults.map((result) => result.mode) })); + """ + ), + ) + assert result["sampleModes"] == ["habit", "monitor", "monitor", "deliberation", "rollback_required"] + assert result["reasons"] == [ + "compression_ratio_drop exceeded baseline", + "sparse_frame_rate increased", + "unseen_signature_rate remained elevated across debounce window", + "deliberation debounce satisfied", + ]