mirror of
https://github.com/morluto/rea.git
synced 2026-11-07 05:56:19 -10:00
fix(javascript): reconcile Node targets with entry assets (#8670)
* fix(javascript): reconcile Node targets with entry assets * test(inspector): read reconciled evidence payload * Update tests/boundary/inspector/javascriptRuntimeNodeTarget.test.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * test(inspector): read the workflow evidence payload * Update tests/boundary/inspector/javascriptRuntimeNodeTarget.test.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * test(javascript): restore Evidence workflow result access --------- Co-authored-by: morluto <[email protected]> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
This commit is contained in:
6 files changed
+174
-2
No files matched your search
@@ -93,6 +93,10 @@ coverage. It does not print captured source text.
|
||||
Runtime targets, frames, scripts, and workers become capture-scoped graph
|
||||
nodes. REA evaluates compatible static candidates conservatively:
|
||||
|
||||
V8 Inspector targets whose protocol type is `node` use JavaScript asset/module
|
||||
candidates for their verified entry location. A target-only correspondence does
|
||||
not mark that script loaded; load-state evidence requires a script observation.
|
||||
|
||||
1. A captured-source SHA-256 plus one matching mapped location yields
|
||||
a content-and-location match.
|
||||
2. When no static candidate exists at a mapped location, one unique exact
|
||||
|
||||
@@ -57,7 +57,10 @@ const classifyLayer = (
|
||||
const direct = new Map<string, string[]>();
|
||||
const ambiguous = new Set<string>();
|
||||
const incomplete = new Set<string>();
|
||||
for (const match of matches)
|
||||
// A target's entry location establishes correspondence, not script presence.
|
||||
for (const match of matches.filter(
|
||||
({ entity_kind }) => entity_kind === "script",
|
||||
))
|
||||
if (
|
||||
match.status === "matched" &&
|
||||
match.static_layer_id === layer.layerId &&
|
||||
|
||||
@@ -94,7 +94,10 @@ const targetEntity = (
|
||||
frameKey: null,
|
||||
scriptKey: null,
|
||||
location,
|
||||
staticKind: "renderer",
|
||||
staticKind:
|
||||
capture.kind === "v8-inspector" && target.type === "node"
|
||||
? "javascript"
|
||||
: "renderer",
|
||||
sourceSha256: null,
|
||||
label: target.title,
|
||||
properties: {
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import { writeFile } from "node:fs/promises";
|
||||
import { join } from "node:path";
|
||||
import { expect } from "vitest";
|
||||
import { javascriptRuntimeReconciliationResultSchema } from "../../../src/domain/javascript/javascriptRuntimeReconciliationSchemas.js";
|
||||
import { nodeReconciliationFixture } from "../../fixtures/nodeRuntimeReconciliation.js";
|
||||
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
||||
import { cliTest } from "../../support/cli/cliFixture.js";
|
||||
|
||||
cliTest.for(["CLI", "stdio MCP"] as const)(
|
||||
"reconciles synthetic Node target entry Evidence through %s",
|
||||
async (surface, { cli }) => {
|
||||
const input = nodeReconciliationFixture(false);
|
||||
let document: unknown;
|
||||
if (surface === "CLI") {
|
||||
const root = await createTestTempDirectory(
|
||||
"rea-node-reconciliation-cli-",
|
||||
);
|
||||
const path = join(root, "input.json");
|
||||
await writeFile(path, JSON.stringify(input));
|
||||
const response = await cli.run({
|
||||
arguments: ["reconcile-javascript-runtime", path, "--format", "json"],
|
||||
});
|
||||
expect(response.exitCode, response.stderr).toBe(0);
|
||||
document = response.json;
|
||||
} else {
|
||||
const call = await cli.sharedMcpCall!();
|
||||
const response = await call("reconcile_javascript_runtime", input);
|
||||
expect(response.isError, JSON.stringify(response)).not.toBe(true);
|
||||
document = response.structuredContent;
|
||||
}
|
||||
const result = javascriptRuntimeReconciliationResultSchema.parse(
|
||||
(document as { normalized_result: unknown }).normalized_result,
|
||||
);
|
||||
expect(result.reconciliations).toEqual([
|
||||
expect.objectContaining({
|
||||
entity_kind: "target",
|
||||
status: "matched",
|
||||
reason: "unique-location-match",
|
||||
confidence: "medium",
|
||||
candidate_static_count: 1,
|
||||
}),
|
||||
]);
|
||||
expect(result.summary).toMatchObject({
|
||||
runtime_scripts: 0,
|
||||
static_loaded: 0,
|
||||
static_not_observed: 0,
|
||||
});
|
||||
expect(
|
||||
result.graph.edges.filter(({ relation }) => relation === "observed_as"),
|
||||
).toHaveLength(1);
|
||||
},
|
||||
);
|
||||
@@ -0,0 +1,37 @@
|
||||
import { expect, test } from "vitest";
|
||||
import { reconcileJavaScriptRuntimeEvidence } from "../../../src/application/javascript/JavaScriptRuntimeReconciliationService.js";
|
||||
import { javascriptRuntimeReconciliationResultSchema } from "../../../src/domain/javascript/javascriptRuntimeReconciliationSchemas.js";
|
||||
import { nodeReconciliationFixture } from "../../fixtures/nodeRuntimeReconciliation.js";
|
||||
|
||||
test.each([false, true])(
|
||||
"matches a synthetic Node entry target without inventing script presence: %s",
|
||||
(scriptObserved) => {
|
||||
const reconciled = reconcileJavaScriptRuntimeEvidence(
|
||||
nodeReconciliationFixture(scriptObserved),
|
||||
);
|
||||
if (!reconciled.ok) throw reconciled.error;
|
||||
const result = javascriptRuntimeReconciliationResultSchema.parse(
|
||||
reconciled.value.evidence.normalized_result,
|
||||
);
|
||||
const target = result.reconciliations.find(
|
||||
({ entity_kind }) => entity_kind === "target",
|
||||
);
|
||||
expect(target).toMatchObject({
|
||||
status: "matched",
|
||||
basis: "artifact-path",
|
||||
confidence: "medium",
|
||||
reason: "unique-location-match",
|
||||
candidate_static_count: 1,
|
||||
});
|
||||
expect(
|
||||
result.graph.nodes.find(
|
||||
({ node_id }) => node_id === target?.static_node_id,
|
||||
)?.kind,
|
||||
).toBe("javascript-asset");
|
||||
expect(result.summary).toMatchObject({
|
||||
runtime_scripts: scriptObserved ? 1 : 0,
|
||||
static_loaded: scriptObserved ? 1 : 0,
|
||||
static_not_observed: 0,
|
||||
});
|
||||
},
|
||||
);
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
import { createJavaScriptRuntimeObservationEvidence } from "../../src/application/javascript/JavaScriptRuntimeObservationEvidence.js";
|
||||
import { JAVASCRIPT_APPLICATION_EVIDENCE_EXAMPLE } from "../../src/contracts/javascript/javascriptRuntimeReconciliationExample.js";
|
||||
import { javascriptRuntimeObservationSchema } from "../../src/domain/javascript/javascriptRuntimeObservation.js";
|
||||
import { V8_INSPECTOR_PROVIDER_IDENTITY } from "../../src/inspector/providerIdentity.js";
|
||||
|
||||
/** Synthetic, schema-validated capture; this is not a live Inspector observation. */
|
||||
export const nodeReconciliationFixture = (scriptObserved: boolean) => {
|
||||
const location = {
|
||||
kind: "file",
|
||||
file_path: "/Applications/Example.app/Contents/Resources/app/renderer.js",
|
||||
};
|
||||
const runtime = javascriptRuntimeObservationSchema.parse({
|
||||
runtime: {
|
||||
product: "node.js/v24.22.1",
|
||||
protocol_version: "1.3",
|
||||
v8_version: null,
|
||||
},
|
||||
target: {
|
||||
target_id: "synthetic-node",
|
||||
protocol_type: "node",
|
||||
attached: null,
|
||||
location,
|
||||
runtime_kind: "unknown",
|
||||
runtime_kind_authority: "not-declared",
|
||||
},
|
||||
capture: {
|
||||
observation_ms: 100,
|
||||
events_observed: scriptObserved ? 1 : 0,
|
||||
events_retained: scriptObserved ? 1 : 0,
|
||||
events_dropped: 0,
|
||||
metadata_bytes_retained: 0,
|
||||
truncated: false,
|
||||
truncation_reasons: [],
|
||||
},
|
||||
scripts: {
|
||||
items: scriptObserved
|
||||
? [
|
||||
{
|
||||
script_key: `v8_script_${"4".repeat(64)}`,
|
||||
location,
|
||||
execution_context_key: null,
|
||||
cdp_hash: null,
|
||||
length: null,
|
||||
is_module: null,
|
||||
status: "observed-loaded",
|
||||
},
|
||||
]
|
||||
: [],
|
||||
observed_total: scriptObserved ? 1 : 0,
|
||||
excluded: { unsupported_location: 0, invalid_protocol_value: 0 },
|
||||
},
|
||||
execution_contexts: [],
|
||||
directly_observed: [],
|
||||
unavailable_without_instrumentation: [],
|
||||
unknowns: ["Synthetic bounded capture."],
|
||||
limitations: ["Synthetic fixture, not a live runtime."],
|
||||
});
|
||||
return {
|
||||
static_layers: [{ analysis: JAVASCRIPT_APPLICATION_EVIDENCE_EXAMPLE }],
|
||||
runtime_observations: [
|
||||
createJavaScriptRuntimeObservationEvidence(
|
||||
"observe_javascript_runtime",
|
||||
{
|
||||
inspector_endpoint: "http://127.0.0.1:9229",
|
||||
target_id: "synthetic-node",
|
||||
observation_ms: 100,
|
||||
},
|
||||
runtime,
|
||||
V8_INSPECTOR_PROVIDER_IDENTITY,
|
||||
),
|
||||
],
|
||||
};
|
||||
};
|
||||
Reference in new issue
Block a user