Skip to content

Drop the stale WhereCore dependency from StuffTestHost#144

Merged
kyleve merged 8 commits into
mainfrom
drop-testhost-wherecore-dep
Jul 27, 2026
Merged

Drop the stale WhereCore dependency from StuffTestHost#144
kyleve merged 8 commits into
mainfrom
drop-testhost-wherecore-dep

Conversation

@kyleve

@kyleve kyleve commented Jul 27, 2026

Copy link
Copy Markdown
Owner

StuffTestHost force-linked WhereCore so that Stuff_WhereCore.bundle and — transitively — the GeoJSON Stuff_RegionKit.bundle were embedded in the host, on the theory that a hosted test bundle's Bundle.module resolves against the host's main bundle.

On Xcode 27 that's no longer necessary. Each .xctest now carries its own copies of the resource bundles for the code it links — WhereUITests.xctest ships Stuff_WhereCore.bundle, Stuff_RegionKit.bundle, Stuff_WhereUI.bundle, and Stuff_LifecycleKitUI.bundle — so SwiftPM's Bundle.module resolves via Bundle(for: BundleFinder.self) (the test bundle) and never falls back to Bundle.main.

So every bundle in the scheme was paying for a host dependency nothing needed, and the full GeoJSON set was being embedded twice.

On the old comment

It cited two canaries as proof the dependency was load-bearing:

Canary Status
WhereUITests.StringsTests No longer exists — went with the String Catalog symbol migration that deleted the hand-maintained Strings facades
SwiftDataInspectorWiringTests Still exists, passes without the dependency

The claim quietly stopped being true rather than being wrong when written. That's why the replacement comment records what makes it unnecessary and what to do instead of re-adding a product here, rather than just deleting the note — the next person hitting a missing-resource failure should give the bundle that needs the resource a dependency on the product that owns it, not re-embed it in the host for everyone.

Verification

  • Stuff-iOS-Tests green.
  • WhereUISnapshotTests green — the resource-sensitive one (Assets.car, GeoJSON, string catalogs), with zero reference images modified. This is the scheme that would fail loudly if a resource lookup silently degraded.
  • Built host confirmed to contain no WhereCore symbols and no resource bundles at all (StuffTestHost.app is now just the executable, dylib, and PlugIns/).
  • ./swiftformat --lint clean.

Notes

Closes the WhereCore half of the StuffTestHost item in TODOs.md; the scene-configuration-name duplication half stays open and is re-filed as its own quick-win.

Independent of #142 / #143 and mergeable in any order, though all three touch TODOs.md, so whichever lands last may need a trivial conflict resolution.

The shared test host force-linked WhereCore so that Stuff_WhereCore.bundle
and (transitively) the GeoJSON Stuff_RegionKit.bundle landed in the host,
on the theory that a hosted bundle's Bundle.module resolves against the
host's main bundle. On Xcode 27 that is no longer necessary: each .xctest
carries its own copies of the resource bundles for the code it links --
WhereUITests.xctest ships Stuff_WhereCore.bundle, Stuff_RegionKit.bundle,
Stuff_WhereUI.bundle and Stuff_LifecycleKitUI.bundle -- so SwiftPM's
Bundle.module finds them via Bundle(for: BundleFinder.self) and never falls
back to Bundle.main.

So every bundle in the scheme was paying for a host dependency that nothing
needed, and the full GeoJSON set was being embedded twice.

Verified by removing it and running both schemes green: Stuff-iOS-Tests and
the image-snapshot scheme, the latter being the resource-sensitive one
(Assets.car, GeoJSON, string catalogs) with no reference images modified.
The built host now contains no WhereCore symbols and no resource bundles at
all.

Worth noting the old comment cited two canaries as proof the dependency was
load-bearing. One of them, WhereUITests.StringsTests, had already ceased to
exist with the String Catalog symbol migration; the other,
SwiftDataInspectorWiringTests, passes without it. The claim had quietly
stopped being true rather than being wrong when written -- so the replacement
comment records what makes it unnecessary and what to do instead of re-adding
a product here, rather than just deleting the note.

Closes the WhereCore half of the StuffTestHost item in TODOs.md; the
scene-configuration-name duplication half stays open.
@kyleve kyleve closed this Jul 27, 2026
@kyleve kyleve reopened this Jul 27, 2026
@kyleve

kyleve commented Jul 27, 2026

Copy link
Copy Markdown
Owner Author

@cursoragent please merge main into this branch

@cursor

cursor Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Taking a look!

Open in Web Open in Cursor 

