Agent skills, system prompts, and AI developer rules for Snapchat/Valdi
# AGENTS.md - Guide for AI Coding Assistants
This document provides context and guidelines for AI coding assistants working with the Valdi codebase.
## Overview
Valdi is a cross-platform UI framework that compiles declarative TypeScript components to native views on iOS, Android, and macOS. Write your UI once, and it runs natively on multiple platforms without web views or JavaScript bridges.
Valdi has been used in production at Snap for 8 years and is now available as open source under the MIT license.
### How Valdi Works
The Valdi compiler takes TypeScript source files (using TSX/JSX syntax) and compiles them into `.valdimodule` files. These compiled modules are read by the Valdi runtime on each platform to render native views. **This is not TypeScript rendered in a WebView** - Valdi generates true native UI components.
## 🚨 AI Anti-Hallucination: This is NOT React!
**CRITICAL**: Valdi uses TSX/JSX syntax but **is fundamentally different from React**. The most common AI error is suggesting React patterns that do not exist in Valdi.
### ❌ FORBIDDEN React Patterns (Do NOT use these)
These React APIs **DO NOT EXIST** in Valdi and will cause compilation errors:
```typescript
// ❌ WRONG - useState does not exist in Valdi
const [count, setCount] = useState(0);
// ❌ WRONG - useEffect does not exist in Valdi
useEffect(() => { ... }, []);
// ❌ WRONG - useContext does not exist in Valdi
const value = useContext(MyContext);
// ❌ WRONG - useMemo, useCallback, useRef do not exist
const memoized = useMemo(() => ..., []);
const callback = useCallback(() => ..., []);
const ref = useRef(null);
// ❌ WRONG - React.Component does not exist
class MyComponent extends React.Component { ... }
// ❌ WRONG - Functional components do not exist
function MyComponent(props) { return <view />; }
const MyComponent = () => <view />;
```
### ⚠️ COMMON AI MISTAKES (Even advanced models make these errors!)
**These patterns DO NOT EXIST in Valdi** but are commonly suggested by AI models:
```typescript
// ❌ WRONG - markNeedsRender() does NOT exist
class MyComponent extends Component {
count = 0;
handleClick() {
this.count++;
this.markNeedsRender(); // ERROR: This method doesn't exist!
}
}
// ❌ WRONG - scheduleRender() exists but is DEPRECATED
class MyComponent extends Component {
count = 0;
handleClick() {
this.count++;
this.scheduleRender(); // DEPRECATED: Use StatefulComponent with setState() instead
}
}
// ❌ WRONG - onMount/onUpdate/onUnmount do NOT exist (React-like names)
class MyComponent extends Component {
onMount() { } // Should be: onCreate()
onUpdate() { } // Should be: onViewModelUpdate(previousViewModel)
onUnmount() { } // Should be: onDestroy()
}
// ❌ WRONG - this.props does NOT exist
class MyComponent extends Component {
onRender() {
<label value={this.props.title} />; // Should be: this.viewModel.title
}
}
// ❌ WRONG - this.context.get() does NOT exist
class MyComponent extends Component {
onRender() {
const service = this.context.get(MyService); // This API doesn't exist!
}
}
// ❌ WRONG - Returning JSX from onRender()
class MyComponent extends Component {
onRender() {
return <view />; // onRender() returns void, not JSX!
}
}
```
**Why these errors happen**: AI models are heavily trained on React code, and Valdi's TSX syntax triggers incorrect React pattern suggestions. Always verify against actual Valdi APIs.
### ✅ CORRECT Valdi Patterns (Use these instead)
Valdi uses a **class-based component model** with explicit lifecycle methods:
```typescript
// ✅ CORRECT - Stateful Valdi component pattern
import { StatefulComponent } from 'valdi_core/src/Component';
class MyComponent extends StatefulComponent<ViewModel, State> {
// State management via StatefulComponent
state = { count: 0 };
// Lifecycle: Called when component is first created
onCreate() {
console.log('Component created');
}
// Lifecycle: Called when viewModel changes
onViewModelUpdate(previousViewModel: ViewModel) {
console.log('ViewModel changed');
}
// Lifecycle: Called before component is removed
onDestroy() {
console.log('Component destroying');
}
// Required: Render method returns void (not JSX!)
onRender() {
// Note: onRender returns VOID, not JSX
// JSX is written as a statement, not returned
<view>
<label value={`Count: ${this.state.count}`} />
<button
title="Increment"
onPress={() => {
this.setState({ count: this.state.count + 1 }); // setState triggers re-render
}}
/>
</view>;
}
}
// For components without state, use Component
import { Component } from 'valdi_core/src/Component';
class SimpleComponent extends Component<ViewModel> {
onRender() {
<label value={this.viewModel.title} />;
}
}
```
### Key Valdi Concepts
1. **State Management**: Use `StatefulComponent` with `setState()`, not `useState`
2. **Props Access**: Use `this.viewModel`, not `this.props`
3. **Re-rendering**: `setState()` automatically triggers re-render
4. **Lifecycle Methods**: `onCreate()`, `onViewModelUpdate()`, `onDestroy()`
5. **Dependency Injection**: Use `createProviderComponent()` + `withProviders()` HOC pattern
6. **Return Type**: `onRender()` returns `void`, not JSX (JSX is written as a statement)
7. **Component Definition**: Always use `class` extending `Component` or `StatefulComponent`, never functions
### State Management with StatefulComponent
```typescript
// ✅ CORRECT - Use StatefulComponent with setState()
class Counter extends StatefulComponent<ViewModel, State> {
state = { count: 0 };
handleClick = () => {
this.setState({ count: this.state.count + 1 }); // Automatically triggers re-render
};
onRender() {
<button title={`Count: ${this.state.count}`} onPress={this.handleClick} />;
}
}
// ❌ WRONG - Using markNeedsRender() doesn't exist
handleClick() {
this.count++;
this.markNeedsRender(); // ERROR: markNeedsRender is not a function!
}
```
### Provider Pattern (Not useContext)
```typescript
// ✅ CORRECT - Valdi provider pattern
import { createProviderComponentWithKeyName } from 'valdi_core/src/provider/createProvider';
import { withProviders } from 'valdi_core/src/provider/withProviders';
import { ProvidersValuesViewModel } from 'valdi_core/src/provider/withProviders';
import { Component } from 'valdi_core/src/Component';
// Step 1: Define service
class MyService {
getData() { return 'data'; }
}
// Step 2: Create provider component
const MyServiceProvider = createProviderComponentWithKeyName<MyService>('MyServiceProvider');
// Step 3: Provide value in parent
class ParentComponent extends Component {
private service = new MyService();
onRender() {
<MyServiceProvider value={this.service}>
<ChildComponentWithProvider />
</MyServiceProvider>;
}
}
// Step 4: Consume in child - extend viewModel from ProvidersValuesViewModel
interface ChildViewModel extends ProvidersValuesViewModel<[MyService]> {
// other props if needed
}
class ChildComponent extends Component<ChildViewModel> {
onRender() {
// Access provider via viewModel.providersValues
const [myService] = this.viewModel.providersValues;
const data = myService.getData();
<label value={data} />;
}
}
// Step 5: Wrap component with provider HOC
const ChildComponentWithProvider = withProviders(MyServiceProvider)(ChildComponent);
```
## Key Technologies
- **Valdi**: TypeScript-based declarative UI framework that compiles to native code
- **TSX/JSX**: React-like syntax for declarative UI (but this is **NOT React** - Valdi compiles to native)
- **Bazel**: Primary build system for reproducible builds
- **TypeScript/JavaScript**: Application and UI layer
- **C++**: Cross-platform runtime and layout engine
- **Swift**: Compiler implementation
- **Kotlin/Java**: Android runtime
- **Objective-C/C++**: iOS runtime
- **Flexbox**: Layout system with automatic RTL support
## Directory Structure
### `/apps/`
Example applications demonstrating Valdi features:
- `helloworld/` - Basic getting started example
- `valdi_gpt/` - More complex demo application
- `benchmark/` - Performance testing app
- `*_example/` - Various feature demonstrations (navigation, managed context, etc.)
### `/compiler/`
The Valdi compiler and companion tools:
- `compiler/` - Swift-based main compiler that transforms TypeScript to native code
- `companion/` - TypeScript-based companion tools for the build process
### `/valdi/`, `/valdi_core/`, `/valdi_protobuf/`
Core Valdi runtime implementations:
- Platform-specific implementations (iOS, Android, macOS)
- Cross-platform C++ core with layout engine
- Protobuf integration for efficient serialization
- Generated code from Djinni interfaces for cross-language bindings
### `/src/valdi_modules/`
Core Valdi TypeScript modules and standard library:
- `valdi_core/` - Core component and runtime APIs (Component, Provider, etc.)
- `valdi_protobuf/` - Protobuf serialization support
- `valdi_http/` - HTTP client module (promise-based network requests)
- `valdi_navigation/` - Navigation utilities
- `valdi_rxjs/` - RxJS integration for reactive programming
- `persistence/` - Key-value storage with encryption and TTL support
- `drawing/` - Managed context for graphics and drawing operations
- `file_system/` - Low-level file I/O operations
- `valdi_web/`, `web_renderer/` - Web runtime implementations
- `foundation/`, `coreutils/` - Common utilities (arrays, Base64, LRU cache, UUID, etc.)
- `worker/` - Worker service support for background JavaScript execution
- Other standard library modules
### `/npm_modules/`
Node.js packages:
- `cli/` - Command-line interface for Valdi development (`valdi` command)
- `eslint-plugin-valdi/` - ESLint rules for Valdi code
### `/bzl/`
Bazel build rules and macros for the Valdi build system
### `/docs/`
Comprehensive documentation:
- Codelabs for learning
- API documentation
- Setup and installation guides
### `/third-party/`
External dependencies and their Bazel build configurations
## Important Conventions
### Build System
1. **Bazel is the primary build system** - Use `bazel build`, `bazel test`, etc.
- Note: `bzl` is an alias for `bazel` - both commands work interchangeably
- The CLI looks for `bazel`, `bzl`, or `bazelisk` executables
2. **MODULE.bazel and WORKSPACE** - Bazel module system is in use
3. **Cross-platform builds** - Code must work on iOS, Android, Linux, and macOS
4. **Platform transitions** - Build rules handle platform-specific compilation automatically
### Code Style
1. **C++**: Follow the project's C++ style conventions
2. **TypeScript**: Use ESLint with Valdi-specific rules
3. **Swift**: Follow Swift conventions for compiler code
4. **Kotlin**: Follow Kotlin conventions for Android runtime
5. **Indentation**: Always match existing file conventions
### Testing
1. Test files are typically in `test/` subdirectories
2. Run tests with `bazel test //path/to:target`
3. All changes should include appropriate tests
4. Use the built-in Valdi testing framework for component tests
### Generated Code
1. **Djinni interfaces** - Some code is generated from `.djinni` files for cross-language bindings
2. **Don't modify generated code** - Change the source `.djinni` file instead
3. Generated files are typically in `generated-src/` directories
## Common Tasks
### Building the Compiler
```bash
# Build the compiler
bazel build //compiler/compiler:valdi-compiler
# After building, move the binary to bin/ directory for use by the toolchain
# The exact path depends on your platform (macos/linux and architecture)
# Example for macOS ARM64:
cp bazel-bin/compiler/compiler/valdi-compiler bin/compiler/macos/arm64/
```
Note: Pre-built compiler binaries are checked in to `/bin/compiler/` for convenience, but you can build and use your own version during development.
### Running Tests
```bash
# Run all tests
bazel test //...
# Run specific test
bazel test //valdi/test:some_test
```
### Installing and Using the CLI
```bash
cd npm_modules/cli
# Install the valdi command-line tool globally
npm run cli:install
# After installation, use the CLI
valdi --help
```
### Creating New Examples
Use existing apps in `/apps/` as templates. Each app needs:
- `BUILD.bazel` file defining build targets
- `package.json` for npm dependencies
- Entry point file (typically `.tsx` for TypeScript JSX)
- Source files in `src/` directory
## Important Files to Review
- `/README.md` - Main project documentation
- `/docs/INSTALL.md` - Installation and setup instructions
- `/docs/DEV_SETUP.md` - Developer environment setup
- `/CONTRIBUTING.md` - Contribution guidelines
- `/CODE_OF_CONDUCT.md` - Community standards
- `/LICENSE` - MIT License information
## Toolchain Locations
Pre-built binaries are stored in `/bin/`:
- Compiler binaries for Linux/macOS
- SQLite compiler for data persistence
- Other build tools
## Platform-Specific Notes
### iOS
- Uses Objective-C++ bridge layer for TypeScript-native communication
- Metal for GPU-accelerated rendering
- See `/valdi/src/ios/` for platform implementations
### Android
- Kotlin/Java implementations
- Uses Android NDK for C++ integration
- See `/valdi/src/android/` for platform implementations
### Web
- TypeScript runtime for web targets
- **Custom views**: Use `webClass` attribute on `<custom-view>`. Factories are registered via `webPolyglotViews` exports and looked up in `WebViewClassRegistry`. Factories receive a container DOM element and can return a `changeAttribute(name, value)` handler.
- **`web_deps` must be a `ts_project`** — never use `filegroup` for web code. Always use typed TypeScript with `ts_project` from `@aspect_rules_ts`.
- See `/src/valdi_modules/src/valdi/web_renderer/` for web implementation
### Desktop (macOS)
- Native macOS implementation using AppKit (`NSView`, `NSWindow`)
- **Platform type**: `PlatformTypeMacOS` (3) — distinct from iOS (2)
- **Class name resolution**: macOS falls through to iOS class names for both built-in elements and custom views. This means `iosClass` works on macOS without specifying `macosClass`.
- **SnapDrawing**: Shares iOS layer classes (registered under iOS names like `SCValdiView`, `SCValdiLabel`)
- See `/valdi/src/valdi/macos/` for desktop implementations
## Development Workflow
1. **Setup environment** - Follow `/docs/DEV_SETUP.md`
2. **Make changes** in appropriate directory
3. **Build locally** with Bazel
4. **Run tests** to verify changes
5. **Run linters** with appropriate tools
6. **Test on multiple platforms** - Changes may affect iOS, Android, and web
7. **Update documentation** if adding features
## Common Patterns
### Component Development
Valdi components follow a class-based pattern with lifecycle methods:
```typescript
import { Component } from 'valdi_core/src/Component';
class MyComponent extends Component {
// Required: Render the component's UI
onRender() {
<view>
<label value="Hello" />
</view>;
}
// Optional lifecycle methods:
// onCreate() - Called when component is first created
// onDestroy() - Called before component is removed
// onViewModelUpdate(previousViewModel) - Called when viewModel updates
}
```
**Key Valdi Concepts:**
- Components use TSX/JSX syntax (similar to React but compiles to native)
- State management via component properties
- Event handlers for user interactions
- Flexbox layout system for positioning
### Native Polyglot Modules
For performance-critical code or platform-specific views, write native implementations with TypeScript bindings:
- Define interfaces in TypeScript
- Specify polyglot modules in build files (BUILD.bazel)
- Implement in C++, Swift, Kotlin, or Objective-C
- Compiler generates type-safe bindings
### Custom Views (`<custom-view>`)
Custom views inject native platform views into Valdi components. Use platform-specific class attributes:
```tsx
<custom-view
iosClass='MyIOSView'
androidClass='com.example.MyAndroidView'
macosClass='MyMacOSView'
webClass='my-web-view'
myAttribute={42}
/>
```
**Platform resolution rules:**
- **macOS falls through to iOS**: If `macosClass` is not specified, `iosClass` is used. This applies both in TypeScript (`JSXBootstrap.ts`) and C++ (`ViewNode.cpp`).
- **Built-in elements** (view, label, image, scroll, etc.) always resolve to iOS class names on macOS (e.g., `SCValdiView`, `SCValdiLabel`).
- **Web** uses `webClass` to look up a factory in `WebViewClassRegistry`. Factories can return a `changeAttribute(name, value)` handler to receive attribute updates.
- See `/docs/docs/native-customviews.md` for full examples on all platforms.
**Bazel dependencies for custom views:**
```python
load("@aspect_rules_ts//ts:defs.bzl", "ts_project")
# Web views MUST be a ts_project, never a filegroup.
# - transpiler = "tsc" is required (aspect_rules_ts does not default it)
# - Provide a dedicated web/tsconfig.json (standalone, not extending the module tsconfig)
# - If web code imports .d.ts from the module's src/, include "src/**/*.d.ts" in srcs
# - Exclude "web/**/*.d.ts" from srcs to avoid TS5055 collisions with composite: true
ts_project(
name = "my_web_views",
srcs = glob([
"web/**/*.ts",
"src/**/*.d.ts", # only if web code imports module type declarations
], exclude = [
"web/**/*.d.ts", # avoid TS5055 output collision with composite
]),
allow_js = True,
composite = True,
transpiler = "tsc",
tsconfig = "web/tsconfig.json",
)
valdi_module(
name = "my_module",
srcs = [...],
ios_deps = [":my_ios_views"], # objc_library
macos_deps = [":my_macos_views"], # objc_library (or omit to share ios_deps)
android_deps = [":my_android_views"], # valdi_android_library
web_deps = [":my_web_views"], # ts_project (never filegroup)
)
```
**Web `tsconfig.json`** — the `web/tsconfig.json` should be standalone (not extending the module-level tsconfig) since `ts_project` compiles independently:
```json
{
"compilerOptions": {
"target": "ES2016",
"module": "commonjs",
"strict": true,
"composite": true,
"allowJs": true,
"lib": ["dom", "ES2019"]
}
}
```
### Worker Services
For background processing:
- Create worker services that run in separate JavaScript contexts
- Communicate via message passing
- See `/docs/docs/advanced-worker-service.md`
### Component Context & Native Integration
Pass data and services between native code and Valdi:
- **Component Context**: Pass native data to Valdi components when instantiating them
- **Native Annotations**: Use TypeScript comments to export components to native platforms
- **Example**: `@Component` and `@ExportModel` annotations define how components are exposed
- See `/docs/docs/native-annotations.md` and `/docs/docs/native-context.md`
### Provider Pattern
Dependency injection for Valdi components:
- Use `Provider` to pass services and data down the component tree
- Similar to React Context but Valdi-specific
- Enables loose coupling and testability
- See `/docs/docs/advanced-provider.md`
### Localization
String management for multi-language support:
- String resources defined in JSON files
- Automatic locale switching based on device settings
- See `/docs/docs/advanced-localization.md`
## Common Pitfalls
1. **Don't skip cross-platform testing** - Changes affect multiple platforms
2. **Don't modify generated code** - Change the source instead
3. **Don't ignore Bazel cache** - Use `bazel clean` sparingly
4. **Don't hardcode platform assumptions** - Use appropriate abstractions
5. **Performance matters** - Valdi is a UI framework where rendering performance is critical
## Architecture Overview
### Compilation Pipeline
1. **TypeScript source** → Valdi compiler (Swift)
2. **Compiler output** → Platform-specific code generation
3. **Native builds** → iOS/Android/macOS apps
4. **Runtime** → C++ layout engine + platform-specific renderers
### Hot Reload System
- Valdi includes instant hot reload during development
- Changes to TypeScript components are reflected in milliseconds
- No need to recompile native code for UI changes
- Use `valdi hotreload` command
### Performance Features
- **View recycling** - Global view pooling reuses native views
- **Viewport-aware rendering** - Only visible views are inflated
- **Independent component rendering** - Components update without parent re-renders
- **Optimized layout** - C++ layout engine with minimal marshalling
## Key Points for AI Assistants
1. **Cross-platform compatibility is critical** - Test implications across iOS, Android, and web
2. **Bazel is non-negotiable** - Don't suggest alternative build systems
3. **Generated code exists** - Some files are auto-generated from Djinni interfaces
4. **Performance is paramount** - This is a production UI framework used at scale
5. **Follow existing patterns** - This is a mature codebase with established conventions
6. **TypeScript is compiled** - Unlike React Native, this doesn't run JavaScript at runtime
7. **Native integration is deep** - Direct access to platform APIs via polyglot modules
## Quick Reference Commands
### For App Development
```bash
# Install Valdi CLI (first time)
cd npm_modules/cli && npm run cli:install
# Setup development environment
valdi dev_setup
# Bootstrap a new project
mkdir my_project && cd my_project
valdi bootstrap
# Install dependencies and build
valdi install ios # or android
# Start hot reload
valdi hotreload
```
### For Platform Development (Contributing to Valdi)
```bash
# Setup development environment (first time)
scripts/dev_setup.sh
# Build everything
bazel build //...
# Run all tests
bazel test //...
# Build and run example app
cd apps/helloworld
valdi install ios # or android
```
## Testing Framework
Valdi includes a built-in testing framework:
- Component-level unit tests
- Mock services and dependencies
- See `/docs/docs/workflow-testing.md`
```typescript
import { TestRunner } from 'valdi_core/src/TestRunner';
TestRunner.test('component renders correctly', () => {
const component = new MyComponent();
// Test assertions
});
```
## Debugging
- **VSCode integration** - Full debugging support with breakpoints
- **Hermes debugger** - For JavaScript debugging
- **Native debugging** - Xcode/Android Studio for platform-specific issues
- See `/docs/docs/workflow-hermes-debugger.md`
## Related Documentation
For more details on specific topics, see the `/docs/` directory:
- Architecture overview
- API reference
- Codelabs for hands-on learning
- Advanced features (animations, gestures, protobuf)
- Native bindings and custom views
## AI Skills (Context for AI Agents)
The Valdi CLI ships context files ("skills") that give AI agents accurate knowledge about Valdi APIs, patterns, and conventions. Install them once to reduce hallucinations:
```bash
npm install -g @snap/valdi
valdi skills install # installs all skills for detected AI agents
# or install by category:
valdi skills install --category=client # module development skills
valdi skills install --category=framework # framework internals skills
```
Skills are bundled inside the npm package — no network access required after install.
## Contributing
This is an open-source project. When contributing:
1. Follow the style guides
2. Include tests for new features
3. Update documentation
4. Ensure cross-platform compatibility
5. See `CONTRIBUTING.md` for full guidelines
## Community & Support
- **Documentation**: Comprehensive docs in `/docs/` directory
- **Examples**: Working examples in `/apps/` directory
- **Issues**: Report bugs and request features via GitHub issues
- **Discussions**: Ask questions and share ideas in GitHub Discussions
---
*This document is intended for AI coding assistants to quickly understand the structure and conventions of the Valdi codebase. For human developers, please refer to the main README.md and comprehensive documentation in `/docs/`.*