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:
N0zoM1z0 committed 2026-10-13 13:52:40 +08:00
1 parent 73dd8d6ed5
commit cb0d8a9fef
5 files changed
+188 -2

No files matched your search

+1
View File
@@ -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.
+1
View File
@@ -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);