Skip to content

Commit 86b1aaa

Browse files
committed
merge development
2 parents 9e53b8b + f6950d6 commit 86b1aaa

File tree

134 files changed

+24745
-3351
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

134 files changed

+24745
-3351
lines changed

Cargo.lock

Lines changed: 466 additions & 13 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,21 @@
11
[workspace]
22
members = [
33
"crates/theater",
4-
"crates/theater-cli",
4+
"crates/theater-cli",
55
"crates/theater-server",
66
"crates/theater-server-cli",
7-
"crates/theater-client"
7+
"crates/theater-client",
8+
"crates/theater-handler-environment",
9+
"crates/theater-handler-filesystem",
10+
"crates/theater-handler-http-client",
11+
"crates/theater-handler-http-framework",
12+
"crates/theater-handler-message-server",
13+
"crates/theater-handler-process",
14+
"crates/theater-handler-random",
15+
"crates/theater-handler-runtime",
16+
"crates/theater-handler-store",
17+
"crates/theater-handler-supervisor",
18+
"crates/theater-handler-timing",
819
]
920
resolver = "2"
1021

@@ -25,8 +36,20 @@ tokio = { version = "1.0", features = ["full"] }
2536
serde = { version = "1.0", features = ["derive"] }
2637
serde_json = "1.0"
2738
tracing = "0.1"
39+
tracing-subscriber = "0.3"
2840
uuid = { version = "1.6", features = ["v4", "serde"] }
2941

3042
theater = { version = "0.2.1", path = "crates/theater" }
3143
theater-client = { version = "0.2.1", path = "crates/theater-client" }
3244
theater-server = { version = "0.2.1", path = "crates/theater-server" }
45+
theater-handler-environment = { version = "0.2.1", path = "crates/theater-handler-environment" }
46+
theater-handler-filesystem = { version = "0.2.1", path = "crates/theater-handler-filesystem" }
47+
theater-handler-http-client = { version = "0.2.1", path = "crates/theater-handler-http-client" }
48+
theater-handler-http-framework = { version = "0.2.1", path = "crates/theater-handler-http-framework" }
49+
theater-handler-message-server = { version = "0.2.1", path = "crates/theater-handler-message-server" }
50+
theater-handler-process = { version = "0.2.1", path = "crates/theater-handler-process" }
51+
theater-handler-random = { version = "0.2.1", path = "crates/theater-handler-random" }
52+
theater-handler-runtime = { version = "0.2.1", path = "crates/theater-handler-runtime" }
53+
theater-handler-store = { version = "0.2.1", path = "crates/theater-handler-store" }
54+
theater-handler-supervisor = { version = "0.2.1", path = "crates/theater-handler-supervisor" }
55+
theater-handler-timing = { version = "0.2.1", path = "crates/theater-handler-timing" }

