---
document_type: "reference_doc"
service: "MOMENTRY_CORE"
title: "Playground Binary Implementation Plan"
date: "2026-03-23"
version: "V1.0"
status: "active"
owner: "Warren"
created_by: "OpenCode"
tags:
  - "binary"
  - "plan"
  - "implementation"
  - "playground"
ai_query_hints:
  - "查詢 Playground Binary Implementation Plan 的內容"
  - "Playground Binary Implementation Plan 的主要目的是什麼？"
  - "如何操作或實施 Playground Binary Implementation Plan？"
---

# Playground Binary Implementation Plan

| Item | Content |
|------|---------|
| Author | Warren |
| Created | 2026-03-23 |
| Document Version | V1.0 |

---

## Version History

| Version | Date | Purpose | Operator | Tool/Model |
|---------|------|---------|----------|------------|
| V1.0 | 2026-03-23 | Create implementation plan | Warren | OpenCode |

---

## Overview

Create separate `momentry_playground` binary with distinct configuration from `momentry` (production).

| Aspect | Production (`momentry`) | Development (`momentry_playground`) |
|--------|------------------------|-------------------------------------|
| **Port** | 3002 | 3003 |
| **Redis Prefix** | `momentry:` | `momentry_dev:` |
| **Worker** | Enabled | Disabled |
| **Purpose** | Production deployment | Testing/Development |

---

## Files to Modify

```
Files Changed: 6 files (+1 new)
├── src/core/config.rs        ← Add server_port(), redis_key_prefix()
├── src/core/db/redis_client.rs ← Replace hardcoded prefixes
├── src/core/cache/redis_cache.rs ← Use configurable prefix
├── src/main.rs               ← Update CLI defaults
├── src/playground.rs         ← NEW: Development binary
├── Cargo.toml                ← Add new binary
└── .env.development          ← NEW: Dev environment config
```

---

## Implementation Steps

### Step 1: Update `src/core/config.rs`

Add after line 51 (after `MEDIA_BASE_URL`):

```rust
pub static SERVER_PORT: Lazy<u16> = Lazy::new(|| {
    env::var("MOMENTRY_SERVER_PORT")
        .unwrap_or_else(|_| "3002".to_string())
        .parse()
        .unwrap_or(3002)
});

pub static REDIS_KEY_PREFIX: Lazy<String> = Lazy::new(|| {
    env::var("MOMENTRY_REDIS_PREFIX")
        .unwrap_or_else(|_| "momentry:".to_string())
});
```

---

### Step 2: Update `src/core/db/redis_client.rs`

Replace all hardcoded `momentry:` prefixes with configurable prefix.

**Import at top:**
```rust
use crate::core::config::REDIS_KEY_PREFIX;
```

**Pattern for each method:**
```rust
let prefix = REDIS_KEY_PREFIX.as_str();
let key = format!("{}job:{}", prefix, uuid);
```

**Affected lines:**

| Line | Key Pattern |
|------|-------------|
| 47 | `job:{uuid}` |
| 81, 109 | `job:{uuid}:processor:{processor}` |
| 136, 146 | `progress:{uuid}` |
| 172 | `jobs:active` |
| 179 | `jobs:active` → `jobs:completed` |
| 187 | `jobs:active` → `jobs:failed` |
| 194 | `jobs:active` |
| 201, 208 | `health:momentry_core` |
| 214 | `monitor:job:{uuid}` |
| 242, 300 | `errors:{uuid}` |
| 258, 281 | `anomaly:alerts`, `anomaly:key:{key_id}` |
| 317, 346, 364, 392, 397 | `worker:job:{uuid}...` |
| 406, 410 | `worker:job:*` |

---

### Step 3: Update `src/core/cache/redis_cache.rs`

**Import:**
```rust
use crate::core::config::REDIS_KEY_PREFIX;
```

**Replace line 10:**
```rust
// Remove: const KEY_PREFIX: &str = "momentry:cache:";
```

**Update `prefixed_key` method (line 24):**
```rust
fn prefixed_key(&self, key: &str) -> String {
    format!("{}cache:{}", REDIS_KEY_PREFIX.as_str(), key)
}
```

**Update tests (lines 161-162):**
```rust
#[test]
fn test_prefixed_key() {
    // Note: This test will use the configured prefix
    let cache = RedisCache::new().unwrap();
    // With default prefix "momentry:"
    assert_eq!(cache.prefixed_key("test"), "momentry:cache:test");
    assert_eq!(cache.prefixed_key("video:abc"), "momentry:cache:video:abc");
}
```

---

### Step 4: Update `src/main.rs`

**Change CLI defaults (Lines 691-695):**

```rust
// Before:
#[arg(long, default_value = "3000")]
port: u16,

// After:
#[arg(long)]
port: Option<u16>,
```

**Update Server match arm (around line 2398):**

```rust
Commands::Server { host, port } => {
    let port = port.unwrap_or_else(|| *crate::core::config::SERVER_PORT);
    momentry_core::api::start_server(&host, port).await?;
    Ok(())
}
```

**Update Redis key usage (Line 1098):**

```rust
// Before:
let key = format!("momentry:job:{}:processor:{}", uuid, processor);

// After:
let key = format!(
    "{}job:{}:processor:{}",
    crate::core::config::REDIS_KEY_PREFIX.as_str(),
    uuid,
    processor
);
```

---

### Step 5: Create `src/playground.rs`

