[GH-ISSUE #3314] [Bug] Exact case-sensitive feature seeds conflate distinct Unicode channels #1186

Closed
opened 2026-10-15 18:31:50 +00:00 by bsakaguchi · 1 comment
Owner

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

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

An exact, case-sensitive IPC channel seed for ff also selects the distinct channel ff, the U+FB00 ligature. analyze-javascript-application preserves both channels as separate graph nodes, but trace-application-feature merges their matching results and adds an unrelated starting node to the requested trace.

Both CLI workflows succeed. The literal seed returns two matches, while the node-ID control returns only the requested ff channel from the same authenticated application Evidence.

Reproduction

  1. Start from a clean source checkout and prepare its locked dependencies and runtime from the repository root:
git clone https://github.com/morluto/rea.git rea-channel-repro
cd rea-channel-repro
git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063
npm ci
npm run build:cached
  1. Run the following commands from the repository root. They create every source and JSON input inline, analyze the fixture, and trace the same application Evidence with a literal channel seed and a node-ID control. Analysis treats the fixture as inert source and needs no Electron installation.
workdir="$(mktemp -d)"
mkdir -p "$workdir/app" "$workdir/home"
cat > "$workdir/app/main.js" <<'JS'
const { ipcMain } = require('electron');
ipcMain.handle('ff', () => 1);
ipcMain.handle('ff', () => 2);
JS
HOME="$workdir/home" node scripts/rea.mjs analyze-javascript-application "$workdir/app" --json > "$workdir/application.json"
node --input-type=module - "$workdir" <<'JS'
import { readFileSync, writeFileSync } from 'node:fs';
import { join } from 'node:path';
const root = process.argv[2];
const application = JSON.parse(readFileSync(join(root, 'application.json'), 'utf8'));
const channels = application.normalized_result.graph.nodes.filter(n => n.kind === 'ipc-channel');
const selected = channels.find(n => n.observations.some(o => o.properties.channel === 'ff'));
for (const [name, seed] of [
  ['literal', { kind: 'channel', value: 'ff', match: 'exact', case_sensitive: true }],
  ['node', { kind: 'node-id', value: selected.node_id }],
]) {
  writeFileSync(join(root, `${name}-input.json`), JSON.stringify({ application, seed, direction: 'outgoing' }));
}
JS
HOME="$workdir/home" node scripts/rea.mjs trace-application-feature "$workdir/literal-input.json" --json > "$workdir/literal-result.json"
HOME="$workdir/home" node scripts/rea.mjs trace-application-feature "$workdir/node-input.json" --json > "$workdir/node-result.json"
node --input-type=module - "$workdir" <<'JS'
import { readFileSync } from 'node:fs';
import { join } from 'node:path';
for (const name of ['literal', 'node']) {
  const result = JSON.parse(readFileSync(join(process.argv[2], `${name}-result.json`), 'utf8')).normalized_result;
  const channels = result.graph.nodes.filter(n => n.kind === 'ipc-channel').flatMap(n => n.observations.map(o => o.properties.channel)).sort();
  console.log(JSON.stringify({ selection: name, matched_seeds: result.summary.matched_seeds, channels }));
}
JS
  1. Compare the two output lines. The observed literal selection contains both ff and ff, while the node-ID selection contains only ff.

Expected behavior

The literal seed should select only the node whose preserved channel value is ff, with matched_seeds: 1. Exact case-sensitive matching must retain the distinction between the two strings. The node-ID control should continue to select the same one node.

docs/javascript-application-workflows.md:45 promises exact or literal substring feature-seed matching. src/domain/javascript/javascriptFeatureTraceSchemas.ts:63 admits channel seeds, and src/domain/javascript/javascriptFeatureTraceSchemas.ts:68 and src/domain/javascript/javascriptFeatureTraceSchemas.ts:69 admit explicit match: "exact" and case_sensitive: true.

Impact and scope

P2 impact is supported. Distinct channel names in a supported local application produce an incorrect seed inventory and include an unrelated node in the requested feature trace. The demonstrated failure is bounded to compatibility-equivalent Unicode strings.

A practical workaround is available: select the exact node ID from the original application's preserved channel values. The executed node-ID control returns only ff from the same authenticated Evidence.

REA version

6.4.0 (main at 8dde6370d0)

Runtime and platform

Node.js v26.10.0; macOS 27.0.1; prebuilt source-checkout runtime. Architecture was not recorded.

Target and artifact context

An inert local JavaScript source directory containing main.js with two Electron ipcMain.handle registrations, one for ff and one for ff. The analyzer preserves separate IPC channel nodes. Both trace requests use the same authenticated application Evidence and direction: "outgoing"; the literal request uses kind: "channel", value: "ff", match: "exact", and case_sensitive: true, while the control uses the selected node's complete ID.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

The CLI reproduction returned:

{"selection":"literal","matched_seeds":2,"channels":["ff","ff"]}
{"selection":"node","matched_seeds":1,"channels":["ff"]}

Typed outcome or recovery expected

Rerun the same fixture and exact case-sensitive literal seed. Both selection methods must return matched_seeds: 1 and channels: ["ff"]. An exact case-sensitive seed for ff must select only that channel. Preserve existing explicit case-insensitive matching and literal substring behavior without adding compatibility-equivalent strings to case-sensitive literal matches.

Analysis or suspected cause

Source-derived reasoning: src/domain/javascript/javascriptFeatureSeed.ts:74 normalizes the query and candidates before comparison. src/domain/javascript/javascriptFeatureSeed.ts:269 applies NFKC even when case sensitivity is enabled. NFKC turns ff into ff, which accounts for the loss of distinction in this exact case-sensitive comparison.

The schema's seed options are at the locators cited under Expected behavior. src/domain/javascript/javascriptFeatureTraceSchemas.ts:91 belongs to native-observation uniqueness validation and does not define those matching options.

Additional context

Related open issue #3189 concerns node-ID seeds ignoring substring and case-insensitive options; this report concerns compatibility folding of exact case-sensitive channel seeds, and its node-ID control succeeds.

Related open issue #3190 concerns route seeds selecting generated display labels; this report loses distinctions between preserved channel values during string normalization.

Related completed issue #1488 concerns NFC inventory-path joins and mentions NFKC query folding separately; this report concerns the exact case-sensitive IPC channel matching contract.

Originally created by @coygeek on GitHub (Oct 14, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/3314 ### 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 An exact, case-sensitive IPC channel seed for `ff` also selects the distinct channel `ff`, the U+FB00 ligature. `analyze-javascript-application` preserves both channels as separate graph nodes, but `trace-application-feature` merges their matching results and adds an unrelated starting node to the requested trace. Both CLI workflows succeed. The literal seed returns two matches, while the node-ID control returns only the requested `ff` channel from the same authenticated application Evidence. ### Reproduction 1. Start from a clean source checkout and prepare its locked dependencies and runtime from the repository root: ```sh git clone https://github.com/morluto/rea.git rea-channel-repro cd rea-channel-repro git checkout 8dde6370d0809ad04cda3342d4e6f290f48aa063 npm ci npm run build:cached ``` 2. Run the following commands from the repository root. They create every source and JSON input inline, analyze the fixture, and trace the same application Evidence with a literal channel seed and a node-ID control. Analysis treats the fixture as inert source and needs no Electron installation. ```sh workdir="$(mktemp -d)" mkdir -p "$workdir/app" "$workdir/home" cat > "$workdir/app/main.js" <<'JS' const { ipcMain } = require('electron'); ipcMain.handle('ff', () => 1); ipcMain.handle('ff', () => 2); JS HOME="$workdir/home" node scripts/rea.mjs analyze-javascript-application "$workdir/app" --json > "$workdir/application.json" node --input-type=module - "$workdir" <<'JS' import { readFileSync, writeFileSync } from 'node:fs'; import { join } from 'node:path'; const root = process.argv[2]; const application = JSON.parse(readFileSync(join(root, 'application.json'), 'utf8')); const channels = application.normalized_result.graph.nodes.filter(n => n.kind === 'ipc-channel'); const selected = channels.find(n => n.observations.some(o => o.properties.channel === 'ff')); for (const [name, seed] of [ ['literal', { kind: 'channel', value: 'ff', match: 'exact', case_sensitive: true }], ['node', { kind: 'node-id', value: selected.node_id }], ]) { writeFileSync(join(root, `${name}-input.json`), JSON.stringify({ application, seed, direction: 'outgoing' })); } JS HOME="$workdir/home" node scripts/rea.mjs trace-application-feature "$workdir/literal-input.json" --json > "$workdir/literal-result.json" HOME="$workdir/home" node scripts/rea.mjs trace-application-feature "$workdir/node-input.json" --json > "$workdir/node-result.json" node --input-type=module - "$workdir" <<'JS' import { readFileSync } from 'node:fs'; import { join } from 'node:path'; for (const name of ['literal', 'node']) { const result = JSON.parse(readFileSync(join(process.argv[2], `${name}-result.json`), 'utf8')).normalized_result; const channels = result.graph.nodes.filter(n => n.kind === 'ipc-channel').flatMap(n => n.observations.map(o => o.properties.channel)).sort(); console.log(JSON.stringify({ selection: name, matched_seeds: result.summary.matched_seeds, channels })); } JS ``` 3. Compare the two output lines. The observed literal selection contains both `ff` and `ff`, while the node-ID selection contains only `ff`. ### Expected behavior The literal seed should select only the node whose preserved channel value is `ff`, with `matched_seeds: 1`. Exact case-sensitive matching must retain the distinction between the two strings. The node-ID control should continue to select the same one node. `docs/javascript-application-workflows.md:45` promises exact or literal substring feature-seed matching. `src/domain/javascript/javascriptFeatureTraceSchemas.ts:63` admits channel seeds, and `src/domain/javascript/javascriptFeatureTraceSchemas.ts:68` and `src/domain/javascript/javascriptFeatureTraceSchemas.ts:69` admit explicit `match: "exact"` and `case_sensitive: true`. ### Impact and scope P2 impact is supported. Distinct channel names in a supported local application produce an incorrect seed inventory and include an unrelated node in the requested feature trace. The demonstrated failure is bounded to compatibility-equivalent Unicode strings. A practical workaround is available: select the exact node ID from the original application's preserved channel values. The executed node-ID control returns only `ff` from the same authenticated Evidence. ### REA version 6.4.0 (main at 8dde6370d0809ad04cda3342d4e6f290f48aa063) ### Runtime and platform Node.js v26.10.0; macOS 27.0.1; prebuilt source-checkout runtime. Architecture was not recorded. ### Target and artifact context An inert local JavaScript source directory containing `main.js` with two Electron `ipcMain.handle` registrations, one for `ff` and one for `ff`. The analyzer preserves separate IPC channel nodes. Both trace requests use the same authenticated application Evidence and `direction: "outgoing"`; the literal request uses `kind: "channel"`, `value: "ff"`, `match: "exact"`, and `case_sensitive: true`, while the control uses the selected node's complete ID. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence The CLI reproduction returned: ```text {"selection":"literal","matched_seeds":2,"channels":["ff","ff"]} {"selection":"node","matched_seeds":1,"channels":["ff"]} ``` ### Typed outcome or recovery expected Rerun the same fixture and exact case-sensitive literal seed. Both selection methods must return `matched_seeds: 1` and `channels: ["ff"]`. An exact case-sensitive seed for `ff` must select only that channel. Preserve existing explicit case-insensitive matching and literal substring behavior without adding compatibility-equivalent strings to case-sensitive literal matches. ### Analysis or suspected cause Source-derived reasoning: `src/domain/javascript/javascriptFeatureSeed.ts:74` normalizes the query and candidates before comparison. `src/domain/javascript/javascriptFeatureSeed.ts:269` applies NFKC even when case sensitivity is enabled. NFKC turns `ff` into `ff`, which accounts for the loss of distinction in this exact case-sensitive comparison. The schema's seed options are at the locators cited under Expected behavior. `src/domain/javascript/javascriptFeatureTraceSchemas.ts:91` belongs to native-observation uniqueness validation and does not define those matching options. ### Additional context Related open issue [#3189](https://github.com/morluto/rea/issues/3189) concerns node-ID seeds ignoring substring and case-insensitive options; this report concerns compatibility folding of exact case-sensitive channel seeds, and its node-ID control succeeds. Related open issue [#3190](https://github.com/morluto/rea/issues/3190) concerns route seeds selecting generated display labels; this report loses distinctions between preserved channel values during string normalization. Related completed issue [#1488](https://github.com/morluto/rea/issues/1488) concerns NFC inventory-path joins and mentions NFKC query folding separately; this report concerns the exact case-sensitive IPC channel matching contract.
Author
Owner

@CassieFox23 commented on GitHub (Oct 15, 2026):

TL;DR: Confirmed on Linux. Main still has this bug. An exact, case-sensitive seed for ff returns two channels instead of one.

Results

I ran your recipe on main 7d1e4d0e2 (#3778), package version 6.5.0. It is 164 commits after your base 8dde6370d. The code at src/domain/javascript/javascriptFeatureSeed.ts line 276 still calls normalize("NFKC") for case-sensitive matches. The one commit that touched that file since your base, 44c9d2924, did not change that line.

I ran your fixture on Node 24.18.0 and on Node 26.11.1. Every trace run exited 0 with an empty stderr, and the analysis run printed only progress lines there. Both Node versions gave the same output.

Seed matched_seeds Channels returned
ff, exact, case-sensitive (your literal) 2 ff and ff
node ID of ff (your control) 1 ff
ff, exact, case-sensitive (extra case) 2 ff and ff
node ID of ff (extra control) 1 ff

The first two rows match your report. The third row shows the same fault from the other side: a seed for the ligature also picks up ff.

No open pull request claims to fix this. I searched for #3314 in open pull request bodies and found none.

Not run

  • Your base 8dde6370d (main already reproduces it).
  • Real Electron, macOS and Windows.
  • Any fix branch.

Thanks, coygeek, for the clear report and the node-ID control.

Commands
npm ci
npm run build:cached
# per Node version, fresh HOME each time
HOME=/tmp/i3314/w24/home node scripts/rea.mjs analyze-javascript-application /tmp/i3314/w24/app --json > /tmp/i3314/w24/application.json
# then your inline script writes the input files, and
HOME=/tmp/i3314/w24/home node scripts/rea.mjs trace-application-feature /tmp/i3314/w24/literal-ff-input.json --json

I split your inline script into small files. The seeds are the same. I added the two ff cases.

Test setup
  • Box: Nobara 44 (Fedora 44 base), Linux 7.2.9
  • CPU: AMD Ryzen 9 5900X, 12 cores / 24 threads
  • RAM: 32 GB DDR4-3600
  • Node: 24.18.0 (build and runs), 26.11.1 (runs); npm 11.16.0
  • Main / your base: 7d1e4d0e2 / 8dde6370d

🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊

<!-- gh-comment-id:6182461482 --> @CassieFox23 commented on GitHub (Oct 15, 2026): **TL;DR:** Confirmed on Linux. Main still has this bug. An exact, case-sensitive seed for `ff` returns two channels instead of one. ### Results I ran your recipe on main 7d1e4d0e2 (#3778), package version 6.5.0. It is 164 commits after your base 8dde6370d. The code at `src/domain/javascript/javascriptFeatureSeed.ts` line 276 still calls `normalize("NFKC")` for case-sensitive matches. The one commit that touched that file since your base, 44c9d2924, did not change that line. I ran your fixture on Node 24.18.0 and on Node 26.11.1. Every trace run exited 0 with an empty stderr, and the analysis run printed only progress lines there. Both Node versions gave the same output. | Seed | `matched_seeds` | Channels returned | |---|---|---| | `ff`, exact, case-sensitive (your literal) | 2 | `ff` and `ff` | | node ID of `ff` (your control) | 1 | `ff` | | `ff`, exact, case-sensitive (extra case) | 2 | `ff` and `ff` | | node ID of `ff` (extra control) | 1 | `ff` | The first two rows match your report. The third row shows the same fault from the other side: a seed for the ligature also picks up `ff`. No open pull request claims to fix this. I searched for #3314 in open pull request bodies and found none. ### Not run - Your base 8dde6370d (main already reproduces it). - Real Electron, macOS and Windows. - Any fix branch. Thanks, coygeek, for the clear report and the node-ID control. <details> <summary>Commands</summary> ```sh npm ci npm run build:cached # per Node version, fresh HOME each time HOME=/tmp/i3314/w24/home node scripts/rea.mjs analyze-javascript-application /tmp/i3314/w24/app --json > /tmp/i3314/w24/application.json # then your inline script writes the input files, and HOME=/tmp/i3314/w24/home node scripts/rea.mjs trace-application-feature /tmp/i3314/w24/literal-ff-input.json --json ``` I split your inline script into small files. The seeds are the same. I added the two `ff` cases. </details> <details> <summary>Test setup</summary> - Box: Nobara 44 (Fedora 44 base), Linux 7.2.9 - CPU: AMD Ryzen 9 5900X, 12 cores / 24 threads - RAM: 32 GB DDR4-3600 - Node: 24.18.0 (build and runs), 26.11.1 (runs); npm 11.16.0 - Main / your base: 7d1e4d0e2 / 8dde6370d </details> 🤖 Generated with Claude Code (Sonnet 5.5, verified by Opus 5.5) 🦊
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#1186
No description provided.