cursoragent and others added 3 commits July 27, 2026 03:06
main's #142 added the broken-snapshots P2 while this branch split the
StuffTestHost item in two: the WhereCore half moved to Completed and the
scene-configuration-name half was re-filed as its own quick-win. Kept
both sides — main's new item plus this branch's split — and dropped the
combined item they both descend from.
…o drop-testhost-wherecore-dep

# Conflicts:
#	TODOs.md
@kyleve

kyleve commented Jul 27, 2026

Copy link
Copy Markdown
Owner Author

@cursoragent Please merge main into this PR. Once merged and pushed, no need to monitor.

@cursor

cursor Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

main is merged into drop-testhost-wherecore-dep and pushed (3174232). The only conflict was the expected one in TODOs.md: main picked up a new feat(PeriscopeCore) P2 while this branch had split the old combined perf(StuffTestHost) item into a scene-name quick-win and moved the WhereCore half to "Completed issues". I kept both live items and dropped main's stale combined entry. Everything else (Project.swift, Package.swift, the CreditKit/About additions from #140) auto-merged, and the branch's net diff against main is still exactly the StuffTestHost dependency removal plus its comment.

While verifying the merge I noticed the module's own docs were left behind by the original change: Shared/StuffTestHost/README.md still said the host embeds Stuff_WhereCore.bundle, and AGENTS.md's "Bundle.module embedding checklist" instructed the next person to add a package product to the host — the precise thing the new Project.swift comment says not to do. I fixed both in a separate commit (4848b67) so it's easy to drop if you'd rather handle it elsewhere.

I couldn't refresh the PR description (the tool reports it isn't agent-managed), so the body still describes the pre-merge state and doesn't mention the docs commit. Verification here was limited to what Linux allows — ./swiftformat --lint is clean and there are no leftover conflict markers; tuist/Xcode aren't available on the cloud VM, so CI is the real check. As you asked, I'm not monitoring it.

Open in Web Open in Cursor 

cursoragent and others added 2 commits July 27, 2026 04:16
The module's README and AGENTS.md still described the host as embedding
Stuff_WhereCore.bundle, and AGENTS.md's checklist told the next person to
add a package product to the host to fix a Bundle.module miss — the exact
thing the new Project.swift comment says not to do.

Co-authored-by: Kyle Van Essen <k@kve.me>
@kyleve kyleve closed this Jul 27, 2026
@kyleve kyleve reopened this Jul 27, 2026
@kyleve

kyleve commented Jul 27, 2026

Copy link
Copy Markdown
Owner Author

@cursoragent can you double check given recent PR 145 merge into main that the changes here are still correct? No need to monitor once / if changes are pushed here, just reply to this PR.

@cursor

cursor Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Re-checked PR #144 against main with #145 merged in, merged main into the branch (ceab72e7, no conflicts this time), and replied on the PR.

The change is still correct — #145 actually reinforces it. #145's central correction (nothing here is a dynamic framework; Xcode's SPM integration links every product statically into each consumer) is exactly the mechanism this PR's manifest comment relies on: WhereUITests.xctest ships Stuff_WhereCore.bundle because it statically links WhereCore, so Bundle.module resolves via Bundle(for:) and never needs the host's Bundle.main. Since that claim now lives in the root AGENTS.md, I pointed the host's note at it instead of leaving it asserting the consequence alone.

The one thing that genuinely needed re-checking was #145 splitting the image snapshots back into three bundles. PeriscopeToolsSnapshotTests and SwiftDataInspectorSnapshotTests postdate this PR's green run, but neither links WhereUI/WhereCore/RegionKit, and nothing in their dependency closure declares resources at all — the only Bundle reads there are three Bundle.main Info.plist lookups in PeriscopeCore. WhereUISnapshotTests is back to the exact shape the PR was verified against. I also refreshed the line numbers in the scene-name TODO item that the merges had moved (60c83a3d), and left the stale MODULE_AUDIT.md line alone since that file is the dated, derived snapshot the weekly triage owns — flagged it in the reply in case you'd rather I fix it here.

Verification was limited to what this Linux VM allows: ./swiftformat --lint clean, ./sync-agents run, no conflict markers, net diff against main still just the four files. tuist/Xcode aren't available, so CI is the real check, and per your note I'm not monitoring it.

Open in Web Open in Cursor 

cursoragent and others added 2 commits July 27, 2026 04:43
…re-dep

Co-authored-by: Kyle Van Essen <k@kve.me>
PR #145 established what makes the removed WhereCore dependency
unnecessary: nothing here is a dynamic framework, and Xcode's SPM
integration links every product statically into each consumer, which is
why a `.xctest` carries its own resource bundles. Point the host's note
at that rule rather than asserting the consequence on its own.

Also refresh the scene-name item's line numbers, which the merges since
have moved.

