* perf(setup): reuse initial diagnostics during planning
* test(setup): verify post-configuration doctor refresh
* style(setup): remove extra test spacing
A file such as Versions/.DS_Store beside the only real version made the
framework look multi-versioned, so its Info.plist became unknown with a
misleading limitation. Version directories are now limited to entries
observed as directories or implied by deeper paths.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(artifacts): accept the bundle subject when extracting an app bundle
For a .app target the session target is the bundle's executable, while the
artifact provider reports the bundle itself as the subject. The subject
check exempted inventory_artifact and inspect_artifact but not
extract_artifact, so extracting any .app failed with execution_failure after
the output tree had already been written, and the output path was never
reported.
* refactor(binary): rename artifact bundle subject helper
isArtifactInventorySubject also covers extract_artifact for .app
bundles, so name it for the shared bundle-subject exemption.
---------
Co-authored-by: Cursor Agent <[email protected]>
* fix(javascript): resolve bare source-map and worker URLs next to the script
A sourceMappingURL like `main.js.map` (the default of webpack, esbuild,
rollup, Vite and rolldown) and `new Worker("worker.js")` were resolved as
module specifiers, so the bare name was looked up as an npm package and the
map or worker script was never linked. Resolve them as URLs relative to the
declaring script.
* fix(javascript): tighten url-reference resolution edge cases
Use looksExternal for non-module contexts so protocol-relative URLs
cannot be misread as root-absolute paths, cover url-reference unit
cases, and assert bare service-worker script resolution.
---------
Co-authored-by: Cursor Agent <[email protected]>
A ZIP or DMG holding two applications produced bridge candidates from
scripts in one app to native code in the other. Candidates now pair only
components owned by the same application root; components outside every
root pair only with each other.
A versioned framework with several real version directories now keeps
its Info.plist unknown even when only one version has a plist, because
Versions/Current, which is not inventoried, decides which applies.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Directory, ZIP, and DMG inventories can hold installers, disk-image
extras, and AppleDouble sidecars beside the app. Those components were
classified as the application's frameworks, libraries, JavaScript, and
metadata, and fed runtime families and bridge candidates. For these
formats, components outside every application root are now left out,
with a limitation that counts them. IPA projection is unchanged.
A selected directory whose name ends in .app is now its own root by
identity, so an empty or partial inventory still reports it. iOS is
claimed only for shallow roots that have an Info.plist, so an empty root
reports no platform.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
The inventory snapshot captures reader provenance before close() runs,
so a `hdiutil info -plist` entry recorded during cleanup never reached
CLI or MCP results. Detach entries already share that timing. Record
neither instead of implying the query is observable.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(artifacts): read AppKit connectors from flat keyed-archive NIBs
Flat AppKit NIBs archive NSNibOutletConnector and NSNibControlConnector
endpoints and the outlet or selector name under NSSource, NSDestination,
and NSLabel. The keyed-archive parser read only source, destination,
and label, so it skipped every such connector: XLD.app's 64 decoded NIBs
had no outlet or target-action edges. The NS-prefixed keys now follow the
same source and destination convention as compiled NIBArchive documents.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(artifacts): read AppKit control connectors from the control to its target
NSNibControlConnector archives NSSource as the sending control and
NSDestination as the action target. The previous commit inverted them
like the unprefixed fields, so every flat AppKit action ran from the
target to the control. The action trace selects a control and follows
control -> action, and the graph keys the selector by its target, so
neither could resolve these actions. The NS-prefixed keys are now read
as source and destination for outlets and actions alike.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(artifacts): key AppKit connector endpoints by their node identity
An NSSource or NSDestination object with an identifier field was
referenced by that identifier, but its node is keyed by its authored
object ID or archive UID. The connector then pointed at a synthetic
unknown node. NS-prefixed endpoints now use the referenced object's
node identity.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
project_apple_application_graph accepted only IPA inventories. It now
also projects macOS .app directories and ZIP or DMG inventories that
contain applications. It reports application roots, platforms, and
every nested bundle with its path-convention role, layout, parent,
Info.plist path, executable candidates, and signing paths:
- roles: app extensions, XPC services (including inside frameworks),
frameworks, login items, system and driver extensions, plug-ins,
resource bundles, and helper or nested applications;
- privileged helpers in Contents/Library/LaunchServices and launchd
plists in Contents/Library/LaunchAgents or LaunchDaemons;
- direct files of Contents/Helpers;
- symlinks by path. Their targets are not inventoried, so versioned
frameworks are described through their real Versions/<version>/
directories.
AppleDouble sidecars (._* and __MACOSX/) are excluded from roles. A DMG
whose contents could not be inventoried is reported as partial. Other
inventory formats remain input errors. The projection still reads no
plist or CMS semantics; it points callers at inspect_plist.
The result schema is additive. projection_id now covers the new fields,
so IPA projection IDs differ from earlier versions.
verify:macos-bundle compiles a source-owned app with clang (CLT only).
It packs it as a directory, a ditto ZIP, and an APFS DMG, and checks
identical anatomy through the CLI with stdio MCP parity. It runs in the
macOS CI job.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An APFS disk image attaches as an image disk plus a synthesized APFS
container, and hdiutil reports both as whole disks. Detaching the
container also ejects the image disk, so the reverse-order detach then
failed with "No such file or directory" and every APFS DMG inventory
ended in "DMG detach or mount-root cleanup failed". APFS is the default
format of `hdiutil create` on current macOS.
When a detach fails, ask `hdiutil info -plist` whether the device is
still attached. A device that is gone counts as detached; one that is
still listed, or whose state cannot be read, remains a cleanup failure.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* fix(artifacts): project compiled AppKit actions from the control to its target
NSNibControlConnector archives the sending control as NSSource and the
action target as NSDestination; a nil target is the first responder. The
NIBArchive projection swapped them for control connectors, so every
compiled AppKit action ran from its target to the control. The action
trace, which follows control -> action, could not find them, and the
selector node was keyed by the control instead of the target class. When
the target was nil, the swapped source was null and the action was
dropped: Chess lost 29 menu actions, KeepingYouAwake 7.
Connectors now keep NSSource -> NSDestination. The NIBArchive test
encoder moves to a fixture module so a bundle test can build compiled
NIBs.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(artifacts): report observed action routes when a compiled route is missing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(artifacts): report compiled connector records when a storyboard route is missing
Co-Authored-By: Claude Opus 5.5 <[email protected]>
* test(artifacts): write the storyboard fixture's action in Cocoa syntax
MacFixture.storyboard declared its button action with the iOS attribute,
<action selector="buildTapped:" destination="vc1">. Cocoa documents
name an action's receiver with target=. ibtool compiled the iOS spelling
into an NSNibControlConnector whose NSSource is the view controller
placeholder and whose NSDestination is the button. That is the reverse
of what Xcode-authored AppKit NIBs contain (Music, Chess,
KeepingYouAwake, and Transmission all archive NSSource as the control),
and it is what the old inversion had matched. With target= the compiled
fixture archives the button as NSSource. The temporary connector dump is
removed; the observed routes remain in the assertion message.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
Compiled UIKit NIBs and storyboards archive outlets and control actions
as UIRuntimeOutletConnection and UIRuntimeEventConnection with
UISource, UIDestination, and UILabel. The NIBArchive projection
recognized only AppKit *Connector classes with NS-prefixed keys, so an
iOS app produced no outlet or action edges: Stash's 64 decoded documents
yielded none. These connections are now projected with the same
source -> destination roles, an event connection carries its
UIEventMask as ui_event_mask, UIClassSwapper reports its UIClassName,
UIProxyObject is a placeholder, and connection records are no longer
emitted as unknown objects.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
- Carry the Info.plist that declared the program file on the target as
bundleInfoPlist, so inspect_plist defaults to it even when the program
file is a symlink into another bundle directory.
- Accept a readable Info.plist symlink, as reading the plist directly did.
- Treat only a missing path as an absent layout or wrapper; a permission
denial now reaches the resolver's permission-denied classification.
Co-Authored-By: Claude Opus 5.5 <[email protected]>