From 11bc7381985b3966bc849010656d3ce7073dfed7 Mon Sep 17 00:00:00 2001 From: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com> Date: Wed, 11 Nov 2026 11:52:57 +0000 Subject: [PATCH] fix(settings): preserve runtime contracts while restacking operation limits Retain the original stack merge resolutions for runtime update modes, migrated guard exceptions, deadline ownership and boundary expectations on the merged settings base. Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> --- scripts/verify/rea-numeric-limits.mjs | 31 +++++-------------- src/application/AnalysisSettings.test.ts | 3 +- src/config/hopperCallerScanBudget.test.ts | 4 +-- src/config/windowsChangeTimeSettle.test.ts | 2 +- src/config/windowsChangeTimeSettle.ts | 6 ++-- src/domain/analysisSettingDefinitions.ts | 11 +++++++ src/kuna/KunaSessionClient.ts | 9 ------ .../kuna/kunaIndependentDecompileMcp.test.ts | 4 +-- .../kuna/kunaSingleFunctionRuns.test.ts | 4 +-- .../providers/kuna/kunaStrings.test.ts | 4 +-- 10 files changed, 31 insertions(+), 47 deletions(-) diff --git a/scripts/verify/rea-numeric-limits.mjs b/scripts/verify/rea-numeric-limits.mjs index 9a6b6de2fe..8b53ab76fe 100644 --- a/scripts/verify/rea-numeric-limits.mjs +++ b/scripts/verify/rea-numeric-limits.mjs @@ -857,20 +857,11 @@ export const parseRuntimeSettingEnvironments = (source) => { return names; }; -/** Existing launch-seed migrations. A new per-call policy is rejected. */ -const TEMPORARY_PER_CALL_LIMITS = new Set([ - "REA_BRIDGE_MAX_REQUEST_BYTES", - "REA_DEBUGGER_COMMAND_DEADLINE_MS", - "REA_DEBUGGER_MAX_COMMAND_OUTPUT_BYTES", - "REA_HOPPER_CALLER_SCAN_BUDGET_MS", - "REA_JEB_MCP_TIMEOUT_MS", - "REA_KUNA_PROJECT_TIMEOUT_MS", - "REA_KUNA_TIMEOUT_MS", - "REA_PWNTOOLS_MAX_INPUT_BYTES", - "REA_WINDOWS_CHANGE_TIME_DRIFT_MS", - "REA_WINDOWS_CHANGE_TIME_SETTLE_MAX_POLLS", - "REA_WINDOWS_CHANGE_TIME_SETTLE_POLL_MS", -]); +/** + * Names still registered as per-call while they move into ANALYSIS_SETTINGS. + * This branch has moved those limits, so the allowlist is empty. + */ +const TEMPORARY_PER_CALL_LIMITS = new Set([]); /** Temporary exceptions must be removed from both the registry and allowlist. */ export const perCallLimitFailures = (listed, temporary) => { @@ -908,15 +899,7 @@ if (direct) { failures.push( `${name} is both a runtime setting and on the exception list`, ); - if (policy === "restart-only" && read === "per-call") - failures.push( - `${name} is restart-only but is recorded as a per-call read`, - ); - if ( - policy === "restart-only" && - read !== "startup" && - read !== "construction" - ) + if (read !== "startup" && read !== "construction") failures.push(`${name} is restart-only but is not read at startup`); } failures.push( @@ -1055,7 +1038,7 @@ if (direct) { process.exit(1); } console.log( - `Numeric REA_ limits: ${runtime.size} runtime, ${[...listed.values()].filter((entry) => entry.policy === "restart-only").length} restart-only, ${[...listed.values()].filter((entry) => entry.policy === "per-call").length} per-call pending migration, ${discovered.size} discovered.`, + `Numeric REA_ limits: ${runtime.size} runtime, ${listed.size} restart-only, ${discovered.size} discovered.`, ); console.log( "Read-site kinds describe syntax only; Node lint does not verify reachability, invocation frequency or complete reader coverage.", diff --git a/src/application/AnalysisSettings.test.ts b/src/application/AnalysisSettings.test.ts index 2b3b3eb3ad..d8214ffe84 100644 --- a/src/application/AnalysisSettings.test.ts +++ b/src/application/AnalysisSettings.test.ts @@ -291,8 +291,7 @@ it("rejects an out-of-range per-call limit and reads an accepted value from the expect(accepted.ok).toBe(true); if (!accepted.ok) return; expect(accepted.value).toMatchObject({ - effect: "immediate", - rea_restart_required: false, + pending_restart: false, }); const parsed = parseConfig({}); if (!parsed.ok) throw parsed.error; diff --git a/src/config/hopperCallerScanBudget.test.ts b/src/config/hopperCallerScanBudget.test.ts index bebeb89091..b1823c0db5 100644 --- a/src/config/hopperCallerScanBudget.test.ts +++ b/src/config/hopperCallerScanBudget.test.ts @@ -85,8 +85,8 @@ it("explains the time budget and that fixed byte allowances have no override", ( source: "setting", minimum: MINIMUM_HOPPER_CALLER_SCAN_BUDGET_MS, maximum: MAX_NODE_TIMER_DELAY_MS, - effect: "immediate", - rea_restart_required: false, + update_mode: "runtime", + pending_restart: false, }); expect(DEFAULT_HOPPER_CALLER_SCAN_BUDGET_MS).toBe(2000); const page = hopperCallerScanRecovery( diff --git a/src/config/windowsChangeTimeSettle.test.ts b/src/config/windowsChangeTimeSettle.test.ts index 269a65bab7..664f45585b 100644 --- a/src/config/windowsChangeTimeSettle.test.ts +++ b/src/config/windowsChangeTimeSettle.test.ts @@ -97,7 +97,7 @@ it.each([ const facts = { setting: bound.setting, constraint: message, - update_mode: "restart" as const, + update_mode: "runtime" as const, }; expect(resolved.error.settings).toEqual([facts]); expect(resolved.error.settings[0]).not.toHaveProperty("pending_restart"); diff --git a/src/config/windowsChangeTimeSettle.ts b/src/config/windowsChangeTimeSettle.ts index 68f59e9c55..1766e23b77 100644 --- a/src/config/windowsChangeTimeSettle.ts +++ b/src/config/windowsChangeTimeSettle.ts @@ -64,7 +64,7 @@ export interface WindowsChangeTimeSettingFailure { readonly floor: number; readonly maximum: number; readonly unit: "ms" | "polls"; - readonly update_mode: "restart"; + readonly update_mode: "runtime"; } /** Effective settle budget and whether each part came from the default or a setting. */ @@ -88,14 +88,14 @@ const windowsChangeTimeSettingFailure = ( floor: bound.floor, maximum: bound.maximum, unit: bound.unit, - update_mode: "restart", + update_mode: "runtime", }); /** Name the rejected value, its source, and the allowed range. */ const windowsChangeTimeSettingMessage = ( failure: WindowsChangeTimeSettingFailure, ): string => - `The supplied ${failure.setting} value is ${failure.currentValue} ${failure.unit} (source ${failure.source}; default ${String(failure.defaultValue)} ${failure.unit}, floor ${String(failure.floor)} ${failure.unit}, maximum ${String(failure.maximum)} ${failure.unit}). Set ${failure.setting} in the environment that starts REA to a positive decimal integer from ${String(failure.floor)} to ${String(failure.maximum)}, then start a new REA invocation.`; + `The supplied ${failure.setting} value is ${failure.currentValue} ${failure.unit} (source ${failure.source}; default ${String(failure.defaultValue)} ${failure.unit}, floor ${String(failure.floor)} ${failure.unit}, maximum ${String(failure.maximum)} ${failure.unit}). windows change-time settings can be changed at runtime with update_analysis_settings. The launch seed ${failure.setting} must be a positive decimal integer from ${String(failure.floor)} to ${String(failure.maximum)}. The rejected value is not active.`; const inRange = (bound: WindowsChangeTimeBound, value: string): boolean => { if (!decimalInteger.test(value)) return false; diff --git a/src/domain/analysisSettingDefinitions.ts b/src/domain/analysisSettingDefinitions.ts index 72b4ed41ca..076071a654 100644 --- a/src/domain/analysisSettingDefinitions.ts +++ b/src/domain/analysisSettingDefinitions.ts @@ -336,6 +336,7 @@ export const ANALYSIS_SETTINGS = { operations: ["match_native_functions"], }, "bridge.max_request_bytes": { + updateMode: "runtime", environment: "REA_BRIDGE_MAX_REQUEST_BYTES", defaultValue: DEFAULT_BRIDGE_MAX_REQUEST_BYTES, minimum: MINIMUM_BRIDGE_MAX_REQUEST_BYTES, @@ -396,6 +397,7 @@ export const ANALYSIS_SETTINGS = { ], }, "debugger.command_deadline_ms": { + updateMode: "runtime", ...timer, environment: "REA_DEBUGGER_COMMAND_DEADLINE_MS", defaultValue: DEFAULT_DEBUGGER_COMMAND_DEADLINE_MS, @@ -413,6 +415,7 @@ export const ANALYSIS_SETTINGS = { ], }, "debugger.max_command_output_bytes": { + updateMode: "runtime", environment: "REA_DEBUGGER_MAX_COMMAND_OUTPUT_BYTES", defaultValue: DEFAULT_DEBUGGER_MAX_COMMAND_OUTPUT_BYTES, minimum: MINIMUM_DEBUGGER_MAX_COMMAND_OUTPUT_BYTES, @@ -424,6 +427,7 @@ export const ANALYSIS_SETTINGS = { operations: ["gdb_console", "rizin_debug_command"], }, "hopper.caller_scan_budget_ms": { + updateMode: "runtime", ...timer, environment: "REA_HOPPER_CALLER_SCAN_BUDGET_MS", defaultValue: DEFAULT_HOPPER_CALLER_SCAN_BUDGET_MS, @@ -436,6 +440,7 @@ export const ANALYSIS_SETTINGS = { ], }, "jeb.mcp_timeout_ms": { + updateMode: "runtime", ...timer, environment: "REA_JEB_MCP_TIMEOUT_MS", defaultValue: DEFAULT_JEB_MCP_TIMEOUT_MS, @@ -448,6 +453,7 @@ export const ANALYSIS_SETTINGS = { ], }, "kuna.project_timeout_ms": { + updateMode: "runtime", ...timer, environment: "REA_KUNA_PROJECT_TIMEOUT_MS", defaultValue: DEFAULT_KUNA_PROJECT_TIMEOUT_MS, @@ -466,6 +472,7 @@ export const ANALYSIS_SETTINGS = { ], }, "kuna.function_timeout_ms": { + updateMode: "runtime", ...timer, environment: "REA_KUNA_TIMEOUT_MS", defaultValue: DEFAULT_KUNA_FUNCTION_TIMEOUT_MS, @@ -478,6 +485,7 @@ export const ANALYSIS_SETTINGS = { ], }, "pwntools.max_input_bytes": { + updateMode: "runtime", environment: "REA_PWNTOOLS_MAX_INPUT_BYTES", defaultValue: DEFAULT_PWNTOOLS_MAX_INPUT_BYTES, minimum: 1, @@ -489,6 +497,7 @@ export const ANALYSIS_SETTINGS = { parameter: "producer_limits.input_bytes", }, "windows.change_time_drift_ms": { + updateMode: "runtime", environment: "REA_WINDOWS_CHANGE_TIME_DRIFT_MS", defaultValue: DEFAULT_WINDOWS_CHANGE_TIME_DRIFT_MS, minimum: 1, @@ -505,6 +514,7 @@ export const ANALYSIS_SETTINGS = { ], }, "windows.change_time_settle_poll_ms": { + updateMode: "runtime", environment: "REA_WINDOWS_CHANGE_TIME_SETTLE_POLL_MS", defaultValue: DEFAULT_WINDOWS_CHANGE_TIME_SETTLE_POLL_MS, minimum: 1, @@ -521,6 +531,7 @@ export const ANALYSIS_SETTINGS = { ], }, "windows.change_time_settle_max_polls": { + updateMode: "runtime", environment: "REA_WINDOWS_CHANGE_TIME_SETTLE_MAX_POLLS", defaultValue: DEFAULT_WINDOWS_CHANGE_TIME_SETTLE_MAX_POLLS, minimum: 1, diff --git a/src/kuna/KunaSessionClient.ts b/src/kuna/KunaSessionClient.ts index 92d469e58b..e9c213bf6e 100644 --- a/src/kuna/KunaSessionClient.ts +++ b/src/kuna/KunaSessionClient.ts @@ -405,8 +405,6 @@ export class KunaSessionClient implements AnalysisClient { #installation: | SharedStartupAttempt> | undefined; - /** Launch configuration captured when the export starts, for its deadline source. */ - #kunaConfiguration: KunaConfiguration | undefined; /** Deadline the project export accepted. Not recomputed from later settings. */ #acceptedProjectDeadline: KunaDeadline | undefined; #inventory: @@ -2101,14 +2099,7 @@ export class KunaSessionClient implements AnalysisClient { return this.#starting; } - #projectDeadline(): KunaDeadline | undefined { - // The export already accepted this deadline. Re-resolving after a reset - // would throw away the value the failed export used. - return this.#acceptedProjectDeadline; - } - async #lookupOrBuild(installation: KunaInstallation): Promise { - this.#kunaConfiguration = installation.configuration; this.#acceptedProjectDeadline ??= kunaProjectDeadline( installation.configuration, ); diff --git a/tests/boundary/providers/kuna/kunaIndependentDecompileMcp.test.ts b/tests/boundary/providers/kuna/kunaIndependentDecompileMcp.test.ts index 57a60cdf9d..e895c6b81d 100644 --- a/tests/boundary/providers/kuna/kunaIndependentDecompileMcp.test.ts +++ b/tests/boundary/providers/kuna/kunaIndependentDecompileMcp.test.ts @@ -84,8 +84,8 @@ it("runs a cold independent request without starting a project export", async () limit: { setting: "kuna.function_timeout_ms", effective_value: 300, - effect: "immediate", - rea_restart_required: false, + update_mode: "runtime", + pending_restart: false, minimum: 1, maximum: 2147483647, }, diff --git a/tests/boundary/providers/kuna/kunaSingleFunctionRuns.test.ts b/tests/boundary/providers/kuna/kunaSingleFunctionRuns.test.ts index 501bf92c7a..dc947cc763 100644 --- a/tests/boundary/providers/kuna/kunaSingleFunctionRuns.test.ts +++ b/tests/boundary/providers/kuna/kunaSingleFunctionRuns.test.ts @@ -372,8 +372,8 @@ it("names the setting that owns a single-function deadline", async () => { limit: { setting: "kuna.function_timeout_ms", effective_value: 300, - effect: "immediate", - rea_restart_required: false, + update_mode: "runtime", + pending_restart: false, }, }, }); diff --git a/tests/boundary/providers/kuna/kunaStrings.test.ts b/tests/boundary/providers/kuna/kunaStrings.test.ts index f09a456cd9..83276a6bcb 100644 --- a/tests/boundary/providers/kuna/kunaStrings.test.ts +++ b/tests/boundary/providers/kuna/kunaStrings.test.ts @@ -438,8 +438,8 @@ it.each([ limit: { setting: "kuna.project_timeout_ms", effective_value: 500, - effect: "immediate", - rea_restart_required: false, + update_mode: "runtime", + pending_restart: false, }, }, });