```rust
use anyhow::{Context, Result};
use clap::{Parser, Subcommand};
// ... same imports as main.rs ...

fn main() -> Result<()> {
    // Load development environment first
    dotenv::from_filename(".env.development").ok();
    
    tracing_subscriber::fmt::init();
    tracing::info!("Starting momentry_playground (development binary)");
    tracing::info!("Port: {}", *momentry_core::core::config::SERVER_PORT);
    tracing::info!("Redis prefix: {}", *momentry_core::core::config::REDIS_KEY_PREFIX);

    let cli = Cli::parse();
    // ... rest identical to main.rs ...
}
```

---

### Step 6: Update `Cargo.toml`

**Add after line 90:**

```toml
[[bin]]
name = "momentry_playground"
path = "src/playground.rs"
```

**Add dependency (if not present):**

```toml
dotenv = "0.15"
```

---

### Step 7: Create `.env.development`

```bash
# Development Environment Configuration
# Used by: momentry_playground binary

# Server Configuration
MOMENTRY_SERVER_PORT=3003
MOMENTRY_REDIS_PREFIX=momentry_dev:

# Worker Configuration (disabled for development)
MOMENTRY_WORKER_ENABLED=false
MOMENTRY_MAX_CONCURRENT=1
MOMENTRY_POLL_INTERVAL=10

# Database (can use separate dev database)
DATABASE_URL=postgres://accusys@localhost:5432/momentry
MONGODB_URL=mongodb://accusys:Test3200Test3200@localhost:27017/admin

# Redis
REDIS_URL=redis://:accusys@localhost:6379
```

---

### Step 8: Update `.env` (Production)

Add these lines:

```bash
# Production Environment Configuration
# Used by: momentry binary

# Server Configuration
MOMENTRY_SERVER_PORT=3002
MOMENTRY_REDIS_PREFIX=momentry:

# Worker Configuration
MOMENTRY_WORKER_ENABLED=true
MOMENTRY_MAX_CONCURRENT=2
MOMENTRY_POLL_INTERVAL=5
```

---

## Testing Checklist

### 1. Build and Run Production Binary

```bash
cargo build --release --bin momentry
cargo run --bin momentry -- server
# Expected: Listening on http://127.0.0.1:3002

cargo run --bin momentry -- worker
# Expected: Worker started with momentry: prefix
```

### 2. Build and Run Development Binary

```bash
cargo build --bin momentry_playground
cargo run --bin momentry_playground -- server
# Expected: Listening on http://127.0.0.1:3003
```

### 3. Verify Redis Key Isolation

```bash
# Production data
redis-cli KEYS "momentry:*"
# Development data
redis-cli KEYS "momentry_dev:*"
# Should be separate
```

### 4. Run Both Simultaneously

```bash
# Terminal 1: Production
cargo run --bin momentry -- server

# Terminal 2: Development
cargo run --bin momentry_playground -- server

# Both should run without port conflicts
```

### 5. Unit Tests

```bash
cargo test --lib
# All tests should pass
```

---

## Redis Key Structure

### Production (`momentry:`)

```
momentry:job:{uuid}                    # Job status
momentry:job:{uuid}:processor:{name}   # Processor progress
momentry:progress:{uuid}               # Progress pub/sub
momentry:jobs:active                   # Active job set
momentry:jobs:completed                # Completed job set
momentry:jobs:failed                   # Failed job set
momentry:health:momentry_core          # Health status
momentry:cache:{key}                   # Cache entries
momentry:worker:job:{uuid}             # Worker job
momentry:worker:job:{uuid}:processor:{name}
```

### Development (`momentry_dev:`)

```
momentry_dev:job:{uuid}
momentry_dev:job:{uuid}:processor:{name}
momentry_dev:progress:{uuid}
momentry_dev:jobs:active
momentry_dev:jobs:completed
momentry_dev:jobs:failed
momentry_dev:health:momentry_core
momentry_dev:cache:{key}
momentry_dev:worker:job:{uuid}
momentry_dev:worker:job:{uuid}:processor:{name}
```

---

## Potential Issues & Solutions

| Issue | Solution |
|-------|----------|
| `dotenv` crate not in dependencies | Add to Cargo.toml |
| Tests use hardcoded prefix | Update tests to use config, or use `#[cfg(test)]` defaults |
| Worker starts in playground | Check `MOMENTRY_WORKER_ENABLED=false` in `.env.development` |
| Port already in use | Graceful error message with suggestion to use `--port` flag |
| Mixed data in Redis | Ensure prefix is loaded before any Redis operations |

---

## Files Summary

| File | Lines Changed | Purpose |
|------|---------------|---------|
| `src/core/config.rs` | +15 | Add SERVER_PORT and REDIS_KEY_PREFIX |
| `src/core/db/redis_client.rs` | ~50 | Replace hardcoded prefixes |
| `src/core/cache/redis_cache.rs` | ~10 | Use configurable prefix |
| `src/main.rs` | ~15 | Update CLI defaults, Redis key usage |
| `src/playground.rs` | NEW (~2800) | Development binary |
| `Cargo.toml` | +4 | Add binary definition |
| `.env.development` | NEW (~20) | Development environment |

**Total**: ~60 lines modified + ~2800 lines new file

---

## Reference Documents

| Document | Purpose |
|----------|---------|
| `docs_v1.0/REFERENCE/SERVICES.md` | Port allocations |
| `docs_v1.0/REFERENCE/MOMENTRY_CORE_REDIS_KEYS.md` | Redis key design |
| `AGENTS.md` | Code style and conventions |

---

## Version History

| Version | Date | Author | Changes |
|---------|------|--------|---------|
| 1.0 | 2025-03-25 | OpenCode | Initial implementation plan |
