mirror of
https://github.com/morluto/rea.git
synced 2026-10-18 20:10:13 -05:00
fix(javascript): preserve recorded POSIX path case on Windows
Follow up on #2422 and the host-relative path diagnosis by @Aurantium-47. Select the recorded path grammar at the shared root matcher, preserving Windows case semantics and both matching and active-layer scope. Verify real npm module data and passive Node Inspector Evidence through Windows CLI and same-connection SDK MCP. Positive normalized results remain unchanged; rejected POSIX roots leave load state unknown. All 101 related tests and static/docs gates pass. Register the existing workflow tests in Windows CI execution and package scope. No IDA or Ghidra engine was used; scoped resources and delivery costs are recorded in experiments/analysis-efficiency/windows-posix-reconciliation.md.
This commit is contained in:
1 parent
73dd8d6ed5
commit
cb0d8a9fef
5 files changed
+188
-2
No files matched your search
@@ -510,6 +510,7 @@ jobs:
|
||||
tests/boundary/filesystem/setupUnavailableProvider.test.ts
|
||||
tests/boundary/filesystem/windowsNpxRegistration.test.ts
|
||||
tests/acceptance/setup/homeDirectoryFallback.test.ts
|
||||
tests/boundary/filesystem/javascriptApplicationWorkflows.test.ts
|
||||
tests/boundary/filesystem/webScriptManifestPaths.test.ts
|
||||
tests/boundary/process/ProcessEvidence.host.test.ts
|
||||
tests/acceptance/webModuleCli.test.ts
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
# Windows reconciliation of recorded POSIX paths
|
||||
|
||||
Follow-up to [#2422](https://github.com/morluto/rea/pull/2422), including
|
||||
@Aurantium-47's diagnosis of the host-dependent relative-path operation.
|
||||
|
||||
## Source pins and real data
|
||||
|
||||
The initial reproduction and passive producer used main
|
||||
fa4b8288655188426222773ead2ccbdaf76334fd. Before branching, main was updated
|
||||
to 8b181716aca2cb467be499cba226abfb5ae19141. The owning matcher and its
|
||||
affected reconciliation consumers were unchanged between those revisions.
|
||||
The newer baseline again reproduced both errors on the real Windows host.
|
||||
|
||||
Input: npm 11.16.0's published lib/utils/validate-lockfile.js, 1,023 bytes,
|
||||
SHA-256 2295e2dac1c5816e1eae2d417804960ebbef17b64a0a44db4f3483ce6e6d9526.
|
||||
The installed copy was independently checked against the exact module in the
|
||||
[official registry package](https://registry.npmjs.org/npm/11.16.0).
|
||||
The 2,945,850-byte tarball matched its published SHA-512 SRI before reading
|
||||
that member; no archive extraction or installation occurred, and the tarball
|
||||
was not retained.
|
||||
|
||||
Two actual Linux filesystem directories, Runtime/App and Runtime/app,
|
||||
held byte-identical module copies and had distinct directory identities.
|
||||
A controlled Node 24.18.0 process loaded the public module and ran its real
|
||||
function against 587 locked dependency records: compatible trees returned zero
|
||||
errors, and one controlled version mismatch returned one error. A passive REA
|
||||
Inspector capture recorded 114 loaded scripts, including the selected module.
|
||||
The provider did not evaluate, pause, resume or read source. The owned process
|
||||
exited normally. Function execution is established by the harness receipt;
|
||||
REA's passive script metadata establishes loaded status.
|
||||
|
||||
Production static analysis inspected both directories plus an independent
|
||||
Static/public-module copy. The latter prevents implicit root matching from
|
||||
hiding the explicit mapping result. Complete static/runtime Evidence was then
|
||||
consumed by the actual **Windows** CLI and SDK 2.3.1 stdio MCP server, using
|
||||
Windows 11 10.0.22631.0 and official Windows x64 Node 24.18.0.
|
||||
No Electron GUI, IDA or Ghidra engine was used.
|
||||
|
||||
## Correctness and provenance
|
||||
|
||||
| Windows reconciliation | Baseline | Candidate |
|
||||
| -------------------------------------- | ------------------------------ | ----------------------------------- |
|
||||
| Exact POSIX input root | Matched, artifact path | Same complete normalized result |
|
||||
| Case-mismatched POSIX input root | Incorrectly matched and loaded | Unknown; static load states unknown |
|
||||
| Exact explicit POSIX mapping | Matched, operator file mapping | Same complete normalized result |
|
||||
| Case-mismatched explicit POSIX mapping | Incorrectly matched and loaded | Unknown; static load states unknown |
|
||||
|
||||
All four CLI and same-connection MCP normalized results agree. The input
|
||||
Evidence, recorded runtime facts and caller-selected root spellings are
|
||||
preserved. Rejected location membership does not establish that a module was
|
||||
absent or unexecuted. The operation remains offline and performs no target or
|
||||
filesystem inspection.
|
||||
|
||||
The shared matcher now chooses posix.relative after identifying POSIX input;
|
||||
Windows coordinates continue to use win32.relative. Both location matching
|
||||
and active-layer scope use this owner. The obsolete host-default import is
|
||||
removed; no downstream repair or new path abstraction is introduced.
|
||||
|
||||
The related suite passed 101 tests: 15 workflow tests, 84 CI scope tests and
|
||||
two curated-path checks. Four simulated Electron Evidence controls cover
|
||||
POSIX mismatch/exact matching, Windows case-insensitive matching, the resulting
|
||||
asset load state and retained mapping spelling. They are service regressions,
|
||||
not real Electron-provider verification. The existing workflow test file is
|
||||
registered in both the Windows execution lane and its package-change scope.
|
||||
|
||||
## Delivery and resource observations
|
||||
|
||||
Probes ran serially at BelowNormal priority with one CPU and 512 MiB Node heap
|
||||
settings for CLI/server/client work. Heap settings do not bound aggregate memory.
|
||||
Four corrected CLI exit-time observations reported 7.343–15.827 seconds CPU and
|
||||
322,308–324,280 KiB peak working set, excluding the probe parent. Wall times
|
||||
ranged from 7.785 to 17.601 seconds. There is no paired resource baseline.
|
||||
|
||||
One complete MCP trial connected in 18,296 ms and discovered 189 tools in
|
||||
875 ms (2,588,336 serialized bytes). The four subsequent same-connection calls
|
||||
took 879, 320, 353 and 422 ms. Their serialized replies were 918,133 / 913,765 /
|
||||
918,571 / 914,171 bytes; inputs totalled 681,312 bytes. Discovery plus these
|
||||
inputs and replies totalled 6,934,288 bytes, excluding initialization, framing
|
||||
and notifications. This is delivery volume, not model token usage. Actual
|
||||
model context and total model task cost were not measured.
|
||||
|
||||
A process-generation sampler recorded 233 observations over 25,671 ms:
|
||||
15,797 ms aggregate observed CPU as a lower bound and a peak sampled working-set
|
||||
sum of 491,597,824 bytes (about 469 MiB). Exited-process tails can be missed;
|
||||
shared pages can be counted twice. This excludes the observer and is not an
|
||||
exact physical-memory peak. The server wrote four structured completion records
|
||||
to stderr; tool replies and normalized results passed the recorded checks.
|
||||
|
||||
Cold/warm caches and observer differences were not normalized. No latency,
|
||||
CPU, memory or model-token improvement is claimed. The fix adds no lookup I/O,
|
||||
engine invocation, retry or cache. Complete default replies remain available;
|
||||
only compact selected findings and hashes were sent to the investigating agent.
|
||||
@@ -56,6 +56,7 @@ export const SCOPES = {
|
||||
"tests/boundary/filesystem/setupUnavailableProvider.test.ts",
|
||||
"tests/boundary/filesystem/windowsNpxRegistration.test.ts",
|
||||
"tests/acceptance/setup/homeDirectoryFallback.test.ts",
|
||||
"tests/boundary/filesystem/javascriptApplicationWorkflows.test.ts",
|
||||
"tests/boundary/filesystem/webScriptManifestPaths.test.ts",
|
||||
"tests/boundary/process/ProcessEvidence.host.test.ts",
|
||||
"tests/acceptance/webModuleCli.test.ts",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { posix, relative, win32 } from "node:path";
|
||||
import { posix, win32 } from "node:path";
|
||||
|
||||
import type { ApplicationNode } from "./javascriptApplicationGraphSchemas.js";
|
||||
import type { JavaScriptApplicationGraph } from "./javascriptApplicationGraph.js";
|
||||
@@ -216,7 +216,7 @@ const pathBelowRoot = (root: string, value: string): string | null => {
|
||||
const remainder =
|
||||
rootStyle === "windows"
|
||||
? win32.relative(root, value)
|
||||
: relative(root, value);
|
||||
: posix.relative(root, value);
|
||||
const portableRemainder = remainder.replaceAll("\\", "/");
|
||||
if (
|
||||
remainder === "" ||
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
type ApplicationVersionComparisonResult,
|
||||
} from "../../../src/domain/javascript/javascriptApplicationVersionComparisonSchemas.js";
|
||||
import { javascriptRuntimeReconciliationResultSchema } from "../../../src/domain/javascript/javascriptRuntimeReconciliationSchemas.js";
|
||||
import { electronPageInspectionSchema } from "../../../src/domain/javascript/electronObservation.js";
|
||||
import { compareJavaScriptApplicationVersions } from "../../../src/domain/javascript/javascriptApplicationVersionComparison.js";
|
||||
import { createJavaScriptApplicationGraph } from "../../../src/domain/javascript/javascriptApplicationGraph.js";
|
||||
import { traceApplicationFeature } from "../../../src/domain/javascript/javascriptFeatureTrace.js";
|
||||
@@ -443,7 +444,98 @@ describe("JavaScript application workflow error diagnostics", () => {
|
||||
});
|
||||
});
|
||||
|
||||
const RUNTIME_PATH_CASES = [
|
||||
{
|
||||
name: "case-sensitive POSIX input root",
|
||||
root: null,
|
||||
path: "/applications/Example.app/Contents/Resources/app/renderer.js",
|
||||
status: "unknown",
|
||||
basis: "none",
|
||||
},
|
||||
{
|
||||
name: "case-sensitive POSIX mapping root",
|
||||
root: "/runtime/App",
|
||||
path: "/runtime/app/renderer.js",
|
||||
status: "unknown",
|
||||
basis: "none",
|
||||
},
|
||||
{
|
||||
name: "matching POSIX mapping root",
|
||||
root: "/runtime/App",
|
||||
path: "/runtime/App/renderer.js",
|
||||
status: "matched",
|
||||
basis: "operator-file-mapping",
|
||||
},
|
||||
{
|
||||
name: "case-insensitive Windows mapping root",
|
||||
root: String.raw`C:\runtime\App`,
|
||||
path: String.raw`c:\runtime\app\renderer.js`,
|
||||
status: "matched",
|
||||
basis: "operator-file-mapping",
|
||||
},
|
||||
] as const;
|
||||
|
||||
describe("JavaScript runtime reconciliation local paths", () => {
|
||||
it.each(RUNTIME_PATH_CASES)(
|
||||
"uses $name on every host",
|
||||
({ root, path, status, basis }) => {
|
||||
const base = JAVASCRIPT_RUNTIME_RECONCILIATION_EXAMPLE;
|
||||
const original = base.runtime_observations[0]!;
|
||||
const inspection = electronPageInspectionSchema.parse(
|
||||
original.normalized_result,
|
||||
);
|
||||
// Keep target and script coordinates consistent so an unrelated target
|
||||
// cannot activate the static layer after its script mapping was rejected.
|
||||
const runtime = createEvidence(undefined, original.provider, {
|
||||
predicateType: original.predicate_type,
|
||||
operation: original.operation,
|
||||
parameters: { ...original.parameters, include_script_sources: false },
|
||||
result: {
|
||||
...inspection,
|
||||
target: { ...inspection.target, file_path: path },
|
||||
scripts: {
|
||||
...inspection.scripts,
|
||||
items: inspection.scripts.items.map((script) => ({
|
||||
...script,
|
||||
file_path: path,
|
||||
source: { included: false, reason: "not-requested" },
|
||||
})),
|
||||
},
|
||||
},
|
||||
confidence: original.confidence,
|
||||
authority: original.authority,
|
||||
});
|
||||
const reconciled = reconcileJavaScriptRuntimeEvidence({
|
||||
static_layers: base.static_layers.map((layer) => ({
|
||||
...layer,
|
||||
runtime_mappings: root === null ? [] : [{ kind: "file-root", root }],
|
||||
})),
|
||||
runtime_observations: [runtime],
|
||||
});
|
||||
if (!reconciled.ok) throw reconciled.error;
|
||||
const result = javascriptRuntimeReconciliationResultSchema.parse(
|
||||
reconciled.value.normalized_result,
|
||||
);
|
||||
expect(
|
||||
result.reconciliations.find(
|
||||
({ entity_kind }) => entity_kind === "script",
|
||||
),
|
||||
).toMatchObject({ status, basis });
|
||||
expect(
|
||||
result.static_load_states.find(
|
||||
({ kind }) => kind === "javascript-asset",
|
||||
),
|
||||
).toMatchObject(
|
||||
status === "matched"
|
||||
? { status: "loaded", reason: "runtime-script-correspondence" }
|
||||
: { status: "unknown", reason: "layer-outside-runtime-scope" },
|
||||
);
|
||||
expect(result.static_layers[0]?.runtime_mappings).toEqual(
|
||||
root === null ? [] : [{ kind: "file-root", root, artifact_prefix: "" }],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
it("resolves relative file mapping roots before deriving result Evidence", () => {
|
||||
const absoluteRoot = resolve(process.cwd(), "runtime-cache-fixture");
|
||||
const relativeRoot = relative(process.cwd(), absoluteRoot);
|
||||
|
||||
Reference in new issue
Block a user