# okstra container — CLI Reference

> `okstra container` is a nonlinear tool that launches code from a verified task as a local docker compose group and monitors each container. Because it is a separate entry point from the phase flags in `okstra.sh`, it is documented here rather than in [cli.md](cli.md). See [project-structure-overview](project-structure-overview.md) §4.3 for the module location.

---

## Overview

- **What it is:** Deploys (`up`) the integrated code for a specific task to a task-specific docker compose container group, continuously monitors logs with a watcher for each container, and manages the lifecycle through `status`/`logs`/`stop-watcher`/`down`.
- **Orchestration only (does not generate files):** **Reuses** the target project's existing `docker-compose.yml`. It does not generate configuration files; if `docker-compose.yml` is missing, it reports what is missing and stops (the same applies if only a `Dockerfile` exists).
- **Nonlinear:** This is not a phase in `PHASE_SEQUENCE`. Any task key can be launched regardless of whether it has passed verification.
- **Single entry point:** The `/okstra-container-build` skill, `bin okstra container`, and Python `okstra_ctl.container` all converge on [`scripts/okstra_ctl/container.py`](../scripts/okstra_ctl/container.py) and its `provision_container_group`.
- **Source of truth (SSOT) for container existence:** Docker labels. `registry.json` is a secondary index containing only tmux sessions/panes/findings.

## Prerequisites

- `docker` / `docker compose` must be installed and operational. `up` fails when they are not installed, but `okstra doctor` emits only `[WARN]` and retains exit code 0 (container functionality is optional).
- The code for the target task key must be integrated into the task-key worktree HEAD. If there are unintegrated stages, `up` merges them (preserving the stage trees) before continuing; if a conflict occurs, it reports the conflicting files and stops.
- `docker-compose.yml` must exist at the worktree root.

## Command format

```
okstra container <up|status|logs|stop-watcher|down> --project-root <PATH> --task-key <KEY> [--text] [options]
```

| sub-command | Behavior | Additional options |
|---|---|---|
| `up` | Verify stage integration → validate `docker-compose.yml` → compose env override → `docker compose -p <project> up -d` → poll health checks → start a tail/watcher pane for each container | — |
| `status` | Show the live container group status by querying Docker labels | — |
| `logs` | Report the watcher directory and registered watcher entries; it does not stream Docker container logs | `--service <NAME>` (filters watcher metadata; all registered watchers when omitted) |
| `stop-watcher` | Stop the watcher panes for the task (containers remain running) | — |
| `down` | Tear down the container group and stop attached watchers | `--all` |

## Arguments

### `--project-root` (required)
Absolute path to the target project root. Required for every sub-command.

### `--task-key` (effectively required)
Identifier of the task to deploy, in the form `<project-id>:<task-group>:<task-id>`. The default is an empty string, but actual operations require a valid task key.

### `--service` (`logs` only)
`--service <NAME>` filters the returned watcher metadata by compose service name. When omitted, all registered watcher entries are returned; an unknown name produces an empty `watchers` object.

The actual `logs` output from `logs_container_group()` contains `watchersDir` and registered watcher entries in `watchers`. It does not run `docker compose logs` and does not stream Docker container logs. Inspect each watcher's `findings_path` and the files beneath `watchersDir` for monitoring results.

`--text` emits command-specific fixed labels for model-facing skills. Without it, the command preserves the full machine JSON bytes and exit codes.

### `--all` (`down` only)
Clean up **all** task container groups and watchers within the current project root's `.okstra/` scope. The boundary is limited to the `<project-root>/.okstra/` prefix, so panes belonging to **other projects** in concurrent sessions are never touched. Without `--all`, a single `down` cleans up exactly the panes for the specified task.

## How `up` works

