* 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>
Decode native binary plists from bounded owned source bytes before conversion can erase scalar values. Share scalar preflight and isolate overlapping decoder records while retaining exact integers, date uncertainty, and original bytes. Refuse numeric payloads that read structural table bytes, and avoid retaining projection metadata when callers only need structural validation.
Closes#5701Closes#5703Closes#5705Closes#5782Closes#5783Closes#5792
* fix(apple): refuse invalid binary dates and unknown sub-millisecond instants
Reachable binary plist dates must use marker 0x33. Other type-3 markers are
malformed and are no longer projected as observed timestamps. A valid float64
date stays known only when it is exactly the canonical millisecond instant;
other finite instants remain typed unknown dates with a counted limitation.
Co-authored-by: morluto <morluto@users.noreply.github.com>
* fix(apple): share one decoder copy and bound binary date payloads
Wide-integer rewrites and unrepresentable date payloads now share the
single parser-input copy already reserved by the decode budget. Reachable
date markers and payloads must end at the offset table even when other
decoder-bounds checks read trailing bytes.
Co-authored-by: morluto <morluto@users.noreply.github.com>
* test(apple): refuse out-of-region dates through the keyed reader
Decoder-bounds date truncation is now asserted on decodeKeyedArchiveBytes,
the path that selects those bounds.
Co-authored-by: morluto <morluto@users.noreply.github.com>
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: morluto <morluto@users.noreply.github.com>
Add a read-only app resource session shared by CLI and MCP. Preserve resource-root provenance without inventing executable identity, reject binary-only snapshots and operations, and retain failed cleanup owners across switches and retries.
Keep filesystem admission failures typed and preserve cancellation ordering from observed cleanup facts. Document resource routing in the packaged investigation skill.
BREAKING CHANGE: resource-only observations may have a null Evidence subject and null executable target digests. Clients must use resource_root and reported per-resource provenance instead.
Refs #5589
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
Interpret complete scalar source at the XML owner instead of accepting numeric prefixes, permissive date strings or nonempty booleans. Retain malformed content as explicit unknown scalars with original literals and reasons, while preserving the surrounding archive and independent numeric facts.
Share the representation across plist, keyed archive, Interface Builder and entitlement projection; remove the superseded numeric fallback and helpers. Parse date fields as UTC, retain real entity/CDATA semantics, and distinguish native integer/real whitespace without ambiguous whitespace backtracking.
Validation: 276 source tests and 60 compiled/public tests passed, with updated scalar regressions rerun; static, architecture and docs checks passed. Native macOS plutil controls and CLI/MCP source-hash/time-zone controls executed. No Linux/Windows or deep-provider qualification. Tiny completed CLI task costs recorded; no performance improvement claimed.
BREAKING CHANGE: Unresolved XML scalar values now use $plist_type unknown wrappers with element, literal and reason. This replaces the former real/null unknown marker and malformed scalar prefix fallback. Supported extended UTC years now preserve their timestamp.
Fixes#4852
Interpret the validated, depth-bounded DOM directly. Collect source number
facts during the same traversal and share them with keyed archives, Interface
Builder and native plist projections. Remove the second XML parse, key aliases,
source-location rewriting and both duplicate XML numeric scanners. Preserve
character references, container/null behavior, safe own-property construction
and prototype omission accounting.
Decode signed hexadecimal integers and combine real text/CDATA. Retain exact
integer identity when a finite integer exceeds even Number's finite range,
using the shared scalar projector. Keep unsupported mixed integer syntax
conservative and report incomplete metadata.
Adapt @apitoagents's hexadecimal regression and literal-parsing proposal from
PR #3475. Native plutil controls establish the supported hex/real cases and
confirm that the synthetic 401-digit overflow case exceeds native bounds;
retaining accepted input does not claim native-format support.
Validation: 274 focused checks in 30 files pass, one skipped; build,
check:fast and docs:check pass. Built CLI/actual stdio MCP, native plist and
Interface Builder controls cover kinds, references, exact digits and digests.
Eight baseline checks fail; native binary conversion independently verifies
updated CDATA real expectations. 48 tiny completed CLI cost comparisons
preserve ordinary XML/binary and all three native replies byte for byte.
Two read-only reviews clear. No statistical speed claim, heavy profiler,
investigated-target execution or real deep-provider verification.
Fixes#3373Fixes#4042Fixes#4789
Reuse byte-derived numeric metadata after graph classification so emitted
attributes preserve exact integers without changing UID links or identities.
Reserve metadata and attribute projection within the shared decode budget.
Validated six CLI cases covering XML, binary, collisions, real values and
incomplete metadata, plus decode-budget regressions. A small cold/warm CLI
comparison showed similar costs; it does not establish a speed improvement.
Refs #3876, #3972.
Co-authored-by: hhang <169027046+xu91102@users.noreply.github.com>
* 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>
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>
Include omitted roots, unresolved retained endpoints and serialized NIB omissions in the owning hierarchy status. Reuse that status for summaries and coverage; remove the adapter recomputation and preserve per-document isolation.
Validation: 18 graph and 12 real NIB filesystem/built CLI tests pass.
Based on #3156.
Co-authored-by: Aliciawque <166712943+Aliciawque@users.noreply.github.com>
* 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>
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.
* fix(apple): join recognized keys from NIBArchive field maps
inspect-asset-catalog returned no resource-key matches for a decoded
NIBArchive resource whose recognized image key is present in the archive. The
NIBArchive projection groups an object's decoded fields under the node's
nibValues attribute beside the class, label and ID it derives from the same
archive, while the AppKit keyed-archive parser flattens the same fields onto
the node. The catalog join read only the top level, so a recognized NSImageName
on a decoded resource produced no row, and the empty-catalog limitation still
claimed resource keys were reported as unmatched.
Read an archive object's fields from both shapes, and give the namespace one
owner by exporting NIB_VALUES_ATTRIBUTE from the projection that writes it.
The recognized-field set stays in the catalog analysis, which owns that
contract.
Verified with 5 focused catalog tests, check:fast and verify:test-discovery on
macOS arm64, and through the public CLI against the reported NIBArchive
fixture. The keyed-archive control keeps its existing row, and a NIBArchive
with only an unrecognized title still produces none. The ibtool-dependent
native hierarchy lane requires full Xcode and was unavailable here.
* fix(apple): normalize resource keys at the NIBArchive producer
Fold the complementary implementation from #2256 into the earlier #2248 proposal, retaining the original contribution history.
Co-authored-by: Thang Luu <76550342+thangldw@users.noreply.github.com>
* fix(apple): restrict catalog joins to parsed resource nodes
---------
Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Thang Luu <76550342+thangldw@users.noreply.github.com>
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.