HANDLER_INTEGRATION_GUIDE.md

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
# Handler Integration Guide
2+
3+
This guide explains how to integrate Theater handlers at different stages of the runtime lifecycle.
4+
5+
## Handler Integration Timeline
6+
7+
Handlers can be integrated at different points depending on their dependencies:
8+
9+
### 1. ✅ Handler Registry Creation (10/11 handlers)
10+
11+
These handlers can be registered when creating the `HandlerRegistry`, before the runtime starts:
12+
13+
| Handler | Dependencies | When to Register |
14+
|---------|--------------|------------------|
15+
| **environment** | Config, Permissions | Registry creation |
16+
| **random** | Config, Permissions | Registry creation |
17+
| **timing** | Config, Permissions | Registry creation |
18+
| **runtime** | Config, `theater_tx`, Permissions | Registry creation |
19+
| **http-client** | Config, Permissions | Registry creation |
20+
| **filesystem** | Config, Permissions | Registry creation |
21+
| **store** | Config, Permissions | Registry creation |
22+
| **supervisor** | Config, Permissions | Registry creation |
23+
| **message-server** | Config, MessageRouter, Permissions | Registry creation |
24+
| **http-framework** | Permissions | Registry creation |
25+
26+
**Key Insight:** Even though `RuntimeHandler` needs the `theater_tx` channel, we create that channel *before* creating the handler registry, so it's available at registration time!
27+
28+
```rust
29+
// Create channels first
30+
let (theater_tx, theater_rx) = mpsc::channel::<TheaterCommand>(32);
31+
32+
// Create registry and pass theater_tx to handlers that need it
33+
let mut registry = HandlerRegistry::new();
34+
registry.register(RuntimeHandler::new(
35+
RuntimeHostConfig {},
36+
theater_tx.clone(), // ✅ Available!
37+
None
38+
));
39+
40+
// Then create runtime
41+
let runtime = TheaterRuntime::new(theater_tx, theater_rx, None, registry).await?;
42+
```
43+
44+
### 2. ⚠️ Actor Creation Time (1/11 handlers)
45+
46+
This handler requires dependencies that only exist when spawning actors:
47+
48+
| Handler | Blocker | When to Integrate |
49+
|---------|---------|-------------------|
50+
| **process** | Requires `ActorHandle` (created during actor spawn) | Per-actor integration |
51+
52+
**Why ProcessHandler is Different:**
53+
54+
```rust
55+
pub fn new(
56+
config: ProcessHostConfig,
57+
actor_handle: ActorHandle, // ❌ Only exists during actor spawning
58+
permissions: Option<ProcessPermissions>,
59+
) -> Self
60+
```
61+
62+
The `ActorHandle` is created by the runtime when spawning an actor, not during handler registry setup. Each actor needs its own ProcessHandler instance with its specific ActorHandle.
63+
64+
**Solution:** ProcessHandler must be integrated during the actor spawning process, typically in TheaterServer or custom actor creation logic.
65+
66+
## Common Misconceptions
67+
68+
### ❌ "Handlers needing runtime resources can't be registered early"
69+
70+
**Reality:** Many handlers need runtime resources like `theater_tx`, but these are created *before* the handler registry, so they're available at registration time.
71+
72+
### ❌ "All handlers are created per-actor"
73+
74+
**Reality:** Handlers registered in the HandlerRegistry are shared across actors (via `create_instance()` cloning). Only handlers requiring per-actor state (like ProcessHandler needing an ActorHandle) must be created per-actor.
75+
76+
### ✅ "Handler dependencies determine integration point"
77+
78+
**Correct:** The key question is "When does this dependency become available?"
79+
- Available before runtime creation?Register in HandlerRegistry
80+
- Available only during actor creation?Integrate per-actor
81+
82+
## Integration Examples
83+
84+
### Example 1: Standard Runtime (10/11 handlers)
85+
86+
```rust
87+
use theater::handler::HandlerRegistry;
88+
use theater::messages::TheaterCommand;
89+
use tokio::sync::mpsc;
90+
91+
async fn create_runtime() -> Result<()> {
92+
// 1. Create channels
93+
let (theater_tx, theater_rx) = mpsc::channel(32);
94+
95+
// 2. Create handler registry
96+
let mut registry = HandlerRegistry::new();
97+
98+
// Register all compatible handlers
99+
registry.register(EnvironmentHandler::new(env_config, None));
100+
registry.register(RandomHandler::new(random_config, None));
101+
registry.register(TimingHandler::new(timing_config, None));
102+
registry.register(RuntimeHandler::new(runtime_config, theater_tx.clone(), None)); //
103+
registry.register(HttpClientHandler::new(http_config, None));
104+
registry.register(FilesystemHandler::new(fs_config, None));
105+
registry.register(StoreHandler::new(store_config, None));
106+
registry.register(SupervisorHandler::new(supervisor_config, None));
107+
108+
let message_router = MessageRouter::new();
109+
registry.register(MessageServerHandler::new(None, message_router));
110+
registry.register(HttpFrameworkHandler::new(None));
111+
112+
// 3. Create runtime
113+
let runtime = TheaterRuntime::new(
114+
theater_tx,
115+
theater_rx,
116+
None,
117+
registry,
118+
).await?;
119+
120+
// 4. Run runtime
121+
runtime.run().await?;
122+
123+
Ok(())
124+
}
125+
```
126+
127+
### Example 2: Per-Actor ProcessHandler (TheaterServer approach)
128+
129+
ProcessHandler integration happens in the actor spawning logic:
130+
131+
```rust
132+
// This happens inside TheaterRuntime::spawn_actor() or similar
133+
async fn spawn_actor_with_process_handler(
134+
manifest: ManifestConfig,
135+
theater_tx: Sender<TheaterCommand>,
136+
) -> Result<TheaterId> {
137+
// 1. Create actor handle
138+
let actor_handle = ActorHandle::new(/* ... */);
139+
140+
// 2. Create ProcessHandler with actor's handle
141+
let process_handler = ProcessHandler::new(
142+
ProcessHostConfig::default(),
143+
actor_handle.clone(), // ✅ Now available
144+
None,
145+
);
146+
147+
// 3. Add to actor's handler list
148+
actor_handlers.push(Box::new(process_handler));
149+
150+
// 4. Continue actor creation...
151+
Ok(actor_id)
152+
}
153+
```
154+
155+
## Summary
156+
157+
- **10/11 handlers** can be registered in the HandlerRegistry at runtime creation
158+
- **1/11 handlers** (ProcessHandler) requires per-actor integration
159+
- The key is understanding *when* each dependency becomes available
160+
- RuntimeHandler was incorrectly assumed to need per-actor integration, but it only needs `theater_tx` which is created before the handler registry
161+
162+
## See Also
163+
164+
- `/crates/theater/examples/full-runtime.rs` - Working example with 10/11 handlers
165+
- `/crates/theater-server/` - Complete server implementation with all 11 handlers

