From 4f13bd6733539cf5d9e00a736cc269b0ed50465a Mon Sep 17 00:00:00 2001 From: N0zoM1z0 <161784452+N0zoM1z0@users.noreply.github.com> Date: Fri, 16 Oct 2026 04:51:55 +0000 Subject: [PATCH] experiment(re)!: separate IR origins from machine instruction facts A real model conflated an optimized CFG edge with the bytes at its instruction origin. Make provenance explicit and add bounded, batched instruction windows from the existing Kuna CLI, preserving exact bytes, per-window failures and Evidence. Record the task-cost comparison without treating the incomplete machine-path answer as an equal-quality win. Verified 33 focused tests, targeted lint, real stock/patched MCP decoding with mixed success, and a real 4096-row capacity run under the resource observer. BREAKING CHANGE: Experimental operation.address/addresses and block.start become origin_address/origin_addresses and origin_start. Printed CFG target_address becomes target_origin; historical source captures are unchanged. Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com> --- experiments/atomic-codemode/README.md | 12 +- experiments/atomic-codemode/api.mjs | 36 +++++- experiments/atomic-codemode/executor.mjs | 5 +- experiments/atomic-codemode/fact-error.mjs | 16 +++ .../atomic-codemode/instruction-facts.mjs | 108 ++++++++++++++++++ .../instruction-facts.test.mjs | 92 +++++++++++++++ experiments/atomic-codemode/native-engine.md | 23 ++++ experiments/atomic-codemode/native-ir.mjs | 18 ++- .../atomic-codemode/native-ir.test.mjs | 5 + experiments/atomic-codemode/provider.mjs | 68 ++++++++--- experiments/atomic-codemode/trials.md | 37 ++++++ experiments/atomic-codemode/verify-mcp.mjs | 16 ++- experiments/atomic-re-tools/kuna-ssa.mjs | 34 ++++-- experiments/atomic-re-tools/kuna-ssa.test.mjs | 2 +- .../atomic-re-tools/verify-relations.mjs | 2 +- 15 files changed, 427 insertions(+), 47 deletions(-) create mode 100644 experiments/atomic-codemode/instruction-facts.mjs create mode 100644 experiments/atomic-codemode/instruction-facts.test.mjs diff --git a/experiments/atomic-codemode/README.md b/experiments/atomic-codemode/README.md index 27f1fdca9..174cf242a 100644 --- a/experiments/atomic-codemode/README.md +++ b/experiments/atomic-codemode/README.md @@ -67,8 +67,16 @@ Runtime successors, memory aliases and machine-semantic equivalence remain unknown. Other providers are not yet wired to this experimental service. The initial [actual model comparison](trials.md) favors the focused traditional -workflow. Code Mode reduces atomic round trips but has not yet established a -whole-task advantage. The revised native relations need real-model evaluation. +workflow. The seeded value-origin task has lower cumulative input and wall time +through Code Mode, but also exposes an IR-to-machine-path error. An equal-quality +whole-task advantage remains unproven; the representation fix needs a new trial. + +`instructions` reads selected machine byte/decode windows through the same +owner. Recovered operations use `origin_address` and blocks `origin_start`; +these locations cannot be substituted for machine branch destinations. The +value-origin trial demonstrates why this boundary matters. The experiment +currently has eleven fact methods, still behind the same four-tool Code Mode +catalog. Completion requires actual model Code Mode use, independently checked task answers, shared atomic/direct/Code Mode semantics, needed relation coverage, diff --git a/experiments/atomic-codemode/api.mjs b/experiments/atomic-codemode/api.mjs index 983c5b56d..9fab90e1f 100644 --- a/experiments/atomic-codemode/api.mjs +++ b/experiments/atomic-codemode/api.mjs @@ -59,12 +59,32 @@ export const methods = { "snapshot", 'await rea.snapshot({entry:"0x1000"})', ), + instructions: definition( + "Batch selected machine instruction windows. Read exact bytes and native disassembly to verify actual control transfers; optimized recovered CFG edges can bypass machine blocks. Address+size with the artifact context identifies the byte range. A caller-selected decode start does not prove execution/alignment.", + { + windows: z + .array( + z.strictObject({ + address: hex, + count: z.number().int().min(1).max(4096).default(16), + }), + ) + .min(1) + .max(16), + fields: z + .array(z.enum(["bytes", "text", "mnemonic", "operands"])) + .min(1) + .optional(), + }, + "instructions", + 'await rea.instructions({windows:[{address:"0x1000",count:8}],fields:["bytes","text"]})', + ), operations: definition( - "Select SSA operations by IDs, instruction origins, block IDs, constant literal or kind. Filters intersect; identities and source lines are always retained. A literal match is not object identity.", + "Select SSA operations by IDs, source instruction origins, block IDs, constant literal or kind. Filters intersect; identities and source lines are always retained. A literal match is not object identity. origin_addresses selects optimized recovered IR origins; use instructions for actual machine control transfers.", { snapshot, ids: z.array(z.string()).max(128).optional(), - addresses: z.array(hex).max(128).optional(), + origin_addresses: z.array(hex).max(128).optional(), block_ids: z.array(z.number().int().nonnegative()).max(128).optional(), constant: hex.optional(), kind: kinds.optional(), @@ -120,12 +140,14 @@ export const methods = { "await rea.values({snapshot:s,ids:op.inputs.map(i=>i.value_id).filter(Boolean)})", ), blocks: definition( - "Read native recovered blocks by stable snapshot-local ID, with ordered predecessors, successor edges and member operation IDs. Requires native IR. A recovered CFG does not establish all runtime successors.", + "Read native recovered blocks by stable snapshot-local ID, with ordered predecessors, successor edges and member operation IDs. origin_start describes optimized IR provenance, not a machine block extent. Requires native IR. Runtime successors remain unknown.", { snapshot, ids: z.array(z.number().int().nonnegative()).max(128).optional(), fields: z - .array(z.enum(["start", "operations", "predecessors", "successors"])) + .array( + z.enum(["origin_start", "operations", "predecessors", "successors"]), + ) .min(1) .optional(), ...page, @@ -169,6 +191,8 @@ export const methods = { // Compact return contracts let code compose before first observing a response. const returns = { + instructions: + "data:{results:[{ok,address,source:{evidence_id,sha256},items:[{address,size,status,bytes?,text?,mnemonic?,operands?}],resume_address,coverage}|{ok:false,address,error}]}; decoded rows describe selected machine bytes, not optimized IR branches. Partial batches retain successful windows.", strings: "data:{source:{evidence_id,sha256},items:[{address,value,string:{encoding,byte_length}}],total,next_offset,coverage}", references: @@ -176,13 +200,13 @@ const returns = { snapshot: "data:{snapshot:string,entry,source:{evidence_id,sha256},coverage:{operations,blocks,control_flow,unrepresented_outgoing_blocks,issues}}", operations: - "data:{items:[{id,address,block,source_line,kind,statement,definitions,constants,inputs?,output?,opcode?}],total,next_offset,evidence_id,coverage}. Native inputs:[{input_index,value_id,phi_predecessor}], output:value_id|null; definitions:[{definition,use,input_index,value_id,phi_predecessor}]. Diagnostic definitions use printed occurrence instead. fields projects columns; identity remains.", + "data:{items:[{id,origin_address,block,source_line,kind,statement,definitions,constants,inputs?,output?,opcode?}],total,next_offset,evidence_id,coverage}. Native inputs:[{input_index,value_id,phi_predecessor,phi_predecessor_edge}], output:value_id|null; definitions:[{definition,use,input_index,value_id,phi_predecessor,phi_predecessor_edge}]. Diagnostic definitions use printed occurrence instead. fields projects columns; identity remains.", relations: "data:{items:definition/use edges OR CFG edges{from,to,operation,polarity?,condition_value?,condition_test?} OR controllers{controller,dependent,edge},total,next_offset,evidence_id,coverage,relation_coverage?}. definitions/uses seeds are operation-ID strings; incoming/outgoing/controllers seeds are numeric block IDs. Native def/use edges include input_index,value_id,phi_predecessor; diagnostic edges include occurrence only.", values: "data:{items:[{id,space,offset,size_bytes,is_constant,is_input,is_annotation,definition,type_name}],total,next_offset,evidence_id,coverage}; definitions are operation IDs, offsets are exact hex strings.", blocks: - "data:{items:[{id,start,operations,predecessors,successors:[{target,index,polarity,condition_value,condition_test}]}],total,next_offset,evidence_id,coverage}; IDs are local to this snapshot.", + "data:{items:[{id,origin_start,operations,predecessors,successors:[{target,index,polarity,condition_value,condition_test}]}],total,next_offset,evidence_id,coverage}; IDs are local to this snapshot.", slice: "data:{operations:[operation rows as above],coverage:{status,boundaries:[{id,reason}],missing,source_issues,runtime_reachability,memory_alias_dependencies},evidence_id}", code: "data:{evidence_id,start_line,text,total_lines,next_offset,coverage}; text is reconstructed C. Use a large limit to inspect/filter the full text within code without returning it all.", diff --git a/experiments/atomic-codemode/executor.mjs b/experiments/atomic-codemode/executor.mjs index 2b7a27cd6..6cb8183df 100644 --- a/experiments/atomic-codemode/executor.mjs +++ b/experiments/atomic-codemode/executor.mjs @@ -129,7 +129,10 @@ const installBridge = ( lease, ); for (const row of [response.data, ...(response.data?.results ?? [])]) { - const id = row?.source?.evidence_id ?? row?.evidence_id; + const id = + row?.source?.evidence_id ?? + row?.evidence_id ?? + row?.error?.details?.evidence_id; if (id) state.evidence.add(id); } if (response.error?.details?.evidence_id) diff --git a/experiments/atomic-codemode/fact-error.mjs b/experiments/atomic-codemode/fact-error.mjs index d53d09e56..0de4803ee 100644 --- a/experiments/atomic-codemode/fact-error.mjs +++ b/experiments/atomic-codemode/fact-error.mjs @@ -1,3 +1,5 @@ +import { projectAnalysisError } from "../../dist/domain/analysisErrorProjection.js"; + // Shared expected-failure algebra; execution adapters do not depend on native protocols. export class FactFailure extends Error { constructor(code, message, details = {}) { @@ -6,3 +8,17 @@ export class FactFailure extends Error { this.details = details; } } + +export const requireResult = (result, code = "provider_failure") => { + if (!result.ok) { + const failure = result.error?._tag + ? projectAnalysisError(result.error) + : result.error; + throw new FactFailure( + failure?.code ?? code, + String(failure?.message ?? failure?.reason ?? failure), + { failure }, + ); + } + return result.value; +}; diff --git a/experiments/atomic-codemode/instruction-facts.mjs b/experiments/atomic-codemode/instruction-facts.mjs new file mode 100644 index 000000000..f4c5ffff3 --- /dev/null +++ b/experiments/atomic-codemode/instruction-facts.mjs @@ -0,0 +1,108 @@ +// The machine-decoding boundary is separate from optimized recovered IR. +import { z } from "zod"; +import { FactFailure } from "./fact-error.mjs"; + +const hex = z.string().regex(/^0x(?:0|[1-9a-f][0-9a-f]{0,15})$/u); +const rowSchema = z.object({ + address_hex: hex, + size: z.number().int().positive().max(65536), + bytes: z.string().regex(/^(?:[0-9a-f]{2})+$/u), + mnemonic: z.string(), + operands: z.string(), + text: z.string(), +}); +const envelopeSchema = z.object({ + binary: z.string(), + kind: z.literal("code"), + start_hex: hex, + end_hex: hex, + count: z.number().int().nonnegative().max(4096), + bytes: z.number().int().nonnegative(), + truncated: z.boolean(), + notes: z.array(z.string()), +}); +const requireFact = (condition, message) => { + if (!condition) throw new FactFailure("invalid_provider_output", message); +}; + +export function parseInstructions( + text, + inputPath, + request, + fields = ["bytes", "text"], +) { + try { + requireFact( + Buffer.byteLength(text) <= 8 * 1024 * 1024, + "Instruction source exceeds transport capacity", + ); + const raw = JSON.parse(text); + requireFact( + Array.isArray(raw?.instructions) && raw.instructions.length <= 4096, + "Instruction window exceeds decoded-row capacity", + ); + const doc = envelopeSchema.parse(raw); + requireFact( + doc.binary === inputPath && doc.start_hex === request.address, + "Instruction source does not match the selected input/window", + ); + requireFact( + doc.count === raw.instructions.length && doc.count <= request.count, + "Instruction count does not match the requested window", + ); + let next = BigInt(request.address); + const items = raw.instructions.map((value) => { + const row = rowSchema.parse(value); + requireFact( + BigInt(row.address_hex) === next && row.bytes.length === row.size * 2, + "Instruction bytes/size or sequential address mismatch", + ); + next += BigInt(row.size); + const fact = { + address: row.address_hex, + size: row.size, + status: row.mnemonic === ".byte" ? "undecoded" : "decoded", + bytes: row.bytes, + mnemonic: row.mnemonic, + operands: row.operands, + text: row.text, + }; + return Object.fromEntries( + [...new Set(["address", "size", "status", ...fields])].map((key) => [ + key, + fact[key], + ]), + ); + }); + requireFact( + next === BigInt(doc.end_hex) && + next - BigInt(request.address) === BigInt(doc.bytes), + "Instruction window end/byte coverage mismatch", + ); + return { + items, + resume_address: doc.end_hex, + coverage: { + authority: "machine bytes and native linear decoding; not recovered IR", + request, + listed_count: items.length, + truncated: doc.truncated, + notes: doc.notes, + alignment: + "caller-selected start; runtime execution and instruction alignment are not established", + }, + }; + } catch (error) { + if (error instanceof FactFailure) throw error; + throw new FactFailure( + "invalid_provider_output", + "Native instruction window does not match the declared format", + { + reason: + error instanceof z.ZodError + ? error.issues[0]?.message.slice(0, 256) + : String(error.message).slice(0, 256), + }, + ); + } +} diff --git a/experiments/atomic-codemode/instruction-facts.test.mjs b/experiments/atomic-codemode/instruction-facts.test.mjs new file mode 100644 index 000000000..b88588192 --- /dev/null +++ b/experiments/atomic-codemode/instruction-facts.test.mjs @@ -0,0 +1,92 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { parseInstructions } from "./instruction-facts.mjs"; + +const document = (address = "0xd97e") => ({ + binary: "/private/input", + kind: "code", + start_hex: address, + end_hex: `0x${(BigInt(address) + 6n).toString(16)}`, + count: 1, + bytes: 6, + truncated: true, + notes: ["selected window"], + instructions: [ + { + address_hex: address, + size: 6, + bytes: "0f87e9080000", + mnemonic: "JA", + operands: "0xe26d", + text: "JA 0xe26d", + }, + ], +}); +const parse = (doc, address = doc.start_hex) => + parseInstructions(JSON.stringify(doc), "/private/input", { + address, + count: 1, + }); + +test("instruction facts preserve exact bytes/addresses and do not substitute an optimized CFG target", () => { + const result = parse(document()); + assert.equal(result.items[0].address, "0xd97e"); + assert.equal(result.items[0].text, "JA 0xe26d"); + assert.equal(result.items[0].bytes, "0f87e9080000"); + assert.equal(result.items[0].status, "decoded"); + assert.deepEqual(result.coverage.notes, ["selected window"]); + const high = document("0x20000000000001"); + high.instructions[0].address = Number(BigInt(high.start_hex)); + assert.equal(parse(high).items[0].address, "0x20000000000001"); +}); + +test("unknown bytes remain undecoded and projection retains byte-range identity", () => { + const doc = document(); + doc.instructions[0].mnemonic = ".byte"; + const result = parseInstructions( + JSON.stringify(doc), + "/private/input", + { address: "0xd97e", count: 1 }, + ["mnemonic"], + ); + assert.deepEqual(result.items[0], { + address: "0xd97e", + size: 6, + status: "undecoded", + mnemonic: ".byte", + }); +}); + +test("wrong target/window, gaps, extent and byte-size contradictions fail at decoding boundary", () => { + for (const mutate of [ + (doc) => { + doc.binary = "/other/input"; + }, + (doc) => { + doc.instructions[0].address_hex = "0xd97f"; + }, + (doc) => { + doc.instructions[0].size = 5; + }, + (doc) => { + doc.end_hex = "0xd985"; + }, + (doc) => { + doc.count = 2; + }, + ]) { + const doc = document(); + mutate(doc); + assert.throws( + () => parse(doc), + (error) => error.code === "invalid_provider_output", + ); + } + assert.throws(() => parse(document(), "0xd970"), /window/u); +}); + +test("decoded-row capacity rejects oversized malformed arrays before expanding errors", () => { + const doc = document(); + doc.instructions = Array.from({ length: 4097 }, () => ({})); + assert.throws(() => parse(doc), /capacity/u); +}); diff --git a/experiments/atomic-codemode/native-engine.md b/experiments/atomic-codemode/native-engine.md index 05348023c..59a4d9660 100644 --- a/experiments/atomic-codemode/native-engine.md +++ b/experiments/atomic-codemode/native-engine.md @@ -76,3 +76,26 @@ Upstream `check-spec` passes. The full upstream corpus, stages and workspace gat have not run; this patch is not declared upstream-ready. SQLite, more complex model tasks, paired warm runs and the revised actual model comparison remain required work. + +The value-origin model comparison exposed a representation boundary: optimized +recovered blocks can absorb a machine predecessor, so an IR edge is not an actual +branch instruction target. The experimental API now names operation locations +`origin_address`, their selector `origin_addresses`, and block locations +`origin_start`; printed CFG targets use `target_origin`. These replace the old +ambiguous fields without aliases. Historical captures retain their pinned old +contracts. Raw upstream JSON is preserved unchanged in Evidence. + +The added `instructions` method batches selected machine-address windows, using +Kuna's existing [disassemble CLI](https://github.com/Noelo-Lab/kuna/blob/fe01d6294eb0d1053ce343c8943646df88e143fc/decompiler/crates/kuna-cli/src/disassemble.rs), +with exact bytes, decoded text, extent, coverage and per-window errors. Kuna +already has focused agent-oriented read surfaces; its port ancestry does not +establish which primitives are missing. No assembly decoding or branch-target +parser is duplicated in the MCP adapter. + +Windows are capped at 4,096 decoded rows and 16 windows per call, sharing the +8 MiB capture and 64 MiB retained-source budgets. A real maximum-row CLI test +returns 4,096 rows in an 842,106-byte source, taking 1.227 family wall seconds, +1.042 CPU seconds and 112.0 MiB kernel memory peak. Both real stock and patched +MCP tests preserve good windows alongside an unmapped-address failure and report +the actual `JA 0xe26d` / `JMP 0xd9c9` bytes. Targeted tests now total 33; the +representation fix still requires a fresh real-model task trial. diff --git a/experiments/atomic-codemode/native-ir.mjs b/experiments/atomic-codemode/native-ir.mjs index ca9eae60a..603fdc171 100644 --- a/experiments/atomic-codemode/native-ir.mjs +++ b/experiments/atomic-codemode/native-ir.mjs @@ -42,7 +42,7 @@ const page = (rows, { offset = 0, limit = 20, fields } = {}) => ({ .map((row) => fields ? Object.fromEntries( - [...new Set(["id", "address", "block", ...fields])] + [...new Set(["id", "origin_address", "block", ...fields])] .filter((key) => Object.hasOwn(row, key)) .map((key) => [key, row[key]]), ) @@ -146,7 +146,11 @@ function validateValues(facts, operations, values) { function indexNative(facts) { const operations = unique(facts.operations), values = unique(facts.values), - blocks = unique(facts.blocks); + blocks = unique( + facts.blocks.map(({ start, ...block }) => + freeze({ ...block, origin_start: start }), + ), + ); validateBlocks(operations, values, blocks); validateOperations(operations, values, blocks); validateValues(facts, operations, values); @@ -181,10 +185,12 @@ function indexNative(facts) { index(constants, value.offset, op.id); } } + const { address: origin_address, ...payload } = op; normalized.set( op.id, freeze({ - ...op, + ...payload, + origin_address, kind: kind(op.opcode), source_line: null, definitions, @@ -224,6 +230,8 @@ function createViews(facts, code, indexes) { operations: normalized.size, blocks: blocks.size, control_flow: "complete-for-recovered-graph", + representation: + "optimized recovered IR; origins do not establish machine branch targets or block extents", runtime_successors: "unknown", memory_alias_dependencies: "unknown", issues: facts.coverage.issues, @@ -274,7 +282,7 @@ function createViews(facts, code, indexes) { function selectOperations(normalized, constants, input = {}) { const ids = input.ids && new Set(input.ids), - addresses = input.addresses && new Set(input.addresses), + addresses = input.origin_addresses && new Set(input.origin_addresses), blockIds = input.block_ids && new Set(input.block_ids); const candidates = ids ? [...ids].flatMap((id) => normalized.get(id) ?? []) @@ -286,7 +294,7 @@ function selectOperations(normalized, constants, input = {}) { return page( [...candidates].filter( (op) => - (!addresses || addresses.has(op.address)) && + (!addresses || addresses.has(op.origin_address)) && (!blockIds || blockIds.has(op.block)) && (!input.constant || op.constants.some((literal) => literal.value === input.constant)) && diff --git a/experiments/atomic-codemode/native-ir.test.mjs b/experiments/atomic-codemode/native-ir.test.mjs index d4222ec44..c2f4152c5 100644 --- a/experiments/atomic-codemode/native-ir.test.mjs +++ b/experiments/atomic-codemode/native-ir.test.mjs @@ -119,6 +119,11 @@ function diamond() { test("native identity, implicit CFG and operand/phi bindings do not depend on display text", () => { const model = parseNativeIR(JSON.stringify(diamond()), "0x1000"); + assert.equal(model.operation("0x3:0").origin_address, "0x3"); + assert.equal(Object.hasOwn(model.operation("0x3:0"), "address"), false); + assert.equal(model.block(1).origin_start, "0x1001"); + assert.equal(Object.hasOwn(model.block(1), "start"), false); + assert.equal(model.select({ origin_addresses: ["0x3"] }).total, 1); assert.deepEqual(model.findConstant("0xffffffffffffffff"), [ "0x1:0", "0x2:0", diff --git a/experiments/atomic-codemode/provider.mjs b/experiments/atomic-codemode/provider.mjs index 89c0a39cc..3655f5591 100644 --- a/experiments/atomic-codemode/provider.mjs +++ b/experiments/atomic-codemode/provider.mjs @@ -8,27 +8,14 @@ import { KunaTargetCommands } from "../../dist/kuna/KunaTargetCommands.js"; import { parseKunaStringInventory } from "../../dist/kuna/KunaStringInventory.js"; import { parseKunaIncomingReferenceFacts } from "../../dist/kuna/KunaReferenceFiles.js"; import { PrivateRuntimeRoot } from "../../dist/process/PrivateRuntimeRoot.js"; -import { projectAnalysisError } from "../../dist/domain/analysisErrorProjection.js"; import { parseNativeIR } from "./native-ir.mjs"; -import { FactFailure } from "./fact-error.mjs"; +import { FactFailure, requireResult } from "./fact-error.mjs"; +import { parseInstructions } from "./instruction-facts.mjs"; import { parseKunaSsaDump, backwardSlice, } from "../atomic-re-tools/kuna-ssa.mjs"; -const requireResult = (result, code = "provider_failure") => { - if (!result.ok) { - const failure = result.error?._tag - ? projectAnalysisError(result.error) - : result.error; - throw new FactFailure( - failure?.code ?? code, - String(failure?.message ?? failure?.reason ?? failure), - { failure }, - ); - } - return result.value; -}; const sha256 = (value) => createHash("sha256").update(value).digest("hex"); const sourceSummary = ({ evidence_id, sha256, bytes, elapsed_ms }) => ({ evidence_id, @@ -297,7 +284,7 @@ export class KunaFactOwner { evidence_id: capture.source.evidence_id, }); const parsed = await this.#load(id); - if (parsed.block(0).start !== input.entry) + if (parsed.block(0).origin_start !== input.entry) throw new FactFailure( "subject_mismatch", "Native SSA entry does not match the requested function entry", @@ -309,6 +296,55 @@ export class KunaFactOwner { coverage: parsed.coverage, }; } + async instructions(input, signal) { + const results = []; + for (const request of input.windows) { + let capture; + try { + capture = await this.#native( + `instructions:${request.address}:${request.count}`, + (path) => [ + "disassemble", + path, + request.address, + "--addr", + "--as", + "code", + "--count", + String(request.count), + "--json", + ], + signal, + ); + const parsed = parseInstructions( + await this.#sourceText(capture.source.evidence_id), + capture.input, + request, + input.fields, + ); + results.push({ + ok: true, + address: request.address, + source: sourceSummary(capture.source), + ...parsed, + }); + } catch (error) { + results.push({ + ok: false, + address: request.address, + error: { + code: error.code ?? "provider_failure", + message: error.message, + details: { + ...error.details, + ...(capture ? { evidence_id: capture.source.evidence_id } : {}), + }, + }, + }); + } + } + return { results }; + } async #load(id) { const descriptor = this.#snapshots.get(id); if (!descriptor) diff --git a/experiments/atomic-codemode/trials.md b/experiments/atomic-codemode/trials.md index 22c0ac55e..83efbb98b 100644 --- a/experiments/atomic-codemode/trials.md +++ b/experiments/atomic-codemode/trials.md @@ -150,3 +150,40 @@ already substantially supported. This is a reason to test task-specific value tracing and small-function counterexamples, rather than to claim that more granular tool catalogs are inherently efficient. Private artifacts are named `zlib-native-codemode-v3*` beside the earlier trials. + +The value-origin task uses the same patched engine in both lanes, with fresh +provider/session caches and the pinned `f2f029c5d` runtime. It asks about the +accumulator at the already identified instruction origin `0xd9c9`; this is a +seeded follow-up investigation, not a repeated string-discovery task. The +traditional client retains its nine selected tools. Both traces use the real +Kuna engine, complete without API errors, preserve the target/runtime digests +and verify profile/payload cleanup with no OOM kills. + +| Measurement | Native IR + Code Mode | Traditional | +| --- | ---: | ---: | +| Outer MCP attempts | 8 | 5 | +| Cumulative model input tokens | 284,638 | 390,557 | +| Included cached input tokens | 220,672 | 326,016 | +| Uncached input tokens | 63,966 | 64,541 | +| Output / included reasoning tokens | 2,601 / 309 | 2,614 / 531 | +| Model runner wall seconds | 72.403 | 91.832 | +| Bounded family elapsed seconds | 72.841 | 92.254 | +| Bounded family CPU seconds | 11.896 | 20.866 | +| Kernel process-family memory peak MiB | 461.6 | 846.0 | + +Both answers recover the three accumulator alternatives, byte loads, shifts, +additions, local bit-count conditions and unknown state. The native answer, +however, says that the branch at `0xd97e` directly reaches `0xd9c9`. This describes +the optimized recovered CFG, but the actual bytes `0f87e9080000` encode +`JA 0xe26d`; `0xe270` then encodes `JMP 0xd9c9`. The traditional answer identifies +that machine predecessor and intervening copy/jump. This was checked separately +with the shipped machine bytes, rather than accepting either model's answer as +the oracle. + +The native trace's lower cumulative input and wall time therefore do **not** yet +establish an equal-correctness completed-task win. Uncached input is also almost +equal; cached cumulative input alone is not an economic-cost measurement. The +representation ambiguity needs a structural fix: distinguish IR origin fields +from physical instruction facts and expose selected instruction windows, then +rerun the unchanged question. Private artifacts are `zlib-value-codemode-v1*` +and `zlib-value-traditional-v1*`. diff --git a/experiments/atomic-codemode/verify-mcp.mjs b/experiments/atomic-codemode/verify-mcp.mjs index 61ad6c6aa..ba865e6ff 100644 --- a/experiments/atomic-codemode/verify-mcp.mjs +++ b/experiments/atomic-codemode/verify-mcp.mjs @@ -80,10 +80,10 @@ const values=await read(rea.values({snapshot:s,ids:phi.items.flatMap(o=>o.inputs return {blocks,controls,guards,phi,values};`, }); assert.equal(native.value.controls.total, 2); - assert.deepEqual(native.value.guards.items.map((op) => op.address).sort(), [ - "0xd9ec", - "0xda32", - ]); + assert.deepEqual( + native.value.guards.items.map((op) => op.origin_address).sort(), + ["0xd9ec", "0xda32"], + ); assert.ok( native.value.phi.items.every((op) => op.inputs.every( @@ -97,6 +97,14 @@ return {blocks,controls,guards,phi,values};`, ); assert.ok(native.value.values.items.length > 0); } + const machine = await call("execute_javascript", { + code: 'const r=await rea.instructions({windows:[{address:"0xd97e",count:1},{address:"0xe26d",count:2},{address:"0xdeadbeef",count:1}]});if(!r.ok)throw new Error(JSON.stringify(r));return r.data;', + }); + assert.equal(machine.value.results[0].items[0].bytes, "0f87e9080000"); + assert.match(machine.value.results[0].items[0].text, /e26d/u); + assert.equal(machine.value.results[1].items[1].address, "0xe270"); + assert.match(machine.value.results[1].items[1].text, /d9c9/u); + assert.equal(machine.value.results[2].ok, false); await call("close_binary"); } catch (error) { failure = error.stack ?? String(error); diff --git a/experiments/atomic-re-tools/kuna-ssa.mjs b/experiments/atomic-re-tools/kuna-ssa.mjs index 3deedf962..f60b668be 100644 --- a/experiments/atomic-re-tools/kuna-ssa.mjs +++ b/experiments/atomic-re-tools/kuna-ssa.mjs @@ -86,7 +86,7 @@ const parseOperation = (match, blockId, sourceLine) => { })); const row = { id, - address: id.split(":")[0], + origin_address: id.split(":")[0], block: blockId, kind, statement, @@ -110,7 +110,7 @@ const controlEdges = (row) => { edges.push({ from: row.block, to: ordinal(target), - target_address: address(targetAddress), + target_origin: address(targetAddress), kind: "conditional", polarity, predicate: branch[3], @@ -122,7 +122,7 @@ const controlEdges = (row) => { edges.push({ from: row.block, to: ordinal(jump[1]), - target_address: address(jump[2]), + target_origin: address(jump[2]), kind: "direct", operation: row.id, source_line: row.source_line, @@ -163,7 +163,7 @@ export const parseKunaSsaDump = (text) => { // Stop is the last instruction's address, NOT an exclusive byte bound. block = { id, - start: address(header[2]), + origin_start: address(header[2]), stop_instruction: address(header[3]), operations: [], source_line: i + 1, @@ -177,7 +177,7 @@ export const parseKunaSsaDump = (text) => { edges.push({ from: block.id, to: ordinal(fallthrough[2]), - target_address: address(fallthrough[3]), + target_origin: address(fallthrough[3]), kind: "printed-implied-goto", operation: null, source_line: i + 1, @@ -204,8 +204,8 @@ export const parseKunaSsaDump = (text) => { validateDefinitions(operations, issues); for (const edge of edges) { assert.equal( - blocks.get(edge.to)?.start, - edge.target_address, + blocks.get(edge.to)?.origin_start, + edge.target_origin, "Branch target identity mismatch", ); indexInto(incoming, edge.to, edge); @@ -224,6 +224,8 @@ export const parseKunaSsaDump = (text) => { identity: { kind: "native-ssa-snapshot", sha256: digest }, coverage: { scope: "printed-SSA", + representation: + "optimized recovered IR; origins are not machine branch targets or block extents", operations: operations.size, blocks: blocks.size, explicit_control_edges: edges.length, @@ -252,7 +254,7 @@ const queryViews = ({ incoming: (id) => incoming.get(id) ?? [], select: ({ ids, - addresses, + origin_addresses, block_ids, constant, kind, @@ -262,7 +264,9 @@ const queryViews = ({ } = {}) => { const selectedIds = ids === undefined ? null : new Set(ids); const locations = - addresses === undefined ? null : new Set(addresses.map(address)); + origin_addresses === undefined + ? null + : new Set(origin_addresses.map(address)); const selectedBlocks = block_ids === undefined ? null : new Set(block_ids); const literalIds = constant === undefined @@ -271,7 +275,7 @@ const queryViews = ({ const rows = [...operations.values()].filter( (row) => (!selectedIds || selectedIds.has(row.id)) && - (!locations || locations.has(row.address)) && + (!locations || locations.has(row.origin_address)) && (!selectedBlocks || selectedBlocks.has(row.block)) && (!literalIds || literalIds.has(row.id)) && (kind === undefined || row.kind === kind), @@ -299,7 +303,15 @@ const page = (rows, offset, limit, fields) => ({ fields === undefined ? row : Object.fromEntries( - [...new Set(["id", "address", "block", "source_line", ...fields])] + [ + ...new Set([ + "id", + "origin_address", + "block", + "source_line", + ...fields, + ]), + ] .filter((key) => Object.hasOwn(row, key)) .map((key) => [key, row[key]]), ), diff --git a/experiments/atomic-re-tools/kuna-ssa.test.mjs b/experiments/atomic-re-tools/kuna-ssa.test.mjs index 23289bf4d..2a6a2f4e2 100644 --- a/experiments/atomic-re-tools/kuna-ssa.test.mjs +++ b/experiments/atomic-re-tools/kuna-ssa.test.mjs @@ -91,7 +91,7 @@ test("phi/load cutoffs preserve boundary rows and do not invent memory or predec test("cloned blocks share starts; operation identity includes sequence, and facts stay immutable", () => { const snapshot = parseKunaSsaDump(dump.replaceAll("0x120", "0x110")); - assert.equal(snapshot.block(1).start, snapshot.block(2).start); + assert.equal(snapshot.block(1).origin_start, snapshot.block(2).origin_start); assert.notEqual(snapshot.block(1).id, snapshot.block(2).id); assert.throws(() => { snapshot.operation("0x100:1").kind = "store"; diff --git a/experiments/atomic-re-tools/verify-relations.mjs b/experiments/atomic-re-tools/verify-relations.mjs index 96c4357cc..a089b747e 100644 --- a/experiments/atomic-re-tools/verify-relations.mjs +++ b/experiments/atomic-re-tools/verify-relations.mjs @@ -49,7 +49,7 @@ for (const [i, expectedLine] of [164, 551, 653].entries()) { const failed = await mappedCode(native, original); assert.equal(failed.status, "unknown"); const sites = answer.direct_uses.map((id) => ({ - from: answer.operations.find((op) => op.id === id).address, + from: answer.operations.find((op) => op.id === id).origin_address, })); const mapped = await mappedCode(native, { functions: original.functions,