fix(native)!: preserve unknown provider symbol addresses

Normalize empty addresses once at the symbol-inventory interpretation
boundary. Keep Swift and Objective-C records with null locations, preserve
the original empty value in evidence.reported_address, and retain partial
coverage instead of failing the entire metadata result. Nonempty provider
address syntax remains unchanged.

Validation: domain and production-registration MCP regressions failed before
the fix. Ten focused files passed 181 tests with 2 skipped, including SDK
converted JSON Schema/catalog validation and graph consumers. npm run check
and docs:check passed (5 documentation tests). Fixture provider only; no
native-provider malformed-symbol or Windows runtime reproduction claimed.

BREAKING CHANGE: Swift symbol address can now be null when the provider has
no location. Clients consuming swift_symbols must handle an unknown address.
The original provider value is retained in optional evidence.reported_address.

Refs #4542
This commit is contained in:
morluto committed 2026-10-17 03:14:30 +08:00
1 parent 060587b456
commit 8116dc0651
4 files changed
+100 -8

No files matched your search

+4
View File
@@ -398,6 +398,10 @@ complete observation.
coroutine tables, and inherited overrides, have explicit unsupported or
partial coverage. Other providers retain the
existing symbol-based inventory with its narrower coverage.
Symbols without a reported location remain in that inventory: Swift symbol
`address` and Objective-C `implementation_address` are null. An empty provider
address is retained as `reported_address` in the symbol's evidence, whose
location also remains unknown. Nonempty provider address syntax is preserved.
## Native desktop observation
@@ -74,4 +74,43 @@ describe("ObjC/Swift metadata", () => {
}),
);
});
it("retains unlocated Swift and Objective-C symbols with explicit unknown addresses and source evidence", () => {
const result = inspectNativeDispatchMetadata([
{ name: "$s4Test7MyClassC", address: "" },
{ name: "-[StoreController buildTapped:]", address: "" },
{ name: "$s5Store7launchyyF", address: "ram:0x3000" },
]);
expect(result.swift_symbols).toMatchObject([
{
mangled_name: "$s4Test7MyClassC",
address: null,
evidence: [{ location: { address: null }, reported_address: "" }],
},
{ mangled_name: "$s5Store7launchyyF", address: "ram:0x3000" },
]);
expect(result.swift_symbols[1]?.evidence[0]).not.toHaveProperty(
"reported_address",
);
expect(result.objc_dispatch_implementations).toMatchObject([
{
class_name: "StoreController",
implementation_address: null,
location: { address: null },
evidence: [{ location: { address: null }, reported_address: "" }],
},
]);
expect(result.objc_classes[0]?.methods[0]).toMatchObject({
selector: "buildTapped:",
address: null,
});
expect(result.coverage).toContainEqual(
expect.objectContaining({
facet: "swift_declarations",
status: "partial",
examined: 2,
decoded: 0,
}),
);
});
});
+15 -8
View File
@@ -124,6 +124,8 @@ export const nativeMetadataEvidenceSchema = z.strictObject({
description: z.string().min(1),
provider: providerSchema.optional(),
location: nativeMetadataLocationSchema,
/** Preserve a provider's source value when it does not establish a location. */
reported_address: z.string().optional(),
artifact_path: z.string().min(1).nullable().default(null),
artifact_sha256: z
.string()
@@ -229,7 +231,7 @@ export const relativePointerSchema = z.strictObject({
/** A raw Swift-mangled symbol when a provider has not decoded its metadata. */
export const swiftSymbolSchema = z.strictObject({
mangled_name: z.string().min(1),
address: z.string().min(1),
address: z.string().min(1).nullable(),
decode: nativeMetadataDecodeSchema,
evidence: z.array(nativeMetadataEvidenceSchema).min(1),
});
@@ -338,11 +340,15 @@ export const inspectNativeDispatchMetadata = (
names: readonly { readonly address: string; readonly name: string }[],
maxRecords = 5_000,
): ObjcSwiftMetadata => {
const symbolEvidence = (name: string, address: string) => [
const symbolEvidence = (
entry: (typeof names)[number],
address: string | null,
) => [
{
kind: "symbol" as const,
description: `Provider symbol ${name}`,
description: `Provider symbol ${entry.name}`,
location: { address, file_offset: null },
...(address === null ? { reported_address: entry.address } : {}),
artifact_path: null,
artifact_sha256: null,
},
@@ -353,6 +359,7 @@ export const inspectNativeDispatchMetadata = (
const examinedNames = names.slice(0, maxRecords);
for (const entry of examinedNames) {
const address = entry.address === "" ? null : entry.address;
const classMatch = /(?:OBJC_CLASS_|OBJC_METACLASS_)\$_([^\s]+)/u.exec(
entry.name,
);
@@ -366,10 +373,10 @@ export const inspectNativeDispatchMetadata = (
class_name: methodMatch[2],
selector,
method_type: methodType,
implementation_address: entry.address,
location: { address: entry.address, file_offset: null },
implementation_address: address,
location: { address, file_offset: null },
decode: { status: "partial", reason: "symbol_name_only" },
evidence: symbolEvidence(entry.name, entry.address),
evidence: symbolEvidence(entry, address),
});
classNames.add(methodMatch[2]);
}
@@ -377,9 +384,9 @@ export const inspectNativeDispatchMetadata = (
if (isSwiftMangledSymbolName(entry.name) || entry.name.startsWith("_Tt"))
swiftSymbols.push({
mangled_name: entry.name,
address: entry.address,
address,
decode: { status: "partial", reason: "mangled_symbol_not_demangled" },
evidence: symbolEvidence(entry.name, entry.address),
evidence: symbolEvidence(entry, address),
});
}
@@ -425,3 +425,45 @@ describe("enhanced MCP tools", () => {
});
});
});
it("retains unlocated provider symbols through the advertised native dispatch MCP contract", async () => {
const ordinary = fixturePort();
const client = await connect({
execute: (operation, parameters, options) =>
operation === "list_names"
? Promise.resolve(
targetObservation([
{ address: "", value: "$s4Test7MyClassC" },
{ address: "", value: "-[Fixture buildTapped:]" },
]),
)
: ordinary.execute(operation, parameters, options),
});
const response = await client.callTool({
name: "inspect_native_dispatch_metadata",
arguments: { max_records: 100 },
});
expect(response.isError).not.toBe(true);
expect(jsonResult(response)).toMatchObject({
provider: { id: "fixture" },
target_sha256: "a".repeat(64),
result: {
swift_symbols: [
{
mangled_name: "$s4Test7MyClassC",
address: null,
evidence: [{ reported_address: "", location: { address: null } }],
},
],
objc_dispatch_implementations: [
{ implementation_address: null, evidence: [{ reported_address: "" }] },
],
objc_classes: [
{
name: "Fixture",
methods: [{ selector: "buildTapped:", address: null }],
},
],
},
});
});