[GH-ISSUE #5695] [Bug] Compiled storyboard segues lack connections while connection coverage reports complete #3447

Closed
opened 2026-10-31 00:40:35 +00:00 by bsakaguchi · 1 comment
Owner

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

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_dir/Demo.app" --json exits 0 but omits an authored show segue from the connection graph. The source scene reports complete connection coverage with zero omissions. The UIStoryboardShowSegueTemplate survives as an unknown node with its identifier, destination controller identifier and action name; the missing result is its semantic connection or an explicit unsupported/omitted connection signal.

Reproduction

Run from a source checkout with locked dependencies installed and the CLI built using npm run build:cached. Requires macOS, Xcode with the iOS platform, a C compiler, Node.js and jq. This script was independently replayed at the baseline and replayed again against a fresh build of current main. Both returned the result below. The fixture app is inspected statically and never launched.

work_dir="$(mktemp -d)"
mkdir -p "$work_dir/home" "$work_dir/cfg" "$work_dir/data" "$work_dir/cache" \
  "$work_dir/state" "$work_dir/tmp" "$work_dir/Demo.app"
cat > "$work_dir/Main.storyboard" <<'XML'
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="SRC-AA-001">
  <dependencies><plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/></dependencies>
  <scenes>
    <scene sceneID="SCN-AA-001"><objects>
      <viewController id="SRC-AA-001" sceneMemberID="viewController">
        <view key="view" id="VW1-AA-001"><rect key="frame" x="0" y="0" width="375" height="667"/></view>
        <connections><segue destination="DST-AA-002" kind="show" identifier="openDetail" id="SEG-AA-001"/></connections>
      </viewController>
      <placeholder placeholderIdentifier="IBFirstResponder" id="FR1-AA-001" sceneMemberID="firstResponder"/>
    </objects></scene>
    <scene sceneID="SCN-AA-002"><objects>
      <viewController id="DST-AA-002" sceneMemberID="viewController">
        <view key="view" id="VW2-AA-002"><rect key="frame" x="0" y="0" width="375" height="667"/></view>
      </viewController>
      <placeholder placeholderIdentifier="IBFirstResponder" id="FR2-AA-002" sceneMemberID="firstResponder"/>
    </objects></scene>
  </scenes>
</document>
XML
cat > "$work_dir/Demo.app/Info.plist" <<'PLIST'
<?xml version="1.0" encoding="UTF-8"?>
<plist version="1.0"><dict><key>CFBundleExecutable</key><string>Demo</string><key>CFBundleIdentifier</key><string>dev.rea.demo</string></dict></plist>
PLIST
printf 'int main(void){return 0;}\n' > "$work_dir/demo.c"
cc "$work_dir/demo.c" -o "$work_dir/Demo.app/Demo"
xcrun ibtool --errors --warnings --compile "$work_dir/Demo.app/Main.storyboardc" \
  "$work_dir/Main.storyboard" --target-device iphone \
  --minimum-deployment-target 17.0 --module Demo
xcrun ibtool --connections --hierarchy --output-format xml1 \
  "$work_dir/Main.storyboard" > "$work_dir/source.plist"
plutil -p "$work_dir/source.plist"
env -i PATH="$PATH" HOME="$work_dir/home" XDG_CONFIG_HOME="$work_dir/cfg" \
  XDG_DATA_HOME="$work_dir/data" XDG_CACHE_HOME="$work_dir/cache" \
  XDG_STATE_HOME="$work_dir/state" TMPDIR="$work_dir/tmp" REA_LOG_LEVEL=silent \
  node scripts/rea.mjs decode-interface-builder "$work_dir/Demo.app" --json \
  > "$work_dir/result.json"
jq '{source_scene: [.normalized_result.documents[]
       | select(.relative_path == "Main.storyboardc/UIViewController-SRC-AA-001.nib")
       | {relative_path, connection_count}],
     source_connection_coverage: [.normalized_result.graph.coverage[]
       | select(.facet == "connections:Main.storyboardc/UIViewController-SRC-AA-001.nib")],
     segue_edges: [.normalized_result.graph.edges[]
       | select(.relation == "segue_to")],
     templates: [.normalized_result.graph.nodes[]
       | select(.attributes.class_name == "UIStoryboardShowSegueTemplate")
       | {kind, name, values: .attributes.nibValues}]}' "$work_dir/result.json"

