[2.18.0] Add Dynamic Capability Discovery and iCloud sync patterns (#62)

* [2.17.0] Expand agent-native skill with mobile app learnings

Major expansion of agent-native-architecture skill based on real-world
learnings from building the Every Reader iOS app.

New reference documents:
- dynamic-context-injection.md: Runtime app state in system prompts
- action-parity-discipline.md: Ensuring agents can do what users can
- shared-workspace-architecture.md: Agents and users in same data space
- agent-native-testing.md: Testing patterns for agent-native apps
- mobile-patterns.md: Background execution, permissions, cost awareness

Updated references:
- architecture-patterns.md: Added Unified Agent Architecture, Agent-to-UI
  Communication, and Model Tier Selection patterns

Enhanced agent-native-reviewer with comprehensive review process covering
all new patterns, including mobile-specific verification.

Key insight: "The agent should be able to do anything the user can do,
through tools that mirror UI capabilities, with full context about the
app state."

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* [2.18.0] Add Dynamic Capability Discovery and iCloud sync patterns

New patterns in agent-native-architecture skill:

- **Dynamic Capability Discovery** - For agent-native apps integrating with
  external APIs (HealthKit, HomeKit, GraphQL), use a discovery tool (list_*)
  plus a generic access tool instead of individual tools per endpoint.
  (Note: Static mapping is fine for constrained agents with limited scope.)

- **CRUD Completeness** - Every entity needs create, read, update, AND delete.

- **iCloud File Storage** - Use iCloud Documents for shared workspace to get
  free, automatic multi-device sync without building a sync layer.

- **Architecture Review Checklist** - Pushes reviewer findings earlier into
  design phase. Covers tool design, action parity, UI integration, context.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Dan Shipper
2025-12-25 13:03:07 -05:00
committed by GitHub
parent 5a79f97374
commit 1bc6bd9164
12 changed files with 3523 additions and 60 deletions

View File

@@ -203,6 +203,259 @@ tool("apply_pending", async () => {
- docs/* (documentation)
</pattern>
<pattern name="unified-agent-architecture">
## Unified Agent Architecture
One execution engine, many agent types. All agents use the same orchestrator but with different configurations.
```
┌─────────────────────────────────────────────────────────────┐
│ AgentOrchestrator │
├─────────────────────────────────────────────────────────────┤
│ - Lifecycle management (start, pause, resume, stop) │
│ - Checkpoint/restore (for background execution) │
│ - Tool execution │
│ - Chat integration │
└─────────────────────────────────────────────────────────────┘
│ │ │
┌─────┴─────┐ ┌─────┴─────┐ ┌─────┴─────┐
│ Research │ │ Chat │ │ Profile │
│ Agent │ │ Agent │ │ Agent │
└───────────┘ └───────────┘ └───────────┘
- web_search - read_library - read_photos
- write_file - publish_to_feed - write_file
- read_file - web_search - analyze_image
```
**Implementation:**
```swift
// All agents use the same orchestrator
let session = try await AgentOrchestrator.shared.startAgent(
config: ResearchAgent.create(book: book), // Config varies
tools: ResearchAgent.tools, // Tools vary
context: ResearchAgent.context(for: book) // Context varies
)
// Agent types define their own configuration
struct ResearchAgent {
static var tools: [AgentTool] {
[
FileTools.readFile(),
FileTools.writeFile(),
WebTools.webSearch(),
WebTools.webFetch(),
]
}
static func context(for book: Book) -> String {
"""
You are researching "\(book.title)" by \(book.author).
Save findings to Documents/Research/\(book.id)/
"""
}
}
struct ChatAgent {
static var tools: [AgentTool] {
[
FileTools.readFile(),
FileTools.writeFile(),
BookTools.readLibrary(),
BookTools.publishToFeed(), // Chat can publish directly
WebTools.webSearch(),
]
}
static func context(library: [Book]) -> String {
"""
You help the user with their reading.
Available books: \(library.map { $0.title }.joined(separator: ", "))
"""
}
}
```
**Benefits:**
- Consistent lifecycle management across all agent types
- Automatic checkpoint/resume (critical for mobile)
- Shared tool protocol
- Easy to add new agent types
- Centralized error handling and logging
</pattern>
<pattern name="agent-to-ui-communication">
## Agent-to-UI Communication
When agents take actions, the UI should reflect them immediately. The user should see what the agent did.
**Pattern 1: Shared Data Store (Recommended)**
Agent writes through the same service the UI observes:
```swift
// Shared service
class BookLibraryService: ObservableObject {
static let shared = BookLibraryService()
@Published var books: [Book] = []
@Published var feedItems: [FeedItem] = []
func addFeedItem(_ item: FeedItem) {
feedItems.append(item)
persist()
}
}
// Agent tool writes through shared service
tool("publish_to_feed", async ({ bookId, content, headline }) => {
let item = FeedItem(bookId: bookId, content: content, headline: headline)
BookLibraryService.shared.addFeedItem(item) // Same service UI uses
return { text: "Published to feed" }
})
// UI observes the same service
struct FeedView: View {
@StateObject var library = BookLibraryService.shared
var body: some View {
List(library.feedItems) { item in
FeedItemRow(item: item)
// Automatically updates when agent adds items
}
}
}
```
**Pattern 2: File System Observation**
For file-based data, watch the file system:
```swift
class ResearchWatcher: ObservableObject {
@Published var files: [URL] = []
private var watcher: DirectoryWatcher?
func watch(bookId: String) {
let path = documentsURL.appendingPathComponent("Research/\(bookId)")
watcher = DirectoryWatcher(path: path) { [weak self] in
self?.reload(from: path)
}
reload(from: path)
}
}
// Agent writes files
tool("write_file", { path, content }) -> {
writeFile(documentsURL.appendingPathComponent(path), content)
// DirectoryWatcher triggers UI update automatically
}
```
**Pattern 3: Event Bus (Cross-Component)**
For complex apps with multiple independent components:
```typescript
// Shared event bus
const agentEvents = new EventEmitter();
// Agent tool emits events
tool("publish_to_feed", async ({ content }) => {
const item = await feedService.add(content);
agentEvents.emit('feed:new-item', item);
return { text: "Published" };
});
// UI components subscribe
function FeedView() {
const [items, setItems] = useState([]);
useEffect(() => {
const handler = (item) => setItems(prev => [...prev, item]);
agentEvents.on('feed:new-item', handler);
return () => agentEvents.off('feed:new-item', handler);
}, []);
return <FeedList items={items} />;
}
```
**What to avoid:**
```swift
// BAD: UI doesn't observe agent changes
// Agent writes to database directly
tool("publish_to_feed", { content }) {
database.insert("feed", content) // UI doesn't see this
}
// UI loads once at startup, never refreshes
struct FeedView: View {
let items = database.query("feed") // Stale!
}
```
</pattern>
<pattern name="model-tier-selection">
## Model Tier Selection
Different agents need different intelligence levels. Use the cheapest model that achieves the outcome.
| Agent Type | Recommended Tier | Reasoning |
|------------|-----------------|-----------|
| Chat/Conversation | Balanced | Fast responses, good reasoning |
| Research | Balanced | Tool loops, not ultra-complex synthesis |
| Content Generation | Balanced | Creative but not synthesis-heavy |
| Complex Analysis | Powerful | Multi-document synthesis, nuanced judgment |
| Profile/Onboarding | Powerful | Photo analysis, complex pattern recognition |
| Simple Queries | Fast/Haiku | Quick lookups, simple transformations |
**Implementation:**
```swift
enum ModelTier {
case fast // claude-3-haiku: Quick, cheap, simple tasks
case balanced // claude-3-sonnet: Good balance for most tasks
case powerful // claude-3-opus: Complex reasoning, synthesis
}
struct AgentConfig {
let modelTier: ModelTier
let tools: [AgentTool]
let systemPrompt: String
}
// Research agent: balanced tier
let researchConfig = AgentConfig(
modelTier: .balanced,
tools: researchTools,
systemPrompt: researchPrompt
)
// Profile analysis: powerful tier (complex photo interpretation)
let profileConfig = AgentConfig(
modelTier: .powerful,
tools: profileTools,
systemPrompt: profilePrompt
)
// Quick lookup: fast tier
let lookupConfig = AgentConfig(
modelTier: .fast,
tools: [readLibrary],
systemPrompt: "Answer quick questions about the user's library."
)
```
**Cost optimization strategies:**
- Start with balanced tier, only upgrade if quality insufficient
- Use fast tier for tool-heavy loops where each turn is simple
- Reserve powerful tier for synthesis tasks (comparing multiple sources)
- Consider token limits per turn to control costs
</pattern>
<design_questions>
## Questions to Ask When Designing
@@ -212,4 +465,7 @@ tool("apply_pending", async () => {
4. **What decisions should be hardcoded?** (security boundaries, approval requirements)
5. **How does the agent verify its work?** (health checks, build verification)
6. **How does the agent recover from mistakes?** (git rollback, approval gates)
7. **How does the UI know when agent changes state?** (shared store, file watching, events)
8. **What model tier does each agent type need?** (fast, balanced, powerful)
9. **How do agents share infrastructure?** (unified orchestrator, shared tools)
</design_questions>