[GH-ISSUE #6082] [Bug] Source-map tracing wraps missing Linux ps capability as execution_failure #3719

Closed
opened 2026-10-31 00:43:21 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 21, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/6082

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Provider or capability discovery

Observed behavior

When Linux ownership preparation cannot find ps, trace_web_source_location returns execution_failure with generic rea doctor advice. The ownership layer already classified the prerequisite gap as AnalysisCapabilityUnavailableError, but the source-map command wraps it as ProviderAdapterError. The actionable missing-procps reason survives only under details.diagnostics.error_message.

Reproduced twice through the production in-memory MCP harness on macOS with simulated Linux host facts and a retained-artifact fixture. The Linux preparation function performed a real ps lookup against an empty PATH; no Linux host or source-map codec was run.

Reproduction

In a source checkout with locked dependencies installed, save this focused test as tests/process-global/sourceMapOwnershipCapability.test.ts:

import { mkdtemp, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { expect, it, vi } from "vitest";
import { WebSourceLocationService } from "../../src/application/WebSourceLocationService.js";
import { AnalysisCapabilityUnavailableError } from "../../src/domain/analysisErrorCore.js";
import { projectAnalysisError } from "../../src/domain/analysisErrorProjection.js";
import { ok } from "../../src/domain/result.js";
import { SourceMapDecoder } from "../../src/javascript/sourceMaps/SourceMapDecoder.js";
import {
  createSystemProcessOwnershipHost,
  systemProcessOwnershipHost,
} from "../../src/process/ProcessOwnershipObservation.js";
import { createApplicationMcpHarness } from "../fixtures/applicationMcpHarness.js";
import { parseMcpToolError } from "../fixtures/mcpToolError.js";
import { webSourceLocationArgs, webSourceLocationFixture } from "../fixtures/webSourceLocation.js";

it("compares the Linux missing-ps capability with its public source-map projection", async () => {
  const directory = await mkdtemp(join(tmpdir(), "rea-missing-ps-"));
  const host = createSystemProcessOwnershipHost("linux", { PATH: directory });
  let harness: Awaited<ReturnType<typeof createApplicationMcpHarness>> | undefined;
  try {
    let capability: unknown;
    try { await host.prepare?.(); } catch (cause) { capability = cause; }
    expect(capability).toBeInstanceOf(AnalysisCapabilityUnavailableError);
    if (!(capability instanceof AnalysisCapabilityUnavailableError)) throw capability;
    const direct = projectAnalysisError(capability);
    expect(direct).toMatchObject({ code: "capability_unavailable", category: "unsupported_provider" });
    expect(direct.message).toContain("procps-compatible ps");
    expect(direct.message).toContain("ENOENT");
    const prepare = vi.spyOn(systemProcessOwnershipHost, "prepare")
      .mockImplementation((signal) => host.prepare!(signal));
    const decoder = new SourceMapDecoder();
    const service = new WebSourceLocationService({ load: async () => ok(webSourceLocationFixture()) }, decoder);
    harness = await createApplicationMcpHarness({ serverOptions: { webSourceLocation: service } });
    for (let attempt = 0; attempt < 2; attempt += 1) {
      const response = await harness.client.callTool({ name: "trace_web_source_location", arguments: webSourceLocationArgs });
      const error = parseMcpToolError(response).error;
      expect(error).toMatchObject({
        code: "execution_failure", category: "execution_failure", retryable: false,
        details: { provider_id: "source-map-decoder", operation: "trace_web_source_location",
          diagnostics: { phase: "process_ownership_preparation", error_message: expect.stringContaining("procps-compatible ps") } },
      });
      expect(error.message).toContain("rea doctor");
      expect(error.message).not.toContain("procps");
      console.log(JSON.stringify({ attempt, direct, public: error }));
    }
    expect(prepare).toHaveBeenCalledTimes(2);
    await harness.client.ping();
  } finally {
    await harness?.close();
    vi.restoreAllMocks();
    await rm(directory, { recursive: true, force: true });
  }
});

Run:

node node_modules/vitest/vitest.mjs run tests/process-global/sourceMapOwnershipCapability.test.ts --maxWorkers=1

Observed: one test passed, covering the producer projection and two public MCP calls. The assertions record the current incorrect behavior. A subsequent MCP ping succeeded. The test deletes the empty PATH directory and restores its spy.

Linux platform selection and a controlled host environment use the existing createSystemProcessOwnershipHost test boundary. The global preparation method is redirected to that function; SourceMapDecoder has no dependency overrides. The repository fixture loader supplies valid generated-script/source-map input, so artifact filesystem admission is not tested. The decoder, source-map command, service, MCP registration and error projection are production code.

Expected behavior

Preserve the ownership producer's existing typed capability failure: code: capability_unavailable, category: unsupported_provider, retryable: false, and primary message/remediation identifying the missing procps-compatible ps prerequisite.

The README prerequisites explicitly cover Linux source-map analysis. The ownership producer deliberately constructs AnalysisCapabilityUnavailableError with that actionable user message. The direct-producer control in the executed test already projects the expected fields.

Impact and scope

P2: a consequential but bounded classification and recovery defect when Linux ownership preparation fails because ps is absent or incompatible. Clients branching on the advertised code/category cannot recognize the prerequisite gap. Operators receive generic primary advice instead of the existing specific recovery message.

Execution is still refused, and the original reason remains readable in nested diagnostics. Supplying the documented prerequisite is the recovery route; recovery after installing procps was not executed. Both tested requests failed during preparation, before codec launch or private runtime acquisition. No artifact mutation, data loss or security boundary crossing was observed. Frequency on real Linux installations is unknown.

REA version

5241597594; source package version 6.7.0.

Runtime and platform

macOS 27.0.1 arm64, Node.js v26.12.0, Vitest 5.0.3. Linux ownership facts were simulated with the existing host factory and an empty PATH; the failed executable lookup was real.

Target and artifact context

Repository retained-artifact fixture: generated JavaScript and a version-3 source map with one mapping to embedded original source. No active binary target, browser, or application process.

Interface

MCP

Earliest failure stage

Setup or provider admission

Relevant output or evidence

The direct ownership error projected as:

{
  "code": "capability_unavailable",
  "category": "unsupported_provider",
  "message": "Linux process ownership inspection requires a procps-compatible ps on REA's PATH before launching a child: spawn ps ENOENT",
  "retryable": false,
  "details": {
    "provider_id": "process-ownership",
    "operation": "prepare_owned_process",
    "reason": "Linux process ownership inspection requires a procps-compatible ps on REA's PATH before launching a child: spawn ps ENOENT"
  }
}

Both public MCP calls returned the following fields:

{
  "code": "execution_failure",
  "category": "execution_failure",
  "message": "Analysis could not complete. Run `rea doctor` and review the reported details; repeating the unchanged request is not expected to succeed.",
  "retryable": false,
  "remediation": {
    "action": "Analysis could not complete. Run `rea doctor` and review the reported details; repeating the unchanged request is not expected to succeed."
  },
  "details": {
    "provider_id": "source-map-decoder",
    "operation": "trace_web_source_location",
    "diagnostics": {
      "map_path": "/analysis/app.js.map",
      "error_message": "Provider process-ownership cannot execute prepare_owned_process: Linux process ownership inspection requires a procps-compatible ps on REA's PATH before launching a child: spawn ps ENOENT",
      "phase": "process_ownership_preparation"
    }
  }
}

Typed outcome or recovery expected

The existing AnalysisCapabilityUnavailableError and its actionable message should survive the preparation catch. The public code/category should remain capability_unavailable / unsupported_provider.

Analysis or suspected cause

Source-derived and consistent with the executed result: the preparation catch wraps every non-cancellation failure in ProviderAdapterError, including existing typed analysis errors. Its later failure mapper already preserves AnalysisError, but preparation occurs before that mapper applies. The projection maps the wrapper to execution_failure.

Production composition constructs the decoder without a preparation override. The CLI registration uses the same service, so CLI exposure is source-derived; only MCP was executed.

Additional context

Checked upstream main at f86206535f8ec71014781f9d432a84b01e183e5d: the relevant ownership preparation, source-map wrapping and composition are unchanged. Nearby shared error-projection edits do not alter these mappings. This was a source comparison; runtime execution against that newer revision was not performed.

Related capability-classification reports have different causes and fixes: #6050 concerns plain errors from missing Windows worker controls after preparation; #5984 concerns sigrok version-probe classification. #3757 and #2763, with PRs #3761 and #2779, concern prerequisite documentation; #2779 explicitly defers runtime error projection. This report concerns losing an already typed capability error in the earlier source-map preparation catch.

NOT_ENOUGH_INFO: first affected revision and frequency on real Linux hosts were not established. No fix or restoration was tested.

Originally created by @coygeek on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6082 ### Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ### Area Provider or capability discovery ### Observed behavior When Linux ownership preparation cannot find `ps`, `trace_web_source_location` returns `execution_failure` with generic `rea doctor` advice. The ownership layer already classified the prerequisite gap as `AnalysisCapabilityUnavailableError`, but the source-map command wraps it as `ProviderAdapterError`. The actionable missing-`procps` reason survives only under `details.diagnostics.error_message`. Reproduced twice through the production in-memory MCP harness on macOS with simulated Linux host facts and a retained-artifact fixture. The Linux preparation function performed a real `ps` lookup against an empty PATH; no Linux host or source-map codec was run. ### Reproduction In a source checkout with locked dependencies installed, save this focused test as `tests/process-global/sourceMapOwnershipCapability.test.ts`: ```ts import { mkdtemp, rm } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { expect, it, vi } from "vitest"; import { WebSourceLocationService } from "../../src/application/WebSourceLocationService.js"; import { AnalysisCapabilityUnavailableError } from "../../src/domain/analysisErrorCore.js"; import { projectAnalysisError } from "../../src/domain/analysisErrorProjection.js"; import { ok } from "../../src/domain/result.js"; import { SourceMapDecoder } from "../../src/javascript/sourceMaps/SourceMapDecoder.js"; import { createSystemProcessOwnershipHost, systemProcessOwnershipHost, } from "../../src/process/ProcessOwnershipObservation.js"; import { createApplicationMcpHarness } from "../fixtures/applicationMcpHarness.js"; import { parseMcpToolError } from "../fixtures/mcpToolError.js"; import { webSourceLocationArgs, webSourceLocationFixture } from "../fixtures/webSourceLocation.js"; it("compares the Linux missing-ps capability with its public source-map projection", async () => { const directory = await mkdtemp(join(tmpdir(), "rea-missing-ps-")); const host = createSystemProcessOwnershipHost("linux", { PATH: directory }); let harness: Awaited<ReturnType<typeof createApplicationMcpHarness>> | undefined; try { let capability: unknown; try { await host.prepare?.(); } catch (cause) { capability = cause; } expect(capability).toBeInstanceOf(AnalysisCapabilityUnavailableError); if (!(capability instanceof AnalysisCapabilityUnavailableError)) throw capability; const direct = projectAnalysisError(capability); expect(direct).toMatchObject({ code: "capability_unavailable", category: "unsupported_provider" }); expect(direct.message).toContain("procps-compatible ps"); expect(direct.message).toContain("ENOENT"); const prepare = vi.spyOn(systemProcessOwnershipHost, "prepare") .mockImplementation((signal) => host.prepare!(signal)); const decoder = new SourceMapDecoder(); const service = new WebSourceLocationService({ load: async () => ok(webSourceLocationFixture()) }, decoder); harness = await createApplicationMcpHarness({ serverOptions: { webSourceLocation: service } }); for (let attempt = 0; attempt < 2; attempt += 1) { const response = await harness.client.callTool({ name: "trace_web_source_location", arguments: webSourceLocationArgs }); const error = parseMcpToolError(response).error; expect(error).toMatchObject({ code: "execution_failure", category: "execution_failure", retryable: false, details: { provider_id: "source-map-decoder", operation: "trace_web_source_location", diagnostics: { phase: "process_ownership_preparation", error_message: expect.stringContaining("procps-compatible ps") } }, }); expect(error.message).toContain("rea doctor"); expect(error.message).not.toContain("procps"); console.log(JSON.stringify({ attempt, direct, public: error })); } expect(prepare).toHaveBeenCalledTimes(2); await harness.client.ping(); } finally { await harness?.close(); vi.restoreAllMocks(); await rm(directory, { recursive: true, force: true }); } }); ``` Run: ```sh node node_modules/vitest/vitest.mjs run tests/process-global/sourceMapOwnershipCapability.test.ts --maxWorkers=1 ``` Observed: one test passed, covering the producer projection and two public MCP calls. The assertions record the current incorrect behavior. A subsequent MCP ping succeeded. The test deletes the empty PATH directory and restores its spy. Linux platform selection and a controlled host environment use the existing `createSystemProcessOwnershipHost` test boundary. The global preparation method is redirected to that function; `SourceMapDecoder` has no dependency overrides. The repository fixture loader supplies valid generated-script/source-map input, so artifact filesystem admission is not tested. The decoder, source-map command, service, MCP registration and error projection are production code. ### Expected behavior Preserve the ownership producer's existing typed capability failure: `code: capability_unavailable`, `category: unsupported_provider`, `retryable: false`, and primary message/remediation identifying the missing procps-compatible `ps` prerequisite. The [README prerequisites](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/README.md#L71-L74) explicitly cover Linux source-map analysis. The [ownership producer](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/process/ProcessOwnershipObservation.ts#L1158-L1175) deliberately constructs `AnalysisCapabilityUnavailableError` with that actionable user message. The direct-producer control in the executed test already projects the expected fields. ### Impact and scope P2: a consequential but bounded classification and recovery defect when Linux ownership preparation fails because `ps` is absent or incompatible. Clients branching on the advertised code/category cannot recognize the prerequisite gap. Operators receive generic primary advice instead of the existing specific recovery message. Execution is still refused, and the original reason remains readable in nested diagnostics. Supplying the documented prerequisite is the recovery route; recovery after installing procps was not executed. Both tested requests failed during preparation, before codec launch or private runtime acquisition. No artifact mutation, data loss or security boundary crossing was observed. Frequency on real Linux installations is unknown. ### REA version 52415975942a4b9c53a817915b313ccc404f280e; source package version 6.7.0. ### Runtime and platform macOS 27.0.1 arm64, Node.js v26.12.0, Vitest 5.0.3. Linux ownership facts were simulated with the existing host factory and an empty PATH; the failed executable lookup was real. ### Target and artifact context Repository retained-artifact fixture: generated JavaScript and a version-3 source map with one mapping to embedded original source. No active binary target, browser, or application process. ### Interface MCP ### Earliest failure stage Setup or provider admission ### Relevant output or evidence The direct ownership error projected as: ```json { "code": "capability_unavailable", "category": "unsupported_provider", "message": "Linux process ownership inspection requires a procps-compatible ps on REA's PATH before launching a child: spawn ps ENOENT", "retryable": false, "details": { "provider_id": "process-ownership", "operation": "prepare_owned_process", "reason": "Linux process ownership inspection requires a procps-compatible ps on REA's PATH before launching a child: spawn ps ENOENT" } } ``` Both public MCP calls returned the following fields: ```json { "code": "execution_failure", "category": "execution_failure", "message": "Analysis could not complete. Run `rea doctor` and review the reported details; repeating the unchanged request is not expected to succeed.", "retryable": false, "remediation": { "action": "Analysis could not complete. Run `rea doctor` and review the reported details; repeating the unchanged request is not expected to succeed." }, "details": { "provider_id": "source-map-decoder", "operation": "trace_web_source_location", "diagnostics": { "map_path": "/analysis/app.js.map", "error_message": "Provider process-ownership cannot execute prepare_owned_process: Linux process ownership inspection requires a procps-compatible ps on REA's PATH before launching a child: spawn ps ENOENT", "phase": "process_ownership_preparation" } } } ``` ### Typed outcome or recovery expected The existing `AnalysisCapabilityUnavailableError` and its actionable message should survive the preparation catch. The public code/category should remain `capability_unavailable` / `unsupported_provider`. ### Analysis or suspected cause Source-derived and consistent with the executed result: [the preparation catch](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/javascript/sourceMaps/SourceMapCommand.ts#L99-L118) wraps every non-cancellation failure in `ProviderAdapterError`, including existing typed analysis errors. [Its later failure mapper](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/javascript/sourceMaps/SourceMapCommand.ts#L287-L312) already preserves `AnalysisError`, but preparation occurs before that mapper applies. [The projection](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/domain/analysisErrorProjection.ts#L242-L262) maps the wrapper to `execution_failure`. [Production composition](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/composition/webSourceLocations.ts#L5-L12) constructs the decoder without a preparation override. The [CLI registration](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/cli/webSourceLocationCommands.ts#L12-L24) uses the same service, so CLI exposure is source-derived; only MCP was executed. ### Additional context Checked upstream main at `f86206535f8ec71014781f9d432a84b01e183e5d`: the relevant ownership preparation, source-map wrapping and composition are unchanged. Nearby shared error-projection edits do not alter these mappings. This was a source comparison; runtime execution against that newer revision was not performed. Related capability-classification reports have different causes and fixes: [#6050](https://github.com/morluto/rea/issues/6050) concerns plain errors from missing Windows worker controls after preparation; [#5984](https://github.com/morluto/rea/issues/5984) concerns sigrok version-probe classification. #3757 and #2763, with PRs #3761 and #2779, concern prerequisite documentation; #2779 explicitly defers runtime error projection. This report concerns losing an already typed capability error in the earlier source-map preparation catch. NOT_ENOUGH_INFO: first affected revision and frequency on real Linux hosts were not established. No fix or restoration was tested.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#3719
No description provided.