Expected behavior

Represent the serialized segue as a connection, allowing an explicitly unresolved destination, or report it as unsupported/omitted with truthful connection coverage. Complete coverage with zero omissions must not silently exclude this connection. Full cross-scene destination resolution is not required to make coverage truthful.

The baseline's docs/native-investigation.md:383-396 documents connections from compiled storyboard resources and explicit unsupported archive forms. docs/tool-design.md:172-175 requires completeness to follow examined records and exhaustiveness. src/domain/apple/interfaceBuilderGraph.ts:418-437 already represents segue_to edges with unresolved-destination support. Apple's ibtool --connections independently identifies this source's show segue as a connection.

Impact and scope

P2: consumers receive an incorrect navigation graph for this compiled storyboard while connection coverage claims completeness. The verifier reproduced an original fixture and this reduced two-scene fixture; the submission replay confirms the reduced fixture on current main. Wider application prevalence and other segue kinds were not measured. No target data is modified.

A verified manual workaround for this fixture is to inspect the retained unknown template node's identifier, destination controller identifier, action name and scene evidence. This recovers the authored navigation facts for an analyst but does not restore the semantic graph edge or truthful coverage.

REA version

rea-agents 6.7.0; independently verified source baseline 9d8e7803326a382cbbb3ffdb900f5e93e5442d68. Reproduced after npm run build:cached on freshly fetched origin/main at 949966712e68d8857a50a165ecddf04c2d7e6d3b, also 6.7.0. The latest published release checked was 6.7.0; these are source-checkout reproductions.

Runtime and platform

Node.js v26.12.0; macOS 27.0.1; arm64. Xcode 27.1 build 27A9275; ibtool 27.1 bundle 25098. Node, OS and architecture were freshly checked during submission; Xcode/ibtool versions are from the verifier's environment record.

Target and artifact context

A locally compiled iPhone storyboard with one show segue, minimum deployment target 17.0, inside a disposable app bundle with a self-built Mach-O executable. HOME and XDG directories are isolated. No native analysis engine, external provider or target execution is needed.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

The source inventory from ibtool --connections contains SEG-AA-001, type IBCocoaTouchStoryboardShowSegue, source SRC-AA-001, destination DST-AA-002, label IBSegueTriggerManual. The compiled-resource CLI result instead contains:

{
  "source_connection_coverage": [{
    "facet": "connections:Main.storyboardc/UIViewController-SRC-AA-001.nib",
    "status": "complete", "reason": null, "examined": 3, "omitted": 0
  }],
  "segue_edges": [],
  "templates": [{
    "kind": "unknown", "name": "openDetail",
    "values": {
      "UIIdentifier": "openDetail",
      "UIDestinationViewControllerIdentifier": "UIViewController-DST-AA-002",
      "UIActionName": "showViewController:sender:"
    }
  }]
}

The three counted connections are internal outlets. The independent verifier also read the compiled NIBArchive table without REA's decoder: source controller object 8 owns UIStoryboardSegueTemplates, pointing to NSArray object 11 with exactly one member, template object 12. Repeated array-key loss is not needed to trigger this case.

The verifier's no-segue control returns no template, no segue edge and complete source-scene connection coverage with examined 2, omitted 0. The compiler omits the now-unreachable destination scene in that control; only the source scene is compared. The submission replay above independently confirms the positive fixture, not that control.

Typed outcome or recovery expected

Proposed restoration check: the original openDetail connection must appear as segue_to with a resolved or explicitly unresolved destination, or its unsupported/omitted form must be disclosed by connection coverage. No segue edge plus complete coverage with zero omissions fails this check. Preserve truthful complete coverage for the no-segue source-scene control without inventing a segue.

