# Plan: Phase 3 — Deploy Hardening

**Requirements:** DEPL-01, DEPL-02, DEPL-03, DEPL-04

## Tasks

### Task 1: Create mise deploy task with safety gates (DEPL-01, DEPL-02, DEPL-03)

**File:** `.mise/tasks/deploy/bigblack` (new file)

```bash
#!/usr/bin/env bash
#MISE description="Deploy to bigblack with safety gates"
#MISE depends=["release:build-all"]

set -euo pipefail

REMOTE="bigblack"
DEPLOY_DIR=$(ssh "$REMOTE" 'systemctl --user show opendeviationbar-sidecar -p ExecStart --value' | grep -oP '(?<=WorkingDirectory=)\S+' || echo "/home/tca/opendeviationbar-py")

echo "=== Deploy to $REMOTE:$DEPLOY_DIR ==="

# DEPL-01: Build time assertion
BUILD_START=$(date +%s)
mise run release:build-all
BUILD_END=$(date +%s)
BUILD_TIME=$((BUILD_END - BUILD_START))
if [ "$BUILD_TIME" -lt 30 ]; then
    echo "❌ ABORT: Build completed in ${BUILD_TIME}s (<30s = stale cached wheel)"
    echo "   Run: cargo clean && mise run release:build-all"
    exit 1
fi
echo "✓ Build time: ${BUILD_TIME}s (>30s, fresh build confirmed)"

# Copy wheel
WHEEL=$(ls -t dist/*.whl | head -1)
echo "→ Copying $WHEEL to $REMOTE:$DEPLOY_DIR/dist/"
scp "$WHEEL" "$REMOTE:$DEPLOY_DIR/dist/"

# DEPL-02: Resolve deploy directory from systemd
echo "→ Deploy directory: $DEPLOY_DIR"

# Install on remote
ssh "$REMOTE" "cd $DEPLOY_DIR && .venv/bin/pip install dist/$(basename "$WHEEL") --force-reinstall --no-deps"

# Stop sidecar + kintsugi
ssh "$REMOTE" 'systemctl --user stop opendeviationbar-sidecar opendeviationbar-kintsugi 2>/dev/null || true'
sleep 2

# Start sidecar
ssh "$REMOTE" 'systemctl --user start opendeviationbar-sidecar'

# DEPL-03: Post-start telemetry gate
echo "→ Waiting for StreamManager started marker..."
for i in $(seq 1 12); do
    if ssh "$REMOTE" "journalctl --user -u opendeviationbar-sidecar --since '60s ago' --no-pager 2>/dev/null | grep -q 'StreamManager started'"; then
        echo "✓ StreamManager started (attempt $i)"
        break
    fi
    if [ "$i" -eq 12 ]; then
        echo "❌ ABORT: StreamManager not started after 60s. Check logs:"
        echo "   ssh $REMOTE 'journalctl --user -u opendeviationbar-sidecar -n 50 --no-pager'"
        exit 1
    fi
    sleep 5
done

# Start kintsugi only after sidecar is confirmed running
ssh "$REMOTE" 'systemctl --user start opendeviationbar-kintsugi'
echo "✓ Kintsugi started"

echo ""
echo "✓ Deploy complete. Monitor:"
echo "  ssh $REMOTE 'journalctl --user -u opendeviationbar-sidecar -f --no-pager'"
```

### Task 2: Add vergen for git hash embedding (DEPL-04)

**File:** `Cargo.toml` (workspace root)

Add to `[workspace.dependencies]`:

```toml
vergen-git2 = { version = "1", features = ["build"] }
```

**File:** `build.rs` (new, workspace root)

```rust
fn main() {
    // DEPL-04: Embed git SHA for deploy verification
    if let Err(e) = vergen_git2::Emit::default()
        .git_sha(true)
        .emit()
    {
        eprintln!("vergen warning: {e}");
    }
}
```

**File:** `src/lib.rs`

Add near module init (inside `#[pymodule]`):

```rust
// DEPL-04: Log git SHA at module load for deploy verification
#[cfg(not(test))]
{
    let sha = option_env!("VERGEN_GIT_SHA").unwrap_or("unknown");
    eprintln!("[opendeviationbar] git_sha={sha}");
}
```

**Alternative (simpler):** If vergen adds too much build complexity, use a Python-side approach:

```python
# In sidecar.py, at startup
import subprocess
try:
    git_sha = subprocess.check_output(
        ["git", "rev-parse", "--short", "HEAD"],
        cwd=Path(__file__).parent.parent.parent,
        text=True,
    ).strip()
except Exception:
    git_sha = "unknown"
logger.info("sidecar_startup", git_sha=git_sha, version=__version__)
```

The Python approach is simpler and doesn't require Rust build changes. Prefer this unless the Rust binary itself needs the SHA.

## Verification

1. `mise run deploy:bigblack` exists and runs the safety gates
2. Build time <30s → ABORT with clear message
3. Deploy uses systemd WorkingDirectory (not hardcoded path)
4. Post-start waits for StreamManager started marker
5. Kintsugi only starts after sidecar is confirmed running
6. Git SHA logged at sidecar startup

## Commit

```
feat: deploy hardening — build assertion, directory resolution, telemetry gate

Adds mise deploy:bigblack task with 3 safety gates:
- DEPL-01: Build time >30s assertion (stale wheel detection)
- DEPL-02: Deploy directory from systemd WorkingDirectory
- DEPL-03: Post-start telemetry gate (StreamManager started)
- DEPL-04: Git SHA logged at sidecar startup

Prevents the root cause chain from the v13.30.0 deploy incident.
```
