-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Improve build-test loop for AI agents #25242
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
c183465
5201ff0
98b0198
6fbb4a7
5b571f6
494071f
40a4354
de78218
72f79a8
d6ab0e4
a6e2fee
cdb999a
fc6ca07
95278f8
0cfeb50
045bd0c
3d2d225
b5a7ab6
7cfc3ca
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,214 @@ | ||
| <<<<<<< HEAD | ||
| @AGENTS.md | ||
| ======= | ||
| # CLAUDE.md | ||
|
|
||
| This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. | ||
|
|
||
| ## Overview | ||
|
|
||
| WordPress for iOS is the official mobile app for WordPress that lets users create, manage, and publish content to their WordPress websites directly from their iPhone or iPad. | ||
|
|
||
| Minimum requires iOS version is iOS 16. | ||
|
|
||
| ## High-Level Architecture | ||
|
|
||
| ### Project Structure | ||
| WordPress-iOS uses a modular architecture with the main app and separate Swift packages: | ||
|
|
||
| - **Main App**: `WordPress/Classes/` - core app functionality | ||
| - **Modules**: `Modules/Sources/` - Reusable Swift packages including: | ||
| - `WordPressUI` - shared UI components | ||
| - `WordPressFlux` - deprecated state management using Flux pattern (DO NOT USE) | ||
| - `WordPressKit` - API client and networking | ||
| - `WordPressShared` - Shared utilities | ||
| - `DesignSystem` - design system | ||
|
|
||
| ### Key Patterns | ||
| - **Architecture**: SwiftUI with MVVM for new features | ||
| - **ViewModels**: Use `@MainActor` class conforming to `ObservableObject` with `@Published` properties | ||
| - **Concurrency**: Swift async/await patterns with `@MainActor` for UI thread safety | ||
| - **Navigation**: SwiftUI NavigationStack | ||
| - **Persistence**: Core Data with `@FetchRequest` for SwiftUI integration | ||
| - **UI**: Progressive SwiftUI adoption using `UIHostingController` bridge pattern | ||
| - **Dependency Injection**: Constructor injection with protocol-based services | ||
|
|
||
| #### Testing Patterns | ||
| - Use Swift Testing for new tests | ||
|
|
||
| ### Important Considerations | ||
| - **Multi-site Support**: Code must handle both WordPress.com and self-hosted sites | ||
| - **Accessibility**: Use proper accessibility labels and traits | ||
| - **Localization**: follow best practices from @docs/localization.md | ||
|
|
||
| ## Build & Test | ||
|
|
||
| ### Prerequisites | ||
|
|
||
| - Xcode version in `.xcode-version`, Ruby version in `.ruby-version` | ||
| - Run `rake dependencies` once after cloning to install gems and download Gutenberg xcframeworks | ||
|
|
||
| ### Commands | ||
|
|
||
| | Task | Command | | ||
| |------|---------| | ||
| | Install dependencies | `rake dependencies` | | ||
| | Lint | `rake lint` | | ||
| | Auto-fix lint issues | `rake lintfix` | | ||
|
|
||
| ### Building and Testing | ||
|
|
||
| Use the `test` Fastlane lane for local testing. | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This might change soon with Xcode 26.3, I haven't had a chance to install the RC yet. |
||
| It skips CI prerequisites and reuses `DerivedData/` for incremental builds. | ||
|
|
||
| **Do not run multiple `fastlane test` invocations in parallel.** | ||
| They share `DerivedData/` and the build database will lock, causing failures. | ||
|
|
||
| ```bash | ||
| # Run all tests | ||
| bundle exec fastlane test | ||
|
|
||
| # Run a specific test target/class/method (fastest option) | ||
| bundle exec fastlane test only_testing:WordPressCoreTests/LockingHashMapTests | ||
| bundle exec fastlane test only_testing:WordPressTest/SomeClass/testMethod | ||
|
|
||
| # Test a different scheme | ||
| bundle exec fastlane test scheme:Jetpack | ||
|
|
||
| # Clean build before testing | ||
| bundle exec fastlane test clean:true | ||
| ``` | ||
|
|
||
| Test targets (use with `only_testing`): | ||
|
|
||
| - `WordPressTest` — main app unit tests | ||
| - `WordPressCoreTests` — core module tests | ||
| - `WordPressSharedTests` / `WordPressSharedObjCTests` | ||
| - `WordPressKitTests` / `WordPressAuthenticatorTests` | ||
| - `WordPressUIUnitTests` / `AsyncImageKitTests` | ||
| - `JetpackStatsWidgetsCoreTests` / `JetpackStatsTests` | ||
| - `WordPressFluxTests` / `WordPressIntelligenceTests` | ||
| - `DesignSystemTests` | ||
|
|
||
| For a compile-only check without running tests: | ||
|
|
||
| ```bash | ||
| xcodebuild build \ | ||
| -workspace WordPress.xcworkspace \ | ||
| -scheme WordPress \ | ||
| -destination 'platform=iOS Simulator,name=<device>' \ | ||
| 2>&1 | xcbeautify | ||
| ``` | ||
|
|
||
| Pick an available simulator with `xcrun simctl list devices available`. | ||
|
|
||
| ### Modules | ||
|
|
||
| Modules under `Modules/` are an SPM package but target **iOS only** — `swift test` does **not** work from the command line. | ||
| Test targets are defined in `Modules/Package.swift` and run via the app's test plans through `xcodebuild`. | ||
|
|
||
| Source → test target mapping follows `{ModuleName}Tests` (e.g., `WordPressCore` → `WordPressCoreTests`). | ||
| Exception: `WordPressUI` → `WordPressUIUnitTests` because `WordPressUITests` is taken by the Xcode UI test target. | ||
| Not all modules have tests. | ||
| Check `Modules/Tests/` to confirm a test target exists before running. | ||
|
|
||
| When adding a new test target in `Modules/Package.swift`, also add it to the default test plan at `Tests/KeystoneTests/WordPressUnitTests.xctestplan`. | ||
| Use `container:../Modules` as the `containerPath` and the SPM target name as the `identifier`. | ||
|
|
||
| ### SwiftLint | ||
|
|
||
| Config: `.swiftlint.yml` (opt-in rules only). | ||
| Run via `rake lint`. | ||
|
|
||
| ## Verifying Changes | ||
|
|
||
| After making changes, close the feedback loop: | ||
|
|
||
| 1. **Lint** — `rake lint` (fast, catches style and localization errors) | ||
| 2. **Build** — `xcodebuild build ...` (catches type errors without running tests) | ||
| 3. **Test** — `bundle exec fastlane test only_testing:TargetName/Class/method` | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think we need to be so prescriptive. LLM is already smart enough to know most of these: the I think most of the time, LLM is smart enough to know which tools to use and how to use them during the job, which usually works out better than telling them what to do (via Claude.md in this case) before the job starts. In general, I'm leaning more towards cutting down the context, rather than adding more. Please note, I'm not really against adding these instructions here. We can see how they work in practice after this PR is merged.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't like being prescriptive either, but every time I ask the model to generate this kind of stuff, it always opts for being verbose and prescriptive. I don't like it, because it seems like a maintenance nightmare. In fact, I had to push back on it enumerating all the module-tests pairs, and force it to track the naming convention instead. I asked why and the reason it gave, which I know is not to be trusted but made sense to me, is that being prescriptive means it has less files to parse when needing to find the answer to which command to run for what. |
||
| ## Coding Standards | ||
| - Follow Swift API Design Guidelines | ||
| - Use strict access control modifiers where possible | ||
| - Use four spaces (not tabs) | ||
| - Lines should not have trailing whitespace | ||
| - Follow the standard formatting practices enforced by SwiftLint | ||
| - Don't create `body` for `View` that are too long | ||
| - Use semantics text sizes like `.headline` | ||
|
|
||
| ## Development Workflow | ||
| - Branch from `trunk` (main branch) | ||
| - PR target should be `trunk` | ||
| - When writing commit messages, never include references to Claude | ||
|
|
||
| ## Release Notes Compilation Process | ||
|
|
||
| ### Overview | ||
| Process for compiling release notes for new iOS versions, maintaining the established tone without App Store character limits. | ||
|
|
||
| ### Step-by-Step Process | ||
|
|
||
| #### 1. Study Previous Release Notes Style | ||
| Use `gh` to fetch releases and analyze professional editorialization patterns: | ||
| - **Version 25.9**: Raw added in commits `ce08612ecc324e981ef9c5898c98bb267cf29721` & `30cd7073802feb8711b2aae8bb69f41fedba1d80`, editorialized in `bc3af0d2c0c8c3dec8556bb4eff7709f3c151c0d` | ||
| - **Version 26.0**: Raw added in commits `8a9e79587924f85e6ac6756fe47d045f7db04ece` & `883acc3324abe45d0e121f3854dc84712b22b4cb`, editorialized in `2ef13c2898c5b58d09c8a3af9f109a47f0bd782c` | ||
|
|
||
| Commands: `gh release view 25.9`, `gh release view 26.0` (note: no 'v' prefix) | ||
|
|
||
| **Important**: GitHub releases only show WordPress release notes. For better Jetpack release notes, use: | ||
| ```bash | ||
| gh api "repos/wordpress-mobile/WordPress-iOS/contents/WordPress/Jetpack/Resources/release_notes.txt?ref=<commit_hash>" --jq -r '.content' | base64 -d | ||
| ``` | ||
|
|
||
| #### 2. Verify Release Branch and Get Last Release Hash | ||
| - Verify current branch follows naming: `release/x.y` (where x.y = last_release + 0.1) | ||
| - Get commit hash for last release: `gh release view <last_version> --json tagName,targetCommitish` | ||
| - Confirm current branch is properly ahead of last release tag | ||
|
|
||
| #### 3. Identify Changes Since Last Release | ||
| Compare current release branch against last release hash using GitHub API (since local commits may not exist due to squashing/rebasing): | ||
| ```bash | ||
| gh api repos/wordpress-mobile/WordPress-iOS/compare/<last_release_hash>...HEAD --jq '.commits[] | "\(.sha[0:7]) \(.commit.message | split("\n")[0])"' | ||
| ``` | ||
| Focus on user-facing changes from squash commit messages. **Important**: When commit messages are unclear or technical, always investigate further: | ||
| - Use `gh pr view <PR_number>` to read PR titles and descriptions | ||
| - Look for keywords indicating user-facing changes: "feat:", new functionality, UI changes, user experience | ||
| - Be especially careful with feature rollouts that may have technical-sounding commit messages but represent new user functionality | ||
| - When in doubt, investigate the PR rather than excluding potentially important features | ||
|
|
||
| #### 4. Compile Raw Release Notes | ||
| Create factual summary including: | ||
| - **Always check RELEASE-NOTES.txt file** (note: hyphen, not underscore) for developer-authored release notes under the version number section. These notes start with `[*]`, `[**]`, or `[***]` (stars indicate importance) and **must be included** in the raw release notes | ||
| - Only user-facing changes (exclude CI, refactoring, technical debt) | ||
| - Prioritize: New features → Improvements → Performance enhancements | ||
| - Use positive language (avoid "bug fix", prefer "improved", "enhanced", "resolved") | ||
| - Mark changes as WordPress-specific, Jetpack-specific, or both | ||
|
|
||
| #### 5. User Confirmation | ||
| Present raw notes to user for: | ||
| - Accuracy verification | ||
| - WordPress vs Jetpack feature classification | ||
| - Any missing or incorrect changes | ||
| - Approval to proceed with editorialization | ||
|
|
||
| #### 6. Editorialization | ||
| Transform raw notes using established playful style: | ||
| - Use engaging, user-friendly language | ||
| - Reference previous release note styles from step 1 | ||
| - Create separate versions for WordPress and Jetpack apps | ||
| - Focus on user benefits and experience improvements | ||
|
|
||
| #### 7. Update Release Notes Files | ||
| Once user confirms the editorialized release notes, **replace** the contents of the following files (discard any existing content): | ||
| - **WordPress release notes**: `WordPress/Resources/release_notes.txt` | ||
| - **Jetpack release notes**: `WordPress/Jetpack/Resources/release_notes.txt` | ||
|
|
||
| Document any process refinements discovered during execution. | ||
|
|
||
| ### Content Guidelines | ||
| - **Include**: New features, UI improvements, performance enhancements, user experience changes | ||
| - **Exclude**: CI changes, code refactoring, dependency updates, internal technical changes | ||
| - **Language**: Positive sentiment, avoid "fix" terminology, focus on improvements and enhancements | ||
| - **Priority Order**: New features → Improvements → Performance → Other user-facing changes | ||
| >>>>>>> c7c40b07a2 (Document test targets and plan convention) | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| import Foundation | ||
|
|
||
| extension Bundle { | ||
| class var designSystemBundle: Bundle { | ||
| #if DEBUG | ||
| // Workaround for https://forums.swift.org/t/swift-5-3-swiftpm-resources-in-tests-uses-wrong-bundle-path/37051 | ||
| if let testBundlePath = ProcessInfo.processInfo.environment["XCTestBundlePath"], | ||
| let bundle = Bundle(path: "\(testBundlePath)/Modules_DesignSystem.bundle") { | ||
| return bundle | ||
| } | ||
| #endif | ||
| return Bundle.module | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like a merge conflict token.