Analysis or suspected cause

Source-derived reasoning: the shared connection admission predicate in src/domain/apple/interfaceBuilderKeyedArchive.ts:42-46 admits runtime UIKit connection classes and AppKit connector classes, excluding UIStoryboardShowSegueTemplate. src/artifacts/apple/InterfaceBuilderAnalysis.ts:575-582 skips non-admitted objects before connection projection. src/artifacts/apple/NibArchive.ts:149-152 likewise excludes the template from its connector count. src/domain/apple/interfaceBuilderGraph.ts:504-540 then reports complete connection coverage because no unsupported/omitted segue record reaches it. These locators refer to the baseline; current main retains the admission/projection behavior. This explains the observed graph result without claiming the raw template data was lost.

Additional context

Prepared with AI assistance and independently verified. Related #5590 concerns omitted AppKit binding connectors, #5540 object-budget attribution, #5541 boolean polarity, and #5638 ordinary UID-shaped dictionaries. Their reports do not cover this UIKit segue-template trigger. Closed #3175 restored keyed UIKit event-action classification; its fix does not admit storyboard segue templates. Open PR #3502 changes ownerless AppKit annotation handling and separately notes repeated-key array loss; this fixture has a one-member template array and remains excluded by connection-class admission. Correcting array-member retention alone does not satisfy the restoration check here.

Separate reports from this run cover compiled AppKit top-level hierarchy omissions and outlet-collection destination resolution. Their closure checks concern hierarchy and outlet destinations; this report concerns segue connection admission and coverage.

