fix(discovery): carry provider repair instructions into capability discovery (#8532)

* fix(discovery): carry provider repair instructions into capability discovery

Provider readiness reported only a failure description, so capability
discovery copied that description into tool_availability remediation and
dropped repairs the owners already had: the JEB discovery-deadline setting,
the ADB/Apktool execute-permission repair kept in diagnostics, and the IDA
and Kuna configuration recovery.

Unavailable ProviderAvailability now requires the owner's remediation
next to its reason, and readiness-derived unavailable capability
descriptors carry it too. Every producer supplies it (ADB, Apktool, JADX,
JEB, Ghidra, Hopper, IDA, Kuna, pcap, owned-process prerequisites, profile
rejection); the inventory uses it verbatim, falling back to the reason
only for static capability facts that declare no repair. ADB/Apktool
configuration failures own remediation as a field instead of diagnostics,
and execution refusals project it as remediation.action. JADX doctor reads
the availability's remediation instead of re-deriving it.

binary_session complete candidate availability and unavailable capability
groups expose remediation (CLI capabilities/providers too); summary omits
it and lists it in omitted_fields. The superseded IDA
configuration_remediation and windows_native_failure.remediation
diagnostics are removed.

Fixes #8506

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(discovery): keep owner repairs through provider selection and capability maps

Address review findings on the readiness-remediation contract:

- Provider selection rejections carry the availability owner's
  remediation, CLI/MCP error details project it, and an explicit
  selection refused by readiness uses it as remediation.action. IDA's
  unreadable registration and Ghidra's Windows readiness repair no
  longer degrade to generic selection advice now that the diagnostics
  copies are gone.
- Ghidra's Windows readiness and existing-project capability maps carry
  the same repair as the availability.
- pcap readiness keeps the limiter owner's repair (now a typed
  remediationAction) and falls back to Wireshark guidance only when the
  failure has none.
- ADB/Apktool repairs are owner constants that are complete sentences.
  The execution refusal keeps its previous message-plus-repair recovery,
  which the compiled CLI contract asserts.
- Regression tests compare with the owning formatter or constant and
  add public MCP and CLI coverage for an explicit IDA selection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kaoru0822andClaude Opus 5.5 authored and GitHub committed 2026-11-01 08:57:11 -08:00
1 parent eb39a07966
commit cbc69537cd
56 files changed
+913 -259

No files matched your search