Co-authored-by: Kyle Van Essen <k@kve.me>
@cursor

cursor Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Posted by an AI agent on kyleve's behalf.

Re-checked against main with #145 in it. The change is still correct, and #145 strengthens its reasoning rather than undercutting it. main is merged in and pushed (ceab72e7); TODOs.md auto-merged cleanly this time, since #145's additions land above and below the line this branch rewrote.

Why #145 helps rather than hurts

#145's central correction is the mechanism this PR's comment depends on. The comment asserts that each .xctest carries its own copies of the resource bundles for the code it links; #145 establishes why — nothing here is a dynamic framework, the local package is an XCLocalSwiftPackageReference handed to Xcode's own SPM integration, and every product links statically into each consumer. WhereUITests.xctest ships Stuff_WhereCore.bundle precisely because it statically links WhereCore. That claim now lives in the root AGENTS.md instead of only in this manifest comment, so I pointed the host's note at it (60c83a3d) rather than leaving it asserting the consequence on its own.

The one thing that actually needed re-checking

#145 undid #143's consolidation and split the image snapshots back into three bundles. Two of them — PeriscopeToolsSnapshotTests and SwiftDataInspectorSnapshotTests — did not exist when this PR's green run happened, so they're the only surface the original verification didn't cover. They're unaffected:

  • Neither links WhereUI, WhereCore, or RegionKit. Their imports are PeriscopeCore/PeriscopeTools/SnapshotKitTesting and SwiftData/SwiftDataInspector/SnapshotKitTesting, so no code in them can reach a WhereCore or RegionKit Bundle.module to begin with.
  • Nothing in their dependency closure declares resources at all. The resource-bearing targets in Package.swift are LifecycleKitUI, RegionKit, WhereCore, WhereUI, and WhereIntents; PeriscopeTools (→ PeriscopeCore/PeriscopeUI/Broadway), SwiftDataInspector, and SnapshotKit carry none. The only Bundle reads in that closure are three Bundle.main lookups in PeriscopeCore (bundleIdentifier, infoDictionary, bundleURL), which read the host's Info.plist and don't care what the host embeds.
  • WhereUISnapshotTests is back to exactly the shape this PR was verified against — a bundle whose productDependency is WhereUI — since Correct the dynamic-framework claim and record the linking spike #145 reverted the rename and re-merge.
  • SnapshotKitTesting's AccessibilitySnapshot resources were never in the host (the host is forbidden from linking SnapshotKitTesting), and the Bundle.module trap Correct the dynamic-framework claim and record the linking spike #145 documents only appears under the unlanded dynamic-framework spike, so it doesn't touch this.

Commits since you last looked

  • 4848b67 — the module docs fix from the previous merge (README.md still claimed the host embeds Stuff_WhereCore.bundle; AGENTS.md's checklist still told the next person to add a package product to the host).
  • 60c83a3d — the cross-reference above, plus refreshed line numbers in the scene-name quick-win, which the merges had moved (AppDelegate.swift:11:12, Project.swift:244:271).

One stale thing I deliberately left

MODULE_AUDIT.md:169 still says the host's WhereCore embed "is documented and verified load-bearing in Project.swift:256". It's the derived, dated snapshot the weekly todo-triage refresh owns, and #143/#145 both left it alone, so I didn't hand-edit it here. Happy to fix it on this branch if you'd rather it not read as current.

Verification limits

Linux cloud VM, so: ./swiftformat --lint clean, ./sync-agents run, no leftover conflict markers, and the net diff against main is still just the four files. tuist/Xcode aren't available here, so CI is the real check — and as you asked, I'm not monitoring it.

@kyleve
kyleve enabled auto-merge (squash) July 27, 2026 04:49
@kyleve

kyleve commented Jul 27, 2026

Copy link
Copy Markdown
Owner Author

@cursoragent can you inspect the failed bundle and include the broken image here?

@cursor

cursor Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

No code changes were needed this turn, so the branch is untouched and clean. Here's what I found.

There is no broken image — the failure isn't a pixel mismatch. I pulled the snapshot-diagnostics-ios artifact from run 30238003263 and parsed the .xcresult by hand (Linux has no xcresulttool; the bundle's blobs are zstd-framed). The whole 28-test run recorded exactly one issue, and it's the settle-timeout guard at SnapshotRenderingSupport.swift:38: "Snapshot content never settled: the content phase … was still changing after 2.5s." That fires before the image comparison, and no second issue was recorded — so all 16 of about()'s comparisons matched their references. Structurally confirmed too: the failure summary's attachments array is empty and there are zero ActionTestAttachment records anywhere in the bundle. swift-snapshot-testing only writes a failure artifact when a compare fails, and none did.

