From 9d8ede44d0e79ff8ac2e6d9070bafc390bd53334 Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Sat, 24 Oct 2026 02:54:11 +0800 Subject: [PATCH] fix(kuna): retain setting-specific doctor recovery --- docs/kuna-provider.md | 4 +++ src/kuna/KunaConfiguration.ts | 14 +++++++++- src/kuna/KunaDoctor.ts | 5 ++-- src/kuna/KunaInstallation.ts | 16 +++++++---- .../providers/kuna/kunaDoctor.test.ts | 27 +++++++++++++++++++ 5 files changed, 58 insertions(+), 8 deletions(-) diff --git a/docs/kuna-provider.md b/docs/kuna-provider.md index 9c4b885cb1..36c844b8c9 100644 --- a/docs/kuna-provider.md +++ b/docs/kuna-provider.md @@ -139,3 +139,7 @@ REA_KUNA_COMMAND=/absolute/path/to/kuna GHIDRA_INSTALL_DIR=/absolute/path/to/ghi The lane compiles a small source-owned fixture with the host C compiler and verifies the MCP workflow end to end. `--targets DIR --functions a,b` adds caller-built binaries. `--compare-ghidra` (with `GHIDRA_INSTALL_DIR`) runs those same functions through Ghidra; it requires `--targets` and `--functions` and refuses empty matrices or requested function names missing from every target. See [testing](testing.md#kuna-lane) for coverage. Failed runs also report process-lineage evidence and exit unsuccessfully. If provider or transport cleanup is unconfirmed, the report says `cleanup: "incomplete"` and records `retained_workspace`; that directory is preserved for investigation and cleanup retries. + +Doctor reports an invalid Kuna cache, job count, or timeout setting with that +setting's correction. Correct or unset the named setting and restart REA; these +configuration rejections do not require replacing the selected Kuna executable. diff --git a/src/kuna/KunaConfiguration.ts b/src/kuna/KunaConfiguration.ts index 4375e3b0f9..d6b796c58b 100644 --- a/src/kuna/KunaConfiguration.ts +++ b/src/kuna/KunaConfiguration.ts @@ -75,13 +75,25 @@ export interface KunaConfigurationFailure { readonly code: "not_configured" | "open_options_invalid" | "unsupported_host"; readonly reason: string; readonly setting: string | null; + readonly remediation: string; } const failure = ( code: KunaConfigurationFailure["code"], reason: string, setting: string | null = null, -): Result => err({ code, reason, setting }); +): Result => + err({ + code, + reason, + setting, + remediation: + code === "unsupported_host" + ? KUNA_UNSUPPORTED_HOST_REMEDIATION + : setting === null || setting === "REA_KUNA_COMMAND" + ? KUNA_COMMAND_REMEDIATION + : `Correct ${setting} to meet the reported constraint, or unset it to use its default. Restart REA after changing the environment.`, + }); const positiveInteger = (value: string): number | undefined => /^[1-9]\d*$/u.test(value) && Number.isSafeInteger(Number(value)) diff --git a/src/kuna/KunaDoctor.ts b/src/kuna/KunaDoctor.ts index da8f95e753..02eca2ce5a 100644 --- a/src/kuna/KunaDoctor.ts +++ b/src/kuna/KunaDoctor.ts @@ -182,11 +182,12 @@ const installationFailure = ( code: failure.code, detail: failure.reason, remediation: - failure.code === "unsupported_host" + failure.remediation ?? + (failure.code === "unsupported_host" ? KUNA_UNSUPPORTED_HOST_REMEDIATION : failure.code === "version_unresolved" ? KUNA_VERSION_PROBE_REMEDIATION - : KUNA_COMMAND_REMEDIATION, + : KUNA_COMMAND_REMEDIATION), classification: failure.code === "unsupported_host" ? "unsupported_host" diff --git a/src/kuna/KunaInstallation.ts b/src/kuna/KunaInstallation.ts index 087dbe5e8e..2197ec101b 100644 --- a/src/kuna/KunaInstallation.ts +++ b/src/kuna/KunaInstallation.ts @@ -120,6 +120,7 @@ export interface KunaReadinessFailure { | "version_unresolved"; readonly reason: string; readonly diagnostics: Readonly>; + readonly remediation?: string; } export interface KunaInstallationOptions { @@ -383,11 +384,16 @@ export const inspectKunaInstallation = async ( ): Promise> => { const configuration = readKunaConfiguration(environment, options.platform); if (!configuration.ok) - return failure(configuration.error.code, configuration.error.reason, { - configured: configuration.error.code !== "not_configured", - ...(configuration.error.setting === null - ? {} - : { setting: configuration.error.setting }), + return err({ + code: configuration.error.code, + reason: configuration.error.reason, + remediation: configuration.error.remediation, + diagnostics: { + configured: configuration.error.code !== "not_configured", + ...(configuration.error.setting === null + ? {} + : { setting: configuration.error.setting }), + }, }); const { command } = configuration.value; assertRegularFileActive(options.signal); diff --git a/tests/boundary/providers/kuna/kunaDoctor.test.ts b/tests/boundary/providers/kuna/kunaDoctor.test.ts index 5a3acd0aef..0679dec747 100644 --- a/tests/boundary/providers/kuna/kunaDoctor.test.ts +++ b/tests/boundary/providers/kuna/kunaDoctor.test.ts @@ -377,3 +377,30 @@ it("projects the fake Kuna through the completed doctor workflow", async () => { await host.close!(); } }); + +it.each([ + ["REA_KUNA_CACHE_DIR", "relative"], + ["REA_KUNA_CACHE_MAX_BYTES", "0"], + ["REA_KUNA_PROJECT_TIMEOUT_MS", "0"], + ["REA_KUNA_TIMEOUT_MS", "0"], + ["REA_KUNA_JOBS", "33"], +] as const)( + "recommends repairing %s without replacing a valid executable", + async (setting, value) => { + const root = await createTestTempDirectory("rea-kuna-setting-remediation-"); + const command = join(root, "caller-kuna"); + const inspection = await inspectKunaProvider({ + REA_KUNA_COMMAND: command, + [setting]: value, + }); + expect(inspection.available).toBe(false); + expect(inspection.checks[0]).toMatchObject({ + code: "open_options_invalid", + classification: "config_drift", + details: { setting }, + remediation: expect.stringContaining(`Correct ${setting}`), + }); + expect(inspection.checks[0]?.remediation).not.toContain("REA_KUNA_COMMAND"); + await expect(lstat(command)).rejects.toMatchObject({ code: "ENOENT" }); + }, +);