* media: check ffmpeg filter support before subtitle operations * sandbox: handle synchronous spawn errors safely in desktop sandbox * Voice-first streaming, non-blocking follow-ups, quiet routines and chat UX Why: questions blocked the chat, follow-up messages had no acknowledgement, message actions sat beside the bubble, and voice turn-taking added latency. What: waiting runs accept steering messages and the bot marks each claimed follow-up as seen; unclaimed follow-ups show as queued. Reaction, copy and reply controls sit under each message. Streaming voice (VAD endpointing, barge-in that stops the run, clause-based TTS), an optimistic input queue, procedural earcons and delta-based quiet routines were added. Onboarding can be skipped straight to the main conversation. Dual-surface layout and project pickers are included. Status colors use semantic tokens. Tested: unit suites for core, adapters, api and web pass, typecheck and Biome are clean. Browser end-to-end tests were not run locally. * Update translation catalogs Re-extracted catalogs for the new and moved strings; the catalog sync test passes. * Add project plan and test readiness notes Notes from the milestone work: feature inventory, test infrastructure and test readiness. No code changes. * fix(ci): align scale guards, space deletion, computer toggle, and mobile expo deps * fix(ci): restore space deletion guard, browser toggle, and run-steps contracts * fix(ci): restore model resolution errors, group stage, and onboarding flow * fix(ci): restore in-app-browser close button, disable stop button while sending, and sync lingui catalogs * fix(ci): address web e2e failures for bot-crud, choice cards, message quote, and voice onboarding * test(web): wait for avatar and rig visibility in oo-avatar motion spec * test(web): wait for persisted message IDs in message-hover-actions pagination spec * Onboarding: always drop the local voice when the voice step is skipped Skipping waited only for the latest voice load and disconnected only when the client saw the connect succeed. An earlier or client-failed connect could still persist the local credential, so a user who skipped voice was left with one. Wait for every load and always disconnect. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wy1md9QE1ayJbDCwJ4vAQs * Shell: hide the optimistic copy once the transcript shows the send A message sent while the bot works streamed into the transcript before its send request resolved, so the same text showed twice until the optimistic bubble was removed. Drop an optimistic message as soon as a persisted user message with the same text appears. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Wy1md9QE1ayJbDCwJ4vAQs --------- Co-authored-by: Claude <noreply@anthropic.com>
1048 lines
35 KiB
TypeScript
1048 lines
35 KiB
TypeScript
import * as fc from "fast-check";
|
||
import { describe, expect, it } from "vitest";
|
||
import {
|
||
computeContentHash,
|
||
computeStructuralDiff,
|
||
DEFAULT_IGNORE_KEYS,
|
||
deterministicStringify,
|
||
evaluateOutputDelta,
|
||
evaluateRoutineNotification,
|
||
murmurHash3,
|
||
murmurHash64,
|
||
RoutinePolicy,
|
||
} from "./routine-policy.js";
|
||
import {
|
||
calculateRoutineBackoffMs,
|
||
formatMinimalRoutineNotification,
|
||
isTransientRoutineError,
|
||
} from "./routine-runner.js";
|
||
import type { OoSoundCue, SoundCueRecipe, SoundDriver } from "./sound.js";
|
||
import { CoreSoundEngine } from "./sound.js";
|
||
import { PcmWavSynthesizer } from "./sound-synthesizer.js";
|
||
|
||
describe("Adversarial Challenge: Sound Synthesizer & Recipes", () => {
|
||
const ALL_CUES: OoSoundCue[] = [
|
||
"app-start",
|
||
"interact",
|
||
"success-resolve",
|
||
"need-you",
|
||
"work-complete",
|
||
"listening-start",
|
||
"listening-stop",
|
||
"speaking-start",
|
||
"speaking-done",
|
||
"attention",
|
||
"message-sent",
|
||
"handoff",
|
||
"group-spark",
|
||
"routine_start",
|
||
"routine_completed_quiet",
|
||
"routine_changed",
|
||
"routine_failure",
|
||
"voice_listen",
|
||
"voice_bargein",
|
||
];
|
||
|
||
describe("Challenge 1: Bit-Exact RIFF WAV Header & PCM Format Conformance", () => {
|
||
it("parses bit-exact 44-byte RIFF headers across all 19 standard cues", () => {
|
||
expect(ALL_CUES.length).toBe(19);
|
||
|
||
for (const cue of ALL_CUES) {
|
||
const wav = PcmWavSynthesizer.renderCueWav(cue);
|
||
expect(wav).toBeInstanceOf(Uint8Array);
|
||
expect(wav.byteLength).toBeGreaterThan(44);
|
||
|
||
const view = new DataView(wav.buffer, wav.byteOffset, wav.byteLength);
|
||
|
||
// 1. ChunkID: 'RIFF' (0x52494646 in big-endian)
|
||
const chunkId = String.fromCharCode(
|
||
view.getUint8(0),
|
||
view.getUint8(1),
|
||
view.getUint8(2),
|
||
view.getUint8(3),
|
||
);
|
||
expect(chunkId).toBe("RIFF");
|
||
expect(view.getUint32(0, false)).toBe(0x52494646);
|
||
|
||
// 2. ChunkSize: 36 + Subchunk2Size
|
||
const chunkSize = view.getUint32(4, true);
|
||
|
||
// 3. Format: 'WAVE' (0x57415645 in big-endian)
|
||
const format = String.fromCharCode(
|
||
view.getUint8(8),
|
||
view.getUint8(9),
|
||
view.getUint8(10),
|
||
view.getUint8(11),
|
||
);
|
||
expect(format).toBe("WAVE");
|
||
expect(view.getUint32(8, false)).toBe(0x57415645);
|
||
|
||
// 4. Subchunk1ID: 'fmt ' (0x666d7420 in big-endian)
|
||
const subchunk1Id = String.fromCharCode(
|
||
view.getUint8(12),
|
||
view.getUint8(13),
|
||
view.getUint8(14),
|
||
view.getUint8(15),
|
||
);
|
||
expect(subchunk1Id).toBe("fmt ");
|
||
expect(view.getUint32(12, false)).toBe(0x666d7420);
|
||
|
||
// 5. Subchunk1Size: 16 for PCM
|
||
const subchunk1Size = view.getUint32(16, true);
|
||
expect(subchunk1Size).toBe(16);
|
||
|
||
// 6. AudioFormat: 1 (PCM uncompressed)
|
||
const audioFormat = view.getUint16(20, true);
|
||
expect(audioFormat).toBe(1);
|
||
|
||
// 7. NumChannels: 1 (Mono)
|
||
const numChannels = view.getUint16(22, true);
|
||
expect(numChannels).toBe(1);
|
||
|
||
// 8. SampleRate: 22050 Hz
|
||
const sampleRate = view.getUint32(24, true);
|
||
expect(sampleRate).toBe(22050);
|
||
|
||
// 9. ByteRate: SampleRate * NumChannels * BitsPerSample / 8 = 22050 * 1 * 2 = 44100
|
||
const byteRate = view.getUint32(28, true);
|
||
expect(byteRate).toBe(44100);
|
||
|
||
// 10. BlockAlign: NumChannels * BitsPerSample / 8 = 1 * 2 = 2
|
||
const blockAlign = view.getUint16(32, true);
|
||
expect(blockAlign).toBe(2);
|
||
|
||
// 11. BitsPerSample: 16
|
||
const bitsPerSample = view.getUint16(34, true);
|
||
expect(bitsPerSample).toBe(16);
|
||
|
||
// 12. Subchunk2ID: 'data' (0x64617461 in big-endian)
|
||
const subchunk2Id = String.fromCharCode(
|
||
view.getUint8(36),
|
||
view.getUint8(37),
|
||
view.getUint8(38),
|
||
view.getUint8(39),
|
||
);
|
||
expect(subchunk2Id).toBe("data");
|
||
expect(view.getUint32(36, false)).toBe(0x64617461);
|
||
|
||
// 13. Subchunk2Size: numSamples * 2
|
||
const subchunk2Size = view.getUint32(40, true);
|
||
expect(subchunk2Size).toBe(wav.byteLength - 44);
|
||
expect(chunkSize).toBe(36 + subchunk2Size);
|
||
expect(subchunk2Size % 2).toBe(0);
|
||
}
|
||
});
|
||
|
||
it("confirms all samples are finite integers clamped strictly within [-32768, 32767]", () => {
|
||
for (const cue of ALL_CUES) {
|
||
const wav = PcmWavSynthesizer.renderCueWav(cue);
|
||
const view = new DataView(wav.buffer, wav.byteOffset, wav.byteLength);
|
||
const numSamples = (wav.byteLength - 44) / 2;
|
||
|
||
let nonZero = 0;
|
||
let maxAbs = 0;
|
||
let invalidSample: number | null = null;
|
||
|
||
for (let i = 0; i < numSamples; i++) {
|
||
const sample = view.getInt16(44 + i * 2, true);
|
||
if (
|
||
sample < -32768 ||
|
||
sample > 32767 ||
|
||
!Number.isFinite(sample) ||
|
||
Number.isNaN(sample)
|
||
) {
|
||
invalidSample = sample;
|
||
break;
|
||
}
|
||
|
||
if (sample !== 0) nonZero++;
|
||
const abs = Math.abs(sample);
|
||
if (abs > maxAbs) maxAbs = abs;
|
||
}
|
||
|
||
expect(invalidSample).toBeNull();
|
||
// Must contain audible acoustic signal (non-zero samples and meaningful amplitude)
|
||
expect(nonZero).toBeGreaterThan(0);
|
||
expect(maxAbs).toBeGreaterThan(100);
|
||
}
|
||
});
|
||
|
||
it("verifies unknown cue fallback returns standard 44-byte empty WAV buffer", () => {
|
||
const fallback = PcmWavSynthesizer.renderCueWav("non_existent_cue" as OoSoundCue);
|
||
expect(fallback.byteLength).toBe(44);
|
||
expect(fallback).toBeInstanceOf(Uint8Array);
|
||
});
|
||
});
|
||
|
||
describe("Challenge 2: Synthesizer Adversarial Boundary & Stress Cases", () => {
|
||
it("handles extreme durations: 0ms, 1ms, and 10,000ms without crashing or throwing", () => {
|
||
// 0ms duration
|
||
const zeroRecipe: SoundCueRecipe = { steps: [], totalDurationMs: 0 };
|
||
const zeroWav = PcmWavSynthesizer.renderRecipeWav(zeroRecipe);
|
||
expect(zeroWav.byteLength).toBe(44);
|
||
const zeroView = new DataView(zeroWav.buffer, zeroWav.byteOffset, zeroWav.byteLength);
|
||
expect(zeroView.getUint32(40, true)).toBe(0); // Subchunk2Size = 0
|
||
expect(zeroView.getUint32(4, true)).toBe(36); // ChunkSize = 36
|
||
|
||
// 1ms duration (23 samples at 22050 Hz)
|
||
const oneRecipe: SoundCueRecipe = {
|
||
steps: [
|
||
{
|
||
kind: "tone",
|
||
frequency: 440,
|
||
delayMs: 0,
|
||
durationMs: 1,
|
||
gain: 0.1,
|
||
wave: "sine",
|
||
},
|
||
],
|
||
totalDurationMs: 1,
|
||
};
|
||
const oneWav = PcmWavSynthesizer.renderRecipeWav(oneRecipe);
|
||
expect(oneWav.byteLength).toBe(44 + Math.ceil(0.001 * 22050) * 2);
|
||
expect(oneWav.byteLength).toBe(90);
|
||
|
||
// 10,000ms duration (220,500 samples)
|
||
const longRecipe: SoundCueRecipe = {
|
||
steps: [
|
||
{
|
||
kind: "tone",
|
||
frequency: 440,
|
||
delayMs: 0,
|
||
durationMs: 10000,
|
||
gain: 0.05,
|
||
wave: "sine",
|
||
},
|
||
],
|
||
totalDurationMs: 10000,
|
||
};
|
||
const longWav = PcmWavSynthesizer.renderRecipeWav(longRecipe);
|
||
expect(longWav.byteLength).toBe(44 + 220500 * 2);
|
||
expect(longWav.byteLength).toBe(441044);
|
||
});
|
||
|
||
it("handles extreme frequencies: 0Hz, 0.01Hz, Nyquist (11025Hz), and 20000Hz", () => {
|
||
const frequencies = [0, 0.01, 100, 11025, 20000];
|
||
|
||
for (const freq of frequencies) {
|
||
const recipe: SoundCueRecipe = {
|
||
steps: [
|
||
{
|
||
kind: "tone",
|
||
frequency: freq,
|
||
delayMs: 0,
|
||
durationMs: 50,
|
||
gain: 0.05,
|
||
wave: "sine",
|
||
},
|
||
{
|
||
kind: "tone",
|
||
frequency: freq,
|
||
delayMs: 0,
|
||
durationMs: 50,
|
||
gain: 0.05,
|
||
wave: "triangle",
|
||
},
|
||
],
|
||
totalDurationMs: 50,
|
||
};
|
||
|
||
const wav = PcmWavSynthesizer.renderRecipeWav(recipe);
|
||
expect(wav.byteLength).toBeGreaterThan(44);
|
||
const view = new DataView(wav.buffer, wav.byteOffset, wav.byteLength);
|
||
const numSamples = (wav.byteLength - 44) / 2;
|
||
|
||
let invalidSample: number | null = null;
|
||
for (let i = 0; i < numSamples; i++) {
|
||
const sample = view.getInt16(44 + i * 2, true);
|
||
if (
|
||
sample < -32768 ||
|
||
sample > 32767 ||
|
||
!Number.isFinite(sample) ||
|
||
Number.isNaN(sample)
|
||
) {
|
||
invalidSample = sample;
|
||
break;
|
||
}
|
||
}
|
||
expect(invalidSample).toBeNull();
|
||
}
|
||
});
|
||
|
||
it("enforces hard clipping and clamping under extreme gain overload (100.0 and -100.0)", () => {
|
||
const overloadRecipe: SoundCueRecipe = {
|
||
steps: [
|
||
{
|
||
kind: "tone",
|
||
frequency: 440,
|
||
delayMs: 0,
|
||
durationMs: 40,
|
||
gain: 1000.0, // Massive positive gain overdrive
|
||
wave: "sine",
|
||
},
|
||
{
|
||
kind: "tone",
|
||
frequency: 880,
|
||
delayMs: 0,
|
||
durationMs: 40,
|
||
gain: -1000.0, // Massive negative gain overdrive
|
||
wave: "triangle",
|
||
},
|
||
],
|
||
totalDurationMs: 40,
|
||
};
|
||
|
||
const wav = PcmWavSynthesizer.renderRecipeWav(overloadRecipe);
|
||
const view = new DataView(wav.buffer, wav.byteOffset, wav.byteLength);
|
||
const numSamples = (wav.byteLength - 44) / 2;
|
||
|
||
let hitMax = false;
|
||
let hitMin = false;
|
||
|
||
for (let i = 0; i < numSamples; i++) {
|
||
const sample = view.getInt16(44 + i * 2, true);
|
||
expect(sample).toBeGreaterThanOrEqual(-32768);
|
||
expect(sample).toBeLessThanOrEqual(32767);
|
||
if (sample === 32767) hitMax = true;
|
||
if (sample === -32768) hitMin = true;
|
||
}
|
||
|
||
// Overdrive must hit the clamping ceilings
|
||
expect(hitMax).toBe(true);
|
||
expect(hitMin).toBe(true);
|
||
});
|
||
|
||
it("stress-tests exponential sweeps under extreme boundaries and identical frequencies", () => {
|
||
const sweepTests: SoundCueRecipe[] = [
|
||
// Identical frequencies (fromHz === toHz) exercises fallback branch
|
||
{
|
||
steps: [
|
||
{
|
||
kind: "sweep",
|
||
fromHz: 440,
|
||
toHz: 440,
|
||
delayMs: 0,
|
||
durationMs: 50,
|
||
gain: 0.05,
|
||
wave: "sine",
|
||
},
|
||
],
|
||
totalDurationMs: 50,
|
||
},
|
||
// Ultra-rapid sweep (1ms)
|
||
{
|
||
steps: [
|
||
{
|
||
kind: "sweep",
|
||
fromHz: 200,
|
||
toHz: 2000,
|
||
delayMs: 0,
|
||
durationMs: 1,
|
||
gain: 0.05,
|
||
wave: "triangle",
|
||
},
|
||
],
|
||
totalDurationMs: 1,
|
||
},
|
||
// Inverted high-to-low sweep
|
||
{
|
||
steps: [
|
||
{
|
||
kind: "sweep",
|
||
fromHz: 8000,
|
||
toHz: 80,
|
||
delayMs: 0,
|
||
durationMs: 100,
|
||
gain: 0.05,
|
||
wave: "sine",
|
||
},
|
||
],
|
||
totalDurationMs: 100,
|
||
},
|
||
// Zero fromHz edge case
|
||
{
|
||
steps: [
|
||
{
|
||
kind: "sweep",
|
||
fromHz: 0,
|
||
toHz: 440,
|
||
delayMs: 0,
|
||
durationMs: 50,
|
||
gain: 0.05,
|
||
wave: "sine",
|
||
},
|
||
],
|
||
totalDurationMs: 50,
|
||
},
|
||
];
|
||
|
||
for (const recipe of sweepTests) {
|
||
const wav = PcmWavSynthesizer.renderRecipeWav(recipe);
|
||
expect(wav.byteLength).toBeGreaterThan(44);
|
||
const view = new DataView(wav.buffer, wav.byteOffset, wav.byteLength);
|
||
const numSamples = (wav.byteLength - 44) / 2;
|
||
|
||
let invalidSample: number | null = null;
|
||
for (let i = 0; i < numSamples; i++) {
|
||
const sample = view.getInt16(44 + i * 2, true);
|
||
if (
|
||
sample < -32768 ||
|
||
sample > 32767 ||
|
||
!Number.isFinite(sample) ||
|
||
Number.isNaN(sample)
|
||
) {
|
||
invalidSample = sample;
|
||
break;
|
||
}
|
||
}
|
||
expect(invalidSample).toBeNull();
|
||
}
|
||
});
|
||
|
||
it("verifies attack/release envelope boundary anomalies", () => {
|
||
const envelopeRecipes: SoundCueRecipe[] = [
|
||
// attackMs = 0, releaseMs = 0
|
||
{
|
||
steps: [
|
||
{
|
||
kind: "tone",
|
||
frequency: 440,
|
||
delayMs: 0,
|
||
durationMs: 40,
|
||
gain: 0.04,
|
||
wave: "sine",
|
||
attackMs: 0,
|
||
releaseMs: 0,
|
||
},
|
||
],
|
||
totalDurationMs: 40,
|
||
},
|
||
// attackMs larger than total duration
|
||
{
|
||
steps: [
|
||
{
|
||
kind: "tone",
|
||
frequency: 440,
|
||
delayMs: 0,
|
||
durationMs: 40,
|
||
gain: 0.04,
|
||
wave: "sine",
|
||
attackMs: 500,
|
||
releaseMs: 500,
|
||
},
|
||
],
|
||
totalDurationMs: 40,
|
||
},
|
||
];
|
||
|
||
for (const recipe of envelopeRecipes) {
|
||
const wav = PcmWavSynthesizer.renderRecipeWav(recipe);
|
||
expect(wav.byteLength).toBeGreaterThan(44);
|
||
const view = new DataView(wav.buffer, wav.byteOffset, wav.byteLength);
|
||
const numSamples = (wav.byteLength - 44) / 2;
|
||
|
||
let invalidSample: number | null = null;
|
||
for (let i = 0; i < numSamples; i++) {
|
||
const sample = view.getInt16(44 + i * 2, true);
|
||
if (!Number.isFinite(sample) || Number.isNaN(sample)) {
|
||
invalidSample = sample;
|
||
break;
|
||
}
|
||
}
|
||
expect(invalidSample).toBeNull();
|
||
}
|
||
});
|
||
});
|
||
|
||
describe("Challenge 3: CoreSoundEngine Quiet Hours & Driver Fault Tolerance", () => {
|
||
it("strictly suppresses all 4 routine cues during quiet hours across timezones", () => {
|
||
const played: OoSoundCue[] = [];
|
||
const driver: SoundDriver = {
|
||
play: (c) => {
|
||
played.push(c);
|
||
},
|
||
isSupported: () => true,
|
||
};
|
||
|
||
const engine = new CoreSoundEngine(driver);
|
||
// Quiet range 0 to 1440 encompasses 24 hours of the day
|
||
engine.setQuietHours({ start: 0, end: 1440 }, "UTC");
|
||
|
||
engine.play("routine_start");
|
||
engine.play("routine_completed_quiet");
|
||
engine.play("routine_changed");
|
||
engine.play("routine_failure");
|
||
expect(played).toEqual([]);
|
||
|
||
// Non-routine cues are permitted
|
||
engine.play("voice_bargein");
|
||
engine.play("interact");
|
||
expect(played).toEqual(["voice_bargein", "interact"]);
|
||
});
|
||
|
||
it("survives throwing sound drivers gracefully", () => {
|
||
const badDriver: SoundDriver = {
|
||
play: () => {
|
||
throw new DOMException("The AudioContext was not allowed to start", "NotAllowedError");
|
||
},
|
||
isSupported: () => true,
|
||
};
|
||
|
||
const engine = new CoreSoundEngine(badDriver);
|
||
expect(() => engine.play("app-start")).not.toThrow();
|
||
expect(() => engine.play("routine_start")).not.toThrow();
|
||
});
|
||
});
|
||
});
|
||
|
||
describe("Adversarial Challenge: MurmurHash3 & Semantic Delta Normalization", () => {
|
||
describe("Challenge 4: MurmurHash3 Avalanche Effect & Math Integrity", () => {
|
||
it("exhibits uniform avalanche distribution on 1-bit input alterations", () => {
|
||
const baseInputs = [
|
||
"https://api.github.com/repos/rakazo/evOo/pulls/42",
|
||
"routine-checkpoint-state-key-2026",
|
||
'{"status":"ok","count":42,"healthy":true}',
|
||
"The quick brown fox jumps over the lazy dog",
|
||
];
|
||
|
||
for (const base of baseInputs) {
|
||
const baseHash = murmurHash64(base);
|
||
expect(baseHash).toHaveLength(16);
|
||
|
||
let totalCharDiffs = 0;
|
||
const trials = base.length;
|
||
|
||
for (let i = 0; i < trials; i++) {
|
||
// Flip a single character
|
||
const altered =
|
||
base.slice(0, i) + String.fromCharCode(base.charCodeAt(i) ^ 0x01) + base.slice(i + 1);
|
||
|
||
const alteredHash = murmurHash64(altered);
|
||
expect(alteredHash).not.toBe(baseHash);
|
||
expect(alteredHash).toHaveLength(16);
|
||
|
||
let diffCount = 0;
|
||
for (let j = 0; j < 16; j++) {
|
||
if (baseHash[j] !== alteredHash[j]) diffCount++;
|
||
}
|
||
totalCharDiffs += diffCount;
|
||
}
|
||
|
||
const avgDiff = totalCharDiffs / trials;
|
||
// High avalanche: at least 70% of hex digits change on average
|
||
expect(avgDiff).toBeGreaterThanOrEqual(10);
|
||
}
|
||
});
|
||
|
||
it("handles zero-length, extreme unicode, surrogate pairs, and 1MB payloads", () => {
|
||
// Empty string determinism
|
||
expect(murmurHash3("")).toBe("00000000");
|
||
expect(murmurHash64("")).toBe("000000002f422be7");
|
||
|
||
// Multi-byte, astral planes, zero-width joiners, emoji sequences
|
||
const complexUnicode = "👩👩👦👦 🏳️🌈 𠮷野家 𝔘𝔫𝔦𝔠𝔬𝔡𝔢 \u0000\u0001\u001F\uFFFF";
|
||
const uHash = murmurHash64(complexUnicode);
|
||
expect(uHash).toHaveLength(16);
|
||
expect(uHash).toBe(murmurHash64(complexUnicode)); // Deterministic
|
||
|
||
// 1MB payload stress test
|
||
const large1MB = "A".repeat(1024 * 1024);
|
||
const start = Date.now();
|
||
const largeHash = murmurHash64(large1MB);
|
||
const duration = Date.now() - start;
|
||
|
||
expect(largeHash).toHaveLength(16);
|
||
expect(duration).toBeLessThan(1500); // Must be fast pure-TS
|
||
});
|
||
});
|
||
|
||
describe("Challenge 5: Semantic Normalizer Key-Order Invariance & Volatile Stripping", () => {
|
||
it("guarantees bit-exact hash equivalence regardless of deep object key order", () => {
|
||
const obj1 = {
|
||
z: 99,
|
||
a: 1,
|
||
m: {
|
||
gamma: [3, 2, 1],
|
||
alpha: {
|
||
subZ: "valZ",
|
||
subA: "valA",
|
||
},
|
||
},
|
||
};
|
||
|
||
const obj2 = {
|
||
a: 1,
|
||
m: {
|
||
alpha: {
|
||
subA: "valA",
|
||
subZ: "valZ",
|
||
},
|
||
gamma: [3, 2, 1],
|
||
},
|
||
z: 99,
|
||
};
|
||
|
||
expect(deterministicStringify(obj1)).toBe(deterministicStringify(obj2));
|
||
expect(computeContentHash(obj1)).toBe(computeContentHash(obj2));
|
||
});
|
||
|
||
it("property test: random key permutations of nested objects produce identical hashes", () => {
|
||
fc.assert(
|
||
fc.property(
|
||
fc.dictionary(
|
||
fc.string({ minLength: 1, maxLength: 8 }),
|
||
fc.oneof(fc.integer(), fc.string(), fc.boolean()),
|
||
{ minKeys: 2, maxKeys: 10 },
|
||
),
|
||
(dict) => {
|
||
const keys = Object.keys(dict);
|
||
const reversedKeys = [...keys].reverse();
|
||
const shuffled: Record<string, unknown> = {};
|
||
for (const k of reversedKeys) {
|
||
shuffled[k] = dict[k];
|
||
}
|
||
|
||
const hOriginal = computeContentHash(dict);
|
||
const hShuffled = computeContentHash(shuffled);
|
||
expect(hOriginal).toBe(hShuffled);
|
||
},
|
||
),
|
||
{ numRuns: 100 },
|
||
);
|
||
});
|
||
|
||
it("strips all DEFAULT_IGNORE_KEYS in deeply nested structures without triggering deltas", () => {
|
||
const canonicalState = {
|
||
service: "indexer",
|
||
status: "healthy",
|
||
recordsProcessed: 14200,
|
||
nested: {
|
||
cluster: "us-east-1",
|
||
replicas: 3,
|
||
},
|
||
};
|
||
|
||
// Polluted state with every volatile key from DEFAULT_IGNORE_KEYS
|
||
const pollutedState = {
|
||
service: "indexer",
|
||
status: "healthy",
|
||
recordsProcessed: 14200,
|
||
// Injected volatile fields at root
|
||
durationMs: 489,
|
||
timestamp: Date.now(),
|
||
runId: "run_999888777",
|
||
attemptId: "att_112233",
|
||
executionId: "exec_uuid_999",
|
||
messageId: "msg_777",
|
||
taskId: "task_42",
|
||
createdAt: "2026-10-12T10:00:00Z",
|
||
updatedAt: "2026-10-12T10:01:00Z",
|
||
startedAt: 1760000000,
|
||
finishedAt: 1760000010,
|
||
nonce: "random_nonce_12345",
|
||
leaseOwner: "worker-node-4",
|
||
leaseFence: 99,
|
||
nested: {
|
||
cluster: "us-east-1",
|
||
replicas: 3,
|
||
// Injected volatile fields nested deep
|
||
durationMs: 12,
|
||
timestamp: 999999,
|
||
runId: "nested_run",
|
||
},
|
||
};
|
||
|
||
expect(DEFAULT_IGNORE_KEYS).toContain("durationMs");
|
||
expect(DEFAULT_IGNORE_KEYS).toContain("timestamp");
|
||
expect(DEFAULT_IGNORE_KEYS).toContain("runId");
|
||
expect(DEFAULT_IGNORE_KEYS).toContain("leaseFence");
|
||
|
||
const hash1 = computeContentHash(canonicalState);
|
||
const hash2 = computeContentHash(pollutedState);
|
||
expect(hash1).toBe(hash2);
|
||
|
||
const delta = evaluateOutputDelta(canonicalState, pollutedState);
|
||
expect(delta.hasChanged).toBe(false);
|
||
expect(delta.deltaSummary).toBe("No changes");
|
||
expect(delta.changeHash).toBe(hash1);
|
||
});
|
||
});
|
||
|
||
describe("Challenge 6: Circular References & Non-Serializable Types Resilience", () => {
|
||
it("gracefully protects against circular references without stack overflow or throws", () => {
|
||
// 1. Direct circular self-reference
|
||
const selfRef: Record<string, unknown> = { id: "node-1" };
|
||
selfRef.self = selfRef;
|
||
expect(() => deterministicStringify(selfRef)).not.toThrow();
|
||
expect(deterministicStringify(selfRef)).toContain('"[Circular]"');
|
||
|
||
// 2. Mutual circular reference
|
||
const nodeA: Record<string, unknown> = { name: "A" };
|
||
const nodeB: Record<string, unknown> = { name: "B" };
|
||
nodeA.peer = nodeB;
|
||
nodeB.peer = nodeA;
|
||
expect(() => deterministicStringify(nodeA)).not.toThrow();
|
||
expect(deterministicStringify(nodeA)).toContain('"[Circular]"');
|
||
|
||
// 3. Array circular reference
|
||
const arr: unknown[] = [1, 2];
|
||
arr.push(arr);
|
||
expect(() => deterministicStringify(arr)).not.toThrow();
|
||
expect(deterministicStringify(arr)).toContain('"[Circular]"');
|
||
|
||
// Hash computation must be deterministic across calls
|
||
const h1 = computeContentHash(nodeA);
|
||
const h2 = computeContentHash(nodeA);
|
||
expect(h1).toBe(h2);
|
||
});
|
||
|
||
it("serializes BigInt, Symbol, Function, Date, Set, Map, and non-finite numbers safely", () => {
|
||
const complexObject = {
|
||
bigInt: 9007199254740993n,
|
||
symbolVal: Symbol("test_sym"),
|
||
fnVal: () => "uncalled",
|
||
date: new Date("2026-10-12T15:30:00.000Z"),
|
||
set: new Set([3, 1, 2]),
|
||
map: new Map([
|
||
["z", 10],
|
||
["a", 20],
|
||
]),
|
||
nan: Number.NaN,
|
||
posInf: Number.POSITIVE_INFINITY,
|
||
negInf: Number.NEGATIVE_INFINITY,
|
||
normalNum: Math.PI,
|
||
};
|
||
|
||
expect(() => deterministicStringify(complexObject)).not.toThrow();
|
||
const str = deterministicStringify(complexObject);
|
||
|
||
// BigInt serialized with 'n'
|
||
expect(str).toContain('"9007199254740993n"');
|
||
// Date in ISO format
|
||
expect(str).toContain('"2026-10-12T15:30:00.000Z"');
|
||
// Set sorted
|
||
expect(str).toContain("[1,2,3]");
|
||
// Map sorted by key
|
||
expect(str).toContain('"a":20');
|
||
// NaN and Infinities normalized to null
|
||
expect(str).toContain('"nan":null');
|
||
expect(str).toContain('"posInf":null');
|
||
expect(str).toContain('"negInf":null');
|
||
// Float rounded to 6 decimal places (3.141593)
|
||
expect(str).toContain('"normalNum":3.141593');
|
||
});
|
||
});
|
||
|
||
describe("Challenge 7: Structural Diff & Large Payload (100KB+) Truncation Stress", () => {
|
||
it("processes 100KB+ complex structures rapidly and truncates summary strictly within limits", () => {
|
||
const largePrev: Record<string, unknown> = {};
|
||
const largeCurr: Record<string, unknown> = {};
|
||
|
||
for (let i = 0; i < 1000; i++) {
|
||
largePrev[`metric_key_${i}`] = {
|
||
value: i * 10,
|
||
description: `Telemetry metric description for node index ${i} in production cluster`,
|
||
tags: ["prod", `region_${i % 5}`],
|
||
};
|
||
|
||
largeCurr[`metric_key_${i}`] = {
|
||
value: i % 2 === 0 ? i * 10 : i * 10 + 1, // Mutate half the keys
|
||
description: `Telemetry metric description for node index ${i} in production cluster`,
|
||
tags: ["prod", `region_${i % 5}`],
|
||
};
|
||
}
|
||
|
||
const jsonSize = JSON.stringify(largePrev).length;
|
||
expect(jsonSize).toBeGreaterThan(100_000); // Exceeds 100KB
|
||
|
||
const start = Date.now();
|
||
const diff = computeStructuralDiff(largePrev, largeCurr, { maxSummaryLength: 50 });
|
||
const duration = Date.now() - start;
|
||
|
||
expect(diff.hasChanged).toBe(true);
|
||
expect(diff.changes.length).toBe(500);
|
||
expect(diff.summary.length).toBeLessThanOrEqual(50);
|
||
expect(diff.summary.endsWith("…")).toBe(true);
|
||
expect(duration).toBeLessThan(350); // Under 350ms
|
||
});
|
||
|
||
it("verifies accurate diff change categorization (added, removed, modified)", () => {
|
||
const prev = {
|
||
deletedField: "old",
|
||
modifiedField: 10,
|
||
unchangedField: "same",
|
||
list: [1, 2],
|
||
};
|
||
const curr = {
|
||
addedField: "new",
|
||
modifiedField: 20,
|
||
unchangedField: "same",
|
||
list: [1, 2, 3],
|
||
};
|
||
|
||
const diff = computeStructuralDiff(prev, curr);
|
||
expect(diff.hasChanged).toBe(true);
|
||
|
||
const kinds = diff.changes.map((c) => c.kind);
|
||
expect(kinds).toContain("added");
|
||
expect(kinds).toContain("removed");
|
||
expect(kinds).toContain("modified");
|
||
});
|
||
});
|
||
|
||
describe("Challenge 8: Routine Notification Decision Matrix Full Conformance", () => {
|
||
const policyNever = new RoutinePolicy("never");
|
||
const policyOnFailure = new RoutinePolicy("on_failure");
|
||
const policyOnChange = new RoutinePolicy("on_change");
|
||
const policyAlways = new RoutinePolicy("always");
|
||
|
||
it("enforces quiet suppression truth table for all modes and outcomes", () => {
|
||
const stateA = { activeAlerts: 0, cpuUsage: 0.25 };
|
||
const stateB = { activeAlerts: 1, cpuUsage: 0.85 };
|
||
const hashA = computeContentHash(stateA);
|
||
const _hashB = computeContentHash(stateB);
|
||
|
||
// 1. NEVER mode: completed or failed is ALWAYS quiet
|
||
const neverComp = evaluateRoutineNotification({
|
||
policy: policyNever,
|
||
status: "completed",
|
||
currentOutput: stateA,
|
||
});
|
||
expect(neverComp.shouldNotify).toBe(false);
|
||
expect(neverComp.quiet).toBe(true);
|
||
expect(neverComp.markUnread).toBe(false);
|
||
expect(neverComp.earconCue).toBeNull();
|
||
|
||
const neverFail = evaluateRoutineNotification({
|
||
policy: policyNever,
|
||
status: "failed",
|
||
error: "Fatal crash",
|
||
});
|
||
expect(neverFail.shouldNotify).toBe(false);
|
||
expect(neverFail.quiet).toBe(true);
|
||
expect(neverFail.markUnread).toBe(false);
|
||
expect(neverFail.earconCue).toBeNull();
|
||
|
||
// 2. ON_FAILURE mode: quiet on completion, loud on failure
|
||
const onFailComp = evaluateRoutineNotification({
|
||
policy: policyOnFailure,
|
||
status: "completed",
|
||
currentOutput: stateA,
|
||
});
|
||
expect(onFailComp.shouldNotify).toBe(false);
|
||
expect(onFailComp.quiet).toBe(true);
|
||
expect(onFailComp.markUnread).toBe(false);
|
||
expect(onFailComp.earconCue).toBe("routine_completed_quiet");
|
||
|
||
const onFailFail = evaluateRoutineNotification({
|
||
policy: policyOnFailure,
|
||
status: "failed",
|
||
error: "Timeout 504",
|
||
});
|
||
expect(onFailFail.shouldNotify).toBe(true);
|
||
expect(onFailFail.quiet).toBe(false);
|
||
expect(onFailFail.markUnread).toBe(true);
|
||
expect(onFailFail.earconCue).toBe("routine_failure");
|
||
|
||
// 3. ON_CHANGE mode: quiet when unchanged, loud when changed or failed
|
||
const onChangeUnchanged = evaluateRoutineNotification({
|
||
policy: policyOnChange,
|
||
status: "completed",
|
||
prevHash: hashA,
|
||
currentOutput: stateA,
|
||
});
|
||
expect(onChangeUnchanged.shouldNotify).toBe(false);
|
||
expect(onChangeUnchanged.quiet).toBe(true);
|
||
expect(onChangeUnchanged.markUnread).toBe(false);
|
||
expect(onChangeUnchanged.earconCue).toBe("routine_completed_quiet");
|
||
|
||
const onChangeChanged = evaluateRoutineNotification({
|
||
policy: policyOnChange,
|
||
status: "completed",
|
||
prevHash: hashA,
|
||
currentOutput: stateB,
|
||
});
|
||
expect(onChangeChanged.shouldNotify).toBe(true);
|
||
expect(onChangeChanged.quiet).toBe(false);
|
||
expect(onChangeChanged.markUnread).toBe(true);
|
||
expect(onChangeChanged.earconCue).toBe("routine_changed");
|
||
|
||
const onChangeFail = evaluateRoutineNotification({
|
||
policy: policyOnChange,
|
||
status: "failed",
|
||
error: "Connection lost",
|
||
});
|
||
expect(onChangeFail.shouldNotify).toBe(true);
|
||
expect(onChangeFail.quiet).toBe(false);
|
||
expect(onChangeFail.markUnread).toBe(true);
|
||
expect(onChangeFail.earconCue).toBe("routine_failure");
|
||
|
||
// 4. ALWAYS mode: notifies on both changed and unchanged completions
|
||
const alwaysUnchanged = evaluateRoutineNotification({
|
||
policy: policyAlways,
|
||
status: "completed",
|
||
prevHash: hashA,
|
||
currentOutput: stateA,
|
||
});
|
||
expect(alwaysUnchanged.shouldNotify).toBe(true);
|
||
expect(alwaysUnchanged.quiet).toBe(false);
|
||
expect(alwaysUnchanged.markUnread).toBe(true);
|
||
expect(alwaysUnchanged.earconCue).toBe("routine_completed_quiet");
|
||
});
|
||
});
|
||
|
||
describe("Challenge 9: Routine Self-Healing Error Classification & Jitter Stress", () => {
|
||
it("exhaustively classifies transient network/overload errors vs terminal failures", () => {
|
||
// Transient candidates
|
||
expect(isTransientRoutineError({ status: 429 })).toBe(true);
|
||
expect(isTransientRoutineError({ status: 502 })).toBe(true);
|
||
expect(isTransientRoutineError({ status: 503 })).toBe(true);
|
||
expect(isTransientRoutineError({ status: 504 })).toBe(true);
|
||
expect(isTransientRoutineError({ code: "ECONNRESET" })).toBe(true);
|
||
expect(isTransientRoutineError({ code: "ETIMEDOUT" })).toBe(true);
|
||
expect(isTransientRoutineError({ code: "UND_ERR_SOCKET" })).toBe(true);
|
||
expect(isTransientRoutineError(new Error("fetch failed"))).toBe(true);
|
||
expect(isTransientRoutineError(new Error("socket hang up"))).toBe(true);
|
||
expect(isTransientRoutineError(new Error("computer busy"))).toBe(true);
|
||
expect(isTransientRoutineError(new Error("sandbox unavailable"))).toBe(true);
|
||
expect(isTransientRoutineError(new Error("Rate limit exceeded"))).toBe(true);
|
||
expect(isTransientRoutineError(new Error("429 Too Many Requests"))).toBe(true);
|
||
expect(isTransientRoutineError(new Error("Model is overloaded"))).toBe(true);
|
||
expect(isTransientRoutineError(new Error("503 Service Unavailable"))).toBe(true);
|
||
|
||
// Terminal candidates
|
||
expect(isTransientRoutineError({ status: 401 })).toBe(false);
|
||
expect(isTransientRoutineError({ status: 403 })).toBe(false);
|
||
expect(isTransientRoutineError(new Error("invalid api key"))).toBe(false);
|
||
expect(isTransientRoutineError(new Error("syntax error at line 4"))).toBe(false);
|
||
expect(isTransientRoutineError(new Error("validation failed"))).toBe(false);
|
||
expect(isTransientRoutineError(new Error("budget exceeded"))).toBe(false);
|
||
expect(isTransientRoutineError(null)).toBe(false);
|
||
expect(isTransientRoutineError(undefined)).toBe(false);
|
||
expect(isTransientRoutineError("string error")).toBe(false);
|
||
});
|
||
|
||
it("verifies exponential backoff with full jitter stays strictly within bounds", () => {
|
||
for (let attempt = 1; attempt <= 10; attempt++) {
|
||
for (let run = 0; run < 20; run++) {
|
||
const delay = calculateRoutineBackoffMs(attempt, {
|
||
baseBackoffMs: 1000,
|
||
maxBackoffMs: 30000,
|
||
});
|
||
expect(delay).toBeGreaterThanOrEqual(0);
|
||
expect(delay).toBeLessThanOrEqual(30000);
|
||
}
|
||
}
|
||
|
||
// Negative and zero attempts return 0
|
||
expect(calculateRoutineBackoffMs(0)).toBe(0);
|
||
expect(calculateRoutineBackoffMs(-5)).toBe(0);
|
||
});
|
||
|
||
it("strips status chrome per AGENTS.md microcopy constraints", () => {
|
||
const minimalNotification = formatMinimalRoutineNotification({
|
||
botName: "DataSync",
|
||
routineName: "Nightly Sync",
|
||
outcome: "completed",
|
||
deltaSummary: "• 14 rows updated in data warehouse.\nVerbose stack trace...",
|
||
});
|
||
|
||
expect(minimalNotification.title).toBe("DataSync");
|
||
expect(minimalNotification.body).toBe("14 rows updated in data warehouse.");
|
||
expect(minimalNotification.body.length).toBeLessThanOrEqual(140);
|
||
expect(minimalNotification.body).not.toContain("Verbose stack trace");
|
||
});
|
||
});
|
||
|
||
describe("Challenge 10: Normalization Options, Exotic Objects & Harmonic Layering", () => {
|
||
it("respects sortArraysOfPrimitives option for non-semantic array orders", () => {
|
||
const arr1 = { tags: ["prod", "alpha", "beta"], counts: [10, 5, 20] };
|
||
const arr2 = { tags: ["beta", "alpha", "prod"], counts: [5, 20, 10] };
|
||
|
||
// Default: sortArraysOfPrimitives is false, order matters
|
||
expect(computeContentHash(arr1)).not.toBe(computeContentHash(arr2));
|
||
|
||
// Enabled: sorts primitive arrays
|
||
const h1 = computeContentHash(arr1, { sortArraysOfPrimitives: true });
|
||
const h2 = computeContentHash(arr2, { sortArraysOfPrimitives: true });
|
||
expect(h1).toBe(h2);
|
||
});
|
||
|
||
it("respects numericPrecision and whitespace normalization", () => {
|
||
const outA = { rate: 1.2345671, text: " hello\r\nworld " };
|
||
const outB = { rate: 1.2345674, text: "hello\nworld" };
|
||
|
||
// Epsilon difference rounded away at precision 5
|
||
expect(computeContentHash(outA, { numericPrecision: 5 })).toBe(
|
||
computeContentHash(outB, { numericPrecision: 5 }),
|
||
);
|
||
|
||
// Whitespace normalized by default
|
||
expect(deterministicStringify(outA)).toContain('"hello\\nworld"');
|
||
});
|
||
|
||
it("handles null-prototype objects, RegExp, and custom ignore keys", () => {
|
||
const nullProto = Object.create(null);
|
||
nullProto.keyA = "valA";
|
||
nullProto.keyB = 123;
|
||
nullProto.customEphemeral = "ephemeral-token";
|
||
|
||
const normalObj = { keyB: 123, keyA: "valA" };
|
||
|
||
expect(() => deterministicStringify(nullProto)).not.toThrow();
|
||
expect(
|
||
computeContentHash(nullProto, {
|
||
ignoreKeys: ["customEphemeral"],
|
||
}),
|
||
).toBe(computeContentHash(normalObj));
|
||
});
|
||
|
||
it("handles harmonic multi-layer recipes and negative delay steps without crashing", () => {
|
||
const complexRecipe: SoundCueRecipe = {
|
||
steps: [
|
||
{
|
||
kind: "tone",
|
||
frequency: 440,
|
||
delayMs: -10, // Negative delay edge case
|
||
durationMs: 60,
|
||
gain: 0.03,
|
||
wave: "sine",
|
||
},
|
||
{
|
||
kind: "tone",
|
||
frequency: 880,
|
||
delayMs: 20,
|
||
durationMs: 0, // 0ms duration edge case
|
||
gain: 0.02,
|
||
wave: "triangle",
|
||
},
|
||
{
|
||
kind: "sweep",
|
||
fromHz: 300,
|
||
toHz: 600,
|
||
delayMs: 30,
|
||
durationMs: 70,
|
||
gain: 0.03,
|
||
wave: "sine",
|
||
},
|
||
{
|
||
kind: "tone",
|
||
frequency: 1200,
|
||
delayMs: 500, // Starts after totalDurationMs
|
||
durationMs: 50,
|
||
gain: 0.02,
|
||
wave: "sine",
|
||
},
|
||
],
|
||
totalDurationMs: 120,
|
||
};
|
||
|
||
const wav = PcmWavSynthesizer.renderRecipeWav(complexRecipe);
|
||
expect(wav.byteLength).toBe(44 + Math.ceil(0.12 * 22050) * 2);
|
||
const view = new DataView(wav.buffer, wav.byteOffset, wav.byteLength);
|
||
expect(view.getUint32(0, false)).toBe(0x52494646); // RIFF
|
||
expect(view.getUint32(8, false)).toBe(0x57415645); // WAVE
|
||
});
|
||
});
|
||
});
|