[GH-ISSUE #6545] [Bug] NIBArchive image views and color wells are classified as resources #3936

Closed
opened 2026-10-31 00:45:22 +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/6545

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

The production analyzeInterfaceBuilderBundle boundary decodes a small generated NIBArchive successfully but labels NSImageView, UIImageView, and NSColorWell as kind: "resource". An NSView control is correctly a view, and an NSImage control is correctly a resource. The result reports complete object, hierarchy, class, and archive coverage with graph.truncated: false. This was observed once against the published release source and once against main; the CLI/MCP adapters and real Apple compiler output were not executed.

Reproduction

From a checkout with its locked dependencies installed, save the following as nib-view-probe.mts outside the checkout and run node --import ./node_modules/tsx/dist/loader.mjs /path/to/nib-view-probe.mts "$PWD". This is a portable version of the executed source-boundary probe. It generates inert NIBArchive bytes using the repository fixture encoder, inspects those bytes through the production bundle analyzer, and removes its temporary bundle. It installs no provider and executes no target.

import { mkdir, mkdtemp, writeFile, rm } from 'node:fs/promises';
import { join } from 'node:path';
import { tmpdir } from 'node:os';
import { pathToFileURL } from 'node:url';
const checkout = process.argv[2]!;
const { encodeNibArchiveFixture } = await import(pathToFileURL(join(checkout, 'src/artifacts/apple/NibArchive.fixture.ts')).href);
const { analyzeInterfaceBuilderBundle } = await import(pathToFileURL(join(checkout, 'src/artifacts/apple/InterfaceBuilderAnalysis.ts')).href);
const root = await mkdtemp(join(tmpdir(), 'nib-view-probe-'));
try {
  const bundlePath = join(root, 'Demo.app');
  const resources = join(bundlePath, 'Contents', 'Resources');
  await mkdir(resources, { recursive: true });
  const bytes = encodeNibArchiveFixture({
    classes: ['NSIBObjectData', 'NSView', 'NSImageView', 'UIImageView', 'NSColorWell', 'NSImage'],
    objects: [
      { classIndex: 0, values: { NSRoot: { ref: 1 } } },
      { classIndex: 1, values: {} },
      { classIndex: 2, values: { NSSuperview: { ref: 1 } } },
      { classIndex: 3, values: { NSSuperview: { ref: 1 } } },
      { classIndex: 4, values: { NSSuperview: { ref: 1 } } },
      { classIndex: 5, values: {} },
    ],
  });
  await writeFile(join(resources, 'Views.nib'), bytes);
  const result = await analyzeInterfaceBuilderBundle({ bundlePath, targetSha256: 'a'.repeat(64) });
  console.log(JSON.stringify({
    nodes: result.graph.nodes.map(({ kind, attributes }: any) => ({ kind, class_name: attributes.class_name })),
    coverage: result.graph.coverage,
    truncated: result.graph.truncated,
  }, null, 2));
} finally {
  await rm(root, { recursive: true, force: true });
}

For a proposed public reproduction, open an executable .app containing such a nib and request decode_interface_builder with complete detail, or use decode-interface-builder APP --json. The release artifact provider admits an active executable .app and calls the same analyzer; main also exposes it through bundle-resource sessions. These public adapter checks were source-inspected, not executed.

Expected behavior

The decoded UI graph should classify these built-in UI objects as views or controls, preserving their class names and distinguishing them from image/color resource objects. Apple documents UIImageView as a view and lists NSImageView among NSControl subclasses. NSColorWell is also an AppKit control. Main already gives declared UIView/NSView and UIControl/NSControl ancestry precedence for keyed archives; the NIBArchive path should preserve the same distinction for these known framework classes.

Impact and scope

The returned graph supplies consequential wrong node kinds while retaining the original class names and hierarchy links. This affects the demonstrated NIBArchive path on both inspected revisions. Source-derived downstream consequence: trace_native_ui_action only admits control/view/view-controller/scene nodes when resolving an action from a UI object ID, so a color-well source node cannot reach that branch after being classified as a resource. That downstream operation was not executed, and this probe contains no action connection. Real-app frequency and additional affected classes are unknown.

A practical manual workaround for graph inspection is to use the preserved attributes.class_name and hierarchy rather than trusting kind for these known classes. Action-selector lookup is a possible separate bypass but was not tested.

REA version

Published rea-agents 6.7.0 source: 27dc42d10249f0410904a75c0997842eb03b931c. Main source: 1635eadfcc49bbb80178838ab74667c2981de383.

Runtime and platform

Node.js v26.12.1; Linux x86_64. This exercised the portable inert NIBArchive decoder and graph projection only. No macOS, Apple SDK, ibtool, native provider, or device was involved.

Target and artifact context

Generated macOS-layout bundle resource directory containing one NIBArchive with six in-bounds objects: one NSIBObjectData root owner, an NSView root, three child UI objects, and an NSImage resource control. The analyzer returned archive SHA-256 1993253411d7aac5c9886d933bcbd0f63c7306d1c251a175cd558fcc3c9928f4. The supplied target digest is a placeholder for source-boundary invocation; target admission was not tested.

Interface

Other

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Both source-boundary invocations returned these same relevant values:

{
  "nodes": [
    {"kind":"scene"},
    {"kind":"view","class_name":"NSView"},
    {"kind":"resource","class_name":"NSImageView"},
    {"kind":"resource","class_name":"UIImageView"},
    {"kind":"resource","class_name":"NSColorWell"},
    {"kind":"resource","class_name":"NSImage"}
  ],
  "truncated": false
}

The document has object_count: 5 and hierarchy_complete: true; object coverage examined five objects and omitted zero, and archive coverage examined one document and omitted zero. All reported facets are complete. This establishes the decoder/projection result for generated bytes, not real-compiler compatibility or full CLI behavior.

Source reachability on the release is ArtifactProvider calling the analyzer after the active executable-app guard. The release classifier checks image/color/font before view/control. Main preserves that fallback ordering and its NIB/ibtool dictionary parser calls the classifier without a class chain. The NIB producer supplies class names and empty class metadata. The downstream UI-object lookup excludes resource nodes.

Analysis or suspected cause

Source-derived: the fallback uses includes("image"), includes("color"), or includes("font") before considering UI class roles. NIBArchive projection emits an ibtool-shaped object dictionary, which does not carry the superclass chain consumed by the newer keyed-archive classification path. Thus the keyed-archive ancestry improvement on main does not change this NIBArchive result.

Additional context

Open and closed searches used UIImageView, NSImageView, image view, image views, classified as resource, NSColorWell, and broader Interface Builder classification/resource terms. Related issues #2176 and #5635 concern missing resource-key joins, not UI objects being assigned resource kinds. No matching report was found. No repair or independent review was performed.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Two production-boundary executions return wrong UI node kinds for the generated valid NIBArchive while retaining the underlying class identities. Wrong graph semantics are consequential; real-app frequency and downstream trace execution remain unmeasured.
  • Workaround status: available
  • Workaround basis: The same observed results retain each exact framework class name and hierarchy, allowing consumers to identify these known UI classes manually without changing the artifact. This restores graph interpretation for the demonstrated inputs; it does not establish automatic trace recovery.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6545 ### 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 The production `analyzeInterfaceBuilderBundle` boundary decodes a small generated NIBArchive successfully but labels `NSImageView`, `UIImageView`, and `NSColorWell` as `kind: "resource"`. An `NSView` control is correctly a view, and an `NSImage` control is correctly a resource. The result reports complete object, hierarchy, class, and archive coverage with `graph.truncated: false`. This was observed once against the published release source and once against main; the CLI/MCP adapters and real Apple compiler output were not executed. ### Reproduction From a checkout with its locked dependencies installed, save the following as `nib-view-probe.mts` outside the checkout and run `node --import ./node_modules/tsx/dist/loader.mjs /path/to/nib-view-probe.mts "$PWD"`. This is a portable version of the executed source-boundary probe. It generates inert NIBArchive bytes using the repository fixture encoder, inspects those bytes through the production bundle analyzer, and removes its temporary bundle. It installs no provider and executes no target. ```ts import { mkdir, mkdtemp, writeFile, rm } from 'node:fs/promises'; import { join } from 'node:path'; import { tmpdir } from 'node:os'; import { pathToFileURL } from 'node:url'; const checkout = process.argv[2]!; const { encodeNibArchiveFixture } = await import(pathToFileURL(join(checkout, 'src/artifacts/apple/NibArchive.fixture.ts')).href); const { analyzeInterfaceBuilderBundle } = await import(pathToFileURL(join(checkout, 'src/artifacts/apple/InterfaceBuilderAnalysis.ts')).href); const root = await mkdtemp(join(tmpdir(), 'nib-view-probe-')); try { const bundlePath = join(root, 'Demo.app'); const resources = join(bundlePath, 'Contents', 'Resources'); await mkdir(resources, { recursive: true }); const bytes = encodeNibArchiveFixture({ classes: ['NSIBObjectData', 'NSView', 'NSImageView', 'UIImageView', 'NSColorWell', 'NSImage'], objects: [ { classIndex: 0, values: { NSRoot: { ref: 1 } } }, { classIndex: 1, values: {} }, { classIndex: 2, values: { NSSuperview: { ref: 1 } } }, { classIndex: 3, values: { NSSuperview: { ref: 1 } } }, { classIndex: 4, values: { NSSuperview: { ref: 1 } } }, { classIndex: 5, values: {} }, ], }); await writeFile(join(resources, 'Views.nib'), bytes); const result = await analyzeInterfaceBuilderBundle({ bundlePath, targetSha256: 'a'.repeat(64) }); console.log(JSON.stringify({ nodes: result.graph.nodes.map(({ kind, attributes }: any) => ({ kind, class_name: attributes.class_name })), coverage: result.graph.coverage, truncated: result.graph.truncated, }, null, 2)); } finally { await rm(root, { recursive: true, force: true }); } ``` For a proposed public reproduction, open an executable `.app` containing such a nib and request `decode_interface_builder` with complete detail, or use `decode-interface-builder APP --json`. The release artifact provider admits an active executable `.app` and calls the same analyzer; main also exposes it through bundle-resource sessions. These public adapter checks were source-inspected, not executed. ### Expected behavior The decoded UI graph should classify these built-in UI objects as views or controls, preserving their class names and distinguishing them from image/color resource objects. Apple documents [UIImageView](https://developer.apple.com/documentation/uikit/uiimageview) as a view and lists `NSImageView` among [NSControl subclasses](https://developer.apple.com/documentation/appkit/nscontrol). `NSColorWell` is also an [AppKit control](https://developer.apple.com/documentation/appkit/nscolorwell). Main already gives declared `UIView`/`NSView` and `UIControl`/`NSControl` ancestry precedence for keyed archives; the NIBArchive path should preserve the same distinction for these known framework classes. ### Impact and scope The returned graph supplies consequential wrong node kinds while retaining the original class names and hierarchy links. This affects the demonstrated NIBArchive path on both inspected revisions. Source-derived downstream consequence: `trace_native_ui_action` only admits control/view/view-controller/scene nodes when resolving an action from a UI object ID, so a color-well source node cannot reach that branch after being classified as a resource. That downstream operation was not executed, and this probe contains no action connection. Real-app frequency and additional affected classes are unknown. A practical manual workaround for graph inspection is to use the preserved `attributes.class_name` and hierarchy rather than trusting `kind` for these known classes. Action-selector lookup is a possible separate bypass but was not tested. ### REA version Published `rea-agents` 6.7.0 source: `27dc42d10249f0410904a75c0997842eb03b931c`. Main source: `1635eadfcc49bbb80178838ab74667c2981de383`. ### Runtime and platform Node.js v26.12.1; Linux x86_64. This exercised the portable inert NIBArchive decoder and graph projection only. No macOS, Apple SDK, `ibtool`, native provider, or device was involved. ### Target and artifact context Generated macOS-layout bundle resource directory containing one NIBArchive with six in-bounds objects: one NSIBObjectData root owner, an NSView root, three child UI objects, and an NSImage resource control. The analyzer returned archive SHA-256 `1993253411d7aac5c9886d933bcbd0f63c7306d1c251a175cd558fcc3c9928f4`. The supplied target digest is a placeholder for source-boundary invocation; target admission was not tested. ### Interface Other ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Both source-boundary invocations returned these same relevant values: ```json { "nodes": [ {"kind":"scene"}, {"kind":"view","class_name":"NSView"}, {"kind":"resource","class_name":"NSImageView"}, {"kind":"resource","class_name":"UIImageView"}, {"kind":"resource","class_name":"NSColorWell"}, {"kind":"resource","class_name":"NSImage"} ], "truncated": false } ``` The document has `object_count: 5` and `hierarchy_complete: true`; object coverage examined five objects and omitted zero, and archive coverage examined one document and omitted zero. All reported facets are complete. This establishes the decoder/projection result for generated bytes, not real-compiler compatibility or full CLI behavior. Source reachability on the release is [ArtifactProvider calling the analyzer after the active executable-app guard](https://github.com/morluto/rea/blob/27dc42d10249f0410904a75c0997842eb03b931c/src/artifacts/ArtifactProvider.ts#L109-L126). The release [classifier checks image/color/font before view/control](https://github.com/morluto/rea/blob/27dc42d10249f0410904a75c0997842eb03b931c/src/domain/apple/interfaceBuilderKeyedArchive.ts#L489-L524). Main preserves that [fallback ordering](https://github.com/morluto/rea/blob/1635eadfcc49bbb80178838ab74667c2981de383/src/domain/apple/interfaceBuilderKeyedArchive.ts#L566-L604) and its [NIB/ibtool dictionary parser calls the classifier without a class chain](https://github.com/morluto/rea/blob/1635eadfcc49bbb80178838ab74667c2981de383/src/domain/apple/interfaceBuilderKeyedArchive.ts#L93-L117). The [NIB producer supplies class names and empty class metadata](https://github.com/morluto/rea/blob/1635eadfcc49bbb80178838ab74667c2981de383/src/artifacts/apple/InterfaceBuilderAnalysis.ts#L718-L727). The downstream [UI-object lookup excludes resource nodes](https://github.com/morluto/rea/blob/1635eadfcc49bbb80178838ab74667c2981de383/src/application/native/NativeUiActionTrace.ts#L344-L355). ### Analysis or suspected cause Source-derived: the fallback uses `includes("image")`, `includes("color")`, or `includes("font")` before considering UI class roles. NIBArchive projection emits an ibtool-shaped object dictionary, which does not carry the superclass chain consumed by the newer keyed-archive classification path. Thus the keyed-archive ancestry improvement on main does not change this NIBArchive result. ### Additional context Open and closed searches used `UIImageView`, `NSImageView`, `image view`, `image views`, `classified as resource`, `NSColorWell`, and broader Interface Builder classification/resource terms. Related issues [#2176](https://github.com/morluto/rea/issues/2176) and [#5635](https://github.com/morluto/rea/issues/5635) concern missing resource-key joins, not UI objects being assigned resource kinds. No matching report was found. No repair or independent review was performed. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Two production-boundary executions return wrong UI node kinds for the generated valid NIBArchive while retaining the underlying class identities. Wrong graph semantics are consequential; real-app frequency and downstream trace execution remain unmeasured. - Workaround status: available - Workaround basis: The same observed results retain each exact framework class name and hierarchy, allowing consumers to identify these known UI classes manually without changing the artifact. This restores graph interpretation for the demonstrated inputs; it does not establish automatic trace recovery.
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#3936
No description provided.