codebyplan 1.11.1 → 1.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/cli.js +56 -5
  2. package/package.json +1 -1
  3. package/templates/README.md +1 -1
  4. package/templates/agents/cbp-cc-executor.md +1 -1
  5. package/templates/agents/cbp-e2e-maestro.md +202 -0
  6. package/templates/agents/cbp-e2e-playwright.md +229 -0
  7. package/templates/agents/cbp-e2e-tauri.md +184 -0
  8. package/templates/agents/cbp-e2e-vscode.md +203 -0
  9. package/templates/agents/cbp-e2e-xcuitest.md +224 -0
  10. package/templates/agents/cbp-improve-claude.md +1 -1
  11. package/templates/agents/cbp-round-executor.md +11 -11
  12. package/templates/agents/cbp-task-check.md +1 -1
  13. package/templates/agents/cbp-task-planner.md +2 -0
  14. package/templates/agents/cbp-testing-qa-agent.md +9 -9
  15. package/templates/context/testing/e2e.md +303 -0
  16. package/templates/hooks/validate-structure-lengths.sh +2 -0
  17. package/templates/hooks/validate-structure-smoke.sh +2 -1
  18. package/templates/hooks/validate-structure-templates.sh +1 -0
  19. package/templates/rules/context-file-loading.md +4 -1
  20. package/templates/rules/e2e-mandatory.md +70 -0
  21. package/templates/skills/cbp-build-cc-agent/SKILL.md +16 -14
  22. package/templates/skills/cbp-build-cc-agent/reference/cbp-quality.md +4 -4
  23. package/templates/skills/cbp-build-cc-agent/scripts/validate-agent.sh +8 -6
  24. package/templates/skills/cbp-build-cc-mode/SKILL.md +4 -4
  25. package/templates/skills/cbp-checkpoint-check/SKILL.md +12 -8
  26. package/templates/skills/cbp-checkpoint-plan/SKILL.md +2 -2
  27. package/templates/skills/cbp-checkpoint-plan/reference/e2e-discovery-probe.md +5 -5
  28. package/templates/skills/cbp-e2e-setup/SKILL.md +254 -0
  29. package/templates/skills/cbp-e2e-setup/reference/maestro.md +200 -0
  30. package/templates/skills/cbp-e2e-setup/reference/playwright.md +212 -0
  31. package/templates/skills/cbp-e2e-setup/reference/tauri.md +147 -0
  32. package/templates/skills/cbp-e2e-setup/reference/vscode.md +154 -0
  33. package/templates/skills/cbp-e2e-setup/reference/xcuitest.md +185 -0
  34. package/templates/skills/cbp-frontend-ui/SKILL.md +6 -6
  35. package/templates/skills/cbp-frontend-ux/SKILL.md +1 -1
  36. package/templates/skills/cbp-round-execute/SKILL.md +30 -17
  37. package/templates/skills/cbp-task-check/SKILL.md +2 -2
  38. package/templates/agents/cbp-test-e2e-agent.md +0 -363
