Contributing to Tiny Workshop #
Thank you for your interest in contributing to Tiny Workshop! This document provides guidelines and information for contributors.
Table of Contents #
- Project Overview
- Architecture
- Development Setup
- Building and Running
- Testing
- Code Style
- Contribution Workflow
- Documentation
- Getting Help
Project Overview #
Tiny Workshop is a 3D interactive AI agent application featuring a Victorian study environment. The project combines:
- Frontend: Fyrox-based 3D application (Rust) that renders the environment, manages the character, and provides the chat interface
- Backend: Bun/TypeScript adapter that bridges the frontend to the Claude Agent SDK
- Communication: IPC via stdin/stdout JSON-RPC protocol
Current Features #
Implemented:
- Chat interface with streaming message support
- z.AI API key authentication with OS keychain integration (with Linux config file fallback)
- IPC communication between frontend and backend
- Cross-platform support (Windows, macOS, Linux)
- HTTP debug server for automated testing
- Animation state system (placeholder cube with color-coded states)
- Room state persistence with atomic writes and multi-instance file locking
- Session logging with JSONL format and crash-safe append-only semantics
- Session resume with SDK context preservation across app restarts
- Room system with surfaces and interactables (sticky notes, image stacks)
- Text rendering for sticky notes using raqote (renders to PNG textures)
- Image texture rendering with LRU cache (50 images or 50MB limit)
- Settings dialog with configurable graphics, audio, and API key management
Planned:
- 3D Victorian study environment with AI character (placeholder geometry in place)
Architecture #
The project follows a multi-process architecture:
┌─────────────────┐ IPC (JSON-RPC) ┌─────────────────┐
│ Fyrox Frontend │ ◄──────────────────────────► │ Backend Adapter │
│ (Rust) │ stdin/stdout messages │ (Bun/TypeScript)│
└─────────────────┘ └────────┬────────┘
│
▼
┌────────────────┐
│ Claude Agent │
│ SDK │
└────────────────┘
Project Structure #
tiny-workshop/
├── docs/design/ # Architecture and design documents
├── tiny-workshop/ # Main Rust workspace
│ ├── editor/ # Fyrox editor application
│ ├── executor/ # Desktop runtime
│ ├── executor-wasm/ # WebAssembly runtime (future)
│ ├── executor-android/ # Android runtime (future)
│ ├── game/ # Core game logic
│ │ └── src/
│ │ ├── lib.rs # Main Game plugin
│ │ ├── ipc.rs # IPC protocol types
│ │ ├── backend.rs # Backend process management
│ │ ├── chat_ui.rs # Chat interface
│ │ ├── credential_manager.rs # OS keychain integration
│ │ ├── debug_server/ # HTTP debug API (feature-gated)
│ │ └── room/ # Room system module
│ │ ├── mod.rs # Surface/interactable management
│ │ ├── fonts.rs # Font loading for text rendering
│ │ ├── text_renderer.rs # Text-to-PNG rendering
│ │ ├── image_cache.rs # LRU texture cache
│ │ └── image_storage.rs # Image file management
│ └── game-dylib/ # Hot-reloadable game plugin
└── backend/ # TypeScript backend adapter
└── packages/
├── ipc-types/ # IPC protocol definitions
├── logger/ # Structured logging
└── adapter/ # Main adapter (SDK integration)
For detailed architecture information, see the design documentation.
Development Setup #
Prerequisites #
For Rust Frontend #
- Rust: Install via rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh - Platform-specific dependencies:
- Linux: X11/Wayland development libraries
# Ubuntu/Debian sudo apt install libxcb-shape0-dev libxcb-xfixes0-dev libxkbcommon-dev libasound2-dev # Fedora sudo dnf install libxcb-devel libxkbcommon-devel alsa-lib-devel - macOS: Xcode Command Line Tools
xcode-select --install - Windows: Visual Studio 2019 or later with C++ tools
- Linux: X11/Wayland development libraries
For Backend Adapter #
- Bun: Install via the official installer
# macOS/Linux curl -fsSL https://bun.sh/install | bash # Windows powershell -c "irm bun.sh/install.ps1 | iex" - Node.js (optional, for compatibility testing): v18 or later
Optional Tools #
- Nix: For reproducible builds (see
tiny-workshop/flake.nix)nix develop
Install Dependencies #
Rust Dependencies #
Dependencies are managed via Cargo and will be automatically downloaded on first build:
cd tiny-workshop
cargo build
Backend Dependencies #
The backend uses pnpm for package management:
cd backend
pnpm install
Building and Running #
Frontend (Rust) #
Development Build #
cd tiny-workshop
cargo build
Release Build #
cargo build --release
Run the Executor #
# Debug build
cargo run --package executor
# Release build
cargo run --package executor --release
# With debug server enabled (for automated testing)
cargo run --package executor --features debug_server
# With hot reloading enabled
cargo run --package executor --features dylib
Hot Reloading (Development) #
For faster iteration during development:
# Use the hot-reload profile
cargo build --profile dev-hot-reload
The engine is optimized even in debug builds (see Cargo.toml profile configuration), providing good performance while allowing code debugging.
Debug Server (Automated Testing) #
The debug server exposes an HTTP API for automated testing and external tooling to interact with the application without using the UI.
Quick Start:
# Using the dev script (recommended)
./dev.sh --debug-server # Linux/macOS
dev.bat --debug-server # Windows
# Or with cargo directly
cargo run --package executor --features debug_server
The server runs on http://localhost:8765 by default (override with TINY_WORKSHOP_DEBUG_PORT).
Example Usage:
# Check if the server is running
curl http://localhost:8765/health
# Get current game state (auth status, backend connection, etc.)
curl http://localhost:8765/state
# Send a message (requires valid authentication)
curl -X POST http://localhost:8765/send \
-H "Content-Type: application/json" \
-d '{"content":"Hello!"}'
# Poll for conversation messages
curl "http://localhost:8765/messages?limit=50"
For full API documentation, see Debug Server Design Doc.
Backend Adapter #
cd backend
# Build all packages
pnpm build
# Run in development mode
pnpm dev
# Type check
pnpm typecheck
Running the Full Application #
- Start the backend adapter (it will listen on stdin/stdout)
- Start the frontend executor (it will spawn the backend as a child process)
The frontend automatically manages the backend lifecycle.
Testing #
Rust Tests #
cd tiny-workshop
# Run all tests
cargo test
# Run all tests including debug server tests
cargo test --features debug_server
# Run tests for a specific package
cargo test --package game
# Run tests with output
cargo test -- --nocapture
Backend Tests #
cd backend
pnpm test
Integration Tests #
Integration tests verify the IPC protocol and multi-process communication by spawning the actual backend process:
# Build the backend first (required for integration tests)
cd backend && pnpm build
# Then run integration tests
cd ../tiny-workshop
cargo test --test ipc_integration
The integration test framework (game/tests/ipc_integration.rs) includes:
test_backend_spawn_and_init- Verifies backend spawning and init message handlingtest_backend_handles_invalid_json- Confirms graceful error handling for malformed JSONtest_backend_shutdown- Tests graceful backend shutdowntest_protocol_version_matches- Ensures frontend/backend protocol version alignmenttest_frontend_message_json_lines_format- Validates JSON lines message formattest_backend_message_deserialization_coverage- Tests deserialization of all message types
Note: Integration tests automatically skip if the backend executable is not found, so CI can run without failing.
Manual Testing Checklist #
Code Style #
Rust #
- Follow the Rust API Guidelines
- Use
rustfmtfor formatting:cargo fmt - Use
clippyfor linting:cargo clippy -- -D warnings - Maximum line length: 100 characters
- Use meaningful variable names and add comments for complex logic
- Prefer immutability (
letoverlet mut) - Use
Resultfor error handling, avoidunwrap()in production code
Fyrox UI Builder Patterns #
When building Fyrox UI widgets, be aware that some builders have their own content systems:
Correct patterns:
// ScrollViewerBuilder - use .with_content() on the builder
ScrollViewerBuilder::new(WidgetBuilder::new())
.with_content(my_content)
.build(ctx);
// ButtonBuilder - use .with_content() for custom widgets, .with_text() for text
ButtonBuilder::new(WidgetBuilder::new())
.with_content(my_custom_widget) // or .with_text("Label")
.build(ctx);
// WindowBuilder - use .with_content()
WindowBuilder::new(WidgetBuilder::new())
.with_content(window_content)
.build(ctx);
Incorrect patterns (will cause visibility issues):
// DON'T use .with_child() on WidgetBuilder for these builders!
ScrollViewerBuilder::new(
WidgetBuilder::new().with_child(content) // WRONG - content behind decorator
).build(ctx);
ButtonBuilder::new(
WidgetBuilder::new().with_child(text) // WRONG - text invisible
).build(ctx);
The .with_child() method on WidgetBuilder adds children to the widget's child list, but specialized builders like ScrollViewerBuilder and ButtonBuilder have internal decorators that may obscure these children. Always use the builder's specific content method (.with_content(), .with_text(), etc.) instead.
TypeScript #
- Use TypeScript strict mode
- Format with Prettier (if configured)
- Use ESLint for linting
- Prefer
async/awaitover raw promises - Use descriptive variable and function names
- Add JSDoc comments for public APIs
Commit Messages #
Follow the Conventional Commits specification:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, etc.)refactor: Code refactoringtest: Adding or updating testschore: Maintenance tasks
Examples:
feat(room): Add image stack support for tool output
Add ability to stack multiple images in the room as persistent
interactables. Images are cached with LRU eviction.
fix(ipc): Handle backend disconnection gracefully
Prevent panic when backend crashes by catching channel
disconnection errors and triggering restart logic.
Contribution Workflow #
1. Plan Your Changes #
- Check the design documents in
docs/design/for relevant architecture - For new features, discuss the approach with maintainers first
- Review open issues in
docs/design/ISSUES.mdto avoid duplicate work
2. Create a Branch #
git checkout -b feature/your-feature-name
3. Make Changes #
- Write code following the style guidelines
- Add tests for new functionality
- Update documentation if needed
- Ensure all tests pass locally
4. Commit #
git add .
git commit -m "feat(scope): your descriptive message"
5. Submit Your Changes #
- Ensure all tests pass
- Update relevant design documents if you've changed architecture
- Include clear descriptions of changes
- Add screenshots/videos for UI changes
- Document test results
6. Code Review #
- Address reviewer feedback
- Keep commits focused and atomic
- Squash commits if requested
7. Integration #
Once approved, maintainers will integrate your changes.
Documentation #
Design Documents #
Before implementing major features, review and update the design documents in docs/design/:
- Frontend Architecture
- Backend Adapter
- IPC Protocol
- Room System
- Animation System
- Persistence System
- Authentication System
- Debug Server
- Open Issues
Adding Documentation #
- Use Markdown for all documentation
- Keep language clear and concise
- Include code examples where appropriate
- Update the table of contents if adding new sections
Inline Documentation #
- Add doc comments (
///in Rust,/** */in TypeScript) for public APIs - Include usage examples in doc comments
- Document parameters, return values, and possible errors
Getting Help #
- Design Questions: Reference the design documents in
docs/design/ - Open Issues: Check
docs/design/ISSUES.mdfor known problems and gaps - Architecture: Review the design docs before starting major work
Development Tips #
Performance Profiling #
- Use
cargo flamegraphfor CPU profiling - Use
tracyfor frame-by-frame analysis - Target: 60fps (<16ms frame time) on integrated graphics
Debugging #
- Use
RUST_LOG=debugfor verbose logging:RUST_LOG=debug cargo run --package executor - Use
lldb(macOS/Linux) or Visual Studio debugger (Windows) for native debugging - Check
tiny-workshop.logfor runtime logs
Memory Management #
- Keep total memory usage under 200MB
- Use LRU caches for images and textures
- Profile with
valgrind(Linux) or Instruments (macOS)
Hot Reloading #
The game logic can be hot-reloaded during development:
- Build with hot-reload profile
- Make changes to
game-dylib/ - Rebuild while executor is running
- Changes load automatically
Platform-Specific Testing #
- Test on all target platforms before submitting
- Use CI/CD for automated cross-platform testing
- Pay attention to file path separators and line endings
License #
By contributing to Tiny Workshop, you agree that your contributions will be licensed under the same license as the project (see LICENSE file).
Thank You! #
Your contributions make Tiny Workshop better for everyone. We appreciate your time and effort!