[GH-ISSUE #8969] [Bug] Devicetree views silently select the first duplicate node path in imported Evidence #7871

Open
opened 2026-11-05 06:45:36 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @xu91102 on GitHub (Nov 5, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/8969

Observed behavior

On current main 71f680d7c1aa7a45ba0325fe87b104744b0c105f, the public inspect-analysis-view CLI accepts a complete Devicetree Evidence record containing duplicate node paths. Exact node/property selection silently chooses the first occurrence. Moving an empty duplicate before the original changes a three-property result to an exhausted empty result, although both records contain the same two matching node identities.

This is an admission/selection defect for caller-supplied or imported Evidence. I am not claiming that inspecting an ordinary DTB produces duplicate paths: the actual DTB parser already rejects them. The malformed normalized records below are deliberate fixtures whose envelope digests were recomputed with the existing createEvidence API. This separates semantic admission from a stale-digest rejection.

Public reproduction

Use Node24.22.1, normal dependencies and npm run build:cached on the revision above. From the repository root:

// Save as reproduce.mjs, then run node reproduce.mjs.
import { mkdtempSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { resolve, join } from 'node:path';
import { pathToFileURL } from 'node:url';
import { spawnSync } from 'node:child_process';
const load = path => import(pathToFileURL(resolve(path)).href);
const { devicetreeFixture } = await load('src/firmware/devicetree/Devicetree.fixture.ts');
const { createEvidence } = await load('dist/domain/evidence.js');
const root = mkdtempSync(join(tmpdir(), 'rea-dtb-paths-'));
const path = join(root, 'board.dtb');
writeFileSync(path, devicetreeFixture());
const call = args => {
  const result = spawnSync(process.execPath, ['scripts/rea.mjs', ...args, '--json'],
    { encoding: 'utf8' });
  return { exit: result.status, value: JSON.parse(result.stdout) };
};
const original = call(['inspect-devicetree', path]).value;
for (const mode of ['valid', 'duplicate-last', 'duplicate-first']) {
  const report = structuredClone(original.normalized_result);
  const index = report.nodes.findIndex(node => node.path === '/serial@1000');
  if (mode !== 'valid') {
    const copy = structuredClone(report.nodes[index]);
    copy.properties = [];
    if (mode === 'duplicate-last') report.nodes.push(copy);
    else report.nodes.splice(index, 0, copy);
    report.coverage.examined_nodes = report.nodes.length;
  }
  const evidence = mode === 'valid' ? original : createEvidence(
    { path, sha256: report.artifact.sha256, format: 'file' }, original.provider,
    { operation: original.operation, parameters: original.parameters,
      result: report, confidence: 'observed', limitations: original.limitations });
  const request = join(root, mode + '.json');
  writeFileSync(request, JSON.stringify({ source: { kind: 'inline', evidence },
    view: { kind: 'devicetree', collection: 'properties', path: '/serial@1000' } }));
  const result = call(['inspect-analysis-view', request]);
  console.log(mode, result.exit, JSON.stringify(result.value.normalized_result));
}

Actual public CLI observations (both nodes and properties collections were exercised):

Parent Matching node rows Exit Selected node property_count Property page
Original valid record 1 0 3 3 rows
Empty duplicate after original 2 0 3 3 rows; exhausted
Empty duplicate before original 2 0 0 0 rows; exhausted

The exact-path node page reports total: 1 in both duplicate cases. There is no ambiguity or input diagnostic. All original DTB bytes and hashes remained unchanged; no target code was executed.

Two refusal controls also passed: changing only the Evidence digest is rejected as invalid_request, and inspecting a DTB with duplicate node names is rejected as invalid_request with the existing Duplicate FDT node path diagnostic. These controls do not substitute for rejecting the correctly hashed but internally ambiguous Evidence.

Expected behavior and impact

Reject duplicate node-path identities at the owning Devicetree result-admission boundary before any exact-path lookup or view is produced. A caller should receive an input issue pointing into the duplicate node path, rather than an apparently complete or exhausted result selected by array order. Ordinary unique-node reports, pagination, source indices, property bytes, references and unknown values should retain their behavior.

P2: an imported/inline malformed complete record can silently hide conflicting properties or give an arbitrary node identity to an exact selector. The observed scope is a generated small DTB followed by deliberately malformed inline Evidence. This does not demonstrate a normal producer failure, file mutation, target execution, a security-boundary bypass, hardware behavior, or malformed-input frequency in real reports.

Source-derived: devicetreeSchema validates field shapes but not unique nodes[].path. projectDevicetreeView then uses findIndex for exact path selection. The ordinary FDT parser already maintains a path set and rejects duplicates; that invariant is lost when the complete result comes through the supported Evidence input route.

I searched open/closed issues and PRs for Devicetree/DTB duplicate paths and node identities. I read the actual related production changes: #7305 is view-test coverage only; #8757 is legacy chosen-console/string-budget work; #7317 repairs empty FDT byte extents; #7739 changes retained-view guidance. None changes this identity-admission boundary. I did not run those foreign heads. No implementation has been made for this report yet.

Environment: macOS arm64, Node24.22.1, source rea-agents6.8.0. Seven initial public CLI calls plus the two refusal controls were retained separately; this is not an upstream CI or cross-platform validation claim.

Update: a later live deduplication check found PR #8970, opened by @ShuaixinHuang, implementing the same duplicate-node-path admission invariant in the shared Devicetree schema. I read its full production patch and have stopped my local implementation as a read-only hold to avoid a duplicate PR. I have not created an upstream PR or run that branch; this does not establish source adoption or author credit. The original reproduction and failure evidence above remain unchanged.

Originally created by @xu91102 on GitHub (Nov 5, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8969 ### Observed behavior On current main `71f680d7c1aa7a45ba0325fe87b104744b0c105f`, the public `inspect-analysis-view` CLI accepts a complete Devicetree Evidence record containing duplicate node paths. Exact node/property selection silently chooses the first occurrence. Moving an empty duplicate before the original changes a three-property result to an exhausted empty result, although both records contain the same two matching node identities. This is an admission/selection defect for caller-supplied or imported Evidence. I am **not** claiming that inspecting an ordinary DTB produces duplicate paths: the actual DTB parser already rejects them. The malformed normalized records below are deliberate fixtures whose envelope digests were recomputed with the existing `createEvidence` API. This separates semantic admission from a stale-digest rejection. ### Public reproduction Use Node24.22.1, normal dependencies and `npm run build:cached` on the revision above. From the repository root: ```js // Save as reproduce.mjs, then run node reproduce.mjs. import { mkdtempSync, writeFileSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { resolve, join } from 'node:path'; import { pathToFileURL } from 'node:url'; import { spawnSync } from 'node:child_process'; const load = path => import(pathToFileURL(resolve(path)).href); const { devicetreeFixture } = await load('src/firmware/devicetree/Devicetree.fixture.ts'); const { createEvidence } = await load('dist/domain/evidence.js'); const root = mkdtempSync(join(tmpdir(), 'rea-dtb-paths-')); const path = join(root, 'board.dtb'); writeFileSync(path, devicetreeFixture()); const call = args => { const result = spawnSync(process.execPath, ['scripts/rea.mjs', ...args, '--json'], { encoding: 'utf8' }); return { exit: result.status, value: JSON.parse(result.stdout) }; }; const original = call(['inspect-devicetree', path]).value; for (const mode of ['valid', 'duplicate-last', 'duplicate-first']) { const report = structuredClone(original.normalized_result); const index = report.nodes.findIndex(node => node.path === '/serial@1000'); if (mode !== 'valid') { const copy = structuredClone(report.nodes[index]); copy.properties = []; if (mode === 'duplicate-last') report.nodes.push(copy); else report.nodes.splice(index, 0, copy); report.coverage.examined_nodes = report.nodes.length; } const evidence = mode === 'valid' ? original : createEvidence( { path, sha256: report.artifact.sha256, format: 'file' }, original.provider, { operation: original.operation, parameters: original.parameters, result: report, confidence: 'observed', limitations: original.limitations }); const request = join(root, mode + '.json'); writeFileSync(request, JSON.stringify({ source: { kind: 'inline', evidence }, view: { kind: 'devicetree', collection: 'properties', path: '/serial@1000' } })); const result = call(['inspect-analysis-view', request]); console.log(mode, result.exit, JSON.stringify(result.value.normalized_result)); } ``` Actual public CLI observations (both `nodes` and `properties` collections were exercised): | Parent | Matching node rows | Exit | Selected node `property_count` | Property page | | --- | ---: | ---: | ---: | --- | | Original valid record | 1 | 0 | 3 | 3 rows | | Empty duplicate after original | 2 | 0 | 3 | 3 rows; exhausted | | Empty duplicate before original | 2 | 0 | 0 | 0 rows; exhausted | The exact-path node page reports `total: 1` in both duplicate cases. There is no ambiguity or input diagnostic. All original DTB bytes and hashes remained unchanged; no target code was executed. Two refusal controls also passed: changing only the Evidence digest is rejected as `invalid_request`, and inspecting a DTB with duplicate node names is rejected as `invalid_request` with the existing `Duplicate FDT node path` diagnostic. These controls do not substitute for rejecting the correctly hashed but internally ambiguous Evidence. ### Expected behavior and impact Reject duplicate node-path identities at the owning Devicetree result-admission boundary before any exact-path lookup or view is produced. A caller should receive an input issue pointing into the duplicate node path, rather than an apparently complete or exhausted result selected by array order. Ordinary unique-node reports, pagination, source indices, property bytes, references and unknown values should retain their behavior. P2: an imported/inline malformed complete record can silently hide conflicting properties or give an arbitrary node identity to an exact selector. The observed scope is a generated small DTB followed by deliberately malformed inline Evidence. This does not demonstrate a normal producer failure, file mutation, target execution, a security-boundary bypass, hardware behavior, or malformed-input frequency in real reports. ### Cause and related work Source-derived: `devicetreeSchema` validates field shapes but not unique `nodes[].path`. `projectDevicetreeView` then uses `findIndex` for exact path selection. The ordinary FDT parser already maintains a path set and rejects duplicates; that invariant is lost when the complete result comes through the supported Evidence input route. I searched open/closed issues and PRs for Devicetree/DTB duplicate paths and node identities. I read the actual related production changes: #7305 is view-test coverage only; #8757 is legacy chosen-console/string-budget work; #7317 repairs empty FDT byte extents; #7739 changes retained-view guidance. None changes this identity-admission boundary. I did not run those foreign heads. No implementation has been made for this report yet. Environment: macOS arm64, Node24.22.1, source `rea-agents`6.8.0. Seven initial public CLI calls plus the two refusal controls were retained separately; this is not an upstream CI or cross-platform validation claim. Update: a later live deduplication check found PR #8970, opened by @ShuaixinHuang, implementing the same duplicate-node-path admission invariant in the shared Devicetree schema. I read its full production patch and have stopped my local implementation as a read-only hold to avoid a duplicate PR. I have not created an upstream PR or run that branch; this does not establish source adoption or author credit. The original reproduction and failure evidence above remain unchanged.
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#7871
No description provided.