mirror of
https://github.com/morluto/rea.git
synced 2026-11-09 00:07:18 +00:00
* feat(native): page retained feature traces * fix(snapshot): retain linked source evidence for replay * test(snapshot): verify linked foreign evidence stays scoped * feat(native): page retained procedure references * docs(mcp): clarify imported residual questions * docs(mcp): guide trace reference classification * fix(cli): report result sources when Evidence publication fails A trace keeps linked sources on the result. Use those records for the undelivered source closure as well as the bundle, so a refused output does not report them as not captured. * ci: rerun pull request checks The merge of origin/main did not start the CI workflow for this head. * fix(native): document shared procedure endpoints and the required selector Describe kind shared and items.endpoints instead of inline procedure copies. State that procedure-references requires a positional selector or --procedure. * fix(native): count procedure-reference endpoints before keeping a row Include the shared endpoint table in the page budget, with JSON punctuation, and store a new procedure only when its row is accepted. * fix(native): measure a procedure endpoint once before its comparison key Size the candidate before building one canonical identity, and reuse that key for the selected-procedure and shared-endpoint checks. * fix(native): reject an oversized procedure endpoint before canonicalizing it When the JSON size already exceeds every stored identity, skip the canonical string and keep the row out of the page. * fix(native): describe procedure-reference direction and coverage Avoid execution wording in the tool description, and give the CLI direction option the help text the catalog requires. * fix(native): compare endpoint size with UTF-8 identity length Keep one running maximum of stored identity bytes. A multibyte selected procedure is recognized when its JSON no longer fits the page. * test(cli): count procedure-references as a dedicated command The MCP tool has a CLI command, so it is no longer an MCP-only exception. * test(native): catch the string-length endpoint comparison Use a 200,000-character multibyte selected procedure and a shorter ASCII endpoint so the old JavaScript length check drops the selected row. * ci: rerun after the binary-layout toolchain timeout The required check failed because the toolchain install exceeded 15 minutes. The same job completed on the previous head, and this commit does not change the procedure-reference behavior. * test: stabilize capture decoder exhaustion coverage --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com>
154 lines
5.0 KiB
TypeScript
154 lines
5.0 KiB
TypeScript
import { readFile, stat, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { Cli } from "incur";
|
|
import pino from "pino";
|
|
import { expect, it } from "vitest";
|
|
import {
|
|
createAnalysisExecution,
|
|
type AnalysisProvider,
|
|
} from "../../../src/application/AnalysisProvider.js";
|
|
import { runDirectAnalysis } from "../../../src/application/DirectAnalysis.js";
|
|
import type { DirectAnalysisDependencies } from "../../../src/application/DirectAnalysisDependencies.js";
|
|
import { registerCoreNativeCommands } from "../../../src/cli/coreNativeCommands.js";
|
|
import { parseConfig } from "../../../src/config/parseConfig.js";
|
|
import { createAnalysisProfile } from "../../../src/domain/analysisProfile.js";
|
|
import { createEvidence } from "../../../src/domain/evidence.js";
|
|
import { parseEvidenceBundle } from "../../../src/domain/evidenceBundle.js";
|
|
import { ok } from "../../../src/domain/result.js";
|
|
import { createTestBinarySession } from "../../fixtures/binarySession.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
|
|
const identity = { id: "fixture", name: "Fixture", version: "1" } as const;
|
|
const effects = {
|
|
mutatesArtifact: false,
|
|
launchesProcess: false,
|
|
mayShowUi: false,
|
|
mayAccessNetwork: false,
|
|
mayWriteFilesystem: false,
|
|
changesPermissions: false,
|
|
requiresRoot: false,
|
|
};
|
|
const procedure = {
|
|
address: "0x1000",
|
|
name: "selected",
|
|
classification: null,
|
|
body: { available: false as const, reason: "unavailable" },
|
|
};
|
|
const result = {
|
|
procedure,
|
|
direction: "incoming" as const,
|
|
reference_kinds_available: false,
|
|
references: [],
|
|
unresolved_calls: [{ address: "0x1010", reason: "unknown target" }],
|
|
};
|
|
|
|
it.each([false, true])(
|
|
"exports linked source Evidence or refuses missing closure (missing=%s)",
|
|
async (missing) => {
|
|
const root = await createTestTempDirectory("rea-procedure-references-cli-");
|
|
const path = join(root, "fixture.hop"),
|
|
output = join(root, "evidence.json");
|
|
await writeFile(path, "synthetic analysis database");
|
|
const history = createEvidence(undefined, identity, {
|
|
operation: "health",
|
|
parameters: {},
|
|
result: "project history",
|
|
...(missing ? { evidenceLinks: ["ev_" + "a".repeat(64)] } : {}),
|
|
});
|
|
const provider: AnalysisProvider = {
|
|
identity: () => identity,
|
|
capabilities: () => [
|
|
{
|
|
provider: identity,
|
|
operation: "procedure_references",
|
|
available: true,
|
|
reason: null,
|
|
cachePolicy: "live",
|
|
limitations: [],
|
|
effects,
|
|
},
|
|
],
|
|
resolveAnalysisProfile: async () =>
|
|
ok({ profile: createAnalysisProfile(identity, { fixture: true }) }),
|
|
createClient: () => ({
|
|
execute: async () =>
|
|
ok(
|
|
createAnalysisExecution(result, identity, {
|
|
sourceEvidence: [history],
|
|
}),
|
|
),
|
|
close: async () => ok(null),
|
|
}),
|
|
};
|
|
const createSession = () => createTestBinarySession(provider);
|
|
const dependencies: DirectAnalysisDependencies = {
|
|
readConfiguration: () => parseConfig({}),
|
|
createBinarySession: createSession,
|
|
createNativeDispatchBinarySession: createSession,
|
|
createArtifactBinarySession: createSession,
|
|
createNativeBinarySession: createSession,
|
|
createManagedBinarySession: createSession,
|
|
};
|
|
const cli = Cli.create("procedure-references", { sync: false });
|
|
registerCoreNativeCommands(
|
|
cli,
|
|
pino({ enabled: false }),
|
|
(target, tool, args, options) =>
|
|
runDirectAnalysis(dependencies, target, tool, args, options),
|
|
);
|
|
let stdout = "";
|
|
await cli.serve(
|
|
[
|
|
"procedure-references",
|
|
path,
|
|
"selected",
|
|
"--provider",
|
|
"fixture",
|
|
"--direction",
|
|
"incoming",
|
|
"--detail",
|
|
"summary",
|
|
"--evidence_output",
|
|
output,
|
|
"--json",
|
|
],
|
|
{
|
|
env: {},
|
|
stdout: (text) => {
|
|
stdout += text;
|
|
},
|
|
exit: () => undefined,
|
|
},
|
|
);
|
|
const response = JSON.parse(stdout);
|
|
if (missing) {
|
|
expect(response).toMatchObject({
|
|
code: "evidence_integrity_mismatch",
|
|
details: { reason: "missing", evidence_id: "ev_" + "a".repeat(64) },
|
|
});
|
|
await expect(stat(output)).rejects.toMatchObject({ code: "ENOENT" });
|
|
} else {
|
|
const bundle = parseEvidenceBundle(
|
|
JSON.parse(await readFile(output, "utf8")),
|
|
);
|
|
const parent = bundle.records.find(
|
|
(record) => record.operation === "procedure_references",
|
|
);
|
|
expect(response.summary.normalized_result).toMatchObject({
|
|
parent_evidence_id: parent?.evidence_id,
|
|
summary: {
|
|
reference_count: 0,
|
|
unresolved_call_count: 1,
|
|
direction: "incoming",
|
|
},
|
|
});
|
|
expect(parent?.evidence_links).toEqual([history.evidence_id]);
|
|
expect(
|
|
bundle.records.some(
|
|
(record) => record.evidence_id === history.evidence_id,
|
|
),
|
|
).toBe(true);
|
|
}
|
|
},
|
|
);
|