+40 -59
View File
@@ -72,10 +72,10 @@ type ConfigurationFailureCode =
| "open_options_invalid";
/**
* Failure kinds tagged at the JADX configuration boundary. Consumers (the
* doctor adapter) project the boundary's remediation and classification for
* a kind; they must not reinterpret the phase/setting evidence to derive
* them.
* Failure kinds tagged at the JADX configuration boundary. Availability
* carries the boundary's remediation for a kind and the doctor adapter
* projects its classification; neither reinterprets the phase/setting
* evidence to derive them.
*/
export type JadxFailureKind =
| "windows_native_artifact"
@@ -101,72 +101,52 @@ export const JADX_FAILURE_KINDS: readonly JadxFailureKind[] = [
"unsupported_host",
];
export interface JadxFailureOutcome {
readonly remediation: string;
readonly classification: DoctorProviderCheck["classification"];
}
/** The boundary-owned repair instruction for one tagged failure kind. */
const jadxFailureRemediation = (kind: JadxFailureKind): string => {
switch (kind) {
case "windows_native_artifact":
case "windows_native_runtime":
return JADX_WINDOWS_NATIVE_REMEDIATION;
case "java_runtime":
case "java_home":
return JADX_JAVA_RUNTIME_REMEDIATION;
case "jvm_options":
return JADX_JVM_OPTIONS_REMEDIATION;
case "operation_timeout":
return JADX_OPERATION_TIMEOUT_REMEDIATION;
case "jar":
return JADX_JAR_CONFIGURATION_REMEDIATION;
case "java_probe":
return JADX_JAVA_PROBE_REMEDIATION;
case "unsupported_host":
return JADX_UNSUPPORTED_HOST_REMEDIATION;
}
};
/**
* Project the boundary-owned remediation and classification for a failure
* kind. A null kind (missing or corrupt tag) keeps JAR guidance, matching
* the previous fallthrough for unclassified failures.
* Project the doctor classification for a failure kind. A null kind (an
* untagged readiness failure) is classified as configuration drift.
*/
export const projectJadxFailureOutcome = (
export const jadxFailureClassification = (
kind: JadxFailureKind | null,
configured: boolean,
): JadxFailureOutcome => {
): DoctorProviderCheck["classification"] => {
const drift = configured ? "config_drift" : "missing_analysis_engine";
switch (kind) {
case "windows_native_artifact":
return {
remediation: JADX_WINDOWS_NATIVE_REMEDIATION,
classification: "missing_dependency",
};
case "windows_native_runtime":
return {
remediation: JADX_WINDOWS_NATIVE_REMEDIATION,
classification: "runtime_failure",
};
case "java_runtime":
return {
remediation: JADX_JAVA_RUNTIME_REMEDIATION,
classification: "missing_dependency",
};
case "java_home":
return {
remediation: JADX_JAVA_RUNTIME_REMEDIATION,
classification: drift,
};
case "jvm_options":
return {
remediation: JADX_JVM_OPTIONS_REMEDIATION,
classification: drift,
};
case "operation_timeout":
return {
remediation: JADX_OPERATION_TIMEOUT_REMEDIATION,
classification: drift,
};
case "jar":
return {
remediation: JADX_JAR_CONFIGURATION_REMEDIATION,
classification: drift,
};
case "java_probe":
return {
remediation: JADX_JAVA_PROBE_REMEDIATION,
classification: drift,
};
return "missing_dependency";
case "windows_native_runtime":
return "runtime_failure";
case "unsupported_host":
return {
remediation: JADX_UNSUPPORTED_HOST_REMEDIATION,
classification: "unsupported_host",
};
return "unsupported_host";
case "java_home":
case "jvm_options":
case "operation_timeout":
case "jar":
case "java_probe":
case null:
return {
remediation: JADX_JAR_CONFIGURATION_REMEDIATION,
classification: drift,
};
return drift;
}
};
@@ -227,6 +207,7 @@ export const inspectJadxAvailability = async (
status: "unavailable",
code: cause.code,
reason: cause.message,
remediation: jadxFailureRemediation(cause.kind),
diagnostics: { ...cause.diagnostics, failure_kind: cause.kind },
};
const prerequisite = ownedProcessPrerequisiteAvailability(cause);
+11 -7
View File
@@ -8,9 +8,10 @@ import type { JsonValue } from "../domain/jsonValue.js";
import {
inspectJadxAvailability,
JADX_FAILURE_KINDS,
projectJadxFailureOutcome,
jadxFailureClassification,
type JadxFailureKind,
} from "./JadxConfiguration.js";
import { JADX_JAR_CONFIGURATION_REMEDIATION } from "./JadxRelease.js";
/** Project the validated JADX configuration into the doctor contract. */
export const inspectJadxProvider = async (
@@ -93,14 +94,14 @@ const configurationCheck = (
: {}),
};
}
// The failure kind is tagged at the configuration boundary; project its
// remediation and classification here instead of reinterpreting the
// phase/setting evidence. Every failed check keeps a remediation.
// Availability carries the configuration boundary's remediation and tags
// the failure kind; project its classification here instead of
// reinterpreting the phase/setting evidence. Every failed check keeps a
// remediation.
const kind =
availability.status === "unavailable"
? asFailureKind(availability.diagnostics["failure_kind"])
: null;
const outcome = projectJadxFailureOutcome(kind, configured);
return {
name: "configuration",
ok: false,
@@ -109,8 +110,11 @@ const configurationCheck = (
availability.status === "available"
? "Validated JADX configuration reported no JAR path."
: availability.reason,
remediation: outcome.remediation,
classification: outcome.classification,
remediation:
availability.status === "available"
? JADX_JAR_CONFIGURATION_REMEDIATION
: availability.remediation,
classification: jadxFailureClassification(kind, configured),
...(kind === "java_probe" && availability.status === "unavailable"
? { details: probeDetails(availability.diagnostics) }
: kind !== null &&