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:
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");
|
||||
|
||||
Reference in new issue
Block a user