fix(experiment): isolate malformed native facet failures

Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com>
Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
This commit is contained in:
committed 2026-10-16 20:51:43 +00:00
1 parent 5ca00675b7
commit e70f2df61e
4 files changed
+95 -11

No files matched your search

@@ -79,6 +79,10 @@ export const readNativeFacet = (fn, name, label, parse) => {
fn[name] === undefined,
`Native ${label} is both present and unavailable`,
);
requireFact(
typeof reason === "string",
`Native ${label} failure reason must be text`,
);
return err(
new FactFailure(
"capability_unavailable",
@@ -0,0 +1,77 @@
import assert from "node:assert/strict";
import test from "node:test";
import { parseNativeIR } from "./native-ir.mjs";
import { readNativeGraphEvidence } from "./native-ir-contract.mjs";
// Minimal valid graph: this regression concerns facet admission, not SSA rules.
const document = () => ({
binary: "/fixture",
count: 1,
language: "c",
error: null,
functions: [
{
address_hex: "0x1000",
error: null,
code: "reconstructed C",
atomic_facts: {
schema_version: 3,
blocks: [
{
id: 0,
start: "0x1000",
operations: [],
predecessors: [],
successors: [],
},
],
operations: [],
values: [],
coverage: {
scope: "retained native graph",
machine_semantic_equivalence: "unknown",
indirect_branch_operations: [],
omitted_definition_ids: [],
issues: [],
},
},
},
],
});
test("malformed facet failure values stay local and preserve healthy graph/code/source views", () => {
for (const name of [
"atomic_facts",
"atomic_calls",
"atomic_parameters",
"atomic_switches",
"atomic_projection",
])
for (const reason of [null, 7, false, {}, []]) {
const doc = document();
if (name === "atomic_facts") delete doc.functions[0].atomic_facts;
doc.functions[0][`${name}_error`] = reason;
const text = JSON.stringify(doc),
model = parseNativeIR(text, "0x1000");
assert.equal(model.code, "reconstructed C");
if (name !== "atomic_facts") {
assert.equal(model.facets.recovered_ir.availability, "available");
assert.equal(model.blocks({}).total, 1);
assert.ok(readNativeGraphEvidence(text).text.length);
} else {
assert.equal(
model.facets.recovered_ir.error.code,
"invalid_provider_output",
);
assert.throws(
() => readNativeGraphEvidence(text),
(e) => e.code === "invalid_provider_output",
);
}
if (name === "atomic_parameters")
assert.equal(
model.facets.parameters.error.code,
"invalid_provider_output",
);
}
});
@@ -69,15 +69,15 @@ const documentSchema = z.object({
code: z.string(),
error: z.null(),
atomic_facts: z.unknown().optional(),
atomic_facts_error: z.string().optional(),
atomic_facts_error: z.unknown().optional(),
atomic_calls: z.unknown().optional(),
atomic_calls_error: z.string().optional(),
atomic_calls_error: z.unknown().optional(),
atomic_parameters: z.unknown().optional(),
atomic_parameters_error: z.string().optional(),
atomic_parameters_error: z.unknown().optional(),
atomic_switches: z.unknown().optional(),
atomic_switches_error: z.string().optional(),
atomic_switches_error: z.unknown().optional(),
atomic_projection: z.unknown().optional(),
atomic_projection_error: z.string().optional(),
atomic_projection_error: z.unknown().optional(),
}),
)
.length(1),
@@ -241,12 +241,15 @@ export function readNativeGraphEvidence(text) {
"This retained source is not a native function record",
);
const { fn } = readFunction(raw);
if (fn.atomic_facts === undefined)
throw new FactFailure(
"capability_unavailable",
"Recovered graph source was not captured",
{ native_error: fn.atomic_facts_error?.slice(0, 1024) },
if (fn.atomic_facts === undefined) {
const graph = readNativeFacet(
fn,
"atomic_facts",
"recovered IR",
(raw) => raw,
);
if (!graph.ok) throw graph.error;
}
const decoded = decodeGraphArchive(fn.atomic_facts);
const view = decoded ?? JSON.stringify(fn.atomic_facts);
requireFact(
@@ -128,7 +128,7 @@ test("unknown type/storage is distinct from native void, malformed identities ca
test("prototype facet works without graph/calls; unavailable/invalid prototype retains code and reason", () => {
const available = model({
atomic_parameters: fixture(),
atomic_facts_error: "capacity",
atomic_facts_error: { malformed: "not a failure string" },
atomic_calls_error: "capacity",
});
assert.equal(available.facets.parameters.availability, "available");