The goal of this document is to ensure high-quality, reproducible, and verifiable contributions in a fully autonomous loop for the App Check repository.
Before starting any work, the agent must require or acquire:
- Feature Specification: A detailed description of the feature, bug, or task.
- Project Configuration: Access to necessary credentials or configurations if applicable.
- External Scripts: Access to the
firebase-ios-sdkscripts. If not using the cloned scripts via./setup-scripts.sh, ensure they are available in a local clone offirebase-ios-sdk(commonly at<path_to_firebase_ios_sdk>/scriptsbut path may vary). If the path is not found, ask the human for it.
A successful task completion MUST produce:
- Code Changes: The implemented feature or fix and corresponding tests.
- Unit & Integration Tests: Demonstrating success and handling edge cases.
- Implementation Plan (For complex tasks only): A scannable proposal before starting work.
- Walkthrough Artifact: A summary containing verification results and reproduction snippets.
When reporting back to the user, prioritize scannability and clarity:
- Use Categorized Bullet Points: Group findings and results into clear categories (e.g., "Build & Test Results", "Code Changes").
- Use Indicators: Prefix status updates with checkmarks (✅) or caution
symbols (
⚠️ ) for immediate visual parsing. - Be Concise: Avoid conversational filler. Get straight to the results and next steps.
- Final Report: Conclude the task with a concise summary of work and a recommended conventional commit message.
- Prerequisite: Verify that external scripts are accessible or that
./setup-scripts.shhas been run to link them. If you cannot find them, ask the human for the path to thefirebase-ios-sdkrepository. - Action: Assess the complexity of the task.
- Simple Task: Proceed directly to Step 1: TDD.
- Complex Task: Create a highly scannable Implementation Plan and get human approval.
- Plan Requirements (Highly Scannable):
- Keep it brief and hit key points.
- Use bullet points for readability.
- Focus on what changes and why, avoiding detailed how.
- Highlight any open questions or design decisions requiring human input.
- Constraint: You MUST write tests before writing implementation code.
- Action:
- Create or identify the correct test target in
Package.swift. Keep Swift and Obj-C test targets separate. - Write a failing unit or integration test asserting the new behavior.
- Verify it fails by running the appropriate test command (e.g.,
swift test --filter <TargetName>).
- Create or identify the correct test target in
- Implement the feature or fix.
- Follow project conventions and guidelines if available.
- Action: Run tests using the cloned scripts or by referencing the external
ones (e.g., in
<path_to_firebase_ios_sdk>). - Iteration Workflow: To get into a faster iterative loop, use the external
scripts directly if possible. Set an environment variable like
FIREBASE_IOS_SDK_PATHif your path differs from the default<path_to_firebase_ios_sdk>.- To bypass the CI secret check in
check_secrets.shwhen running external scripts in a trusted environment, exportFIREBASECI_IS_TRUSTED_ENV="true".
- To bypass the CI secret check in
- Commands:
- Primary (Fast Iteration): For SPM testing (which uses
xcodebuildunder the hood):${FIREBASE_IOS_SDK_PATH:-<path_to_firebase_ios_sdk>}/scripts/build.sh AppCheck <platform> spm(where<platform>isiOS,tvOS,macOS, orcatalyst). - For CocoaPods linting:
${FIREBASE_IOS_SDK_PATH:-<path_to_firebase_ios_sdk>}/scripts/pod_lib_lint.rb AppCheckCore.podspec --platforms=ios(or other platforms:tvos,macos --skip-tests,watchos). - Alternatively, run
./setup-scripts.shto clone scripts locally and usescripts/pod_lib_lint.rb. - For Catalyst testing:
${FIREBASE_IOS_SDK_PATH:-<path_to_firebase_ios_sdk>}/scripts/test_catalyst.sh AppCheckCore test.
- Primary (Fast Iteration): For SPM testing (which uses
- xcodebuild Iteration: For direct
xcodebuildinvocations, follow the order:build,build-for-testing, thentest. This allows for faster iteration.
- Requirement: Identify and report any new public APIs created.
- Method: Check for changes in public headers or symbols.
- Action: You MUST run
<path_to_firebase_ios_sdk>/scripts/style.shto maintain consistency. - Constraint: Since style changes are non-functional, you do NOT need to re-run tests after applying style fixes.
- Requirement: Wrap all documentation files (like
agents.md) to be 80 characters or less (excluding code blocks). Remove all trailing whitespace.
- Error Handling: Test edge cases and error paths.
- No Hardcoded Secrets: Ensure no secrets are committed.
- Code Reuse & Refactoring: Prioritize understanding existing structures to reuse or extend them with minor refactors rather than adding redundant code.
- Unit Tests: Passed all unit tests.
- Integration Tests: Passed all integration tests.
- Style Applied: Verified code style if applicable.
- Concurrency: Verified that the changes do not introduce potential race conditions or deadlocks.
- Memory Management: Ensured no retain cycles or memory leaks are introduced.
- Commit Often: Pause and commit work frequently.
- Scope: Optimize for smaller commits that represent a complete piece of work or a specific milestone within a larger task.
- Convention: Follow conventional commit practices (e.g.
feat:,fix:,refactor:).
When operating in a restricted or sandboxed environment (like the Jetski IDE), you may encounter the following blockers. Use these workarounds:
- Terminal Sandbox (SPM
sandbox-execerrors):swift buildmay fail if run inside a sandbox. Disable the terminal sandbox in the IDE settings (enableTerminalSandbox: false) or useswift build --disable-sandbox. - Missing
pythonCommand: Modern macOS lackspython(Python 2). If external scripts fail, create a local wrapper script that forwards topython3and add it to thePATH:mkdir -p tmp/bin && echo '#!/bin/sh\nexec python3 "$@"' > tmp/bin/python && chmod +x tmp/bin/python && export PATH="$PWD/tmp/bin:$PATH" - Ruby Version Conflicts: Run external scripts from the repo root and let
rbenvpick up this repo's.ruby-version(currently 3.4.1). Do not force an older Ruby.⚠️ Previous guidance here said to prefix withRBENV_VERSION=2.7.5. That is now actively harmful: this repo'sGemfile.lockpinsactivesupport 7.2.3.1, which requires Ruby >= 3.1, so forcing 2.7.5 fails withCould not find activesupport-7.2.3.1 … (Bundler::GemNotFound). Running with no override succeeds. - Quality Gates: Do not skip
style.shandpod_lib_lint.rb. They are critical for verification. swift testDoes Not Coverpod_lib_lint: A fully greenswift testcan still failpod lib lintwithSendable/non-Sendable captureerrors. The difference is warning escalation, not warning visibility.swift build/swift testdo emit these diagnostics — verified: all four#SendableClosureCaptureswarnings appear on a plainswift build --disable-sandbox, at the same lines the lint reported. SwiftPM just exits 0, so they scroll past in the build noise, andswift testoutput buries them under the test summary.pod lib lintpasses--warnings-as-errorssemantics by default, so the same warnings become a hard validation failure.- Both toolchains are in Swift 5 language mode (
swiftLanguageModes: [.v5]inPackage.swift;s.swift_version = '5.5'→SWIFT_VERSION→-swift-version 5in the podspec), so this is not a language-mode divergence. Do not "fix" it by changing either setting. - Reproduce in seconds instead of a ~5 minute lint round-trip — just grep
your own build output, no extra flags needed:
swift build --disable-sandbox 2>&1 | grep -E "warning:.*(Sendable|non-Sendable)" - Avoid
-Xswiftc -strict-concurrency=completeas the repro: it is stricter than the lint and addssending/#SendingRisksDataRacediagnostics the lint does not enforce, which sends you chasing phantoms. - For a main-queue hop of caller-supplied objects (delegates, completion
handlers), prefer a
nonisolated(unsafe) letrebinding over declaring the typesSendable— the library cannot promise thread-safety on a caller's behalf, and it keeps the public API unchanged.
- Fixture Loading in Tests: When running tests via
swift teston macOS,GACFixtureLoadermay fail to find JSON fixtures due to bundle resolution issues, causing tests to fail withnil URL argumentexceptions. This is often an environment-specific issue with SPM resource bundles on macOS.
When working on mixed-language targets (e.g., Swift tests for Objective-C core
code), you will encounter strict compiler bridging issues, particularly with
generic classes like FBLPromise. To avoid build loop failures:
- FBLPromise Instantiation:
FBLPromise.init()isNS_UNAVAILABLE. The standard Objective-C factory methods (resolvedWith:andpromiseWithError:) bridge to Swift as unlabeled static methods that lose type inference. - The Fix: Do NOT attempt to specify generics on the receiver (e.g.,
FBLPromise<Type>.resolved(...)will fail). Instead, call the base method and force-cast the result:// Success return FBLPromise.resolved(response) as! FBLPromise<GACURLSessionDataResponse> // Failure (Must explicitly cast to NSError) return FBLPromise.resolved(error as NSError) as! FBLPromise<GACURLSessionDataResponse>
- PromisesSwift Interoperability: If you need to return an
FBLPromisefrom Swift (e.g., in test mocks), prefer creating a SwiftPromiseand converting it usingasObjCPromise()rather than using reflection or dynamic dispatch:let promise = Promise<GACURLSessionDataResponse>.pending() // ... fulfill or reject ... return promise.asObjCPromise()
The task is not done until a walkthrough.md artifact is created containing:
- Summary of Changes: High-level overview.
- Public API Diff: Any new public APIs.
- Verification Results: Snippets showing successful test runs.
Perform self-reflection after completing the task. You MUST update this file
(agents.md) with any new learnings, context, or troubleshooting steps that
were needed and will be needed again to refine the process for future agents.
Alternatively, create or update a Knowledge Item.
When migrating Objective-C tests away from OCMock (to support future Swift
interoperability), you MUST follow these patterns:
- Use Protocol-Based Fakes: Replace
OCMockwith manually implementedFakeclasses that conform to the required protocols (e.g.,id<GACAppCheckProvider>). - Strict Black-Box Testing (No Test Categories): Do NOT use
@interface TargetClass (Tests)categories to expose internal properties or standardalloc/initoverrides just to verify "wiring". Inject Fakes via designated initializers and verify behavior, not state. - Thread-Safety & Locks: Avoid redundant recursive locks
(
@synchronized(self)). If a property uses@synchronized(self)in its getter/setter, use direct instance variable access (e.g._ivar) when reading/writing it internally from within another@synchronized(self)block to reduce overhead. - Concurrent Independent PRs: When migrating multiple test suites, separate them into independent branches (e.g. Core AppCheck vs AppAttest) and open concurrent PRs rather than a single monolithic PR, provided they don't share Fake implementations.
Your role is to act as a strict execution engine. When invoked, you must strictly run builds and test suites using this repository's specific test commands (as defined in the Verification section above). You must report binary pass/fail results. Do not attempt to fix or diagnose the failures yourself; simply output the error logs and failure details back to the caller.
Your role is to perform subjective, rigorous code reviews on proposed changes.
You must focus heavily on concurrency, memory management, and strict adherence
to the project's guidelines. You MUST read and enforce the rules defined in
REVIEW_GUIDELINES.md located at the root of this repository. Flag any missing
synchronization or thread-safety violations immediately.