It also isn't this branch. main fails identically — run 30237703576 records the same test, same line 38, byte-identical message, and 30235435693 is a third instance. This branch's earlier run 30236840000 was green. The closest thing to "the image" is what the run compared against and matched:

AboutSettingsViewSnapshotTests reference images, light and dark iPhone

The issue landed about 20 seconds into the 56-second test, and the message names only the erased hosting-controller type — so which of the 16 configurations stalled can't be recovered from the bundle, which is itself a gap worth closing. The screen fits a flake class already ledgered at Where/TODOs.md:113: AboutSettingsView is a Form in a NavigationStack with an inline title, so it carries the iOS 26 glass toolbar whose material adapts quietly a few hundred milliseconds after hosting. That entry's symptom was a mismatch baking pre-adaptation glass; this is the other end of the same race. I posted all of this on the PR and offered two follow-ups I did not take unprompted: filing the flake in Where/TODOs.md, and threading the snapshot identifier into the timeout message so it names the case and configuration.

Open in Web Open in Cursor 

@cursor

cursor Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Posted by an AI agent on kyleve's behalf.

Downloaded the snapshot-diagnostics-ios artifact from run 30238003263 and read the .xcresult directly (Linux, so no xcresulttool — the bundle's blobs are zstd-framed, decompressed and parsed by hand).

There is no broken image, because nothing mismatched. The failure isn't a pixel diff — it's the settle-timeout guard, and every one of about()'s 16 comparisons passed.

What the bundle actually contains

One issue in the entire 28-test, 526.6s run:

SnapshotRenderingSupport.swift:38 — Issue recorded: Snapshot content never settled: the content phase for UIHostingController<ModifiedContent<…, _TransactionModifier>> was still changing after 2.5s, so this capture is an arbitrary frame of whatever is still moving.

That's reportIfUnsettled's .timedOut arm. It records before the capture is compared, and the run recorded no second issue — so all 16 assertSnapshot calls (10 Default × .screenDefaults, plus DirtyTree/Unattributed/LibrariesOnly light+dark) matched their references within tolerance. Confirmed structurally too: the failure summary's attachments array is empty, and there are zero ActionTestAttachment records anywhere in the bundle. swift-snapshot-testing only writes a failure artifact when a compare fails, and none did.

So the closest thing to "the image" is what the run compared against and matched:

AboutSettingsViewSnapshotTests reference images, light and dark iPhone

It isn't this branch

main fails identically. Run 30237703576 (main, 04:39Z) records the same AboutSettingsViewSnapshotTests.about() timeout, same file, same line 38, byte-identical message; 30235435693 (main, 03:47Z) is the third. This branch's own earlier run 30236840000 was green on the same code path, so it's intermittent and predates the merge.

What I think it is

The issue landed ~20s into the 56.1s test (issue stamped 04:56:49.258Z; the test started ~04:56:29.2Z), and the message doesn't name the case or configuration — only the erased hosting-controller type — so which of the 16 captures stalled isn't recoverable from the bundle. That's a real diagnostic gap: a settle timeout is the one failure that can't be traced back to an image.

The screen fits a flake class already ledgered in Where/TODOs.md:113: AboutSettingsView is a Form inside a NavigationStack with an inline title, so it gets the iOS 26 glass toolbar whose material adapts a few hundred ms after hosting, quietly. There the symptom was a mismatch (a capture baking the pre-adaptation glass); this is the other end of the same race — the adaptation still drifting when the 2.5s budget expires. The remedy that entry settled on is SnapshotSettle.settledAtLeast(minDuration:) on the affected case.

Want me to take either of these?

Neither is filed, and neither belongs in this PR's diff as it stands:

  1. File the flake in Where/TODOs.md (it's agent-found and currently unrecorded).
  2. Thread the snapshot identifier into reportIfUnsettled's message, so a timeout names the case + configuration instead of a generic type.

Say the word and I'll do them here or on a separate branch. Nothing about the StuffTestHost change is implicated either way — the test job passed on this branch, and the resource-sensitive suites all compared clean.

@cursor

cursor Bot commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

Posted by an AI agent on kyleve's behalf.

The image in the comment above degraded to an artifact link. Since the only images involved are the committed references — the ones the timed-out run compared against and matched — here they are inline, straight from the repo:

about.Default_iPhone about.Default_iPhone_dark

Unchanged on this branch, and unchanged by the failing run — the inline glass toolbar behind the "About" title is the chrome I suspect never finished adapting.

@kyleve
kyleve merged commit 77305d1 into main Jul 27, 2026
7 of 8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants