[GH-ISSUE #6623] [Bug] tRPC query discovery drops a local client named history #4007

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

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

Before submitting

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

Area

Evidence, artifacts, or reconstruction

Observed behavior

analyze-javascript-application omits archive from history.archive.query() when history is a function-local constant initialized by createTRPCClient with an HTTP batch link. An equivalent artifact using the local name client produces archive with mechanism trpc:procedure:query. Both analyses recover the transport URL, exit 0, and report zero parse failures and complete application-graph coverage. The procedure path is the ordinary name archive; no router property uses a browser-global spelling.

Reproduction

Set REA_ROOT to a built REA checkout or an extracted installed package, then run the following in a disposable working directory. The files are inert JavaScript artifacts; REA does not execute their imports or send requests, so installing tRPC and starting a server are unnecessary.

mkdir -p trpc-root/ordinary trpc-root/local-history trpc-root/global-marker
cat > trpc-root/ordinary/main.mjs <<'JS'
import { createTRPCClient, httpBatchLink } from "@trpc/client";
export function loadArchive() {
  const client = createTRPCClient({ links: [httpBatchLink({ url: "/rpc" })] });
  return client.archive.query();
}
JS
cat > trpc-root/local-history/main.mjs <<'JS'
import { createTRPCClient, httpBatchLink } from "@trpc/client";
export function loadArchive() {
  const history = createTRPCClient({ links: [httpBatchLink({ url: "/rpc" })] });
  return history.archive.query();
}
JS
cat > trpc-root/global-marker/main.mjs <<'JS'
const marker = "/trpc";
navigator.permissions.query({ name: "geolocation" });
window.navigator.permissions.query({ name: "camera" });
history.back();
window.history.back();
JS
for name in ordinary local-history global-marker; do
  node "$REA_ROOT/scripts/rea.mjs" analyze-javascript-application "$PWD/trpc-root/$name" --format json --full-output > "trpc-root/$name.json"
done
node --input-type=module <<'JS'
import { readFileSync } from 'node:fs';
for (const name of ['ordinary', 'local-history', 'global-marker']) {
  const output = readFileSync(`trpc-root/${name}.json`, 'utf8');
  const result = JSON.parse(output.trim().split('\n').at(-1)).data.normalized_result;
  const values = result.graph.nodes
    .filter(node => node.kind === 'endpoint')
    .flatMap(node => node.observations.map(item => item.properties))
    .filter(p => p.endpoint_kind === 'network')
    .map(p => `${p.value} [${p.mechanism}]`).sort();
  console.log(name, JSON.stringify(values), `parse_failures=${result.statistics.parse_failures}`, `graph_coverage=${result.graph.coverage.status}`);
}
JS

Each final artifact was analyzed through both the assigned source build and the published-package launcher. Both returned the selected output below. Additional controls changing only the transport URL to /trpc retained the same procedure discrepancy. The separate directories prevent aggregation of identical procedure names from masking one missing observation.

Expected behavior

Both factory-bound local clients should produce archive with trpc:procedure:query, as well as their HTTP-link URL. The documented vanilla-query rule uses the same lexical factory binding. The browser-global exclusion applies to browser-owned values; the local history constant shadows that name and contains a tRPC proxy. The genuine browser APIs in the third artifact should continue producing no procedures. Neither the documented contract nor the original feature discussion reserves these local identifier spellings irrespective of binding.

Impact and scope

The endpoint inventory loses a supported procedure for the tested local binding name while analysis succeeds. This is a bounded static-reconstruction defect. Application-graph coverage is complete; the separate semantic graph reports unknown coverage for both client variants. No request execution, data loss, privilege change, or security boundary crossing was observed. NOT_ENOUGH_INFO: real-application prevalence and downstream consequences were not measured; other browser-global spellings were not tested.

REA version

Assigned source baseline bcd1f95e688a2ac020a9d13f9c85e0ce321cb6b6, reporting 6.7.0; independently reproduced with the published npm rea-agents@6.7.0 archive. Its SHA-512 integrity matched registry metadata and its runtime dependency versions matched their exact published pins. The registry's latest published version was checked during verification.

Runtime and platform

Node.js v24.22.1; macOS 27.0.1 arm64. CLI state and temporary paths were isolated. No browser or native analysis engine was started.

Target and artifact context

Three self-authored directory artifacts, each containing the inline main.mjs. The local clients each have one unconditional factory initializer, no assignment, and a nonempty httpBatchLink configuration. Their single-segment archive paths and /rpc URL require recognition through the factory binding rather than the source-marker heuristic.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

  • Expected source: docs/javascript-artifact-reconstruction.md:285-301 documents lexical factory-bound query recognition and browser-global exclusions; src/domain/javascript/javascriptRpcEndpoints.ts:75-76 describes the latter as browser-owned roots.
  • Failure source: src/domain/javascript/javascriptRpcEndpoints.ts:188-202 rejects the identifier spelling before consulting factory-bound roots; src/domain/javascript/javascriptSemanticAnalysis.ts:562-578 supplies those lexical identities. The supplemental binding-index probe identifies both local roots as clients, but the public CLI below records the procedure only for client.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

Selected output in both tested builds:

ordinary ["/rpc [trpc:link:httpBatchLink]","archive [trpc:procedure:query]"] parse_failures=0 graph_coverage=complete
local-history ["/rpc [trpc:link:httpBatchLink]"] parse_failures=0 graph_coverage=complete
global-marker [] parse_failures=0 graph_coverage=complete

The application graphs are nontruncated. Supplemental source observations show client and local history each present in the factory-root set, with no such root in the genuine-browser fixture. Those observations support the cause analysis; the complete public-CLI runs establish the omitted endpoint.

Typed outcome or recovery expected

Return a network endpoint observation for archive in both local-client artifacts, preserving its query mechanism and source location. Continue excluding the genuine browser APIs. The original artifact bytes should not require a local-variable rename to recover the procedure candidate.

Analysis or suspected cause

Source-derived reasoning: the root identifier is compared against browser-global spellings before the already-computed factory identity is considered. That discards the local history client even though binding analysis recognizes it. The later first-router-segment exclusion is independent: this fixture uses archive, which passes that check. Correcting only router-property handling can therefore leave this root failure unchanged. No implementation approach is prescribed.

Additional context

Read the original merged RPC recognition PR, including its later router-name observation. This report concerns the local factory-bound root and has a separate restoration signal. The official tRPC vanilla-client source constructs a proxy and derives the procedure from members before query; the caller's local variable spelling does not enter that path. Fresh open/closed issue searches found no matching root-binding report. Related closed GraphQL reports concern different syntax and exclusion rules.

Restoration check

Analyze the unchanged inline artifacts. Both ordinary and local-history must contain /rpc with trpc:link:httpBatchLink and archive with trpc:procedure:query; global-marker must contain no network endpoints. The same equality must hold when both link URLs change to /trpc. Retain the normal lexical-binding constraints, including rejection of unrelated shadowed or reassigned clients. These are proposed passing criteria; no repair was made or tested.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Public CLI runs in both tested builds omit a supported static procedure for one bounded local-name shape while the ordinary-name control succeeds; no severe downstream failure or security impact was demonstrated.
  • Workaround status: unverified
  • Workaround basis: The ordinary-name control produces the missing procedure, but renaming a variable changes the analyzed artifact. No supported invocation was demonstrated to recover the endpoint for the original bytes.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6623 ## 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 Evidence, artifacts, or reconstruction ## Observed behavior `analyze-javascript-application` omits `archive` from `history.archive.query()` when `history` is a function-local constant initialized by `createTRPCClient` with an HTTP batch link. An equivalent artifact using the local name `client` produces `archive` with mechanism `trpc:procedure:query`. Both analyses recover the transport URL, exit 0, and report zero parse failures and complete application-graph coverage. The procedure path is the ordinary name `archive`; no router property uses a browser-global spelling. ## Reproduction Set `REA_ROOT` to a built REA checkout or an extracted installed package, then run the following in a disposable working directory. The files are inert JavaScript artifacts; REA does not execute their imports or send requests, so installing tRPC and starting a server are unnecessary. ```sh mkdir -p trpc-root/ordinary trpc-root/local-history trpc-root/global-marker cat > trpc-root/ordinary/main.mjs <<'JS' import { createTRPCClient, httpBatchLink } from "@trpc/client"; export function loadArchive() { const client = createTRPCClient({ links: [httpBatchLink({ url: "/rpc" })] }); return client.archive.query(); } JS cat > trpc-root/local-history/main.mjs <<'JS' import { createTRPCClient, httpBatchLink } from "@trpc/client"; export function loadArchive() { const history = createTRPCClient({ links: [httpBatchLink({ url: "/rpc" })] }); return history.archive.query(); } JS cat > trpc-root/global-marker/main.mjs <<'JS' const marker = "/trpc"; navigator.permissions.query({ name: "geolocation" }); window.navigator.permissions.query({ name: "camera" }); history.back(); window.history.back(); JS for name in ordinary local-history global-marker; do node "$REA_ROOT/scripts/rea.mjs" analyze-javascript-application "$PWD/trpc-root/$name" --format json --full-output > "trpc-root/$name.json" done node --input-type=module <<'JS' import { readFileSync } from 'node:fs'; for (const name of ['ordinary', 'local-history', 'global-marker']) { const output = readFileSync(`trpc-root/${name}.json`, 'utf8'); const result = JSON.parse(output.trim().split('\n').at(-1)).data.normalized_result; const values = result.graph.nodes .filter(node => node.kind === 'endpoint') .flatMap(node => node.observations.map(item => item.properties)) .filter(p => p.endpoint_kind === 'network') .map(p => `${p.value} [${p.mechanism}]`).sort(); console.log(name, JSON.stringify(values), `parse_failures=${result.statistics.parse_failures}`, `graph_coverage=${result.graph.coverage.status}`); } JS ``` Each final artifact was analyzed through both the assigned source build and the published-package launcher. Both returned the selected output below. Additional controls changing only the transport URL to `/trpc` retained the same procedure discrepancy. The separate directories prevent aggregation of identical procedure names from masking one missing observation. ## Expected behavior Both factory-bound local clients should produce `archive` with `trpc:procedure:query`, as well as their HTTP-link URL. The documented vanilla-query rule uses the same lexical factory binding. The browser-global exclusion applies to browser-owned values; the local `history` constant shadows that name and contains a tRPC proxy. The genuine browser APIs in the third artifact should continue producing no procedures. Neither the documented contract nor the original feature discussion reserves these local identifier spellings irrespective of binding. ## Impact and scope The endpoint inventory loses a supported procedure for the tested local binding name while analysis succeeds. This is a bounded static-reconstruction defect. Application-graph coverage is complete; the separate semantic graph reports unknown coverage for both client variants. No request execution, data loss, privilege change, or security boundary crossing was observed. NOT_ENOUGH_INFO: real-application prevalence and downstream consequences were not measured; other browser-global spellings were not tested. ## REA version Assigned source baseline `bcd1f95e688a2ac020a9d13f9c85e0ce321cb6b6`, reporting `6.7.0`; independently reproduced with the published npm `rea-agents@6.7.0` archive. Its SHA-512 integrity matched registry metadata and its runtime dependency versions matched their exact published pins. The registry's latest published version was checked during verification. ## Runtime and platform Node.js v24.22.1; macOS 27.0.1 arm64. CLI state and temporary paths were isolated. No browser or native analysis engine was started. ## Target and artifact context Three self-authored directory artifacts, each containing the inline `main.mjs`. The local clients each have one unconditional factory initializer, no assignment, and a nonempty `httpBatchLink` configuration. Their single-segment `archive` paths and `/rpc` URL require recognition through the factory binding rather than the source-marker heuristic. ## Interface CLI ## Earliest failure stage Native analysis or reconstruction ## Relevant output or evidence - Expected source: `docs/javascript-artifact-reconstruction.md:285-301` documents lexical factory-bound query recognition and browser-global exclusions; `src/domain/javascript/javascriptRpcEndpoints.ts:75-76` describes the latter as browser-owned roots. - Failure source: `src/domain/javascript/javascriptRpcEndpoints.ts:188-202` rejects the identifier spelling before consulting factory-bound roots; `src/domain/javascript/javascriptSemanticAnalysis.ts:562-578` supplies those lexical identities. The supplemental binding-index probe identifies both local roots as clients, but the public CLI below records the procedure only for `client`. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete Selected output in both tested builds: ```text ordinary ["/rpc [trpc:link:httpBatchLink]","archive [trpc:procedure:query]"] parse_failures=0 graph_coverage=complete local-history ["/rpc [trpc:link:httpBatchLink]"] parse_failures=0 graph_coverage=complete global-marker [] parse_failures=0 graph_coverage=complete ``` The application graphs are nontruncated. Supplemental source observations show `client` and local `history` each present in the factory-root set, with no such root in the genuine-browser fixture. Those observations support the cause analysis; the complete public-CLI runs establish the omitted endpoint. ## Typed outcome or recovery expected Return a network endpoint observation for `archive` in both local-client artifacts, preserving its query mechanism and source location. Continue excluding the genuine browser APIs. The original artifact bytes should not require a local-variable rename to recover the procedure candidate. ## Analysis or suspected cause Source-derived reasoning: the root identifier is compared against browser-global spellings before the already-computed factory identity is considered. That discards the local `history` client even though binding analysis recognizes it. The later first-router-segment exclusion is independent: this fixture uses `archive`, which passes that check. Correcting only router-property handling can therefore leave this root failure unchanged. No implementation approach is prescribed. ## Additional context Read the original merged [RPC recognition PR](https://github.com/morluto/rea/pull/1670), including its later router-name observation. This report concerns the local factory-bound root and has a separate restoration signal. The official [tRPC vanilla-client source](https://github.com/trpc/trpc/blob/v11.19.0/packages/client/src/createTRPCClient.ts) constructs a proxy and derives the procedure from members before `query`; the caller's local variable spelling does not enter that path. Fresh open/closed issue searches found no matching root-binding report. Related closed GraphQL reports concern different syntax and exclusion rules. ## Restoration check Analyze the unchanged inline artifacts. Both `ordinary` and `local-history` must contain `/rpc` with `trpc:link:httpBatchLink` and `archive` with `trpc:procedure:query`; `global-marker` must contain no network endpoints. The same equality must hold when both link URLs change to `/trpc`. Retain the normal lexical-binding constraints, including rejection of unrelated shadowed or reassigned clients. These are proposed passing criteria; no repair was made or tested. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Public CLI runs in both tested builds omit a supported static procedure for one bounded local-name shape while the ordinary-name control succeeds; no severe downstream failure or security impact was demonstrated. - Workaround status: unverified - Workaround basis: The ordinary-name control produces the missing procedure, but renaming a variable changes the analyzed artifact. No supported invocation was demonstrated to recover the endpoint for the original bytes.
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#4007
No description provided.