Commit Graph
49 Commits
Author SHA1 Message Date
morluto fd297b1b17 fix(apple): preserve bounded archive diagnostics (#7890)
* fix(apple): preserve bounded archive diagnostics

* test(apple): assert diagnostic facts without freezing prose
2026-10-29 07:36:29 +08:00
morluto 59a70adefd fix(artifacts): preserve source and coverage facts 2026-10-27 07:35:13 +08:00
morluto 3052a067a3 fix(apple): derive NIB hierarchy from observed relationships
Replace overlapping traversals with one bounded relationship owner. Preserve superview direction, treat conflicting or unsupported registry placement as unknown, and retain source facts and coverage.
2026-10-27 07:09:25 +08:00
morluto eb06e263cb fix(plist): own dictionary decoding across Apple workflows
Preserve XML, OpenStep and binary dictionary keys at their format owners. Share validated binary reference facts with materialization while retaining scalar precision, UID identity and bounded expansion. Migrate archive, Interface Builder, native and app-bundle consumers, then remove obsolete key-omission accounting and schema copies.

Validated source regressions and compiled CLI/MCP workflows, including native plist fidelity and archive identity. Typecheck, lint, docs and test discovery pass. No performance benchmarks were required.
2026-10-26 22:20:00 +08:00
morluto 662e79330f test(apple): author admitted archives and typed binary UID references 2026-10-24 08:22:14 +08:00
morluto d98105d650 test(apple): assert observed collection edge contract 2026-10-24 04:05:23 +08:00
morluto 8b7de17833 merge: reconcile upstream boundary and format fixes 2026-10-24 03:59:44 +08:00
morluto 584791fedf fix(apple): own ordered nib collections and registry parentage 2026-10-24 03:16:30 +08:00
morluto 79323128f2 fix(apple): qualify unexamined nib content coverage 2026-10-24 03:11:07 +08:00
morluto 1c57eed275 fix(apple): retain binary UID identity through archive projection 2026-10-24 03:11:07 +08:00
morluto e21b131f25 fix(apple): classify Interface Builder edges at their source
Pass hierarchy or connection identity from the producing call rather than
searching graph IDs containing caller-selected paths and authored IDs. Keep
connection omissions from making a retained hierarchy appear partial.

Replace mirrored ID-substring test oracles with exact endpoint relationships.
Cover marker-bearing paths and authored identities through domain, CLI/MCP,
summary and byte-retention boundaries.

Validation: affected source and compiled/public tests passed, including native
Foundation/ibtool fixture lanes; one Linux-only invalid-byte filename case
skipped on macOS. Static, docs and discovery checks passed.
2026-10-24 02:56:59 +08:00
morluto 2721650884 fix(native): bind signing facts and preserve native output semantics 2026-10-24 02:45:14 +08:00
morluto 878da39caf chore: reconcile current main boundary fixes 2026-10-24 02:06:18 +08:00
morluto 6fbe67d66e fix(apple)!: use archive UIDs for keyed nib identity
Use original object-table UIDs for inventory, hierarchy and connector references. Retain authored identity fields as data, refuse forged reference aliases and select UIKit top-level collections without substituting unrelated roots.

BREAKING CHANGE: Keyed archives that previously used authored objectID or object-id graph identities now use decimal table UIDs. Authored values remain attributes; ibtool and native NIBArchive identities retain their existing source semantics.

Fixes #6849
Fixes #6850
2026-10-24 02:00:09 +08:00
morluto 5e4004c413 fix(artifacts): preserve traversal coverage through bundle workflows
Retain producer traversal uncertainty through inventory, extraction, Interface Builder decoding and executable-root discovery. Keep known copied files and decoded facts with honest coverage; share omission normalization and bound omitted archive paths with their source reasons.

Fixes #6852
Fixes #6825
Fixes #6582
2026-10-24 02:00:09 +08:00
morluto 78f8eca531 fix(apple): preserve bundle discovery coverage and decode failures 2026-10-24 01:47:31 +08:00
morluto 7a93cfd118 test(apple): retain NIBArchive limitation path 2026-10-23 21:28:11 +08:00
morluto fabb68bb9b test(apple): isolate NIBArchive real projection group 2026-10-23 21:28:11 +08:00
morluto 5e16e30c6b test(apple): assert projected interface builder facts 2026-10-23 21:28:10 +08:00
morluto 43e7917ec2 fix(apple): preserve NIBArchive special real values
Fixes #6583
2026-10-23 21:28:10 +08:00
Dragoloneandmorluto 740f107ff9 fix(apple): stop counting single-object NIB connectors as ownerless (#3502)
* fix(apple): stop counting single-object NIB connectors as ownerless

The NIBArchive projection counts every object whose class name contains
"Connector" and has no resolvable NSSource as an ownerless connection
(#2484). AppKit also archives connector classes that annotate one object
and never encode NSSource: tooltips (NSIBHelpConnector), accessibility
attributes (NSNibAXAttributeConnector) and user-defined runtime
attributes (NSIBUserDefinedRuntimeAttributesConnector). Each of them
marked the document's connection coverage partial with
connection_unresolved, raised connection_count, set graph.truncated and
added the "declare no resolvable owner" limitation, although no
connection was dropped. The keyed-archive producer does not treat these
classes as connections.

Skip AppKit connectors that encode no NSSource key. A connector whose
NSSource is encoded but nil or malformed, and every UIKit runtime
connection, is still counted as ownerless.

Across 3924 NIBArchive files shipped with macOS system apps and Xcode,
688 reported this false partial coverage (for example Preview's
PaperKitWindowController.nib, whose only other connector is one outlet);
none do after the change.

* test(apple): keep unusable AppKit connector sources counted as ownerless

Only an absent NSSource marks a single-object annotation. Cover an AppKit outlet connector whose NSSource is nil or a non-reference value, which must still be reported as an unresolved, omitted connection.

* test(apple): split unusable connector source cases into their own block

Keep the connector describe block within the max-lines-per-function limit.

* fix(apple): keep unknown NIB connectors unresolved

* fix(apple): preserve unknown AppKit NIB connectors

* fix(apple): share annotation connector semantics

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-23 02:33:26 +08:00
morluto 14cd78c322 fix(apple): retain unsupported NIB connectors in connection coverage
Recognize AppKit binding connectors as explicit unknown connection facts and
report partial semantic coverage rather than silently omitting them. Share
format-aware connector admission across keyed archives and NIBArchive.

Make the keyed archive table own parsed class and superclass facts for names,
array hierarchy traversal, inherited connectors, and class-table projection.
Preserve malformed class evidence and exclude unrelated custom class names.

Validation: 84 focused tests passed, including compiled CLI, filesystem,
MCP summary, and retained views; check:fast and test discovery passed.
Independent source review found no blocker. Production CLI and stdio MCP
replayed a read-only copy of Terminal 2.15's picker NIB: 13 connections with
partial unsupported-type coverage, versus baseline 9 with complete coverage.
Advertised schemas and retained coverage results validated with the SDK/Ajv.
No app, analysis target, or optional deep provider was executed.

Fixes #5590
2026-10-21 00:31:00 +08:00
morluto d1adc60795 test(contracts): align plist and UI finalization regressions
Update public-boundary expectations after the explicit XML unknown-scalar
and native UI deadline provenance changes. Retain raw malformed literals,
independent exact integers, expanded-year ISO dates, and partial UI reports.
Assert successful cleanup releases its lease and failed cleanup retains it
until close retries. Keep malformed-input and true date-range coverage.

The shared number fixture owns CLI/filesystem/MCP expectations. Native
plutil controls confirm integer entities are invalid plist literals, even
though they are valid XML. No runtime behavior or CI gates change.

Validation: 187 source cases, 138 focused public cases, final 97-case
rerun after fixture typing/group changes, full static/architecture checks,
and test discovery (1591 files).
2026-10-19 06:22:32 +08:00
Kaoru0822andClaude Opus 5.5 3776f218e5 fix(apple): bound Interface Builder XML plists by shared value depth (#4114)
* fix(apple): bound Interface Builder XML plists by shared value depth

The Interface Builder preflight rejected XML archives whose raw element
depth exceeded 128, but the shared plist parser bounds zero-based value
depth at 128; the plist envelope and dictionary keys add elements, not
value depth. Archives whose deepest value sat at depth 127 or 128 were
therefore omitted as over budget although keyed-archive inspection
decodes them. The preflight's duplicate depth rule is removed; the
shared parser's depth limit is mapped to the existing budget omission,
so deeper archives stay explicitly omitted.

Fixes #3949

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(apple): return the decode reservation of a depth-rejected archive

Review follow-up. The removed element-depth preflight rejected an archive
before its plist representation was reserved; the shared depth limit now
rejects it during decoding, after the reservation. A large over-depth
archive therefore kept its share of the aggregate budget and could
starve later valid archives. A document omitted by the budget after
reserving now releases that reservation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 07:38:07 -07:00
Kaoru0822andClaude Opus 5.5 7a66470385 fix(apple): report plist dates the decoder cannot represent as unknown (#3793)
plutil writes years past 9999 and negative years in a form the pinned
XML decoder cannot parse, and binary plists can hold dates outside the
JavaScript Date range. The decoder returns an Invalid Date. Its
projection's toISOString() call then threw, so inspect_plist failed the
whole plist as "unreadable_output" with a `rea doctor` remediation.
Keyed archive inspection and Interface Builder decoding rejected the
archive in the same way.

projectPlistValue, the shared projection, now reports such a date as
{ "$plist_type": "date", "iso8601": null } and counts it. inspect_plist,
keyed archive inspection and Interface Builder decoding each add a
counted limitation, as they already do for non-finite reals.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:13:01 -07:00
Rohit Ghumareandmorluto d005bd5dcd fix(javascript): preserve CommonJS lookup and analysis provenance (#2306)
* fix(javascript): preserve CommonJS directory and ancestor lookup

* test(apple): bound cold archive fixture compilation

* test(release): scope preflight schema to selected jobs

* fix(javascript): retain requested analysis limits in Evidence

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 23:31:30 +08:00
xin zhuangandmorluto e103a89889 fix(artifacts): preserve NIB key and class name identity (#2109)
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
2026-10-13 22:36:06 +08:00
吴佳浩Albenand吴佳浩 cae7620b77 fix(apple): report a recognized connector that cannot name its owner (#2484)
* fix(apple): report a connector that cannot name its owner as an omission

A connector is recognized by class name and then dropped by an early
`continue` when its owner cannot be resolved. Nothing recorded the drop, so
connection coverage derived `complete` with `examined: 0` and `omitted: 0`
for a connector that was present, recognized and discarded.

Both producers of that coverage contract are affected and are changed
together, because they share the facet and the reason value:

- the keyed-archive parser records the drop as `unresolvedConnections` and
  reports it through the `connections:<path>` facet, the document
  `connection_count` and the graph omission total;
- the NIBArchive projection in the artifact adapter recorded nothing at all,
  and the projected dictionary carried no trace of the dropped connector for
  a downstream consumer to find. Its count is taken where the connector is
  discarded and plumbed through the same per-document omission channel that
  already carries incomplete hierarchies, so the reason follows the cause.

An unresolved source cannot be represented as a connection at all, because
connection identity requires a non-empty source; that is unlike an unresolved
destination, which the graph already retains as an unresolved edge.
`connection_limit_reached` stays reserved for connections dropped by the hard
cap and `serialized_view_hierarchy_incomplete` for hierarchy omissions.

`connection_count` also includes the unresolved connectors. That field already
counts connectors the parser recognized but could not emit, because
connections dropped by the hard cap are counted there too, so unresolved
connectors follow the existing rule rather than a new one.

Verified with 98 focused Interface Builder and catalog tests, check:fast and
verify:test-discovery on macOS arm64, and through the public CLI: a rooted
archive holding one ownerless UIRuntimeOutletConnection reported complete with
examined 0 and omitted 0 before this change and reports partial with the reason
connection_unresolved and omitted 1 after it.

* fix(apple): count ownerless NIBArchive connectors and keep the limit reason

Greptile found two defects in the NIBArchive half of this change.

The document summary was left unchanged, so the same recognized connector
counted as one in a keyed archive and zero in a NIBArchive. The dictionary
parser only sees connectors that were projected, so the omission recorded
where the connector was dropped is added to the matching document here.

The facet reason was overwritten unconditionally, so a NIBArchive holding both
a connection dropped by the limit and an ownerless connector reported
connection_unresolved and lost the cause a higher limit could recover. The
existing reason is kept when the graph already named one, which is the
precedence the keyed-archive producer applies.

Verified with `npm run test:focused` over the bundle suite (42 passed, 1
skipped), `npm run lint` (0 errors) and `npm run check:fast` on macOS arm64.
Reverting the reason precedence alone fails only the limit case; reverting the
document count fails the count assertion in both cases.

The new case moved into its own describe block because the enclosing callback
crossed the repository's max-lines-per-function bound otherwise.

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-13 21:19:17 +08:00
TonyWu 7bc57e1e64 test(apple): skip ibtool-compiled NIB tests when Xcode ibtool is unavailable (#2070)
Command Line Tools install the /usr/bin/ibtool and xcrun shims without ibtool itself, so the existing /usr/bin/ibtool existence guard never skipped the storyboard test, and the native NIB hierarchy suite had no guard. On a Command Line Tools-only macOS host these seven tests failed at fixture compilation. Probe xcrun --find ibtool once and use it for both suites.
2026-10-13 06:07:16 +09:00
morluto 8d4d631606 fix(apple): decode UIKit keyed-archive outlet connections
Select authoritative connection fields by runtime class in a shared owner used by keyed archives and NIBArchive projection. Preserve archive identities, endpoint relationships and coverage, and replace duplicate synthetic outlet assertions with a rooted public CLI fixture.

Validation: compiled CLI acceptance against the issue-sourced rooted UIKit fixture and 41 filesystem tests. Native UIKit framework validation was not rerun. Fixes #2177.
2026-10-13 03:47:45 +08:00
morluto 8d3cfef753 fix(plist): disclose binary prototype-key omissions
Reuse the bounded binary preflight to observe dictionary keys before the pinned decoder drops them. Keyed archive results disclose the omission and Interface Builder marks archive decoding partial. XML keeps its existing exact counts; binary reporting deliberately leaves the count unknown because overwritten subtrees and decoder projection can change visibility.

Remove the superseded size-only preflight contract. Cover ASCII, UTF-16 and extended lengths, shared references, nested and shadowed dictionaries, and the value-only negative control. Synthetic plist and filesystem fixtures; no plutil or ibtool verification.

Fixes #1594. Supersedes #1593.
2026-10-11 07:02:39 +08:00
morluto e1df5e8555 fix(apple): account for repeated archive projections 2026-10-09 06:32:10 +08:00
morluto dbcf8c3391 fix(apple): budget Interface Builder decoding and projection 2026-10-09 05:15:23 +08:00
Rudy Mizrahi CelekliandN0zoM1z0 f8e143a3bc fix(apple): decode Interface Builder XML byte encodings (#1095)
* test(apple): qualify native Interface Builder XML encodings

Exercise actual AppKit/Foundation XML archive representations through filesystem and compiled CLI consumers. Preserve independent UID/hierarchy and original-byte provenance oracles.

Tests-only candidate qualification on unchanged native Apple CI; no production or workflow changes.

Signed-off-by: Rudy Celekli <rudy@gradiahq.com>

* fix(apple): share BOM-aware Interface Builder XML decoding

Reuse the existing fatal BOM-aware XML plist decoder for Interface Builder archives. Preserve original serialized-byte digests, binary decoding, and frozen native producer/consumer tests.

Signed-off-by: Rudy Celekli <rudy@gradiahq.com>

* fix(apple): reject unsupported XML encoding declarations

---------

Signed-off-by: Rudy Celekli <rudy@gradiahq.com>
Co-authored-by: N0zoM1z0 <1705004398@qq.com>
2026-10-09 01:11:49 +08:00
morluto 71ac83f3dd fix(apple): traverse keyed archive hierarchy collections (#976) 2026-10-07 23:00:43 +08:00
ysopwnable ef1a0cc65a refactor(apple): group artifact producers and verification (#952)
* refactor(apple): group artifact producers and verification

* test(apple): run affected native plist CLI regressions
2026-10-07 21:09:33 +08:00
Kaoru0822 aa8082ea6c Merge pull request #913 from morluto/fix/uikit-runtime-connections
fix(artifacts): project UIKit runtime outlet and event connections
2026-10-07 04:00:25 -07:00
morluto 1d769cd261 chore: merge main and preserve UIKit and AppKit connections 2026-10-07 18:02:05 +08:00
morluto 071cfd1bb6 chore: merge main and preserve plist and AppKit regressions 2026-10-07 18:02:05 +08:00
Kaoru0822andClaude Opus 5.5 900d7c8606 fix(artifacts): project compiled AppKit actions from the control to its target (#905)
* 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 <noreply@anthropic.com>

* test(artifacts): report observed action routes when a compiled route is missing

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* test(artifacts): report compiled connector records when a storyboard route is missing

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* 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 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 17:40:18 +08:00
morluto 96a9f32dc1 chore: merge main and resolve PR #899 conflicts 2026-10-07 17:28:55 +08:00
Kaoru0822andClaude Opus 5.5 8b243877c2 fix(artifacts): project UIKit runtime outlet and event connections
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 <noreply@anthropic.com>
2026-10-07 09:10:53 +00:00
Kaoru0822andClaude Opus 5.5 53bfa7e3f7 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 <noreply@anthropic.com>
2026-10-07 09:06:36 +00:00
Kaoru0822andClaude Opus 5.5 d421d3f286 test(artifacts): report compiled connector records when a storyboard route is missing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 09:00:41 +00:00
Kaoru0822andClaude Opus 5.5 c60b737f7d test(artifacts): report observed action routes when a compiled route is missing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 08:46:57 +00:00
Kaoru0822andClaude Opus 5.5 b08228d19f 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 <noreply@anthropic.com>
2026-10-07 08:39:27 +00:00
Kaoru0822andClaude Opus 5.5 2a4667bb79 fix(artifacts): decode flat keyed-archive NIBs with data and shared references
decode_interface_builder failed on every flat (keyed-archive) NIB that
holds plist data or dates. Archived colors keep their components as
data, so this covers nearly every AppKit NIB: XLD.app decoded 0 of 64.
The decoded Uint8Array and Date values failed JSON validation. They now
use the shared plist projection, which keeps them as explicit
$plist_type values.

Decoding those archives exposed a second failure. The keyed-archive
parser inlined every UID reference up to 16 levels for every object, so
an archive whose views reference their window, superview, and next key
view expanded exponentially. A 9 KB NIB took 3 s, and a 158 KB
MainMenu.nib did not finish. A reference now resolves to its string, or
to a stub naming the referenced object's archive UID, authored ID, and
class. The hierarchy walk visits each object once. The parser also reads
XML CF$UID references and treats UID 0 as nil.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 08:10:25 +00:00
Kaoru0822andClaude Opus 5.5 6729ee7636 fix(plist): report omitted __proto__ entries in signatures and Interface Builder
#885 decodes XML plists that contain a __proto__ key by omitting those
entries, but signature entitlements and Interface Builder plists dropped
the omission count. inspect_signature returned an entitlement set that
looked complete, and archive_decode stayed complete. Both now report the
omission: a signature limitation, and a partial archive_decode facet with
reason dictionary_entries_omitted and a per-document limitation.

The placeholder that stands in for __proto__ was reserved against the raw
XML text, so an entity-encoded key such as &#95;&#95;rea_prototype_key__
decoded to the placeholder and was omitted as if it were __proto__. The
placeholder is now reserved against the decoded text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 07:51:37 +00:00
morluto f72931d9dd test: eliminate fixture races and prune redundant scaffolding (#849)
* test: eliminate fixture races and prune redundant scaffolding

* fix(test): publish launcher capture readiness without renaming
2026-10-07 12:25:37 +08:00