# okstra-manager

Use this to bundle okstra tasks across multiple projects into a single manager-owned context. The authoritative contract is [`skills/okstra-manager/SKILL.md`](../../../skills/okstra-manager/SKILL.md); the CLI implementation is [`src/commands/manager.mjs`](../../../src/commands/manager.mjs) and [`scripts/okstra_ctl/manager_cli.py`](../../../scripts/okstra_ctl/manager_cli.py).

## When to Use

- The user says something like "multiple projects", "cross-project", "okstra manager", or "group projects together".
- Register projects under a manager, or discover candidate projects.
- Plan child project tasks under a shared task-group/task-id and assign roles/directives.
- Sync child project `.okstra` state into the manager snapshot, or view status.
- Prepare a launch packet and a manager child context for running a specific child task.

## Execution Rules

1. Every command starts with the literal `okstra`. Do not wrap it in shell variables, `$(...)`, `&&`, `eval`, or a leading env assignment.
2. The fixed CLI fields are the source of truth. Do not reconstruct manager state or child launch args from docs/memory.
   Nested project, manifest, child, snapshot, and directive values use numbered count/name/value rows; carry every returned row.
3. `--workspace-root` is owned by the Node wrapper. The CLI rejects it if the user passes it.
4. `new project`'s `--project-root` must be an already-existing directory. It performs setup-equivalent registration only when there is no `.okstra/project.json` inside it.
5. The public child task identity is `project-id:task-group:task-id`. The `new task --task` example shows the full key form first.
6. When the actual child task id differs under the same manager task id, pass `--child-task-id <id>` to both `task assign` and `task run`.

## Command Surface

```bash
okstra manager init --manager-id <manager-id>
okstra manager discover-projects
okstra manager new project --manager-id <manager-id> --project-id <project-id> --project-root <abs-path> [--role <role>] [--tag <tag>]
okstra manager new task-group --manager-id <manager-id> --task-group <task-group>
okstra manager new task --manager-id <manager-id> --task-group <task-group> --task-id <task-id> [--task <project-id:task-group:task-id> ...] [--objective <text>] [--common-brief <path>] [--progress-mode <manual|auto>]
okstra manager task assign --manager-id <manager-id> --task-group <task-group> --task-id <task-id> --project-id <project-id> [--child-task-id <child-task-id>] [--role <role>] [--tag <tag>] [--assignment <text>]
okstra manager task note --manager-id <manager-id> --task-group <task-group> --task-id <task-id> --scope <shared|project> [--project-id <project-id>] --body <text>
okstra manager task sync --manager-id <manager-id> --task-group <task-group> --task-id <task-id>
okstra manager task status --manager-id <manager-id> --task-group <task-group> --task-id <task-id>
okstra manager task run --manager-id <manager-id> --project-id <project-id> --task-group <task-group> --task-id <task-id> [--child-task-id <child-task-id>]
```

## Storage Model

Manager state is stored under `~/.okstra/managers/<manager-id>/`, split into two layers: the manager root and the task directory.

Directly under the manager root:

- `manager.json`: manager id / schema / createdAt
- `projects.json`: registered projectId, projectRoot, role, tags

Under the task directory `task-groups/<safe-group>/<safe-task>/`:

- `manifest.json`: manager task objective, common brief, progress mode
- `children.json`: child task plan, assignment, launch metadata
- `directives.jsonl`: shared/project directive rows
- `snapshots.json`: the read-side snapshot `task sync` read from the project-local `.okstra`
- `events.jsonl`: manager events such as `task-created` and `child-launch-prepared`
- `child-context/<safe-project>-<safe-task>.md`: the child lead context `task run` produced

A segment whose slug is empty (e.g. a non-ASCII task-group/task-id) uses a `u-<sha1-prefix>` path segment, but the manifest and the child `taskKey` preserve the original input value.

## Child launch

`task run` does not run the child work directly; it prepares a launch packet. The key fixed fields of the returned packet:

- `Task key`: the child's `project-id:task-group:task-id` (the public child-identity key — also recorded on the `child-launch-prepared` event)
- `Backend`: `tmux-child-lead` if `$TMUX` is present, otherwise `subagent-child-lead`
- `Worker dispatch backend`: always `subagent` in v1
- `Project root`: the child project root
- `Context path`: the manager child context markdown
- every numbered `Run arg N`: the ordered `okstra run ... --directive "Read manager child context: ..."` arguments for the host launcher to use

When packet creation succeeds, that child launch's status in `children.json` is updated to `prepared`, and a `child-launch-prepared` is appended to `events.jsonl`. On failure it does not modify the project-local task state.
