Merge Broadway into Stuff (Shared/Broadway)#72
Merged
Conversation
- Add Tuist project manifest (Project.swift) and config (Tuist.swift) - Add BroadwayCatalog SwiftUI app target (iOS + Mac Catalyst, iOS 26.0) - Add BroadwayUI reusable framework target - Add unit tests for both targets using Swift Testing - Pin Tuist version via mise (.mise.toml) - Add ide script for project generation (with optional -i for tuist install) - Update .gitignore for Tuist/Xcode generated artifacts - Add README.md and AGENTS.md documenting the repo Made-with: Cursor
- Add BroadwayCore framework target with sources and tests - BroadwayUI now depends on BroadwayCore - Extract shared destinations/deployment into top-level constants - Add framework() helper to eliminate target boilerplate - Update README.md and AGENTS.md with new target and dependency graph Made-with: Cursor
## Summary Introduces the foundational type system for BroadwayCore, the root UIKit integration layer in BroadwayUI, and supporting test infrastructure. ### Core primitives - **`CopyOnWrite`** — property wrapper providing value-semantic copy-on-write storage (`@_spi(CopyOnWrite)` for unsafe direct access) - **`AnyEquatable`** — type-erased equatable box for heterogeneous containers - **`TypeIdentifier`** — lightweight `ObjectIdentifier`-based type key with debug output - **`EquatableIgnored`** — property wrapper that excludes a value from `Equatable` comparisons ### Environment system - **`BAccessibility`** — snapshot of `UIAccessibility` settings with a mockable `SettingsProvider` protocol and a `@MainActor`-isolated `Observer` for live change tracking (supports `NotificationCenter` injection for testing) - **`BTraits`** — type-keyed container for environment traits (currently holds `BAccessibility`) - **`BThemes`** — type-keyed container for theme values conforming to `BTheme` (with `defaultValue` support) - **`BStylesheets`** — lazy, cached stylesheet resolver with transitive dependency support, typed `StylesheetError`, and cycle detection via `defer`-based stack unwinding - **`BContext`** — root environment container composing traits, themes, and stylesheets; updating traits/themes passes values through to the stylesheet cache - **`BStylesheet`** / **`SlicingContext`** / **`StylesheetError`** — stylesheet protocol, creation context, and typed errors (each in their own file) ### UIKit integration - **`BContextTrait`** — `UITraitDefinition` bridging `BContext` into `UITraitCollection` / `UIMutableTraits` - **`BRootViewController`** — container view controller that manages `BContext`, observes accessibility changes, and propagates context via `traitOverrides` ### Test infrastructure - **`BroadwayTesting`** — shared test utility framework with `XCTestCase` extensions (`show`, `waitFor`, `assertThrowsError`) - **`BroadwayTestHost`** — minimal UIKit app target serving as a dedicated test host for `BroadwayCoreTests` and `BroadwayUITests` ### Tooling & CI - **SwiftFormat** — pinned via `mise`, with `.swiftformat` config (`--extension-acl on-declarations`), `./swiftformat` runner script, CI lint gate, and auto-formatting pre-commit hook - **CI** — `format` job (lint) + `test` job (all targets via `mise exec -- tuist test`) ### Sendable / concurrency - `BAccessibility`, `BTraits`, `BThemes`, `BContext`, `CopyOnWrite`, `AnyEquatable`, `TypeIdentifier` all conform to `Sendable` (some `@unchecked` where `AnyHashable` storage prevents automatic inference) - `BAccessibility.Observer` and `BRootViewController` are `@MainActor`-isolated ## Test coverage ~100 test cases across 11 test files covering all new types, including stylesheet dependency chains, cycle detection, observer lifecycle, accessibility mocking, and hosted UI tests. ## Stats 38 files changed, ~2,600 lines added
## Summary Adds a `sync-agents` script (Ruby) that keeps AI agent configuration in sync across Cursor, Codex, and Claude Code from a single source of truth (`AGENTS.md` files and `.agents/skills/`). **What it does:** - Generates `CLAUDE.md` from each `AGENTS.md` (Cursor and Codex read `AGENTS.md` natively). - Syncs `.agents/skills/` to `.claude/skills/` for Claude Code. - Manages external skills from GitHub repos, pinned to commit SHAs via `.agents/external-skills.json`. - Runs incrementally — skips files already up to date. **Commands:** - `./sync-agents` — generate CLAUDE.md files and sync skills - `./sync-agents --add <url> [name]` — add an external skill from GitHub, pinned to the current commit - `./sync-agents --install` — fetch all external skills from the manifest (skips if present) - `./sync-agents --update` — re-fetch all external skills at latest commit - `./sync-agents --git-add` — sync + stage changed files (used by pre-commit hook) **Other changes:** - Simplify root `AGENTS.md` and `README.md`; move module-specific details into nested `AGENTS.md` files for BroadwayCore, BroadwayUI, and BroadwayCatalog. - Remove `Plans/` directory. - Add convention: shell scripts > ~20 lines should use Ruby. - Generated files (`CLAUDE.md`, `.claude/skills/`, external skills) are gitignored. `./ide` runs `./sync-agents --install` to populate them on setup. - Pre-commit hook runs `./sync-agents --git-add` and SwiftFormat automatically. - Install SwiftUI Pro skill from twostraws/swiftui-agent-skill as first external skill.
## Summary - **Unified trait observation:** `BTraitsValue` declares an `Observer` associated type conforming to `BTraitsValueObserver`. `BTraitsObserver` coordinates per-type observers from `BTraits` registrations, using `currentValue(from:)` and `makeObserver(with:onChange:)`, with **idempotent** `start()` / `stop()` at the coordinator. - **Explicit observers:** Every `BTraitsValue` implements `makeObserver` (no protocol default). `NeverObserver` remains available for traits with nothing live to subscribe to. - **`BTraits` registration:** `.system`, `register(_:)`, and `readCurrentValues(from:)` centralize which types participate. - **`BContext`:** Separates `baseTraits` and subtree `traitOverrides`; merged `traits` drives equality and **stylesheet config**. `BStylesheets` keeps **private** trait/theme storage; `BContext` syncs via `updateTraits` / `updateThemes` (package-visible from the Swift package boundary). - **`BRootViewController`:** Lazy setup in `viewIsAppearing`; one `BTraitsObserver(traits: .system, from: self)` replaces ad hoc accessibility wiring. - **`BTraitOverridesViewController`:** Applies overrides from the **full inherited** context (parent `traitCollection.bContext` when embedded); refreshes in **`didMove(toParent:)`** and on trait changes; child creation still deferred to `viewIsAppearing`. - **UIKit / SwiftUI:** Trait bridging (`BMode`, `BContentSizeCategory`, `UIViewControllerTraitObserver`), SwiftUI environment and modifiers for `BContext` / overrides. - **`BAccessibility.Observer`:** Internal initializer only (no public `observe` factory); review chose to keep construction package-internal until a public API is needed. - **Build layout:** Root **`Package.swift`** ships **BroadwayCore**, **BroadwayUI**, and **BroadwayTesting** (sources under `*/Sources`). **Tuist** generates the Xcode workspace for **BroadwayCatalog**, **BroadwayTestHost**, and **`.xctest`** bundles that depend on those products. **BroadwayTesting** in SPM links UIKit only (no `.xctest` link on the library); see `Project.swift` comment near the local package. - **Concurrency:** `Sendable` / `@unchecked Sendable` where required for Swift 6 (e.g. `BMode`, `BContentSizeCategory`, `SettingsProvider` usage). - **Docs:** `AGENTS.md` / `README.md` describe hybrid SPM + Tuist workflows and how to run tests.
Pure-formatting step (no behavior change): the imported tree shipped Broadway's own .swiftformat, which was dropped in the move. Reformat under Stuff's .swiftformat so ./swiftformat --lint passes.
Wire BroadwayCore, BroadwayUI (->Core), and BroadwayTesting (->Core) as package products/targets under Shared/Broadway/*. Not yet referenced by any Tuist target, so existing targets are unaffected.
Add the BroadwayCatalog app, BroadwayCore/UI/Catalog test bundles (com.stuff.* IDs) via a broadwayUnitTests helper that links BroadwayTesting instead of WhereTesting, and register them in the Stuff-iOS-Tests scheme plus per-bundle test schemes. Retarget BroadwayTesting.show() to a scene-based hostKeyWindow() lookup so its hosted tests run inside the shared, scene-based StuffTestHost (its window lives on the UIWindowSceneDelegate, not the app delegate). Rename the misnamed XCTestCaseAdditions.swift (no XCTest) to BroadwayTesting.swift.
Add per-module README.md for BroadwayCore/UI/Testing/Catalog plus a Shared/Broadway group README/AGENTS, refresh the module AGENTS.md files to Stuff's convention (link back to root + group, invariants, testing pointer), and list the Broadway modules/targets in the root AGENTS.md. CLAUDE.md files are regenerated by ./sync-agents (gitignored).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Imports the Broadway design-system repo into this monorepo under
Shared/Broadway/*, preserving git history, and fully wires it into the SPM/Tuist build, CI scheme, and docs.What moved
Brought over (with history) into
Shared/Broadway/:BroadwayCore/— library (BContext, traits/themes/stylesheets, utilities)BroadwayUI/— library (BRootViewController, trait-override VCs) → BroadwayCoreBroadwayTesting/— UIKit test helpers → used by the hosted test bundlesBroadwayCatalog/— component showcase appDropped as overlapping scaffolding (Stuff already has these): Broadway's
Package.swift,Project.swift,Tuist.swift,ide/swiftformat/sync-agents,.mise.toml,.swiftformat,.github/,.githooks/,.agents/,LICENSE, root docs, andBroadwayTestHost/.How history was preserved
git filter-reporewrote the Broadway history to the finalShared/Broadway/*paths (dropping the scaffolding), then it was merged in with--allow-unrelated-histories. Broadway's original commits are now ancestors, andgit log --followtraces imported files back to their Broadway history.Wiring
Package.swift: addedBroadwayCore,BroadwayUI(→ Core),BroadwayTesting(→ Core) products/targets.Project.swift: added theBroadwayCatalogapp andBroadwayCoreTests/BroadwayUITests/BroadwayCatalogTestsbundles via a newbroadwayUnitTestshelper that linksBroadwayTesting(notWhereTesting, to avoid pulling in the Where domain). All bundle IDs renamedcom.broadway.*→com.stuff.*. Registered in theStuff-iOS-TestsCI scheme plus per-bundle test schemes.BroadwayTesting.show()now finds the host window via a scene-basedhostKeyWindow()(matchingWhereTesting) so Broadway's hosted tests run insideStuffTestHost(which is scene-based). Renamed the misnamedXCTestCaseAdditions.swift→BroadwayTesting.swift.README.md+ refreshedAGENTS.md, aShared/Broadway/group README/AGENTS, and the rootAGENTS.mdupdated../sync-agentsregenerated the gitignoredCLAUDE.mds.Validation (macOS)
./swiftformat --lintclean (298 files)tuist generatecleantuist test Stuff-iOS-Tests— full suite passes, including the Broadway suites and theshow()-basedBRootViewControllerTestsrunning green inStuffTestHost.Follow-up
The old Broadway repo is retired in a companion PR that clears it and leaves a README pointing here.