1. **Resolve source tree** — Use the task-key worktree HEAD. Merge unintegrated stages (`teardown=False`, preserving stage worktrees). Stop and identify conflicting files if a conflict occurs (retries are safe because the already-merged operation is idempotent).
2. **Validate configuration file** — Check for `docker-compose.yml` at the worktree root. If it is missing, stop after identifying the missing filename (do not generate it).
3. **Scan bind mounts** — If the normalized output of `docker compose config` contains a host path outside the worktree (`../` or an absolute path), **warn and continue** (do not stop; warn that the container may write files to the host).
4. **Compose env override** — In the okstra-owned worktree, layer task overrides over `.env`, write the result to `env.override`, and pass the files in the order `--env-file <worktree .env> --env-file <env.override>` (the latter takes precedence).
5. **Deploy** — Run `docker compose -p <project-name> ... up -d`. Obtain the service list from `docker compose config --services` (the canonical source).
6. **Poll health checks** — Use `docker compose ps` to verify that each service has started. Success means reaching healthy for services with a health check, or `running` for services without one.
7. **Start monitoring** — If `tmux` is available, start a tail pane and watcher agent for each container in the detached session `okstra-container-<slug>`. If it is unavailable, launch only the containers and state "monitoring disabled (tmux unavailable)" in the result.

### Fixed defaults (not currently exposed as CLI flags)

| Value | Default | Meaning |
|---|---|---|
| healthcheck timeout | 120 seconds | Stop and report services that failed to start after this limit |
| healthcheck interval | 3 seconds | Polling interval for `docker compose ps` |
| watcher scan interval | 5 seconds | Interval for scanning incremental watcher logs |

> These values exist as named arguments to `provision_container_group`, but because they are not exposed as CLI flags, they currently operate as fixed values.

## watcher (two-stage error trigger)

One watcher per container runs in the detached session.

1. **Lightweight scan** — Fetch incremental logs with `docker compose logs --since` and match only regular expressions (`ERROR`/`FATAL`/`Exception`/`Traceback`/abnormal exit codes, and so on). If there are no matches, proceed to the next interval without an LLM call → zero token cost during healthy periods.
2. **Deep analysis** — Only when a pattern is detected, the watcher AI analyzes the relevant log window and appends its findings to `findings.md`. Identical error signatures are debounced (meaningful numbers such as HTTP statuses and exit codes are preserved, while only noise such as timestamps and pids is normalized). The watcher **only detects and reports**; it does not modify code or configuration.

Watcher/tail panes carry only the dedicated `@okstra_container_run` tag and survive a Claude session ending — no session-end hook reclaims panes any more. Stop them with `stop-watcher` or `down`.

## Labels and artifacts

Three labels are attached to deployed containers. The label queries used by `status`/`down` use them to locate the group.

| Label | Value |
|---|---|
| `okstra.task-key` | `<project-id>:<task-group>:<task-id>` |
| `okstra.project-name` | compose project name `okstra-<proj>-<group>-<task>` |
| `okstra.run-trace` | container session name (`okstra-container-<slug>`) |

All okstra artifacts are stored under `<project-root>/.okstra/tasks/<group>/<task-id>/container/` (the original project files remain unchanged):

```
container/
├── env.override            # per-task variables layered over the project .env
├── registry.json           # tmux sessions/panes/findings (flock-guarded secondary index)
├── deploy-state.json       # compose project name, running containers, labels
└── watchers/<service>-findings.md   # per-watcher error analysis log
```

## Exit behavior/output

Each sub-command returns exit code 0 on success. `--text` writes its command-specific fixed labels for model callers; the default writes the full machine JSON contract. Validation failures (missing configuration files, merge conflicts, health check timeouts, and so on) terminate abnormally with a `PrepareError` that identifies what went wrong.

## Usage examples

```bash
# Deploy and start monitoring
okstra container up --project-root /path/to/proj --task-key proj:auth:login-fix --text

# Status
okstra container status --project-root /path/to/proj --task-key proj:auth:login-fix

# Filter watcher metadata for one service
okstra container logs --project-root /path/to/proj --task-key proj:auth:login-fix --service api

# Stop only the watcher (keep containers running)
okstra container stop-watcher --project-root /path/to/proj --task-key proj:auth:login-fix

# Tear down the group
okstra container down --project-root /path/to/proj --task-key proj:auth:login-fix

# Remove every container group for this project
okstra container down --project-root /path/to/proj --all
```
