From 020a1bfe853a7c68fe227125a237fcfc5e7d1e0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=A9=98?= Date: Sat, 25 Apr 2026 01:52:58 +0000 Subject: [PATCH] refactor(core): remove unnecessary | null, unify timestamp naming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - SenseReflexConfig.on: string[] | null → string[] (empty = no conditions) - NerveConfig.workflows: Record | null → Record (empty = no workflows) - Signal.ts → Signal.timestamp - SenseInfo.lastSignalTs → SenseInfo.lastSignalTimestamp - All consumers across daemon/cli/store updated - parseNerveConfig: on defaults to [], workflows defaults to {} Fixes #108 --- packages/cli/src/__tests__/sense-list.test.ts | 48 ++++++++++++++----- packages/cli/src/commands/sense.ts | 5 +- packages/cli/src/commands/validate.ts | 2 +- packages/cli/src/daemon-client.ts | 2 +- packages/core/src/__tests__/config.test.ts | 13 ++--- packages/core/src/config.ts | 14 +++--- packages/core/src/types.ts | 8 ++-- .../daemon/src/__tests__/daemon-ipc.test.ts | 16 ++++++- .../daemon/src/__tests__/hot-reload.test.ts | 10 ++-- .../src/__tests__/kernel-integration.test.ts | 2 +- .../src/__tests__/kernel-phase6.test.ts | 10 ++-- .../__tests__/kernel-trigger-sense.test.ts | 2 +- .../kernel-workflow-integration.test.ts | 6 +-- packages/daemon/src/__tests__/kernel.test.ts | 6 +-- .../__tests__/log-store-integration.test.ts | 10 ++-- .../src/__tests__/phase6-integration.test.ts | 12 ++--- .../reflex-scheduler-throttle-pending.test.ts | 4 +- .../src/__tests__/reflex-scheduler.test.ts | 10 ++-- .../daemon/src/__tests__/signal-bus.test.ts | 2 +- packages/daemon/src/kernel.ts | 10 ++-- packages/daemon/src/reflex-scheduler.ts | 2 +- packages/daemon/src/workflow-manager.ts | 2 +- 22 files changed, 117 insertions(+), 79 deletions(-) diff --git a/packages/cli/src/__tests__/sense-list.test.ts b/packages/cli/src/__tests__/sense-list.test.ts index 2be5bc8..fa86657 100644 --- a/packages/cli/src/__tests__/sense-list.test.ts +++ b/packages/cli/src/__tests__/sense-list.test.ts @@ -29,10 +29,22 @@ const SAMPLE_SENSES: SenseInfo[] = [ group: "system", throttle: 5000, timeout: 3000, - lastSignalTs: 1_700_000_000_000, + lastSignalTimestamp: 1_700_000_000_000, + }, + { + name: "disk-usage", + group: "system", + throttle: 30000, + timeout: null, + lastSignalTimestamp: null, + }, + { + name: "active-tasks", + group: "tasks", + throttle: 10000, + timeout: 30000, + lastSignalTimestamp: null, }, - { name: "disk-usage", group: "system", throttle: 30000, timeout: null, lastSignalTs: null }, - { name: "active-tasks", group: "tasks", throttle: 10000, timeout: 30000, lastSignalTs: null }, ]; // --------------------------------------------------------------------------- @@ -100,14 +112,14 @@ describe("formatSenseList", () => { expect(output).toContain("—"); }); - it("shows '(never)' when lastSignalTs is null", () => { + it("shows '(never)' when lastSignalTimestamp is null", () => { const output = formatSenseList(SAMPLE_SENSES); expect(output).toContain("(never)"); }); - it("shows ISO timestamp when lastSignalTs is set", () => { + it("shows ISO timestamp when lastSignalTimestamp is set", () => { const output = formatSenseList(SAMPLE_SENSES); - // cpu-usage has lastSignalTs = 1_700_000_000_000 + // cpu-usage has lastSignalTimestamp = 1_700_000_000_000 expect(output).toContain(new Date(1_700_000_000_000).toISOString()); }); }); @@ -157,11 +169,19 @@ reflexes: [] ); const result = sensesFromConfig(path); expect(result).toHaveLength(2); - expect(result[0]).toMatchObject({ name: "cpu-usage", group: "system", lastSignalTs: null }); - expect(result[1]).toMatchObject({ name: "disk-usage", group: "system", lastSignalTs: null }); + expect(result[0]).toMatchObject({ + name: "cpu-usage", + group: "system", + lastSignalTimestamp: null, + }); + expect(result[1]).toMatchObject({ + name: "disk-usage", + group: "system", + lastSignalTimestamp: null, + }); }); - it("always sets lastSignalTs to null (static fallback)", () => { + it("always sets lastSignalTimestamp to null (static fallback)", () => { const path = join(tmpDir, "nerve.yaml"); writeFileSync( path, @@ -173,7 +193,7 @@ reflexes: [] `.trim(), ); const result = sensesFromConfig(path); - expect(result[0].lastSignalTs).toBeNull(); + expect(result[0].lastSignalTimestamp).toBeNull(); }); it("populates throttle and timeout from config", () => { @@ -238,7 +258,13 @@ describe("listSensesViaDaemon", () => { it("resolves with populated senses array", async () => { const senses: SenseInfo[] = [ - { name: "cpu-usage", group: "system", throttle: 5000, timeout: 3000, lastSignalTs: 12345 }, + { + name: "cpu-usage", + group: "system", + throttle: 5000, + timeout: 3000, + lastSignalTimestamp: 12345, + }, ]; const server = createServer((s) => { s.on("data", () => { diff --git a/packages/cli/src/commands/sense.ts b/packages/cli/src/commands/sense.ts index 6712bfb..640fe0a 100644 --- a/packages/cli/src/commands/sense.ts +++ b/packages/cli/src/commands/sense.ts @@ -43,7 +43,8 @@ export function formatSenseList(senses: SenseInfo[]): string { lines.push(` group: ${s.group}\n`); lines.push(` throttle: ${formatDuration(s.throttle)}\n`); lines.push(` timeout: ${formatDuration(s.timeout)}\n`); - const lastSignal = s.lastSignalTs !== null ? new Date(s.lastSignalTs).toISOString() : "(never)"; + const lastSignal = + s.lastSignalTimestamp !== null ? new Date(s.lastSignalTimestamp).toISOString() : "(never)"; lines.push(` last signal: ${lastSignal}\n`); } return lines.join(""); @@ -64,7 +65,7 @@ export function sensesFromConfig(configPath: string): SenseInfo[] { group: cfg.group, throttle: cfg.throttle, timeout: cfg.timeout, - lastSignalTs: null, + lastSignalTimestamp: null, })); } diff --git a/packages/cli/src/commands/validate.ts b/packages/cli/src/commands/validate.ts index 1a8e860..87ddca4 100644 --- a/packages/cli/src/commands/validate.ts +++ b/packages/cli/src/commands/validate.ts @@ -31,7 +31,7 @@ export const validateCommand = defineCommand({ const config = result.value; const senseCount = Object.keys(config.senses).length; const reflexCount = config.reflexes.length; - const workflowCount = config.workflows ? Object.keys(config.workflows).length : 0; + const workflowCount = Object.keys(config.workflows).length; process.stdout.write( `✅ nerve.yaml is valid — ${senseCount} sense(s), ${reflexCount} reflex(es), ${workflowCount} workflow(s)\n`, diff --git a/packages/cli/src/daemon-client.ts b/packages/cli/src/daemon-client.ts index c085257..eea565f 100644 --- a/packages/cli/src/daemon-client.ts +++ b/packages/cli/src/daemon-client.ts @@ -28,7 +28,7 @@ function isSenseInfo(value: unknown): value is SenseInfo { typeof value.group === "string" && (value.throttle === null || typeof value.throttle === "number") && (value.timeout === null || typeof value.timeout === "number") && - (value.lastSignalTs === null || typeof value.lastSignalTs === "number") + (value.lastSignalTimestamp === null || typeof value.lastSignalTimestamp === "number") ); } diff --git a/packages/core/src/__tests__/config.test.ts b/packages/core/src/__tests__/config.test.ts index ebf27ed..6c9cff5 100644 --- a/packages/core/src/__tests__/config.test.ts +++ b/packages/core/src/__tests__/config.test.ts @@ -50,7 +50,7 @@ describe("parseNerveConfig", () => { kind: "sense", sense: "cpu", interval: 30_000, - on: null, + on: [], }); expect(result.value.reflexes[1]).toEqual({ kind: "sense", @@ -58,7 +58,7 @@ describe("parseNerveConfig", () => { interval: null, on: ["high_usage"], }); - expect(result.value.workflows?.alert).toEqual({ + expect(result.value.workflows.alert).toEqual({ concurrency: 2, overflow: "queue", maxQueue: 10, @@ -85,11 +85,12 @@ senses: group: system reflexes: - sense: cpu + interval: 1s `; const result = parseNerveConfig(yaml); expect(result.ok).toBe(true); if (!result.ok) return; - expect(result.value.workflows).toBeNull(); + expect(result.value.workflows).toEqual({}); }); it("sense config has null for omitted throttle/timeout/gracePeriod", () => { @@ -142,11 +143,11 @@ workflows: const result = parseNerveConfig(yaml); expect(result.ok).toBe(true); if (!result.ok) return; - expect(result.value.workflows?.alert).toEqual({ + expect(result.value.workflows.alert).toEqual({ concurrency: 1, overflow: "drop", }); - expect("maxQueue" in (result.value.workflows?.alert ?? {})).toBe(false); + expect("maxQueue" in result.value.workflows.alert).toBe(false); }); it("overflow: queue defaults maxQueue to 100", () => { @@ -163,7 +164,7 @@ workflows: const result = parseNerveConfig(yaml); expect(result.ok).toBe(true); if (!result.ok) return; - expect(result.value.workflows?.alert).toEqual({ + expect(result.value.workflows.alert).toEqual({ concurrency: 1, overflow: "queue", maxQueue: 100, diff --git a/packages/core/src/config.ts b/packages/core/src/config.ts index 65eb2d8..600df7a 100644 --- a/packages/core/src/config.ts +++ b/packages/core/src/config.ts @@ -76,8 +76,8 @@ function validateSenseConfig(name: string, raw: unknown): Result { }); } -function parseOnField(index: number, obj: Record): Result { - if (obj.on === undefined || obj.on === null) return ok(null); +function parseOnField(index: number, obj: Record): Result { + if (obj.on === undefined || obj.on === null) return ok([]); if (!Array.isArray(obj.on) || !obj.on.every((item): item is string => typeof item === "string")) { return err(new Error(`reflexes[${index}].on: must be an array of strings`)); } @@ -88,7 +88,7 @@ function parseSenseReflex( index: number, obj: Record, senseNames: Set, - on: string[] | null, + on: string[], ): Result { if (typeof obj.sense !== "string") { return err(new Error(`reflexes[${index}].sense: must be a string`)); @@ -100,7 +100,7 @@ function parseSenseReflex( const intervalResult = parseDurationField(obj.interval, `reflexes[${index}].interval`); if (!intervalResult.ok) return intervalResult; - if (intervalResult.value === null && on !== null && on.length === 0) { + if (intervalResult.value === null && on.length === 0) { return err( new Error(`reflexes[${index}]: sense reflex must have at least one of "interval" or "on"`), ); @@ -245,10 +245,8 @@ function parseReflexes( return ok(reflexes); } -function parseWorkflows( - obj: Record, -): Result | null> { - if (obj.workflows === undefined || obj.workflows === null) return ok(null); +function parseWorkflows(obj: Record): Result> { + if (obj.workflows === undefined || obj.workflows === null) return ok({}); if (!isPlainRecord(obj.workflows)) { return err(new Error("workflows: must be an object if provided")); diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts index a5539f6..ad831ea 100644 --- a/packages/core/src/types.ts +++ b/packages/core/src/types.ts @@ -2,7 +2,7 @@ export type Signal = { id: number; senseId: string; payload: unknown; - ts: number; + timestamp: number; }; export type SenseConfig = { @@ -18,14 +18,14 @@ export type SenseInfo = { group: string; throttle: number | null; timeout: number | null; - lastSignalTs: number | null; + lastSignalTimestamp: number | null; }; export type SenseReflexConfig = { kind: "sense"; sense: string; interval: number | null; - on: string[] | null; + on: string[]; }; /** Reflexes only schedule Senses; workflow launches come from Sense return values. */ @@ -49,7 +49,7 @@ export type NerveConfig = { maxRounds: number; senses: Record; reflexes: ReflexConfig[]; - workflows: Record | null; + workflows: Record; }; // --------------------------------------------------------------------------- diff --git a/packages/daemon/src/__tests__/daemon-ipc.test.ts b/packages/daemon/src/__tests__/daemon-ipc.test.ts index 7b47daf..688250d 100644 --- a/packages/daemon/src/__tests__/daemon-ipc.test.ts +++ b/packages/daemon/src/__tests__/daemon-ipc.test.ts @@ -200,8 +200,20 @@ describe("daemon-ipc — list-senses", () => { it("responds ok:true with senses populated from listSenses", async () => { const sensesData = [ - { name: "cpu-usage", group: "system", throttle: 5000, timeout: 3000, lastSignalTs: 1000 }, - { name: "disk-usage", group: "system", throttle: 30000, timeout: null, lastSignalTs: null }, + { + name: "cpu-usage", + group: "system", + throttle: 5000, + timeout: 3000, + lastSignalTimestamp: 1000, + }, + { + name: "disk-usage", + group: "system", + throttle: 30000, + timeout: null, + lastSignalTimestamp: null, + }, ]; const listSenses = vi.fn(() => sensesData); server = createDaemonIpcServer(sockPath, makeMockWorkflowManager() as never, { diff --git a/packages/daemon/src/__tests__/hot-reload.test.ts b/packages/daemon/src/__tests__/hot-reload.test.ts index d92072d..4bdb52f 100644 --- a/packages/daemon/src/__tests__/hot-reload.test.ts +++ b/packages/daemon/src/__tests__/hot-reload.test.ts @@ -250,7 +250,7 @@ describe("Kernel — workflow hot reload via file-watcher (Phase 3)", () => { const logStore = makeLogStore(); const config: NerveConfig = { senses: {}, - reflexes: [{ kind: "workflow", workflow: "my-wf", on: null } as any], + reflexes: [], workflows: { "my-wf": { concurrency: 1, overflow: "drop" } }, maxRounds: 10, }; @@ -285,7 +285,7 @@ describe("Kernel — workflow hot reload via file-watcher (Phase 3)", () => { const logStore = makeLogStore(); const initialConfig: NerveConfig = { senses: {}, - reflexes: [{ kind: "workflow", workflow: "old-wf", on: null } as any], + reflexes: [], workflows: { "old-wf": { concurrency: 1, overflow: "drop" } }, maxRounds: 10, }; @@ -307,7 +307,7 @@ describe("Kernel — workflow hot reload via file-watcher (Phase 3)", () => { const newConfig: NerveConfig = { senses: {}, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, }; kernel.reloadConfig(newConfig); @@ -328,7 +328,7 @@ describe("Kernel — workflow hot reload via file-watcher (Phase 3)", () => { const logStore = makeLogStore(); const initialConfig: NerveConfig = { senses: {}, - reflexes: [{ kind: "workflow", workflow: "my-wf", on: null } as any], + reflexes: [], workflows: { "my-wf": { concurrency: 1, overflow: "drop" } }, maxRounds: 10, }; @@ -344,7 +344,7 @@ describe("Kernel — workflow hot reload via file-watcher (Phase 3)", () => { // Reload with updated concurrency — should NOT spawn a new workflow worker const newConfig: NerveConfig = { senses: {}, - reflexes: [{ kind: "workflow", workflow: "my-wf", on: null } as any], + reflexes: [], workflows: { "my-wf": { concurrency: 5, overflow: "queue", maxQueue: 50 } }, maxRounds: 10, }; diff --git a/packages/daemon/src/__tests__/kernel-integration.test.ts b/packages/daemon/src/__tests__/kernel-integration.test.ts index d86daef..9bd868e 100644 --- a/packages/daemon/src/__tests__/kernel-integration.test.ts +++ b/packages/daemon/src/__tests__/kernel-integration.test.ts @@ -26,7 +26,7 @@ function makeConfig(overrides: Partial = {}): NerveConfig { "cpu-usage": { group: "system", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, ...overrides, }; diff --git a/packages/daemon/src/__tests__/kernel-phase6.test.ts b/packages/daemon/src/__tests__/kernel-phase6.test.ts index 5fd62bc..8eab8d4 100644 --- a/packages/daemon/src/__tests__/kernel-phase6.test.ts +++ b/packages/daemon/src/__tests__/kernel-phase6.test.ts @@ -73,7 +73,7 @@ function makeConfig(overrides: Partial = {}): NerveConfig { "cpu-usage": { group: "system", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, ...overrides, }; @@ -180,7 +180,7 @@ describe("kernel — reloadConfig", () => { "net-rx": { group: "network", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, }); @@ -197,7 +197,7 @@ describe("kernel — reloadConfig", () => { "net-rx": { group: "network", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, }; const kernel = createKernel(config, "/tmp/nerve-test"); @@ -212,7 +212,7 @@ describe("kernel — reloadConfig", () => { "cpu-usage": { group: "system", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, }); @@ -235,7 +235,7 @@ describe("kernel — reloadConfig", () => { "disk-usage": { group: "system", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, }); diff --git a/packages/daemon/src/__tests__/kernel-trigger-sense.test.ts b/packages/daemon/src/__tests__/kernel-trigger-sense.test.ts index 297d22c..ecdc458 100644 --- a/packages/daemon/src/__tests__/kernel-trigger-sense.test.ts +++ b/packages/daemon/src/__tests__/kernel-trigger-sense.test.ts @@ -92,7 +92,7 @@ function makeConfig(overrides: Partial = {}): NerveConfig { "cpu-usage": { group: "system", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, ...overrides, }; diff --git a/packages/daemon/src/__tests__/kernel-workflow-integration.test.ts b/packages/daemon/src/__tests__/kernel-workflow-integration.test.ts index 87d7fbf..4966041 100644 --- a/packages/daemon/src/__tests__/kernel-workflow-integration.test.ts +++ b/packages/daemon/src/__tests__/kernel-workflow-integration.test.ts @@ -95,7 +95,7 @@ function makeConfig(overrides: Partial = {}): NerveConfig { "cpu-usage": { group: "system", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, ...overrides, }; @@ -298,7 +298,7 @@ describe("kernel + workflowManager integration", () => { "cpu-usage": { group: "system", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, }); @@ -366,7 +366,7 @@ describe("kernel + workflowManager integration", () => { "cpu-usage": { group: "system", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, }; kernel.reloadConfig(newConfig); diff --git a/packages/daemon/src/__tests__/kernel.test.ts b/packages/daemon/src/__tests__/kernel.test.ts index f581740..f758f10 100644 --- a/packages/daemon/src/__tests__/kernel.test.ts +++ b/packages/daemon/src/__tests__/kernel.test.ts @@ -59,7 +59,7 @@ function makeConfig(overrides: Partial = {}): NerveConfig { "cpu-usage": { group: "system", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, ...overrides, }; @@ -200,7 +200,7 @@ describe("kernel — groupForSense mapping", () => { "net-usage": { group: "network", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, }; const kernel = createKernel(config, "/tmp/nerve-test"); @@ -215,7 +215,7 @@ describe("kernel — groupForSense mapping", () => { senses: { "cpu-usage": { group: "system", throttle: null, timeout: null, gracePeriod: null }, }, - reflexes: [{ kind: "sense", sense: "cpu-usage", interval: 500, on: null }], + reflexes: [{ kind: "sense", sense: "cpu-usage", interval: 500, on: [] }], }); createKernel(config, "/tmp/nerve-test"); diff --git a/packages/daemon/src/__tests__/log-store-integration.test.ts b/packages/daemon/src/__tests__/log-store-integration.test.ts index c00712d..e627208 100644 --- a/packages/daemon/src/__tests__/log-store-integration.test.ts +++ b/packages/daemon/src/__tests__/log-store-integration.test.ts @@ -29,7 +29,7 @@ describe("LogStore + ReflexScheduler integration", () => { "cpu-usage": { group: "system", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [{ kind: "sense", sense: "cpu-usage", interval: null, on: ["cpu-usage"] }], - workflows: null, + workflows: {}, maxRounds: 10, }; const bus = createSignalBus(); @@ -38,7 +38,7 @@ describe("LogStore + ReflexScheduler integration", () => { logStore, }); - const signal: Signal = { id: 1, senseId: "cpu-usage", payload: 42, ts: Date.now() }; + const signal: Signal = { id: 1, senseId: "cpu-usage", payload: 42, timestamp: Date.now() }; bus.emit(signal); const logs = logStore.query({ source: "reflex", type: "run_start" }); @@ -56,8 +56,8 @@ describe("LogStore + ReflexScheduler integration", () => { senses: { "cpu-usage": { group: "system", throttle: null, timeout: null, gracePeriod: null }, }, - reflexes: [{ kind: "sense", sense: "cpu-usage", interval: 1000, on: null }], - workflows: null, + reflexes: [{ kind: "sense", sense: "cpu-usage", interval: 1000, on: [] }], + workflows: {}, maxRounds: 10, }; const bus = createSignalBus(); @@ -88,7 +88,7 @@ describe("LogStore + ReflexScheduler integration", () => { "cpu-usage": { group: "system", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [{ kind: "sense", sense: "cpu-usage", interval: null, on: ["cpu-usage"] }], - workflows: null, + workflows: {}, maxRounds: 10, }; const bus = createSignalBus(); diff --git a/packages/daemon/src/__tests__/phase6-integration.test.ts b/packages/daemon/src/__tests__/phase6-integration.test.ts index d129088..6915542 100644 --- a/packages/daemon/src/__tests__/phase6-integration.test.ts +++ b/packages/daemon/src/__tests__/phase6-integration.test.ts @@ -23,7 +23,7 @@ function makeConfig(overrides: Partial = {}): NerveConfig { "cpu-usage": { group: "system", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, ...overrides, }; @@ -136,7 +136,7 @@ describe("phase6 — reloadConfig", () => { "net-rx": { group: "network", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, }; @@ -156,7 +156,7 @@ describe("phase6 — reloadConfig", () => { "net-rx": { group: "network", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, }; kernel = createKernel(config, "/tmp/nerve-phase6-test", { @@ -171,7 +171,7 @@ describe("phase6 — reloadConfig", () => { "cpu-usage": { group: "system", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, }; @@ -202,7 +202,7 @@ describe("phase6 — error isolation", () => { "bad-sense": { group: "mixed", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, }; @@ -306,7 +306,7 @@ describe("phase6 — getHealth", () => { "net-rx": { group: "network", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, }; kernel.reloadConfig(newConfig); diff --git a/packages/daemon/src/__tests__/reflex-scheduler-throttle-pending.test.ts b/packages/daemon/src/__tests__/reflex-scheduler-throttle-pending.test.ts index bea9b3e..cf18866 100644 --- a/packages/daemon/src/__tests__/reflex-scheduler-throttle-pending.test.ts +++ b/packages/daemon/src/__tests__/reflex-scheduler-throttle-pending.test.ts @@ -10,14 +10,14 @@ function makeConfig(overrides: Partial = {}): NerveConfig { "cpu-usage": { group: "system", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, ...overrides, }; } function makeSignal(senseId: string, payload: unknown = 1): Signal { - return { id: 1, senseId, payload, ts: Date.now() }; + return { id: 1, senseId, payload, timestamp: Date.now() }; } describe("ReflexScheduler — throttle + pending deferred trigger", () => { diff --git a/packages/daemon/src/__tests__/reflex-scheduler.test.ts b/packages/daemon/src/__tests__/reflex-scheduler.test.ts index 764ba2c..cac2a6e 100644 --- a/packages/daemon/src/__tests__/reflex-scheduler.test.ts +++ b/packages/daemon/src/__tests__/reflex-scheduler.test.ts @@ -16,14 +16,14 @@ function makeConfig(overrides: Partial = {}): NerveConfig { "system-health": { group: "derived", throttle: null, timeout: null, gracePeriod: null }, }, reflexes: [], - workflows: null, + workflows: {}, maxRounds: 10, ...overrides, }; } function makeSignal(senseId: string, payload: unknown = 1): Signal { - return { id: 1, senseId, payload, ts: Date.now() }; + return { id: 1, senseId, payload, timestamp: Date.now() }; } // --------------------------------------------------------------------------- @@ -41,7 +41,7 @@ describe("ReflexScheduler — interval reflex", () => { it("fires triggerFn on schedule", () => { const triggered: string[] = []; const config = makeConfig({ - reflexes: [{ kind: "sense", sense: "cpu-usage", interval: 1000, on: null }], + reflexes: [{ kind: "sense", sense: "cpu-usage", interval: 1000, on: [] }], }); const bus = createSignalBus(); // Use a ref so the triggerFn can call back into the scheduler @@ -66,7 +66,7 @@ describe("ReflexScheduler — interval reflex", () => { it("stops firing after stop() is called", () => { const triggered: string[] = []; const config = makeConfig({ - reflexes: [{ kind: "sense", sense: "cpu-usage", interval: 500, on: null }], + reflexes: [{ kind: "sense", sense: "cpu-usage", interval: 500, on: [] }], }); const bus = createSignalBus(); const ref: { scheduler: ReturnType | null } = { @@ -89,7 +89,7 @@ describe("ReflexScheduler — interval reflex", () => { it("starts from current time — does not compensate for past intervals", () => { const triggered: string[] = []; const config = makeConfig({ - reflexes: [{ kind: "sense", sense: "cpu-usage", interval: 1000, on: null }], + reflexes: [{ kind: "sense", sense: "cpu-usage", interval: 1000, on: [] }], }); const bus = createSignalBus(); const scheduler = createReflexScheduler(config, bus, (name) => triggered.push(name)); diff --git a/packages/daemon/src/__tests__/signal-bus.test.ts b/packages/daemon/src/__tests__/signal-bus.test.ts index 7cc1801..149feec 100644 --- a/packages/daemon/src/__tests__/signal-bus.test.ts +++ b/packages/daemon/src/__tests__/signal-bus.test.ts @@ -4,7 +4,7 @@ import type { Signal } from "@uncaged/nerve-core"; import { createSignalBus } from "../signal-bus.js"; function makeSignal(senseId: string, payload: unknown = 1): Signal { - return { id: 1, senseId, payload, ts: Date.now() }; + return { id: 1, senseId, payload, timestamp: Date.now() }; } describe("createSignalBus", () => { diff --git a/packages/daemon/src/kernel.ts b/packages/daemon/src/kernel.ts index 17d8db7..8d7fe21 100644 --- a/packages/daemon/src/kernel.ts +++ b/packages/daemon/src/kernel.ts @@ -161,14 +161,14 @@ export function createKernel( id: nextSignalId(), senseId: msg.sense, payload: route.payload, - ts: Date.now(), + timestamp: Date.now(), }; logStore.append({ source: "sense", type: "signal", refId: msg.sense, payload: JSON.stringify(route.payload), - ts: signal.ts, + ts: signal.timestamp, }); bus.emit(signal); } @@ -239,7 +239,7 @@ export function createKernel( function reloadConfig(newConfig: NerveConfig): void { const oldGroups = collectSenseGroups(config); const oldConfig = config; - const oldWorkflows = config.workflows ?? {}; + const oldWorkflows = config.workflows; config = newConfig; scheduler.stop(); scheduler = createReflexScheduler(config, bus, triggerFn, { @@ -247,7 +247,7 @@ export function createKernel( }); workflowManager.updateConfig(newConfig); - const newWorkflows = newConfig.workflows ?? {}; + const newWorkflows = newConfig.workflows; for (const workflowName of Object.keys(oldWorkflows)) { if (!(workflowName in newWorkflows)) { @@ -327,7 +327,7 @@ export function createKernel( group: senseConfig.group, throttle: senseConfig.throttle, timeout: senseConfig.timeout, - lastSignalTs: lastEntry !== null ? lastEntry.ts : null, + lastSignalTimestamp: lastEntry !== null ? lastEntry.ts : null, }; }); }, diff --git a/packages/daemon/src/reflex-scheduler.ts b/packages/daemon/src/reflex-scheduler.ts index 2bf2060..ac61845 100644 --- a/packages/daemon/src/reflex-scheduler.ts +++ b/packages/daemon/src/reflex-scheduler.ts @@ -164,7 +164,7 @@ export function createReflexScheduler( intervals.push(id); } - if (senseReflex.on !== null && senseReflex.on.length > 0) { + if (senseReflex.on.length > 0) { const watchedSenses = new Set(senseReflex.on); const unsub = bus.subscribe((signal) => { if (watchedSenses.has(signal.senseId)) { diff --git a/packages/daemon/src/workflow-manager.ts b/packages/daemon/src/workflow-manager.ts index e65ba84..f030915 100644 --- a/packages/daemon/src/workflow-manager.ts +++ b/packages/daemon/src/workflow-manager.ts @@ -217,7 +217,7 @@ export function createWorkflowManager( } function workflowConfig(workflowName: string): WorkflowConfig | null { - return config.workflows?.[workflowName] ?? null; + return config.workflows[workflowName] ?? null; } function toWorkflowRunStatus(eventType: string): WorkflowRunStatus | null { -- 2.43.0