[GH-ISSUE #7426] [Bug] NSTextFieldCell ownership falsely marks compiled nib hierarchy incomplete #4434

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

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

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

node scripts/rea.mjs decode-interface-builder "$WORK/text.app" --format json returns hierarchy_complete: false, hierarchy coverage partial with two omissions, and graph.truncated: true for a small real compiled AppKit nib containing one text field. The window, content view and text field are all present with their visual hierarchy links. The reported unknown parentage concerns the standard non-view NSTextFieldCell and its owning text field.

A button fixture with the same window/content structure reports complete hierarchy, zero omissions and graph.truncated: false. Each fixture was decoded twice with the same result; all compiler, source-oracle and CLI invocations exited 0.

Reproduction

Run from a clean checkout at the source revision below, with its locked dependencies and built runtime already available. Requires macOS, Xcode command-line tools, Python 3 and Node.js 24 on PATH. The script generates both XIBs, compiles actual NIBArchives, and inspects the resource-only bundles without launching either app. HOME, XDG directories and TMPDIR for the compiler and REA are isolated under $WORK.

set -eu
WORK="$(mktemp -d)"
export WORK
python3 - <<'PYTHON'
from pathlib import Path
import os, subprocess, json, platform
root=Path(os.environ['WORK'])
checkout=Path.cwd()
env={'PATH':os.environ['PATH'],'REA_LOG_LEVEL':'silent'}
for key, folder in [('HOME','home'),('XDG_CONFIG_HOME','config'),('XDG_DATA_HOME','data'),('XDG_CACHE_HOME','cache'),('XDG_STATE_HOME','state'),('XDG_RUNTIME_DIR','runtime'),('TMPDIR','temp')]:
    p=root/folder;p.mkdir(exist_ok=True);env[key]=str(p)
print('Node',subprocess.check_output(['node','--version'],env=env,text=True).strip())
print('REA',subprocess.check_output(['node','scripts/rea.mjs','--version'],cwd=checkout,env=env,text=True).strip())
print('OS',subprocess.check_output(['sw_vers','-productVersion'],text=True).strip(),platform.machine())
controls={
 'text':'''<textField id="control"><rect key="frame" x="20" y="60" width="120" height="22"/><textFieldCell key="cell" title="Sample" id="cell"><font key="font" metaFont="system"/><color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/><color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/></textFieldCell></textField>''',
 'button':'''<button id="control"><rect key="frame" x="20" y="60" width="120" height="22"/><buttonCell key="cell" type="push" title="Sample" bezelStyle="rounded" id="cell"><font key="font" metaFont="system"/></buttonCell></button>'''
}
for label,control in controls.items():
    app=root/(label+'.app');resources=app/'Contents/Resources';resources.mkdir(parents=True,exist_ok=True)
    (app/'Contents/Info.plist').write_text('''<?xml version="1.0" encoding="UTF-8"?><plist version="1.0"><dict><key>CFBundleExecutable</key><string>Sample</string><key>CFBundleIdentifier</key><string>org.example.sample</string><key>CFBundlePackageType</key><string>APPL</string></dict></plist>''')
    xib=root/(label+'.xib')
    xib.write_text('''<?xml version="1.0" encoding="UTF-8"?><document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"><dependencies><deployment identifier="macosx"/><plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22505"/></dependencies><objects><customObject id="-2" userLabel="File's Owner" customClass="NSWindowController"/><customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/><customObject id="-3" userLabel="Application" customClass="NSObject"/><window title="Sample" id="window"><windowStyleMask key="styleMask" titled="YES" closable="YES"/><rect key="contentRect" x="100" y="100" width="240" height="120"/><rect key="screenRect" x="0" y="0" width="1920" height="1080"/><view key="contentView" id="content"><rect key="frame" x="0" y="0" width="240" height="120"/><subviews>'''+control+'''</subviews></view></window></objects></document>''')
    result=subprocess.run(['xcrun','ibtool','--compile',str(resources/'Main.nib'),str(xib)],env=env,capture_output=True,text=True)
    (root/(label+'-compile.log')).write_text(result.stdout+result.stderr);print(label,'compile exit',result.returncode);result.check_returncode()
    oracle=subprocess.run(['xcrun','ibtool','--objects','--hierarchy','--output-format','xml1',str(xib)],env=env,capture_output=True,text=True)
    (root/(label+'-oracle.plist')).write_text(oracle.stdout);print(label,'oracle exit',oracle.returncode);oracle.check_returncode()
    for n in range(2):
        result=subprocess.run(['node','scripts/rea.mjs','decode-interface-builder',str(app),'--format','json'],cwd=checkout,env=env,capture_output=True,text=True)
        (root/f'{label}-{n}.json').write_text(result.stdout);(root/f'{label}-{n}.stderr').write_text(result.stderr)
        print(label,n,'CLI exit',result.returncode);result.check_returncode()
        obj=json.loads(result.stdout);r=obj.get('normalized_result',obj)
        print(json.dumps({'label':label,'run':n,'hierarchy_complete':r['documents'][0]['hierarchy_complete'],'coverage':[f for f in r['graph']['coverage'] if f['facet'].startswith('hierarchy:') or f['facet'] in ('archive_decode','archive_object_decode')],'truncated':r['graph']['truncated']},indent=2))
PYTHON

Expected behavior

Retain standard non-view cell ownership as raw facts without counting it as an unplaced visual view. For this fixture, the decoded window/content/text-field hierarchy should have complete coverage, hierarchy_complete: true, and no cell-registry parentage limitation. This expectation does not require adding a visual containment edge for the cell or inferring the ancestry of arbitrary custom classes.

Expected source: docs/native-investigation.md:701 documents recognized hierarchy relationships and states that known non-view cell ownership/control-view backreferences remain raw facts. Expected source: tests/acceptance/applications/interfaceBuilderNibRelationships.test.ts:159 tests this behavior using NSButtonCell. The installed AppKit SDK declares NSTextFieldCell : NSActionCell : NSCell : NSObject, the same non-view inheritance family as NSButtonCell.

Impact and scope

P2: this is a bounded, repeatable correctness defect in completeness and truncation reporting for a supported compiled AppKit input. It falsely tells callers that the recovered view hierarchy is incomplete, despite preserving the demonstrated view links. No object parsing failure, missing text-field view link, target execution or security impact was observed.

A manual inspection workaround is available for this fixture: the returned graph preserves the exact cell/control class names, the control's NSCell reference, the cell's reciprocal NSControlView reference, and the window/content/control links. These facts allow inspection of the demonstrated structure without changing the archive; they do not correct the automated coverage flags. Consumers that require an accurate complete/partial signal have no verified automatic workaround.

Only NSTextFieldCell and the NSButtonCell control were independently executed. Other cell types, real-app frequency, MCP behavior and older versions are NOT_ENOUGH_INFO; they were not tested.

REA version

Exact node scripts/rea.mjs --version output: 6.8.0.
Source commit: f8f7ac96de8a66a4ab9a23d87e04779bfcd11045.

Runtime and platform

Node.js v24.22.1; macOS 27.0.1; arm64. Actual xcrun ibtool compilation and source-side object/hierarchy inspection were used. No deep-analysis provider or application GUI was used.

Target and artifact context

Two synthetic resource-only .app bundles, each with a readable Info.plist and one Contents/Resources/Main.nib compiled from the generated MacOSX.Cocoa XIB. One contains an NSTextField/NSTextFieldCell; the control contains an NSButton/NSButtonCell. No executable is required for this supported resource-bundle entry point.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

The executed CLI results, on both runs per fixture:

Field Text field Button control
hierarchy_complete false true
Hierarchy status/reason partial / serialized_view_hierarchy_incomplete complete / null
Hierarchy examined/omitted 4 / 2 4 / 0
archive_decode partial / serialized_archive_content_unexamined complete / null
archive_object_decode complete complete
graph.truncated true false

The text-field result reports: NSIBObjectData object 1: NSObjectsKeys/NSObjectsValues pair child 16 with registry value 13; NSRoot is 2. This decoded registry association does not establish visual parentage.

Inspection of those actual compiled bytes with the existing decoder identifies object 16 as NSTextFieldCell with NSControlView -> 13, object 13 as NSTextField with NSCell -> 16, and the ordered registry pair 16 -> 13. The public graph preserves both ownership references and NSWindowTemplate -> NSView -> NSTextField. The source-side ibtool oracle also identifies the text field and cell; its ownership-inclusive hierarchy is not being treated as proof that an AppKit cell is a visual view.

As an additional diagnostic, changing only the decoded cell's class name in memory to NSButtonCell, leaving every field and registry record unchanged, makes the existing hierarchy projector return zero omissions and no registry diagnostic with the identical view tree. This was a projection diagnostic, not a modified nib, a native object of that substituted class, or a tested repair.

Failure source: src/artifacts/apple/NibViewHierarchy.ts:28 recognizes exactly NSCell, NSActionCell and NSButtonCell. Failure source: src/artifacts/apple/NibViewHierarchy.ts:108 counts NSTextFieldCell.NSControlView as an unrecognized view-shaped field; src/artifacts/apple/NibViewHierarchy.ts:168 also admits the cell registry association as unknown parentage. The two paths account for the observed two omissions. Failure source: src/artifacts/apple/InterfaceBuilderAnalysis.ts:237 propagates those omissions and unexamined hierarchy sources into the public result.

Typed outcome or recovery expected

Decode the unchanged text-field fixture with complete hierarchy coverage and zero hierarchy omissions while retaining its object inventory, reciprocal ownership facts and window/content/control links. Preserve partial coverage for genuinely unknown custom-cell semantics or actual missing/limited hierarchy records. No implementation or restoration was tested.

Additional context

Open/closed searches covered nib hierarchy, cell parentage, NSControlView, NSTextFieldCell and NSButtonCell. Related #781 concerns missing explicit view links; #5693 concerns omitted custom/registry structure reported as complete; #5540 concerns reasons under explicit low object budgets; #6545 and #5720 concern graph presentation kinds; #6849 and #6850 concern keyed-archive identities/roots. Their failures differ from falsely qualifying known non-view cell ownership as incomplete visual hierarchy. The NSTextFieldCell mention in localization feature #4309 is unrelated.

The responsible hierarchy source is byte-identical on sampled main commit a116f2a7ae8e7ae18b6d0a24211abdb2b920a7db; this was a source freshness comparison, not a runtime rerun on that revision. The report's runtime evidence is from the baseline above. No earlier known-good version was established.

Originally created by @coygeek on GitHub (Oct 27, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7426 ## 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 `node scripts/rea.mjs decode-interface-builder "$WORK/text.app" --format json` returns `hierarchy_complete: false`, hierarchy coverage `partial` with two omissions, and `graph.truncated: true` for a small real compiled AppKit nib containing one text field. The window, content view and text field are all present with their visual hierarchy links. The reported unknown parentage concerns the standard non-view `NSTextFieldCell` and its owning text field. A button fixture with the same window/content structure reports complete hierarchy, zero omissions and `graph.truncated: false`. Each fixture was decoded twice with the same result; all compiler, source-oracle and CLI invocations exited 0. ## Reproduction Run from a clean checkout at the source revision below, with its locked dependencies and built runtime already available. Requires macOS, Xcode command-line tools, Python 3 and Node.js 24 on PATH. The script generates both XIBs, compiles actual NIBArchives, and inspects the resource-only bundles without launching either app. HOME, XDG directories and TMPDIR for the compiler and REA are isolated under `$WORK`. ```sh set -eu WORK="$(mktemp -d)" export WORK python3 - <<'PYTHON' from pathlib import Path import os, subprocess, json, platform root=Path(os.environ['WORK']) checkout=Path.cwd() env={'PATH':os.environ['PATH'],'REA_LOG_LEVEL':'silent'} for key, folder in [('HOME','home'),('XDG_CONFIG_HOME','config'),('XDG_DATA_HOME','data'),('XDG_CACHE_HOME','cache'),('XDG_STATE_HOME','state'),('XDG_RUNTIME_DIR','runtime'),('TMPDIR','temp')]: p=root/folder;p.mkdir(exist_ok=True);env[key]=str(p) print('Node',subprocess.check_output(['node','--version'],env=env,text=True).strip()) print('REA',subprocess.check_output(['node','scripts/rea.mjs','--version'],cwd=checkout,env=env,text=True).strip()) print('OS',subprocess.check_output(['sw_vers','-productVersion'],text=True).strip(),platform.machine()) controls={ 'text':'''<textField id="control"><rect key="frame" x="20" y="60" width="120" height="22"/><textFieldCell key="cell" title="Sample" id="cell"><font key="font" metaFont="system"/><color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/><color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/></textFieldCell></textField>''', 'button':'''<button id="control"><rect key="frame" x="20" y="60" width="120" height="22"/><buttonCell key="cell" type="push" title="Sample" bezelStyle="rounded" id="cell"><font key="font" metaFont="system"/></buttonCell></button>''' } for label,control in controls.items(): app=root/(label+'.app');resources=app/'Contents/Resources';resources.mkdir(parents=True,exist_ok=True) (app/'Contents/Info.plist').write_text('''<?xml version="1.0" encoding="UTF-8"?><plist version="1.0"><dict><key>CFBundleExecutable</key><string>Sample</string><key>CFBundleIdentifier</key><string>org.example.sample</string><key>CFBundlePackageType</key><string>APPL</string></dict></plist>''') xib=root/(label+'.xib') xib.write_text('''<?xml version="1.0" encoding="UTF-8"?><document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="22505" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES"><dependencies><deployment identifier="macosx"/><plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="22505"/></dependencies><objects><customObject id="-2" userLabel="File's Owner" customClass="NSWindowController"/><customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/><customObject id="-3" userLabel="Application" customClass="NSObject"/><window title="Sample" id="window"><windowStyleMask key="styleMask" titled="YES" closable="YES"/><rect key="contentRect" x="100" y="100" width="240" height="120"/><rect key="screenRect" x="0" y="0" width="1920" height="1080"/><view key="contentView" id="content"><rect key="frame" x="0" y="0" width="240" height="120"/><subviews>'''+control+'''</subviews></view></window></objects></document>''') result=subprocess.run(['xcrun','ibtool','--compile',str(resources/'Main.nib'),str(xib)],env=env,capture_output=True,text=True) (root/(label+'-compile.log')).write_text(result.stdout+result.stderr);print(label,'compile exit',result.returncode);result.check_returncode() oracle=subprocess.run(['xcrun','ibtool','--objects','--hierarchy','--output-format','xml1',str(xib)],env=env,capture_output=True,text=True) (root/(label+'-oracle.plist')).write_text(oracle.stdout);print(label,'oracle exit',oracle.returncode);oracle.check_returncode() for n in range(2): result=subprocess.run(['node','scripts/rea.mjs','decode-interface-builder',str(app),'--format','json'],cwd=checkout,env=env,capture_output=True,text=True) (root/f'{label}-{n}.json').write_text(result.stdout);(root/f'{label}-{n}.stderr').write_text(result.stderr) print(label,n,'CLI exit',result.returncode);result.check_returncode() obj=json.loads(result.stdout);r=obj.get('normalized_result',obj) print(json.dumps({'label':label,'run':n,'hierarchy_complete':r['documents'][0]['hierarchy_complete'],'coverage':[f for f in r['graph']['coverage'] if f['facet'].startswith('hierarchy:') or f['facet'] in ('archive_decode','archive_object_decode')],'truncated':r['graph']['truncated']},indent=2)) PYTHON ``` ## Expected behavior Retain standard non-view cell ownership as raw facts without counting it as an unplaced visual view. For this fixture, the decoded window/content/text-field hierarchy should have complete coverage, `hierarchy_complete: true`, and no cell-registry parentage limitation. This expectation does not require adding a visual containment edge for the cell or inferring the ancestry of arbitrary custom classes. Expected source: `docs/native-investigation.md:701` documents recognized hierarchy relationships and states that known non-view cell ownership/control-view backreferences remain raw facts. Expected source: `tests/acceptance/applications/interfaceBuilderNibRelationships.test.ts:159` tests this behavior using NSButtonCell. The installed AppKit SDK declares `NSTextFieldCell : NSActionCell : NSCell : NSObject`, the same non-view inheritance family as NSButtonCell. ## Impact and scope P2: this is a bounded, repeatable correctness defect in completeness and truncation reporting for a supported compiled AppKit input. It falsely tells callers that the recovered view hierarchy is incomplete, despite preserving the demonstrated view links. No object parsing failure, missing text-field view link, target execution or security impact was observed. A manual inspection workaround is available for this fixture: the returned graph preserves the exact cell/control class names, the control's `NSCell` reference, the cell's reciprocal `NSControlView` reference, and the window/content/control links. These facts allow inspection of the demonstrated structure without changing the archive; they do not correct the automated coverage flags. Consumers that require an accurate complete/partial signal have no verified automatic workaround. Only NSTextFieldCell and the NSButtonCell control were independently executed. Other cell types, real-app frequency, MCP behavior and older versions are NOT_ENOUGH_INFO; they were not tested. ## REA version Exact `node scripts/rea.mjs --version` output: `6.8.0`. Source commit: `f8f7ac96de8a66a4ab9a23d87e04779bfcd11045`. ## Runtime and platform Node.js v24.22.1; macOS 27.0.1; arm64. Actual `xcrun ibtool` compilation and source-side object/hierarchy inspection were used. No deep-analysis provider or application GUI was used. ## Target and artifact context Two synthetic resource-only `.app` bundles, each with a readable Info.plist and one `Contents/Resources/Main.nib` compiled from the generated MacOSX.Cocoa XIB. One contains an NSTextField/NSTextFieldCell; the control contains an NSButton/NSButtonCell. No executable is required for this supported resource-bundle entry point. ## Interface CLI ## Earliest failure stage Native analysis or reconstruction ## Relevant output or evidence The executed CLI results, on both runs per fixture: | Field | Text field | Button control | | --- | --- | --- | | `hierarchy_complete` | false | true | | Hierarchy status/reason | partial / serialized_view_hierarchy_incomplete | complete / null | | Hierarchy examined/omitted | 4 / 2 | 4 / 0 | | `archive_decode` | partial / serialized_archive_content_unexamined | complete / null | | `archive_object_decode` | complete | complete | | `graph.truncated` | true | false | The text-field result reports: `NSIBObjectData object 1: NSObjectsKeys/NSObjectsValues pair child 16 with registry value 13; NSRoot is 2. This decoded registry association does not establish visual parentage.` Inspection of those actual compiled bytes with the existing decoder identifies object 16 as NSTextFieldCell with `NSControlView -> 13`, object 13 as NSTextField with `NSCell -> 16`, and the ordered registry pair `16 -> 13`. The public graph preserves both ownership references and `NSWindowTemplate -> NSView -> NSTextField`. The source-side ibtool oracle also identifies the text field and cell; its ownership-inclusive hierarchy is not being treated as proof that an AppKit cell is a visual view. As an additional diagnostic, changing only the decoded cell's class name in memory to NSButtonCell, leaving every field and registry record unchanged, makes the existing hierarchy projector return zero omissions and no registry diagnostic with the identical view tree. This was a projection diagnostic, not a modified nib, a native object of that substituted class, or a tested repair. Failure source: `src/artifacts/apple/NibViewHierarchy.ts:28` recognizes exactly NSCell, NSActionCell and NSButtonCell. Failure source: `src/artifacts/apple/NibViewHierarchy.ts:108` counts NSTextFieldCell.NSControlView as an unrecognized view-shaped field; `src/artifacts/apple/NibViewHierarchy.ts:168` also admits the cell registry association as unknown parentage. The two paths account for the observed two omissions. Failure source: `src/artifacts/apple/InterfaceBuilderAnalysis.ts:237` propagates those omissions and unexamined hierarchy sources into the public result. ## Typed outcome or recovery expected Decode the unchanged text-field fixture with complete hierarchy coverage and zero hierarchy omissions while retaining its object inventory, reciprocal ownership facts and window/content/control links. Preserve partial coverage for genuinely unknown custom-cell semantics or actual missing/limited hierarchy records. No implementation or restoration was tested. ## Additional context Open/closed searches covered nib hierarchy, cell parentage, NSControlView, NSTextFieldCell and NSButtonCell. Related #781 concerns missing explicit view links; #5693 concerns omitted custom/registry structure reported as complete; #5540 concerns reasons under explicit low object budgets; #6545 and #5720 concern graph presentation kinds; #6849 and #6850 concern keyed-archive identities/roots. Their failures differ from falsely qualifying known non-view cell ownership as incomplete visual hierarchy. The NSTextFieldCell mention in localization feature #4309 is unrelated. The responsible hierarchy source is byte-identical on sampled main commit `a116f2a7ae8e7ae18b6d0a24211abdb2b920a7db`; this was a source freshness comparison, not a runtime rerun on that revision. The report's runtime evidence is from the baseline above. No earlier known-good version was established.
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#4434
No description provided.