NOT_ENOUGH_INFO: no last known good version or regression introduction was established. MCP transport and wider application coverage were not tested.

Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5695 ### 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_dir/Demo.app" --json` exits 0 but omits an authored show segue from the connection graph. The source scene reports complete connection coverage with zero omissions. The `UIStoryboardShowSegueTemplate` survives as an unknown node with its identifier, destination controller identifier and action name; the missing result is its semantic connection or an explicit unsupported/omitted connection signal. ### Reproduction Run from a source checkout with locked dependencies installed and the CLI built using `npm run build:cached`. Requires macOS, Xcode with the iOS platform, a C compiler, Node.js and jq. This script was independently replayed at the baseline and replayed again against a fresh build of current main. Both returned the result below. The fixture app is inspected statically and never launched. ```sh work_dir="$(mktemp -d)" mkdir -p "$work_dir/home" "$work_dir/cfg" "$work_dir/data" "$work_dir/cache" \ "$work_dir/state" "$work_dir/tmp" "$work_dir/Demo.app" cat > "$work_dir/Main.storyboard" <<'XML' <?xml version="1.0" encoding="UTF-8"?> <document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="SRC-AA-001"> <dependencies><plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/></dependencies> <scenes> <scene sceneID="SCN-AA-001"><objects> <viewController id="SRC-AA-001" sceneMemberID="viewController"> <view key="view" id="VW1-AA-001"><rect key="frame" x="0" y="0" width="375" height="667"/></view> <connections><segue destination="DST-AA-002" kind="show" identifier="openDetail" id="SEG-AA-001"/></connections> </viewController> <placeholder placeholderIdentifier="IBFirstResponder" id="FR1-AA-001" sceneMemberID="firstResponder"/> </objects></scene> <scene sceneID="SCN-AA-002"><objects> <viewController id="DST-AA-002" sceneMemberID="viewController"> <view key="view" id="VW2-AA-002"><rect key="frame" x="0" y="0" width="375" height="667"/></view> </viewController> <placeholder placeholderIdentifier="IBFirstResponder" id="FR2-AA-002" sceneMemberID="firstResponder"/> </objects></scene> </scenes> </document> XML cat > "$work_dir/Demo.app/Info.plist" <<'PLIST' <?xml version="1.0" encoding="UTF-8"?> <plist version="1.0"><dict><key>CFBundleExecutable</key><string>Demo</string><key>CFBundleIdentifier</key><string>dev.rea.demo</string></dict></plist> PLIST printf 'int main(void){return 0;}\n' > "$work_dir/demo.c" cc "$work_dir/demo.c" -o "$work_dir/Demo.app/Demo" xcrun ibtool --errors --warnings --compile "$work_dir/Demo.app/Main.storyboardc" \ "$work_dir/Main.storyboard" --target-device iphone \ --minimum-deployment-target 17.0 --module Demo xcrun ibtool --connections --hierarchy --output-format xml1 \ "$work_dir/Main.storyboard" > "$work_dir/source.plist" plutil -p "$work_dir/source.plist" env -i PATH="$PATH" HOME="$work_dir/home" XDG_CONFIG_HOME="$work_dir/cfg" \ XDG_DATA_HOME="$work_dir/data" XDG_CACHE_HOME="$work_dir/cache" \ XDG_STATE_HOME="$work_dir/state" TMPDIR="$work_dir/tmp" REA_LOG_LEVEL=silent \ node scripts/rea.mjs decode-interface-builder "$work_dir/Demo.app" --json \ > "$work_dir/result.json" jq '{source_scene: [.normalized_result.documents[] | select(.relative_path == "Main.storyboardc/UIViewController-SRC-AA-001.nib") | {relative_path, connection_count}], source_connection_coverage: [.normalized_result.graph.coverage[] | select(.facet == "connections:Main.storyboardc/UIViewController-SRC-AA-001.nib")], segue_edges: [.normalized_result.graph.edges[] | select(.relation == "segue_to")], templates: [.normalized_result.graph.nodes[] | select(.attributes.class_name == "UIStoryboardShowSegueTemplate") | {kind, name, values: .attributes.nibValues}]}' "$work_dir/result.json" ``` ### Expected behavior Represent the serialized segue as a connection, allowing an explicitly unresolved destination, or report it as unsupported/omitted with truthful connection coverage. Complete coverage with zero omissions must not silently exclude this connection. Full cross-scene destination resolution is not required to make coverage truthful. The baseline's `docs/native-investigation.md:383-396` documents connections from compiled storyboard resources and explicit unsupported archive forms. `docs/tool-design.md:172-175` requires completeness to follow examined records and exhaustiveness. `src/domain/apple/interfaceBuilderGraph.ts:418-437` already represents `segue_to` edges with unresolved-destination support. Apple's `ibtool --connections` independently identifies this source's show segue as a connection. ### Impact and scope P2: consumers receive an incorrect navigation graph for this compiled storyboard while connection coverage claims completeness. The verifier reproduced an original fixture and this reduced two-scene fixture; the submission replay confirms the reduced fixture on current main. Wider application prevalence and other segue kinds were not measured. No target data is modified. A verified manual workaround for this fixture is to inspect the retained unknown template node's identifier, destination controller identifier, action name and scene evidence. This recovers the authored navigation facts for an analyst but does not restore the semantic graph edge or truthful coverage. ### REA version `rea-agents` 6.7.0; independently verified source baseline `9d8e7803326a382cbbb3ffdb900f5e93e5442d68`. Reproduced after `npm run build:cached` on freshly fetched `origin/main` at `949966712e68d8857a50a165ecddf04c2d7e6d3b`, also 6.7.0. The latest published release checked was 6.7.0; these are source-checkout reproductions. ### Runtime and platform Node.js v26.12.0; macOS 27.0.1; arm64. Xcode 27.1 build 27A9275; ibtool 27.1 bundle 25098. Node, OS and architecture were freshly checked during submission; Xcode/ibtool versions are from the verifier's environment record. ### Target and artifact context A locally compiled iPhone storyboard with one show segue, minimum deployment target 17.0, inside a disposable app bundle with a self-built Mach-O executable. HOME and XDG directories are isolated. No native analysis engine, external provider or target execution is needed. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence The source inventory from `ibtool --connections` contains `SEG-AA-001`, type `IBCocoaTouchStoryboardShowSegue`, source `SRC-AA-001`, destination `DST-AA-002`, label `IBSegueTriggerManual`. The compiled-resource CLI result instead contains: ```json { "source_connection_coverage": [{ "facet": "connections:Main.storyboardc/UIViewController-SRC-AA-001.nib", "status": "complete", "reason": null, "examined": 3, "omitted": 0 }], "segue_edges": [], "templates": [{ "kind": "unknown", "name": "openDetail", "values": { "UIIdentifier": "openDetail", "UIDestinationViewControllerIdentifier": "UIViewController-DST-AA-002", "UIActionName": "showViewController:sender:" } }] } ``` The three counted connections are internal outlets. The independent verifier also read the compiled NIBArchive table without REA's decoder: source controller object 8 owns `UIStoryboardSegueTemplates`, pointing to NSArray object 11 with exactly one member, template object 12. Repeated array-key loss is not needed to trigger this case. The verifier's no-segue control returns no template, no segue edge and complete source-scene connection coverage with examined 2, omitted 0. The compiler omits the now-unreachable destination scene in that control; only the source scene is compared. The submission replay above independently confirms the positive fixture, not that control. ### Typed outcome or recovery expected Proposed restoration check: the original `openDetail` connection must appear as `segue_to` with a resolved or explicitly unresolved destination, or its unsupported/omitted form must be disclosed by connection coverage. No segue edge plus complete coverage with zero omissions fails this check. Preserve truthful complete coverage for the no-segue source-scene control without inventing a segue. ### Analysis or suspected cause Source-derived reasoning: the shared connection admission predicate in `src/domain/apple/interfaceBuilderKeyedArchive.ts:42-46` admits runtime UIKit connection classes and AppKit connector classes, excluding `UIStoryboardShowSegueTemplate`. `src/artifacts/apple/InterfaceBuilderAnalysis.ts:575-582` skips non-admitted objects before connection projection. `src/artifacts/apple/NibArchive.ts:149-152` likewise excludes the template from its connector count. `src/domain/apple/interfaceBuilderGraph.ts:504-540` then reports complete connection coverage because no unsupported/omitted segue record reaches it. These locators refer to the baseline; current main retains the admission/projection behavior. This explains the observed graph result without claiming the raw template data was lost. ### Additional context Prepared with AI assistance and independently verified. Related #5590 concerns omitted AppKit binding connectors, #5540 object-budget attribution, #5541 boolean polarity, and #5638 ordinary UID-shaped dictionaries. Their reports do not cover this UIKit segue-template trigger. Closed #3175 restored keyed UIKit event-action classification; its fix does not admit storyboard segue templates. Open PR #3502 changes ownerless AppKit annotation handling and separately notes repeated-key array loss; this fixture has a one-member template array and remains excluded by connection-class admission. Correcting array-member retention alone does not satisfy the restoration check here. Separate reports from this run cover [compiled AppKit top-level hierarchy omissions](https://github.com/morluto/rea/issues/5693) and [outlet-collection destination resolution](https://github.com/morluto/rea/issues/5692). Their closure checks concern hierarchy and outlet destinations; this report concerns segue connection admission and coverage. NOT_ENOUGH_INFO: no last known good version or regression introduction was established. MCP transport and wider application coverage were not tested.
Author
Owner

@matbrgz commented on GitHub (Oct 20, 2026):

Cross-referencing with canonical issue #5540 (decode-interface-builder misattributes object-limit omissions to the connection budget) and sibling NIB decoding reports #5541, #5590, #5692, #5693.

Consolidated under Cluster AX (Compiled Interface Builder / NIB Decoding & Object Limit Anomalies) in tracker #4330.

<!-- gh-comment-id:6304472336 --> @matbrgz commented on GitHub (Oct 20, 2026): Cross-referencing with canonical issue #5540 (`decode-interface-builder misattributes object-limit omissions to the connection budget`) and sibling NIB decoding reports #5541, #5590, #5692, #5693. Consolidated under **Cluster AX (Compiled Interface Builder / NIB Decoding & Object Limit Anomalies)** in tracker #4330.
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#3447
No description provided.