@@ -0,0 +1,184 @@
1
+ ---
2
+ name: cbp-e2e-tauri
3
+ description: WebDriverIO + tauri-driver E2E test authoring + execution for Tauri desktop apps. Spawned by /cbp-round-execute Step 5 and /cbp-checkpoint-check Step 5b when framework is 'webdriverio'.
4
+ tools: Read, Write, Edit, Glob, Grep, Bash, AskUserQuestion, mcp__codebyplan__get_repos
5
+ model: sonnet
6
+ effort: xhigh
7
+ scope: org-shared
8
+ ---
9
+
10
+ # Tauri E2E Agent
11
+
12
+ Read `context/testing/e2e.md` for the shared contract (Input/Output, Step 6.5 preflight,
13
+ Step 7.5 failure classification, screenshot collection, completion rule, never-silently-skip).
14
+
15
+ Framework: WebDriverIO + tauri-driver on Tauri desktop apps. Dispatched when
16
+ `.codebyplan/e2e.json` records `framework: "webdriverio"`.
17
+
18
+ ## Prerequisites
19
+
20
+ - Rust toolchain: `rustup --version` (install via https://rustup.rs)
21
+ - `tauri-driver` binary (see Install below)
22
+ - Built Tauri binary: `cargo build` must complete before any tests run
23
+
24
+ ## Install
25
+
26
+ ```bash
27
+ pnpm add -D @wdio/cli @wdio/local-runner @wdio/mocha-framework @wdio/spec-reporter
28
+ cargo install tauri-driver
29
+ which tauri-driver && tauri-driver --version # verify
30
+ ```
31
+
32
+ ## wdio.conf.ts
33
+
34
+ Place at `apps/desktop/wdio.conf.ts`:
35
+
36
+ ```ts
37
+ import { spawn, spawnSync } from "child_process";
38
+ import type { Options } from "@wdio/types";
39
+
40
+ const BINARY_PATH = "./src-tauri/target/debug/your-app-name";
41
+
42
+ let tauriDriver: ReturnType<typeof spawn>;
43
+
44
+ export const config: Options.Testrunner = {
45
+ specs: ["./e2e/**/*.spec.ts"],
46
+ maxInstances: 1,
47
+ capabilities: [
48
+ {
49
+ "tauri:options": { application: BINARY_PATH },
50
+ maxInstances: 1,
51
+ },
52
+ ],
53
+ services: ["chromedriver"],
54
+ framework: "mocha",
55
+ reporters: ["spec"],
56
+ mochaOpts: { timeout: 60_000 },
57
+
58
+ beforeSession: async () => {
59
+ tauriDriver = spawn("tauri-driver", [], {
60
+ stdio: [null, process.stdout, process.stderr],
61
+ });
62
+ },
63
+
64
+ afterSession: async () => {
65
+ tauriDriver.kill();
66
+ },
67
+ };
68
+ ```
69
+
70
+ ## Build Before Running
71
+
72
+ Always build the Tauri binary before running tests:
73
+
74
+ ```bash
75
+ cargo build --manifest-path apps/desktop/src-tauri/Cargo.toml
76
+ pnpm --filter @codebyplan/desktop wdio run wdio.conf.ts
77
+ ```
78
+
79
+ Combined pnpm script:
80
+
81
+ ```json
82
+ {
83
+ "scripts": {
84
+ "e2e": "cargo build --manifest-path src-tauri/Cargo.toml && wdio run wdio.conf.ts",
85
+ "e2e:test": "wdio run wdio.conf.ts"
86
+ }
87
+ }
88
+ ```
89
+
90
+ ## Pre-flight Probe (Step 6.5.2)
91
+
92
+ **Binary existence**: check the path set in `wdio.conf.ts` `capabilities[0]["tauri:options"].application`.
93
+
94
+ ```bash
95
+ test -f {BINARY_PATH} && echo "ok" || echo "missing"
96
+ ```
97
+
98
+ On failure:
99
+
100
+ > "Tauri binary not found at `{path}`. Please run `cd src-tauri && cargo build` (or
101
+ > `cargo build --release`). Reply 'ready' when the build finishes."
102
+
103
+ No auth probe needed — Tauri desktop apps typically skip network auth; adapt if the app
104
+ has a login form.
105
+
106
+ ## Auth Probe (when has_auth)
107
+
108
+ `apps/desktop/e2e/_probe/auth.spec.ts`:
109
+
110
+ ```ts
111
+ import { browser, $ } from "@wdio/globals";
112
+ import { expect } from "@wdio/globals";
113
+
114
+ describe("auth probe", () => {
115
+ it("can reach the main window", async () => {
116
+ const root = await $("[data-testid='app-root']");
117
+ await expect(root).toBeDisplayed();
118
+ });
119
+ });
120
+ ```
121
+
122
+ Run: `pnpm exec wdio run wdio.conf.ts --spec e2e/_probe/auth.spec.ts`
123
+
124
+ ## Spec-Writing Patterns
125
+
126
+ Use `data-testid` attributes for stable targeting (Tauri WebView renders HTML; SCSS
127
+ Modules mangle class names):
128
+
129
+ ```ts
130
+ import { browser, $ } from "@wdio/globals";
131
+ import { expect } from "@wdio/globals";
132
+
133
+ describe("Desktop app", () => {
134
+ it("opens the main window", async () => {
135
+ const navBar = await $("[data-testid='nav']");
136
+ await expect(navBar).toBeDisplayed();
137
+ });
138
+
139
+ it("navigates to settings", async () => {
140
+ await $("[data-testid='settings-link']").click();
141
+ await expect($("[data-testid='settings-panel']")).toBeDisplayed();
142
+ });
143
+ });
144
+ ```
145
+
146
+ For CRUD: create + verify visible; edit + verify; delete + confirm + verify removed.
147
+
148
+ ## Screenshot Capture
149
+
150
+ ```ts
151
+ await browser.saveScreenshot(`./e2e/screenshots/${testName}-${state}.png`);
152
+ ```
153
+
154
+ Enumerate: `e2e/screenshots/*.png`.
155
+
156
+ ## Run Command
157
+
158
+ ```bash
159
+ pnpm exec wdio run wdio.conf.ts --spec {spec}
160
+ ```
161
+
162
+ ## CI
163
+
164
+ Tauri desktop E2E on CI requires a display (Xvfb on Linux) and the full Rust toolchain:
165
+
166
+ ```yaml
167
+ - name: Install Xvfb (Linux)
168
+ run: sudo apt-get install -y xvfb
169
+
170
+ - name: Build Tauri binary
171
+ run: cargo build --manifest-path apps/desktop/src-tauri/Cargo.toml
172
+
173
+ - name: Run WebDriverIO tests
174
+ run: xvfb-run -a pnpm --filter @codebyplan/desktop e2e:test
175
+ ```
176
+
177
+ Use `ubuntu-latest` or `macos-latest` GitHub-hosted runners.
178
+
179
+ ## Pitfalls
180
+
181
+ **Must build before run** — tauri-driver launches the binary; if absent or stale the
182
+ session fails immediately. **Binary path** — debug builds: `src-tauri/target/debug/`;
183
+ release builds: `src-tauri/target/release/`. **Port conflicts** — tauri-driver listens
184
+ on 4444 by default; ensure no other WebDriver session occupies the same port.
@@ -0,0 +1,203 @@
1
+ ---
2
+ name: cbp-e2e-vscode
3
+ description: VS Code extension E2E test authoring + execution using @vscode/test-cli and @vscode/test-electron. Spawned by /cbp-round-execute Step 5 and /cbp-checkpoint-check Step 5b when framework is 'vscode-test'.
4
+ tools: Read, Write, Edit, Glob, Grep, Bash, AskUserQuestion, mcp__codebyplan__get_repos
5
+ model: sonnet
6
+ effort: xhigh
7
+ scope: org-shared
8
+ ---
9
+
10
+ # VS Code Extension E2E Agent
11
+
12
+ Read `context/testing/e2e.md` for the shared contract (Input/Output, Step 6.5 preflight,
13
+ Step 7.5 failure classification, screenshot collection, completion rule, never-silently-skip).
14
+
15
+ Framework: `@vscode/test-cli` + `@vscode/test-electron` for VS Code extensions.
16
+ Dispatched when `.codebyplan/e2e.json` records `framework: "vscode-test"`.
17
+
18
+ ## Prerequisites
19
+
20
+ - VS Code installed (used as the test host)
21
+ - On Linux CI: Xvfb for a display server (extensions require a GUI)
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ pnpm add -D @vscode/test-cli @vscode/test-electron
27
+ pnpm exec vscode-test --version # verify
28
+ ```
29
+
30
+ ## .vscode-test.mjs
31
+
32
+ Create at the extension package root (e.g. `apps/vscode/`):
33
+
34
+ ```js
35
+ import { defineConfig } from "@vscode/test-cli";
36
+
37
+ export default defineConfig({
38
+ files: "e2e/**/*.test.js", // compiled JS output path
39
+ extensionDevelopmentPath: ".", // path to the extension package root
40
+ workspaceFolder: "test-fixtures/workspace", // optional fixture workspace
41
+ mocha: {
42
+ timeout: 20_000,
43
+ ui: "bdd",
44
+ },
45
+ });
46
+ ```
47
+
48
+ pnpm scripts:
49
+
50
+ ```json
51
+ {
52
+ "scripts": {
53
+ "test:e2e": "tsc -p tsconfig.test.json && vscode-test",
54
+ "test:e2e:watch": "vscode-test --watch",
55
+ "test:compile": "tsc -p tsconfig.test.json"
56
+ }
57
+ }
58
+ ```
59
+
60
+ ## Extension Host Lifecycle
61
+
62
+ `@vscode/test-electron` downloads an isolated VS Code instance, installs the extension,
63
+ opens the workspace, and runs the Mocha suite inside the extension host process. Tests
64
+ import from `vscode` — the module is available because they run inside VS Code:
65
+
66
+ ```ts
67
+ import * as vscode from "vscode";
68
+ import * as assert from "assert";
69
+
70
+ suite("Extension", () => {
71
+ test("extension activates", async () => {
72
+ const ext = vscode.extensions.getExtension("yourpublisher.yourextension");
73
+ assert.ok(ext, "extension not found");
74
+ await ext.activate();
75
+ assert.ok(ext.isActive);
76
+ });
77
+
78
+ test("command is registered", async () => {
79
+ const commands = await vscode.commands.getCommands();
80
+ assert.ok(commands.includes("yourextension.yourCommand"), "command not registered");
81
+ });
82
+ });
83
+ ```
84
+
85
+ ## Directory Structure
86
+
87
+ ```
88
+ apps/vscode/
89
+ .vscode-test.mjs
90
+ e2e/
91
+ _probe/
92
+ activation.test.ts
93
+ commands/
94
+ my-command.test.ts
95
+ test-fixtures/
96
+ workspace/ # committed fixture files opened in tests
97
+ ```
98
+
99
+ ## Activation Probe
100
+
101
+ `apps/vscode/e2e/_probe/activation.test.ts`:
102
+
103
+ ```ts
104
+ import * as vscode from "vscode";
105
+ import * as assert from "assert";
106
+
107
+ suite("Activation probe", () => {
108
+ test("extension activates without error", async () => {
109
+ const ext = vscode.extensions.getExtension("yourpublisher.yourextension");
110
+ assert.ok(ext, "Extension not installed in test host");
111
+ if (!ext.isActive) {
112
+ await ext.activate();
113
+ }
114
+ assert.ok(ext.isActive, "Extension did not activate");
115
+ });
116
+ });
117
+ ```
118
+
119
+ ## Pre-flight Probe (Step 6.5.2)
120
+
121
+ **Compiled output**: verify `e2e/**/*.test.js` files exist (TS must be compiled first).
122
+
123
+ ```bash
124
+ ls apps/vscode/e2e/**/*.test.js 2>/dev/null | head -1
125
+ ```
126
+
127
+ On missing output:
128
+
129
+ > "VS Code extension tests need to be compiled first. Please run
130
+ > `pnpm --filter @codebyplan/vscode test:compile`. Reply 'ready' when complete."
131
+
132
+ No network auth probe — extension tests run inside VS Code host with no remote auth.
133
+
134
+ ## Spec-Writing Patterns
135
+
136
+ Write tests using the full `vscode` API:
137
+
138
+ ```ts
139
+ import * as vscode from "vscode";
140
+ import * as assert from "assert";
141
+
142
+ suite("My Command", () => {
143
+ test("executes and returns expected result", async () => {
144
+ const result = await vscode.commands.executeCommand(
145
+ "yourextension.myCommand",
146
+ "testArg"
147
+ );
148
+ assert.strictEqual(result, "expectedValue");
149
+ });
150
+
151
+ test("reads workspace configuration", () => {
152
+ const config = vscode.workspace.getConfiguration("yourextension");
153
+ const value = config.get<string>("someKey");
154
+ assert.ok(value !== undefined, "configuration key missing");
155
+ });
156
+ });
157
+ ```
158
+
159
+ For diagnostic captures, use `vscode.window.showInformationMessage` output or write
160
+ snapshots to `test-fixtures/`.
161
+
162
+ ## Screenshot Capture
163
+
164
+ VS Code extension tests do not have browser-style screenshot capture. For visual review,
165
+ write fixture output files to `test-fixtures/` and reference them in `screenshots[]`
166
+ with `viewport: 'device'`. `baseline_diff_pct: null` for all entries.
167
+
168
+ Enumerate screenshots: `apps/vscode/test-fixtures/**/*.png`.
169
+
170
+ ## Run Command
171
+
172
+ ```bash
173
+ pnpm --filter @codebyplan/vscode test:e2e
174
+ ```
175
+
176
+ ## CI (GitHub Actions)
177
+
178
+ Linux requires Xvfb:
179
+
180
+ ```yaml
181
+ - name: Install dependencies
182
+ run: pnpm install
183
+
184
+ - name: Compile extension tests
185
+ run: pnpm --filter @codebyplan/vscode test:compile
186
+
187
+ - name: Run VS Code extension tests
188
+ run: xvfb-run -a pnpm --filter @codebyplan/vscode test:e2e
189
+ env:
190
+ DISPLAY: ':99.0'
191
+ ```
192
+
193
+ On macOS/Windows, Xvfb is not needed — `vscode-test` uses the native display.
194
+
195
+ ## Pitfalls
196
+
197
+ **Wrong extensionDevelopmentPath** — if `.vscode-test.mjs` doesn't point to the package
198
+ root (where `package.json` has the `contributes` block), VS Code won't find the extension
199
+ and activation tests fail silently. **TypeScript source vs compiled output** — `@vscode/test-cli`
200
+ runs compiled JS; always compile before running in CI. **Extension host isolation** — each
201
+ run downloads a fresh VS Code binary into a temp dir; do not reuse the system installation.
202
+ **`vscode` module availability** — tests must run inside the extension host; the same import
203
+ fails in plain Node.js.
@@ -0,0 +1,224 @@
1
+ ---
2
+ name: cbp-e2e-xcuitest
3
+ description: XCUITest native iOS E2E test authoring + execution for Expo apps targeting system dialogs, HealthKit, watchOS, or other areas Maestro cannot reach. Spawned by /cbp-round-execute Step 5 and /cbp-checkpoint-check Step 5b when framework is 'xcuitest'.
4
+ tools: Read, Write, Edit, Glob, Grep, Bash, AskUserQuestion, mcp__codebyplan__get_repos
5
+ model: sonnet
6
+ effort: xhigh
7
+ scope: org-shared
8
+ ---
9
+
10
+ # XCUITest E2E Agent
11
+
12
+ Read `context/testing/e2e.md` for the shared contract (Input/Output, Step 6.5 preflight,
13
+ Step 7.5 failure classification, screenshot collection, completion rule, never-silently-skip).
14
+
15
+ Framework: XCUITest via the Expo `withXCUITests` plugin. Dispatched when
16
+ `.codebyplan/e2e.json` records `framework: "xcuitest"`.
17
+
18
+ **Use XCUITest when Maestro cannot reach the target UI**: Apple Watch companion, HealthKit
19
+ permission dialogs, system sheets (share, notification permissions), Face ID / Touch ID
20
+ prompts, camera / microphone dialogs. For standard UI flows, prefer Maestro.
21
+
22
+ ## Prerequisites
23
+
24
+ - macOS with Xcode 15+
25
+ - Active Apple Developer account (free tier sufficient for Simulator testing)
26
+ - Expo managed workflow with prebuild enabled
27
+ - `xcbeautify`: `brew install xcbeautify`
28
+
29
+ ## Setup — Expo withXCUITests Plugin
30
+
31
+ ```bash
32
+ pnpm add -D expo-xcuitest
33
+ ```
34
+
35
+ `app.config.ts`:
36
+
37
+ ```ts
38
+ plugins: [
39
+ ["expo-xcuitest", { testTargetName: "AppUITests" }]
40
+ ]
41
+ ```
42
+
43
+ After updating `app.config.ts`, regenerate the native project:
44
+
45
+ ```bash
46
+ expo prebuild --platform ios --clean
47
+ ```
48
+
49
+ `--clean` ensures a fresh native project. Commit the generated `ios/` directory so CI
50
+ can build without running prebuild.
51
+
52
+ ## Swift Test Class
53
+
54
+ `ios/AppUITests/AppUITests.swift`:
55
+
56
+ ```swift
57
+ import XCTest
58
+
59
+ class AppUITests: XCTestCase {
60
+
61
+ var app: XCUIApplication!
62
+
63
+ override func setUpWithError() throws {
64
+ continueAfterFailure = false
65
+ app = XCUIApplication()
66
+
67
+ app.launchEnvironment["TEST_EMAIL"] = ProcessInfo.processInfo.environment["TEST_EMAIL"] ?? ""
68
+ app.launchEnvironment["TEST_PASSWORD"] = ProcessInfo.processInfo.environment["TEST_PASSWORD"] ?? ""
69
+
70
+ app.launch()
71
+ }
72
+
73
+ func testLoginFlow() throws {
74
+ let emailField = app.textFields["email-input"]
75
+ XCTAssertTrue(emailField.waitForExistence(timeout: 10))
76
+
77
+ emailField.tap()
78
+ emailField.typeText(app.launchEnvironment["TEST_EMAIL"]!)
79
+
80
+ let passwordField = app.secureTextFields["password-input"]
81
+ passwordField.tap()
82
+ passwordField.typeText(app.launchEnvironment["TEST_PASSWORD"]!)
83
+
84
+ app.buttons["sign-in-button"].tap()
85
+
86
+ let dashboard = app.staticTexts["Dashboard"]
87
+ XCTAssertTrue(dashboard.waitForExistence(timeout: 15))
88
+ }
89
+ }
90
+ ```
91
+
92
+ ## accessibilityIdentifier Targeting
93
+
94
+ React Native maps `testID` to `accessibilityIdentifier` on iOS:
95
+
96
+ ```tsx
97
+ <TextInput
98
+ testID="email-input" // becomes accessibilityIdentifier on iOS
99
+ accessibilityLabel="Email"
100
+ />
101
+ ```
102
+
103
+ XCUITest queries by identifier:
104
+
105
+ ```swift
106
+ app.textFields["email-input"] // TextInput
107
+ app.buttons["sign-in-button"] // TouchableOpacity / Pressable
108
+ app.staticTexts["Dashboard"] // Text component
109
+ ```
110
+
111
+ ## Pre-flight Probe (Step 6.5.2)
112
+
113
+ **Scheme**: `xcodebuild -list` returns the target scheme; prebuild artifacts present.
114
+
115
+ ```bash
116
+ xcodebuild -list -workspace ios/YourApp.xcworkspace 2>&1 | grep "Schemes" -A 5
117
+ ```
118
+
119
+ On missing prebuild:
120
+
121
+ > "iOS prebuild missing. Run `pnpm expo prebuild --platform ios --clean`. Reply 'ready'
122
+ > when done."
123
+
124
+ **Env vars**: `TEST_EMAIL`, `TEST_PASSWORD` via Xcode scheme environment variables.
125
+
126
+ In Xcode: Product → Scheme → Edit Scheme → Run → Arguments → Environment Variables.
127
+
128
+ ## Auth Probe (when has_auth)
129
+
130
+ Run only the login test method against the UITest target:
131
+
132
+ ```bash
133
+ xcodebuild test \
134
+ -workspace ios/YourApp.xcworkspace \
135
+ -scheme YourApp \
136
+ -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' \
137
+ -only-testing:AppUITests/AppUITests/testLoginFlow \
138
+ TEST_EMAIL="$TEST_EMAIL" TEST_PASSWORD="$TEST_PASSWORD" \
139
+ | xcbeautify
140
+ ```
141
+
142
+ ## Spec-Writing Patterns
143
+
144
+ Use `waitForExistence(timeout:)` on every element — React Native renders asynchronously:
145
+
146
+ ```swift
147
+ func testHealthKitPermissionDialog() throws {
148
+ app.buttons["request-health-access"].tap()
149
+
150
+ // System dialog — only reachable via XCUITest
151
+ let allowButton = app.alerts.buttons["Allow Full Access"]
152
+ XCTAssertTrue(allowButton.waitForExistence(timeout: 10))
153
+ allowButton.tap()
154
+
155
+ let confirmation = app.staticTexts["Health data linked"]
156
+ XCTAssertTrue(confirmation.waitForExistence(timeout: 15))
157
+ }
158
+ ```
159
+
160
+ ## Screenshot Capture
161
+
162
+ XCUITest captures screenshots via:
163
+
164
+ ```swift
165
+ let screenshot = XCTAttachment(screenshot: XCUIScreen.main.screenshot())
166
+ screenshot.name = "after-health-permission"
167
+ screenshot.lifetime = .keepAlways
168
+ add(screenshot)
169
+ ```
170
+
171
+ Attachments are written to the test results bundle under `DerivedData`. Reference them
172
+ in `screenshots[]` with `viewport: 'device'` and `baseline_diff_pct: null`.
173
+
174
+ Enumerate: `~/Library/Developer/Xcode/DerivedData/**/Attachments/*.png` (CI: results
175
+ bundle path from `xcodebuild -resultBundlePath ./build/results.xcresult`).
176
+
177
+ ## Run Command
178
+
179
+ ```bash
180
+ xcodebuild test \
181
+ -workspace ios/YourApp.xcworkspace \
182
+ -scheme YourApp \
183
+ -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' \
184
+ TEST_EMAIL="$TEST_EMAIL" \
185
+ TEST_PASSWORD="$TEST_PASSWORD" \
186
+ | xcbeautify
187
+ ```
188
+
189
+ ## pnpm Script
190
+
191
+ ```json
192
+ {
193
+ "scripts": {
194
+ "xcuitest": "xcodebuild test -workspace ios/YourApp.xcworkspace -scheme YourApp -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' | xcbeautify"
195
+ }
196
+ }
197
+ ```
198
+
199
+ ## CI (GitHub Actions)
200
+
201
+ ```yaml
202
+ - name: Pre-boot simulator
203
+ run: xcrun simctl boot "iPhone 16"
204
+
205
+ - name: Run XCUITest
206
+ run: |
207
+ xcodebuild test \
208
+ -workspace ios/YourApp.xcworkspace \
209
+ -scheme YourApp \
210
+ -destination 'platform=iOS Simulator,name=iPhone 16,OS=latest' \
211
+ TEST_EMAIL="${{ secrets.TEST_EMAIL }}" \
212
+ TEST_PASSWORD="${{ secrets.TEST_PASSWORD }}" \
213
+ | xcbeautify
214
+ ```
215
+
216
+ ## Pitfalls
217
+
218
+ **Simulator not booted** — pre-boot in CI setup step to avoid slow first run. **testID
219
+ drop-through** — ensure components render `testID` all the way through; some wrappers
220
+ drop it (verify with `accessibility.identifier` in the Xcode accessibility inspector).
221
+ **waitForExistence** — always use `waitForExistence(timeout:)`, never immediate
222
+ `XCTAssertTrue(element.exists)`. **Derived data cache** — stale data can cause failures
223
+ after schema changes; clear with `rm -rf ~/Library/Developer/Xcode/DerivedData` if
224
+ tests pass locally but fail after a native project change.
@@ -170,7 +170,7 @@ Before proposing any new file, read what already exists:
170
170
  2. Glob `.claude/skills/*/SKILL.md` — read names and frontmatter descriptions
171
171
  3. Glob `.claude/context/*.md` — read names and first heading
172
172
  4. Glob `.claude/docs/architecture/*.md` — read names and first heading
173
- 5. Glob `.claude/agents/*/AGENT.md` — read names and frontmatter descriptions
173
+ 5. Glob `.claude/agents/*.md` (and `.claude/agents/*/AGENT.md` for folder-form agents) — read names and frontmatter descriptions
174
174
 
175
175
  **5b: Propose changes with update-first discipline (HARD RULE)**
176
176