[GH-ISSUE #6458] [Bug] trace-javascript-semantics rejects retained empty event names #3873

Closed
opened 2026-10-31 00:44:48 +00:00 by bsakaguchi · 2 comments
Owner

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

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 successfully retains an EventEmitter event and its listener with properties.event_name: "". Passing that unchanged Evidence to trace-javascript-semantics with {"kind":"event","name":""} exits 1 with invalid_request. Selecting the retained event by its semantic-node ID returns the event, listener, and registration relation successfully. A nonempty event-name query also succeeds.

The empty-name rejection reproduced in two requests against each of two independently generated application Evidence results: four failures in four requests.

Reproduction

From a fresh checkout at 9eb829aca34be36ff112826337996f6139f82eff, prepare the locked dependencies and runtime artifacts:

npm ci && npm run build:cached

Run this block from the repository root. It creates the complete fixture inline and uses an isolated HOME. The public CLI analysis and trace calls were executed against an already built checkout of that revision; the fresh-checkout preparation above was not rerun for these observations.

python3 - <<'PY'
import json, os, subprocess, tempfile
from pathlib import Path

with tempfile.TemporaryDirectory() as directory:
    work = Path(directory)
    app = work / 'app'
    app.mkdir()
    (work / 'home').mkdir()
    (app / 'package.json').write_text(json.dumps({
        'name': 'empty-event-fixture', 'version': '1.0.0', 'main': 'index.cjs'
    }))
    (app / 'index.cjs').write_text('''const { EventEmitter } = require('node:events');
const bus = new EventEmitter();
bus.on('', function emptyListener() { return 17; });
bus.on('data', function dataListener(value) { return value; });
module.exports = bus;
''')
    env = dict(os.environ, HOME=str(work / 'home'), REA_LOG_LEVEL='silent')
    def cli(*args):
        run = subprocess.run(
            ['node', 'scripts/rea.mjs', *map(str, args), '--json'],
            env=env, capture_output=True, text=True, timeout=120)
        return run.returncode, json.loads(run.stdout)
    code, application = cli('analyze-javascript-application', app)
    assert code == 0, application
    graph = application['normalized_result']['semantic_graph']
    event = next(n for n in graph['nodes'] if n['kind'] == 'event'
                 and n['properties'].get('event_name') == '')
    print('retained empty event:', json.dumps(event['properties']['event_name']))
    for label, seed in [
        ('empty-1', {'kind': 'event', 'name': ''}),
        ('empty-2', {'kind': 'event', 'name': ''}),
        ('named', {'kind': 'event', 'name': 'data'}),
        ('exact', {'kind': 'semantic-node', 'node_id': event['node_id']}),
        ('missing', {'kind': 'event', 'name': 'absent'})
    ]:
        request = work / (label + '.json')
        request.write_text(json.dumps({'application': application, 'query': {
            'seed': seed, 'direction': 'forward-influence'}}))
        code, output = cli('trace-javascript-semantics', request)
        result = output.get('normalized_result')
        if result is None:
            print(label, code, output.get('code'),
                  json.dumps(output.get('details', {}).get('issues')))
        else:
            print(label, code, result['status'],
                  result['summary']['total_seed_matches'],
                  sorted(n['kind'] for n in result['nodes']),
                  len(result['relations']))
PY

Expected behavior

The empty-name event seed should reach exact-name matching and select the retained event and listener. For this fixture, its reachable nodes and relation should match the successful exact-node control; its seed set should contain both matching nodes.

The event identity contract preserves complete event names, distinguishes dynamic names as null, and directs callers to query the preserved name or use graph IDs. The event matcher selects event and listener nodes by exact event_name. The two-match expectation is source-derived; the currently rejected empty-name query was not observed succeeding.

Impact and scope

P2: a retained event identity cannot be queried through its documented name selector. The failure is bounded to the tested empty-name input, and the original Evidence remains available. A demonstrated workaround is to find the event whose properties.event_name equals "" and use its node_id in a semantic-node seed. That returned the event, listener, and one relation without changing the target or Evidence.

Real-application prevalence and other platforms are NOT_ENOUGH_INFO; only the reduced local CLI fixture was exercised.

REA version

6.7.0 (source 9eb829aca3)

Runtime and platform

Node v26.12.1; macOS 27.0.1; arm64. No native analysis engine is involved.

Target and artifact context

A local CommonJS application directory with two literal node:events EventEmitter listener registrations. Trace input contains the complete, unmodified Evidence returned by the static JavaScript analyzer.

Interface

CLI

Earliest failure stage

Request parsing or validation

Relevant output or evidence

Observed through the public CLI:

analyze-javascript-application: exit 0; retained event names ["", "data"]
empty-1: exit 1, invalid_request
empty-2: exit 1, invalid_request
issues: [{"path":["query","seed","name"],"reason":"out_of_range","minimum":1}]
named: exit 0, ambiguous, 2 seed matches, [event, listener], 1 relation
exact: exit 0, found, 1 seed match, [event, listener], 1 relation
missing: exit 0, partial, 0 seed matches, [], 0 relations

Typed outcome or recovery expected

Return semantic trace Evidence for the matching retained empty-name nodes. Preserve the existing nonempty-name and absent-name results and the graph's coverage semantics. Exact-node selection currently provides a working recovery path.

Analysis or suspected cause

Source-derived explanation: the event seed schema requires name: z.string().min(1). The CLI input contract composes this query schema. This rejects the empty name before the exact-name matcher can inspect the graph.

The event and listener producer preserves the raw name on both nodes, consistent with the observed analyzer output.

Additional context

Related #4228 and its merged #4239 address empty property-name seeds, explicitly preserving other seed constraints. They do not address this event-name selector.

The repository-pinned Node 24.18.0 runtime, MCP, installed packages, and post-fix behavior were not tested. Last known good version is NOT_ENOUGH_INFO; no regression boundary was established.

Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6458 ### 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` successfully retains an EventEmitter event and its listener with `properties.event_name: ""`. Passing that unchanged Evidence to `trace-javascript-semantics` with `{"kind":"event","name":""}` exits 1 with `invalid_request`. Selecting the retained event by its semantic-node ID returns the event, listener, and registration relation successfully. A nonempty event-name query also succeeds. The empty-name rejection reproduced in two requests against each of two independently generated application Evidence results: four failures in four requests. ### Reproduction From a fresh checkout at `9eb829aca34be36ff112826337996f6139f82eff`, prepare the locked dependencies and runtime artifacts: ```sh npm ci && npm run build:cached ``` Run this block from the repository root. It creates the complete fixture inline and uses an isolated HOME. The public CLI analysis and trace calls were executed against an already built checkout of that revision; the fresh-checkout preparation above was not rerun for these observations. ```sh python3 - <<'PY' import json, os, subprocess, tempfile from pathlib import Path with tempfile.TemporaryDirectory() as directory: work = Path(directory) app = work / 'app' app.mkdir() (work / 'home').mkdir() (app / 'package.json').write_text(json.dumps({ 'name': 'empty-event-fixture', 'version': '1.0.0', 'main': 'index.cjs' })) (app / 'index.cjs').write_text('''const { EventEmitter } = require('node:events'); const bus = new EventEmitter(); bus.on('', function emptyListener() { return 17; }); bus.on('data', function dataListener(value) { return value; }); module.exports = bus; ''') env = dict(os.environ, HOME=str(work / 'home'), REA_LOG_LEVEL='silent') def cli(*args): run = subprocess.run( ['node', 'scripts/rea.mjs', *map(str, args), '--json'], env=env, capture_output=True, text=True, timeout=120) return run.returncode, json.loads(run.stdout) code, application = cli('analyze-javascript-application', app) assert code == 0, application graph = application['normalized_result']['semantic_graph'] event = next(n for n in graph['nodes'] if n['kind'] == 'event' and n['properties'].get('event_name') == '') print('retained empty event:', json.dumps(event['properties']['event_name'])) for label, seed in [ ('empty-1', {'kind': 'event', 'name': ''}), ('empty-2', {'kind': 'event', 'name': ''}), ('named', {'kind': 'event', 'name': 'data'}), ('exact', {'kind': 'semantic-node', 'node_id': event['node_id']}), ('missing', {'kind': 'event', 'name': 'absent'}) ]: request = work / (label + '.json') request.write_text(json.dumps({'application': application, 'query': { 'seed': seed, 'direction': 'forward-influence'}})) code, output = cli('trace-javascript-semantics', request) result = output.get('normalized_result') if result is None: print(label, code, output.get('code'), json.dumps(output.get('details', {}).get('issues'))) else: print(label, code, result['status'], result['summary']['total_seed_matches'], sorted(n['kind'] for n in result['nodes']), len(result['relations'])) PY ``` ### Expected behavior The empty-name event seed should reach exact-name matching and select the retained event and listener. For this fixture, its reachable nodes and relation should match the successful exact-node control; its seed set should contain both matching nodes. The [event identity contract](https://github.com/morluto/rea/blob/9eb829aca34be36ff112826337996f6139f82eff/docs/javascript-application-workflows.md#L282-L290) preserves complete event names, distinguishes dynamic names as `null`, and directs callers to query the preserved name or use graph IDs. The [event matcher](https://github.com/morluto/rea/blob/9eb829aca34be36ff112826337996f6139f82eff/src/domain/javascript/javascriptSemanticQueryIdentity.ts#L76-L80) selects event and listener nodes by exact `event_name`. The two-match expectation is source-derived; the currently rejected empty-name query was not observed succeeding. ### Impact and scope P2: a retained event identity cannot be queried through its documented name selector. The failure is bounded to the tested empty-name input, and the original Evidence remains available. A demonstrated workaround is to find the event whose `properties.event_name` equals `""` and use its `node_id` in a `semantic-node` seed. That returned the event, listener, and one relation without changing the target or Evidence. Real-application prevalence and other platforms are NOT_ENOUGH_INFO; only the reduced local CLI fixture was exercised. ### REA version 6.7.0 (source 9eb829aca34be36ff112826337996f6139f82eff) ### Runtime and platform Node v26.12.1; macOS 27.0.1; arm64. No native analysis engine is involved. ### Target and artifact context A local CommonJS application directory with two literal `node:events` EventEmitter listener registrations. Trace input contains the complete, unmodified Evidence returned by the static JavaScript analyzer. ### Interface CLI ### Earliest failure stage Request parsing or validation ### Relevant output or evidence Observed through the public CLI: ```text analyze-javascript-application: exit 0; retained event names ["", "data"] empty-1: exit 1, invalid_request empty-2: exit 1, invalid_request issues: [{"path":["query","seed","name"],"reason":"out_of_range","minimum":1}] named: exit 0, ambiguous, 2 seed matches, [event, listener], 1 relation exact: exit 0, found, 1 seed match, [event, listener], 1 relation missing: exit 0, partial, 0 seed matches, [], 0 relations ``` ### Typed outcome or recovery expected Return semantic trace Evidence for the matching retained empty-name nodes. Preserve the existing nonempty-name and absent-name results and the graph's coverage semantics. Exact-node selection currently provides a working recovery path. ### Analysis or suspected cause Source-derived explanation: the [event seed schema](https://github.com/morluto/rea/blob/9eb829aca34be36ff112826337996f6139f82eff/src/domain/javascript/javascriptSemanticQuerySchemas.ts#L50-L53) requires `name: z.string().min(1)`. The [CLI input contract](https://github.com/morluto/rea/blob/9eb829aca34be36ff112826337996f6139f82eff/src/domain/javascript/javascriptSemanticTraceSchemas.ts#L13-L16) composes this query schema. This rejects the empty name before the exact-name matcher can inspect the graph. The [event and listener producer](https://github.com/morluto/rea/blob/9eb829aca34be36ff112826337996f6139f82eff/src/application/javascript/JavaScriptSemanticGraphAsyncProjection.ts#L56-L101) preserves the raw name on both nodes, consistent with the observed analyzer output. ### Additional context Related [#4228](https://github.com/morluto/rea/issues/4228) and its merged [#4239](https://github.com/morluto/rea/pull/4239) address empty property-name seeds, explicitly preserving other seed constraints. They do not address this event-name selector. The repository-pinned Node 24.18.0 runtime, MCP, installed packages, and post-fix behavior were not tested. Last known good version is NOT_ENOUGH_INFO; no regression boundary was established.
Author
Owner

@Lew1sWong commented on GitHub (Oct 23, 2026):

Reproduced on current main (e32a0494d) with the issue's fixture: both empty-name event seeds fail with invalid_request (minimum: 1), while the semantic-node seed for the same event succeeds. I'd like to work on a fix and will open a PR shortly.

<!-- gh-comment-id:6357319539 --> @Lew1sWong commented on GitHub (Oct 23, 2026): Reproduced on current main (e32a0494d) with the issue's fixture: both empty-name event seeds fail with `invalid_request` (`minimum: 1`), while the `semantic-node` seed for the same event succeeds. I'd like to work on a fix and will open a PR shortly.
Author
Owner

@coygeek commented on GitHub (Oct 24, 2026):

Checked the reported fix on main at d87e73f2db.

The event seed now uses z.string() with no minimum length, so an empty name reaches the exact event_name matcher. The application test includes empty and data listeners and asserts the returned seeds all retain the empty identity. Public Evidence acceptance analyzes an empty-name EventEmitter listener and traces it through MCP successfully.

Implementation: javascriptSemanticQuerySchemas.ts, JavaScriptSemanticGraphSeedQueries.test.ts.

The relevant regression checks in JavaScriptSemanticGraphSeedQueries.test.ts, javascriptGraphQueryCoverage.test.ts passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.

<!-- gh-comment-id:6380714931 --> @coygeek commented on GitHub (Oct 24, 2026): Checked the reported fix on `main` at d87e73f2db50be72d33a4b3c7199af35518b441c. The event seed now uses z.string() with no minimum length, so an empty name reaches the exact event_name matcher. The application test includes empty and data listeners and asserts the returned seeds all retain the empty identity. Public Evidence acceptance analyzes an empty-name EventEmitter listener and traces it through MCP successfully. Implementation: [javascriptSemanticQuerySchemas.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/javascript/javascriptSemanticQuerySchemas.ts#L51), [JavaScriptSemanticGraphSeedQueries.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/application/javascript/JavaScriptSemanticGraphSeedQueries.test.ts#L68). The relevant regression checks in [JavaScriptSemanticGraphSeedQueries.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/application/javascript/JavaScriptSemanticGraphSeedQueries.test.ts), [javascriptGraphQueryCoverage.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/tests/acceptance/applications/javascriptGraphQueryCoverage.test.ts) passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.
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#3873
No description provided.