HANDLER_MIGRATION.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Handler Migration Summary: Random Handler
2+
3+
## What We Did
4+
5+
Successfully migrated the `random` handler from the Theater core runtime into a standalone `theater-handler-random` crate.
6+
7+
## Key Changes
8+
9+
### 1. Created New Crate Structure
10+
- `/crates/theater-handler-random/`
11+
- `Cargo.toml` - Dependencies and metadata
12+
- `src/lib.rs` - Handler implementation
13+
- `README.md` - Documentation
14+
15+
### 2. Trait Simplification
16+
**Before:**
17+
```rust
18+
fn setup_host_functions(
19+
&mut self,
20+
actor_component: &mut ActorComponent,
21+
) -> Pin<Box<dyn Future<Output = Result<()>> + Send + '_>>;
22+
```
23+
24+
**After:**
25+
```rust
26+
fn setup_host_functions(
27+
&mut self,
28+
actor_component: &mut ActorComponent,
29+
) -> Result<()>;
30+
```
31+
32+
**Why:** None of the handlers actually used `.await` in their setup functions. Making them synchronous:
33+
- Eliminated complex lifetime issues
34+
- Made the code more honest about what it does
35+
- Simplified implementation for all future handlers
36+
37+
### 3. Handler Implementation
38+
- Renamed `RandomHost``RandomHandler`
39+
- Implemented the `Handler` trait with synchronous `setup_host_functions` and `add_export_functions`
40+
- Kept the async closures for actual random operations (those ARE async)
41+
- Maintained all existing functionality and chain event logging
42+
43+
### 4. Dependencies
44+
The handler crate depends on:
45+
- Core theater crate (for trait definitions and types)
46+
- Wasmtime (for WASM integration)
47+
- Random generation (`rand`, `rand_chacha`)
48+
- Standard async/logging tools
49+
50+
## Migration Pattern for Other Handlers
51+
52+
Based on this migration, here's the pattern for migrating other handlers:
53+
54+
1. **Create the crate** with proper Cargo.toml
55+
2. **Copy the host implementation** from `/crates/theater/src/host/`
56+
3. **Rename** `XxxHost``XxxHandler`
57+
4. **Implement the `Handler` trait:**
58+
- `create_instance()` - Clone yourself
59+
- `start()` - Async startup (keep as-is)
60+
- `setup_host_functions()` - Now synchronous!
61+
- `add_export_functions()` - Now synchronous!
62+
- `name()`, `imports()`, `exports()` - Metadata
63+
5. **Update imports** to use `theater::` prefix
64+
6. **Test** and document
65+
66+
## Benefits
67+
68+
**Cleaner architecture** - Handlers are independent modules
69+
**Easier to maintain** - Each handler can evolve separately
70+
**Better testing** - Test handlers in isolation
71+
**Simpler lifetimes** - Synchronous trait methods avoid lifetime complexity
72+
**Third-party handlers** - Clear pattern for custom handlers
73+
74+
## Next Steps
75+
76+
Recommended order for migrating remaining handlers:
77+
1.`random` - DONE!
78+
2.`environment` - DONE!
79+
3. `timing` - Also straightforward
80+
4. `http-client` - Moderate complexity
81+
5. `filesystem` - Larger but well-isolated
82+
6. `process`, `supervisor`, `store` - More complex, do last
83+
7. `message-server`, `http-framework` - Most complex
84+
85+
## Testing
86+
87+
The migrated handlers:
88+
- ✅ Compile without errors
89+
- ✅ All unit tests pass
90+
- ✅ Maintain backward compatibility
91+
- ✅ Integrate with Theater runtime via `Handler` trait
92+
93+
## Completed Migrations
94+
95+
### 1. Random Handler
96+
- **Crate**: `theater-handler-random`
97+
- **Status**: ✅ Complete
98+
- **Notes**: First migration, served as the documented example
99+
100+
### 2. Timing Handler
101+
- **Crate**: `theater-handler-timing`
102+
- **Status**: ✅ Complete
103+
- **Notes**: Completed prior to environment handler
104+
105+
### 3. Environment Handler
106+
- **Crate**: `theater-handler-environment`
107+
- **Status**: ✅ Complete (2025-11-30)
108+
- **Notes**:
109+
- Fixed wasmtime version (26.0 → 31.0)
110+
- Corrected closure signatures for func_wrap
111+
- Updated all config fields in tests and docs
112+
- All tests passing
113+
114+
### 4. HTTP Client Handler
115+
- **Crate**: `theater-handler-http-client`
116+
- **Status**: ✅ Complete (2025-11-30)
117+
- **Notes**:
118+
- Migrated component types (HttpRequest, HttpResponse)
119+
- Preserved async operations with func_wrap_async
120+
- Permission checking maintained
121+
- All tests passing (3 unit + 1 doc)

0 commit comments

Comments
 (0)