fix(kuna): retain setting-specific doctor recovery

This commit is contained in:
morluto committed 2026-10-24 03:00:08 +08:00
1 parent ca8b6727c3
commit 9d8ede44d0
5 files changed
+58 -8

No files matched your search

+4
View File
@@ -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.
+13 -1
View File
@@ -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<never, KunaConfigurationFailure> => err({ code, reason, setting });
): Result<never, KunaConfigurationFailure> =>
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))
+3 -2
View File
@@ -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"
+11 -5
View File
@@ -120,6 +120,7 @@ export interface KunaReadinessFailure {
| "version_unresolved";
readonly reason: string;
readonly diagnostics: Readonly<Record<string, JsonValue>>;
readonly remediation?: string;
}
export interface KunaInstallationOptions {
@@ -383,11 +384,16 @@ export const inspectKunaInstallation = async (
): Promise<Result<KunaInstallation, KunaReadinessFailure>> => {
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);
@@ -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" });
},
);