okstra 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.kr.md CHANGED
@@ -109,6 +109,8 @@ okstra install # 'npx -y okstra@latest install' 와 동일
109
109
 
110
110
  글로벌 설치는 Node CLI 를 PATH 에 등록할 뿐입니다. 런타임(`~/.okstra/`) 과 Claude 스킬(`~/.claude/skills/`) 은 여전히 `okstra install` 이 생성합니다 — `npm i -g` 에 포함되지 않습니다. 이후 업그레이드: `npm i -g okstra@latest && okstra install`. 글로벌 바이너리 제거: `npm uninstall -g okstra` (`~/.okstra/` 는 그대로; 그것까지 지우려면 `okstra uninstall`).
111
111
 
112
+ **글로벌 설치 시 스킬 동작.** 모든 okstra 스킬은 PATH 에 잡힌 `okstra` 를 자동 감지하여 `npx -y okstra@latest` 대신 우선 사용합니다. 즉 글로벌 설치를 해두면 매 스킬 호출(`okstra-run`, `okstra-status`, `okstra-history`, `okstra-schedule`, `okstra-report-finder`, `okstra-time-summary`, `okstra-setup` Step 2 의 Step 0) 마다 npx 가 패키지 fetch / 버전 체크하던 비용이 사라집니다. 스킬이 본인이 설치한 버전을 그대로 쓰므로 **업그레이드 타이밍은 사용자가 통제** 합니다 — 더 이상 호출마다 `@latest` 가 강제되지 않습니다. 새 릴리스를 받으려면 원하는 시점에 `npm i -g okstra@latest && okstra install` 을 실행하세요. `okstra` 가 PATH 에 없으면 스킬은 자동으로 npx fallback 으로 동작하므로 글로벌 설치가 없는 환경에서도 변경 없이 그대로 동작합니다.
113
+
112
114
  ### 3.2 프로젝트 등록 (프로젝트당 1회)
113
115
 
114
116
  CLI 에서:
package/README.md CHANGED
@@ -108,6 +108,8 @@ okstra install # same as 'npx -y okstra@latest install'
108
108
 
109
109
  The global install only registers the Node CLI on your PATH. The runtime (`~/.okstra/`) and the Claude skills (`~/.claude/skills/`) are still provisioned by `okstra install` — they are not part of `npm i -g`. To upgrade later: `npm i -g okstra@latest && okstra install`. To remove the global binary: `npm uninstall -g okstra` (leaves `~/.okstra/` untouched; remove that with `okstra uninstall`).
110
110
 
111
+ **Skill behaviour with a global install.** All okstra skills auto-detect a PATH-resolved `okstra` and prefer it over `npx -y okstra@latest`. That means a global install removes the per-call npx fetch / version-check from every skill invocation (Step 0 of `okstra-run`, `okstra-status`, `okstra-history`, `okstra-schedule`, `okstra-report-finder`, `okstra-time-summary`, `okstra-setup` Step 2). Since the skill uses your globally installed version directly, *you* control upgrade timing — `@latest` is no longer forced on each call. Run `npm i -g okstra@latest && okstra install` whenever you want to pull a new release. If `okstra` is not on PATH the skill silently falls back to npx, so machines without a global install keep working unchanged.
112
+
111
113
  ### 3.2 Register a project (once per project)
112
114
 
113
115
  From the CLI:
@@ -162,7 +164,7 @@ Notable flags added in 0.7.0 / 0.8.0:
162
164
 
163
165
  Recent workflow additions (post-0.8.0, on `main`):
164
166
 
165
- - **Isolated executor worktree for `--task-type implementation`** — prepare automatically runs `git worktree add ~/.okstra/worktrees/<project>/<group>/<task>-<seq>` on a fresh branch `<work-category-prefix>-<task-id-segment>-<seq>` branched from `HEAD`. Executor and verifiers operate inside that worktree so the caller's working tree stays clean, and the worktree is preserved after the run as the canonical artefact for PR authoring and rollback. Skip paths: when the caller is already inside another worktree or `project_root` is not a git repo, provisioning no-ops. Manual cleanup: `git worktree remove <path>` → `git branch -D <branch>`. Details: [`docs/kr/architecture.md`](docs/kr/architecture.md) (*Task type* section) and [`docs/kr/cli.md#--executor`](docs/kr/cli.md#--executor).
167
+ - **Isolated task worktree for every task-type** — prepare automatically runs `git worktree add ~/.okstra/worktrees/<project>/<group>/<task>/` on a fresh branch `<work-category-prefix>-<task-id-segment>` branched from the main worktree's `HEAD` the first time a task-key is seen. Every subsequent phase of the same task-key (`requirements-discovery` `error-analysis` → `implementation-planning` → `implementation`) reuses the same path and branch, so phase N inherits the working-tree state phase N-1 left behind. A global registry at `~/.okstra/worktrees/registry.json` (flock-guarded) reserves task-keys and branches across concurrent runs; all path/branch segments are sanitised (`/`, `:`, etc. → `-`). The worktree is preserved after every run for follow-up phases, PR authoring, and rollback. Skip paths: when the caller is already inside another worktree or `project_root` is not a git repo, provisioning no-ops. Manual cleanup: `git worktree remove <path>` → `git branch -D <branch>` plus removing the task-key entry from the registry. Details: [`docs/kr/architecture.md`](docs/kr/architecture.md) (*Task type* section) and [`docs/kr/cli.md#--executor`](docs/kr/cli.md#--executor).
166
168
  - **`release-handoff` lifecycle phase** — runs after `final-verification` returns `verdict=accepted`. The lead drafts a commit message and PR body via a Claude worker, then prompts the user with `AskUserQuestion` for three choices: action (`commit only` / `commit + PR` / `skip`), PR base branch (`staging` / `preprod` / `prod` / `main` / `dev` / free-form), and message handling (`use as-is` / `edit then proceed` / `cancel`). Only user-selected mutating git/gh commands run. Force-push, base-branch direct push, hook bypass (`--no-verify`), and release publishing (`gh release`, `npm publish`, ...) are forbidden. Source code is not edited in this phase. Profile: [`prompts/profiles/release-handoff.md`](prompts/profiles/release-handoff.md).
167
169
 
168
170
  ### 3.5 Ops commands
@@ -321,7 +321,7 @@ Claude launch prompt 본문은 항상 `prompts/launch.template.md` 템플릿에
321
321
  공통 제약:
322
322
 
323
323
  - `implementation`을 제외한 모든 phase는 source code edit, build, migration, deployment, 그 밖의 state-mutating 명령을 금지합니다(`final-verification`은 read-only 테스트 명령만 허용). `implementation`은 승인된 plan의 파일 목록 안에서만 edit/commit이 허용되며, `git push`·publish·deploy·실제 migration·third-party write API는 여전히 금지됩니다.
324
- - **`implementation` 격리 worktree (BLOCKING)**: `--task-type implementation` run은 prepare 단계에서 `okstra-ctl` 이 자동으로 `git worktree` 를 생성해 executor·verifier가 모두 안에서 작업하도록 강제합니다. 위치는 `~/.okstra/worktrees/<project-id>/<task-group-segment>/<task-id-segment>-<run-seq>` 이고, 브랜치 이름은 `<work-category-prefix>-<task-id-segment>-<run-seq>` (예: `feat-dev-9436-001`, `fix-dev-7311-002`) 입니다. base ref 는 prepare 시점의 `HEAD`. caller 가 이미 다른 worktree 안에 있거나 project_root 가 git repo 가 아니면 provisioning 은 skip 되고 executor 는 project_root 에서 그대로 작업합니다. worktree 는 run 종료 후 자동 삭제되지 않으며 PR 작성·rollback 검증·후속 `final-verification` 의 권위 artefact 입니다. 수동 cleanup: `git -C <project_root> worktree remove <path>` `git -C <project_root> branch -D <branch>`. 자세한 동작은 `prompts/profiles/implementation.md` 의 *Executor worktree* 블록과 `agents/SKILL.md` 의 *Implementation phase: Executor binding Executor worktree* 섹션 참고.
324
+ - **모든 task-type 격리 worktree (BLOCKING)**: 모든 task-type 번째 phase prepare 단계에서 `okstra-ctl` 이 자동으로 task-key 단위 `git worktree` 를 생성하고, 같은 task-key 이후 phase (`requirements-discovery` → `error-analysis` → `implementation-planning` → `implementation`) 는 동일한 worktree·브랜치를 재사용합니다. 위치는 `~/.okstra/worktrees/<project-id>/<task-group-segment>/<task-id-segment>/` (segment 의 `/`·`:` 등 특수문자는 `-` 로 정규화) 이고, 브랜치 이름은 `<work-category-prefix>-<task-id-segment>` (예: `feat-dev-9436`, `fix-dev-7311`) 입니다. base ref 는 첫 phase prepare 시점의 main worktree `HEAD`. `~/.okstra/worktrees/registry.json` (flock-guarded) 가 task-key → path/branch 매핑을 전역 관리해 동시 실행 시 path·branch 충돌을 방지합니다. `.project-docs/`, `.scratch/`, `graphify-out/` 은 main worktree 에서 symlink 로 연결되어 모든 task 가 동일한 shared state 를 봅니다. caller 가 이미 다른 worktree 안에 있거나 project_root 가 git repo 가 아니면 provisioning 은 skip 되고 executor 는 project_root 에서 그대로 작업합니다. worktree 는 run 종료 후 자동 삭제되지 않으며 후속 phase·PR 작성·rollback 검증의 권위 artefact 입니다. 수동 cleanup: `git -C <main-worktree> worktree remove <path>` `git -C <main-worktree> branch -D <branch>` + registry 항목 삭제. 자세한 동작은 `prompts/profiles/implementation.md` 의 *Task worktree* 블록과 `agents/SKILL.md` 의 *Task worktree (BLOCKING for every task-type)* 섹션 참고.
325
325
  - `implementation` 과 `release-handoff` 를 제외한 모든 phase 는 source code edit, build, migration, deployment, 그 밖의 state-mutating 명령을 금지합니다 (`final-verification` 은 read-only 테스트 명령만 허용). `implementation` 은 승인된 plan 의 파일 목록 안에서만 edit/commit 이 허용되며, `git push`·publish·deploy·실제 migration·third-party write API 는 여전히 금지됩니다. `release-handoff` 는 source code 자체는 수정하지 않고, 사용자가 메뉴로 선택한 commit / push / PR 명령만 실행합니다 (force push, base 브랜치 직접 push, hook bypass, release publish 는 여전히 금지).
326
326
  - 사용자가 "다음 단계 진행해" 같은 표현을 보내도, 그 발화만으로 다음 phase가 자동 시작되지 않습니다. 다음 phase는 새 `okstra.sh` 실행으로만 시작합니다.
327
327
  - **Authority & permissions assumption (HARD RULE — 모든 task-type 및 `okstra-schedule` 공통)**: 사용자(및 팀)는 예상되는 모든 작업에 대해 완전한 권한·승인 권한을 보유한다고 가정합니다. 외부 승인, 서드파티 액세스, 역할/IAM 권한, 조직적 sign-off, 법무·보안 검토, 벤더 협의, "권한 보유 여부 확인" 같은 항목을 routing 결정·missing inputs·clarification questions·risk·dependency·open questions·effort/day 추정에 포함하지 않습니다. okstra 내부 phase 핸드오프(`User Approval Request` 등)는 사용자 본인이 즉시 승인 가능한 내부 게이트이므로 영향 없으며, `implementation`의 forbidden actions(`git push`, prod deploy, shared-DB migration 등)도 권한 사유가 아닌 **안전 사유**로 계속 적용됩니다.
package/docs/kr/cli.md CHANGED
@@ -288,7 +288,7 @@ fallback 기본값은 아래와 같습니다.
288
288
  - Executor 의 모델은 provider 별 worker 모델 플래그를 그대로 재사용합니다. 즉 `--executor codex` 이면 Executor 의 모델은 `--codex-model` (기본 `gpt-5.5`), `--executor gemini` 이면 `--gemini-model` (기본 `auto`) 가 됩니다.
289
289
  - Claude/Codex/Gemini 세 verifier 는 executor provider 와 관계없이 항상 dispatch 됩니다. Executor 와 같은 provider 라도 별도 CLI 세션으로 verifier 가 호출되어 context 가 분리되므로 self-review 안전장치는 유지됩니다.
290
290
  - 실제 파일 변경은 Codex/Gemini 의 경우 각 CLI 의 auto-edit 모드 (예: `codex exec --full-auto`) 를 통해 일어나며, Claude-side Edit/Write tool 을 거치지 않습니다.
291
- - **Executor worktree (자동 격리)**: prepare 단계에서 `okstra-ctl` 이 `~/.okstra/worktrees/<project-id>/<task-group-segment>/<task-id-segment>-<run-seq>` 에 `git worktree` 를 생성하고, 브랜치 `<work-category-prefix>-<task-id-segment>-<run-seq>` 를 prepare 시점 `HEAD` 에서 분기합니다. Executor 의 Edit/Write/build/test/commit 모두 이 worktree 안에서 수행되며, verifier 들도 같은 worktree 를 읽어 동일한 diff 를 봅니다. caller 가 이미 다른 worktree 안에 있거나 project_root 가 git repo 가 아니면 provisioning 은 skip 되고 status 가 `skipped-in-worktree` / `skipped-not-git` 로 기록됩니다. 경로·브랜치 충돌은 `PrepareError` 로 즉시 실패시키며, run 종료 후 worktree 는 자동 삭제하지 않습니다(수동: `git worktree remove` → `git branch -D`).
291
+ - **Task worktree (모든 task-type 자동 격리)**: 모든 task-type 의 첫 번째 phase prepare 단계에서 `okstra-ctl` 이 `~/.okstra/worktrees/<project-id>/<task-group-segment>/<task-id-segment>/` 에 `git worktree` 를 생성하고, 브랜치 `<work-category-prefix>-<task-id-segment>` 를 main worktree `HEAD` 에서 분기합니다. 같은 task-key 의 이후 phase 는 동일한 path/branch 를 재사용하므로 status 가 `reused` 로 기록됩니다 (run-prep 시점에 새 `git worktree add` 가 일어나지 않음). 모든 segment 의 `/`·`:` 등 특수문자는 `-` 로 정규화되며, `~/.okstra/worktrees/registry.json` 가 task-key → path/branch 매핑을 전역 관리합니다 (flock-guarded). Executor 의 Edit/Write/build/test/commit, verifier 의 read 는 모두 이 worktree 안에서 수행됩니다. caller 가 이미 다른 worktree 안에 있거나 project_root 가 git repo 가 아니면 provisioning 은 skip 되고 status 가 `skipped-in-worktree` / `skipped-not-git` 로 기록됩니다. 경로·브랜치 충돌은 `PrepareError` 로 즉시 실패시키며, run 종료 후 worktree 는 자동 삭제하지 않습니다 (수동: `git worktree remove` → `git branch -D` + registry 항목 삭제).
292
292
 
293
293
  예:
294
294
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.10.0",
3
+ "version": "0.12.0",
4
4
  "description": "Multi-agent cross-verification orchestrator runtime + Claude Code skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.10.0",
3
- "builtAt": "2026-05-12T16:26:44.380Z",
2
+ "package": "0.12.0",
3
+ "builtAt": "2026-05-12T18:39:56.088Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -107,18 +107,21 @@ Lead MUST dispatch Edit/Write-bearing work only through the `workerAgent` declar
107
107
 
108
108
  Executor is chosen at run-prep time via `--executor <claude|codex|gemini>` (or `OKSTRA_DEFAULT_EXECUTOR`, fallback `claude`); the model used by the executor is taken from the corresponding worker model flag (`--claude-model` / `--codex-model` / `--gemini-model`). For Codex/Gemini executors, the underlying file mutation happens inside the executor CLI's own auto-edit mode (e.g. `codex exec --full-auto`), not through Claude-side Edit/Write tools.
109
109
 
110
- #### Executor worktree (BLOCKING for `implementation`)
110
+ #### Task worktree (BLOCKING for every task-type)
111
111
 
112
- For `--task-type implementation` runs, `okstra-ctl` provisions a dedicated `git worktree` at run-prep time. Lead, the Executor, and every verifier MUST treat the provisioned worktree as the canonical working directory.
112
+ `okstra-ctl` provisions a dedicated `git worktree` per task-key at run-prep time of the **first** phase, and every subsequent phase of the same task-key reuses the same worktree on the same branch. Lead, the Executor, and every verifier MUST treat the provisioned worktree as the canonical working directory regardless of task-type.
113
113
 
114
- - Location: `~/.okstra/worktrees/<project-id>/<task-group-segment>/<task-id-segment>-<run-seq>` (override `OKSTRA_HOME` only for tests).
115
- - Branch: `<work-category-prefix>-<task-id-segment>-<run-seq>` (e.g. `feat-dev-9436-001`, `fix-dev-7311-002`). Branched from `HEAD` of `project_root` at prep time; base SHA is recorded in `EXECUTOR_WORKTREE_BASE_REF`.
116
- - The worktree path, branch, base ref, and provisioning status (`created` | `skipped-in-worktree` | `skipped-not-git`) are exposed through the launch prompt's `## Executor Worktree` section and the implementation profile's worktree block.
117
- - **Skip conditions** (worktree provisioning is a no-op; executor uses `project_root` directly):
114
+ - One worktree per task-key — `requirements-discovery`, `error-analysis`, `implementation-planning`, and `implementation` all share the same path and branch so phase N inherits the working-tree state phase N-1 left behind.
115
+ - Location: `~/.okstra/worktrees/<project-id>/<task-group-segment>/<task-id-segment>/` (override `OKSTRA_HOME` only for tests). All segments are sanitised `/`, `:`, and other special chars collapse to `-`.
116
+ - Branch: `<work-category-prefix>-<task-id-segment>` (e.g. `feat-dev-9436`, `fix-dev-7311`). Branched from `HEAD` of the repo's **main** worktree at the first phase's prep time; base SHA is recorded in `EXECUTOR_WORKTREE_BASE_REF`.
117
+ - A global registry at `~/.okstra/worktrees/registry.json` (flock-guarded) maps each task-key to its path + branch and prevents concurrent runs from colliding. Branch names are globally unique across task-keys on this machine.
118
+ - Sync dirs (`.project-docs`, `.scratch`, `graphify-out` by default; override with `OKSTRA_WORKTREE_SYNC_DIRS`) are symlinked from the **main worktree** so every task observes the same shared state irrespective of which checkout invoked okstra.
119
+ - The path, branch, base ref, and provisioning status (`created` | `reused` | `skipped-in-worktree` | `skipped-not-git`) are exposed through the launch prompt's `## Executor Worktree` section and the implementation profile's worktree block.
120
+ - **Skip conditions** (worktree provisioning is a no-op; task uses `project_root` directly):
118
121
  - `project_root` is already inside a non-main worktree (the run reuses the caller's worktree to avoid nesting).
119
122
  - `project_root` is not inside a git repository at all.
120
- - **Failure mode**: any other error during `git worktree add` (path collision, branch collision, detached-HEAD with no SHA) raises `PrepareError`. Re-run after manually removing the stale path/branch — the worktree is intentionally not garbage-collected.
121
- - **Lifecycle**: kept after the run for manual PR authoring, rollback verification, and follow-up `final-verification` runs. Manual cleanup: `git -C <project_root> worktree remove <path>` then `git -C <project_root> branch -D <branch>`.
123
+ - **Failure mode**: any other error during `git worktree add` (on-disk path collision not tracked by the registry, branch collision with a different task-key, detached-HEAD with no SHA) raises `PrepareError`. Re-run after manually removing the stale path/branch — the worktree is intentionally not garbage-collected.
124
+ - **Lifecycle**: kept after the run for follow-up phases, manual PR authoring, rollback verification, and `final-verification`. Manual cleanup: `git -C <main-worktree> worktree remove <path>` then `git -C <main-worktree> branch -D <branch>`; remove the corresponding registry entry by hand or via `okstra worktree release <task-key>` (when available).
122
125
 
123
126
  ## Phase 1: Task-bundle intake and required reading order
124
127
 
@@ -160,8 +163,9 @@ These phases are governed by [okstra-team-contract](./skills/okstra-team-contrac
160
163
 
161
164
  1. Call `TeamCreate(team_name: "okstra-<task-key>", description: "Lead-plus-worker okstra run for <task-key>")`.
162
165
  2. Record the `TeamCreate` outcome in team-state under `teamCreate: { attempted: true, status: "ok"|"error", error?: <message> }` before any dispatch. This is the audit trail that justifies a later no-`team_name` fallback.
163
- 3. If `TeamCreate` succeeds, proceed to Phase 4 (dispatch with `team_name`).
164
- 4. If `TeamCreate` fails (tool unavailable, permission denied, environment lacks Agent Teams support), proceed to Phase 5 fallback (dispatch with `run_in_background: true` and no `team_name`).
166
+ 3. Verify `team-state.lead.sessionId` is populated. The `okstra.sh` exec path fills it automatically (`generate_claude_session_id` → `claude --session-id ...`). The render-only / in-session takeover path (`okstra-run` skill) auto-detects the live session's jsonl via `resolve_inproc_lead_session_id`, but the detector is best-effort and may return empty if `~/.claude/projects/<encoded-cwd>/` is unreadable or has no jsonl yet. If `lead.sessionId` is empty at this point, write the running session's id into team-state before proceeding — Phase 7 token-usage collection depends on it and will fail with `lead jsonl not found (sessionId=)` otherwise.
167
+ 4. If `TeamCreate` succeeds, proceed to Phase 4 (dispatch with `team_name`).
168
+ 5. If `TeamCreate` fails (tool unavailable, permission denied, environment lacks Agent Teams support), proceed to Phase 5 fallback (dispatch with `run_in_background: true` and no `team_name`).
165
169
 
166
170
  Use agent and subagent names that map cleanly to the selected worker roles. Do not create ambiguous role names that differ from `Claude worker`, `Codex worker`, `Gemini worker`, or `Report writer worker`.
167
171
 
@@ -14,6 +14,8 @@ Invoke the `okstra` skill now. Read the manifests below for all task metadata, p
14
14
  - Phase advancement requires a new okstra invocation launched with `--task-type {{WORKFLOW_NEXT_RECOMMENDED_PHASE}}` after this run's final report is written and approved. The lead must not write source code, run builds/migrations/deployments, or otherwise produce artifacts of a different phase from inside this run.
15
15
  - See `Lifecycle Phase Boundaries` in the okstra skill (`agents/SKILL.md`) for the canonical rules and the phase-transition checklist.
16
16
 
17
+ {{TEAM_CREATION_GATE}}
18
+
17
19
  ## Project Root
18
20
 
19
21
  - Absolute project root: `{{PROJECT_ROOT}}`
@@ -44,9 +46,10 @@ Invoke the `okstra` skill now. Read the manifests below for all task metadata, p
44
46
  - Branch: `{{EXECUTOR_WORKTREE_BRANCH}}`
45
47
  - Base ref: `{{EXECUTOR_WORKTREE_BASE_REF}}`
46
48
  - Note: `{{EXECUTOR_WORKTREE_NOTE}}`
47
- - For `implementation` runs with status `created`, the Executor MUST perform every Edit / Write / build / test command inside the working tree path above. Verifiers (Claude / Codex / Gemini) read from the SAME path so they observe the exact diff produced by the Executor.
48
- - The worktree is preserved after the run (no automatic cleanup). PR authoring, manual review, and rollback verification happen from the worktree branch.
49
- - For any other status (`skipped-non-implementation`, `skipped-in-worktree`, `skipped-not-git`), the run operates directly in `{{PROJECT_ROOT}}` and this section is informational only.
49
+ - For any task-type with status `created` or `reused`, every role (lead, executor, verifier) MUST anchor reads and writes to the working tree path above phase N inherits the working-tree state phase N-1 left behind. Verifiers read from the SAME path so they observe the exact diff produced by the Executor.
50
+ - Branch and path are globally reserved per task-key via `~/.okstra/worktrees/registry.json`; concurrent okstra runs on this machine cannot collide.
51
+ - The worktree is preserved after every run (no automatic cleanup) and is reused by every subsequent phase of the same task-key. Manual cleanup when fully done: `git worktree remove <path>` → `git branch -D <branch>` + remove the task-key entry from the registry.
52
+ - For status `skipped-in-worktree` or `skipped-not-git`, the run operates directly in `{{PROJECT_ROOT}}` and this section is informational only.
50
53
 
51
54
  ## Available MCP Servers
52
55
 
@@ -27,17 +27,17 @@
27
27
  - **CLI ack** — the user runs `okstra ... --task-type implementation --approved-plan <path> --approve`. The CLI invocation itself is modelled as the user's act of approval; the runtime (`okstra_ctl.run._apply_cli_approval`) flips the checkbox in the report file and appends an audit line `- 승인 일시 (CLI ack): <ISO8601> — recorded by \`okstra --approve\`` before the standard regex validation runs. Use this when running unattended or when you want a single command to both approve and launch the next phase.
28
28
  - The `--approve` flag is **only meaningful with `--task-type implementation` and `--approved-plan <path>`**. Passing it with any other task-type causes `PrepareError` (the runtime refuses to silently ignore approval signals). It is also a no-op if the file already carries a valid approval marker (idempotent — only an audit line is appended, the marker is not re-toggled).
29
29
  - the file's `Recommended option` and its bite-sized step list become the authoritative scope for this run; any deviation must be justified in the final report and routed back to a new `implementation-planning` run instead of being silently expanded.
30
- - Executor worktree (provisioned by `okstra-ctl` at run-prep time, fixed for this run):
31
- - Status: `{{EXECUTOR_WORKTREE_STATUS}}` (one of: `created` | `skipped-in-worktree` | `skipped-not-git`)
32
- - Working tree path: `{{EXECUTOR_WORKTREE_PATH}}` — when status is `created`, this is a fresh `git worktree` rooted under `~/.okstra/worktrees/<project>/<task-group>/<task-id>-<seq>`; when skipped, this is the caller's `project_root`.
33
- - Branch: `{{EXECUTOR_WORKTREE_BRANCH}}` — empty when status is `skipped-*`. The branch name encodes `<work-category-prefix>-<task-id-segment>-<seq>`.
34
- - Base ref: `{{EXECUTOR_WORKTREE_BASE_REF}}` — commit SHA the worktree was branched from; canonical `<base>` for every `git diff` / `git log` in this run.
30
+ - Task worktree (provisioned by `okstra-ctl` at the first phase's run-prep time, reused for every subsequent phase of this task-key):
31
+ - Status: `{{EXECUTOR_WORKTREE_STATUS}}` (one of: `created` | `reused` | `skipped-in-worktree` | `skipped-not-git`)
32
+ - Working tree path: `{{EXECUTOR_WORKTREE_PATH}}` — when status is `created` or `reused`, this is the task's `git worktree` rooted at `~/.okstra/worktrees/<project>/<task-group>/<task-id>/` (segments sanitised — `/` `:` → `-`). When skipped, this is the caller's `project_root`.
33
+ - Branch: `{{EXECUTOR_WORKTREE_BRANCH}}` — empty when status is `skipped-*`. The branch name encodes `<work-category-prefix>-<task-id-segment>` and is globally unique across task-keys via `~/.okstra/worktrees/registry.json`.
34
+ - Base ref: `{{EXECUTOR_WORKTREE_BASE_REF}}` — commit SHA the worktree was branched from at the first phase; canonical `<base>` for every `git diff` / `git log` in this run.
35
35
  - Provisioning note: `{{EXECUTOR_WORKTREE_NOTE}}`
36
- - **Executor behaviour**: when status is `created`, the Executor MUST run every Edit / Write / build / test / commit command with the working tree path above as cwd. Treat it as `project_root` for the duration of this run. Do NOT mutate the caller's original checkout. Do NOT `cd` out of the worktree to reach files; if a file outside the worktree is needed, the dependency is a planning gap — record it in `Out-of-plan edits` and continue.
36
+ - **Executor behaviour**: when status is `created` or `reused`, the Executor MUST run every Edit / Write / build / test / commit command with the working tree path above as cwd. Treat it as `project_root` for the duration of this run. Do NOT mutate the caller's original checkout. Do NOT `cd` out of the worktree to reach files; if a file outside the worktree is needed, the dependency is a planning gap — record it in `Out-of-plan edits` and continue.
37
37
  - **Verifier behaviour**: all three verifier roles read from the SAME working tree path so they observe the exact diff the Executor produced. Verifiers remain strictly read-only there.
38
- - **Lifecycle**: the worktree is kept after the run completes (no automatic cleanup). It is the canonical artefact for manual PR authoring, rollback verification, and follow-up `final-verification` runs. Cleanup, when desired, is manual: `git -C <project_root> worktree remove <path>` followed by `git -C <project_root> branch -D <branch>`.
38
+ - **Lifecycle**: the worktree is kept after the run completes (no automatic cleanup) and is reused by every subsequent phase of the same task-key. Cleanup, when the task is fully done, is manual: `git -C <main-worktree> worktree remove <path>` followed by `git -C <main-worktree> branch -D <branch>`, plus removing the task-key entry from `~/.okstra/worktrees/registry.json`.
39
39
  - **Skipped paths**: when status is `skipped-in-worktree` or `skipped-not-git`, the executor operates in `project_root` as before. Cite the status in the final report's metadata header so reviewers know which path was taken.
40
- - **Synced state directories (symlinks into the original `project_root`)**: at provision time `okstra-ctl` symlinks `.project-docs/`, `.scratch/`, and `graphify-out/` from the original `project_root` into the worktree (override via `OKSTRA_WORKTREE_SYNC_DIRS`; empty string disables). These are NOT independent copies — writes through them land in `project_root`. Inside this run the executor MUST confine writes under these paths to its own task scope (i.e. only `.project-docs/okstra/tasks/<this-task-id>/...`). Reading from elsewhere under the symlinks (other tasks, `graphify-out/GRAPH_REPORT.md`, `.scratch/` issues) is allowed and expected for context.
40
+ - **Synced state directories (symlinks into the MAIN worktree)**: at provision time `okstra-ctl` symlinks `.project-docs/`, `.scratch/`, and `graphify-out/` from the repo's **main worktree** into the task worktree (override via `OKSTRA_WORKTREE_SYNC_DIRS`; empty string disables). These are NOT independent copies — writes through them land in the main worktree. Inside this run the executor MUST confine writes under these paths to its own task scope (i.e. only `.project-docs/okstra/tasks/<this-task-id>/...`). Reading from elsewhere under the symlinks (other tasks, `graphify-out/GRAPH_REPORT.md`, `.scratch/` issues) is allowed and expected for context.
41
41
  - Pre-implementation context exploration (executor before first edit):
42
42
  - **Mandatory skill invocation — `tdd`**: BEFORE the first `Edit` or `Write` call, the executor MUST invoke the `tdd` skill via the `Skill` tool and follow its red-green-refactor loop for every code change in this run. This is a hard requirement, not a recommendation; skipping it is a `contract-violated` outcome. The skill governs HOW each step is executed (failing test first → minimal implementation → refactor); it does not override the approved plan's WHAT/file scope.
43
43
  - Order of operations per plan step: (1) write/extend the test that captures the step's acceptance criterion and confirm it fails for the right reason, (2) commit the failing test (`test(<scope>): ...`), (3) implement the minimum change to make it pass, (4) commit the implementation (`feat|fix(<scope>): ...`), (5) refactor without changing behaviour and commit separately if any cleanup is made (`refactor(<scope>): ...`). The failing-then-passing transition between steps (2) and (4) is the `TDD evidence` required by the final report.
@@ -1,14 +1,15 @@
1
1
  # Release Handoff Profile
2
2
 
3
3
  - Purpose: take an `accepted` final-verification verdict and turn it into a delivered commit and/or pull request, with explicit user selection at every mutating step
4
- - Required workers:
5
- - claude
6
- - report-writer
7
- {{INCLUDE:_common-contract.md}}
8
- - Team contract (phase-specific overrides):
9
- - `Claude lead` is the **executor of every git / gh command** in this phase. Workers never call mutating git commands themselves.
10
- - `Claude worker` (drafter) is read-only and produces **commit message candidate(s) and a PR body candidate** in markdown; the lead presents these to the user, accepts edits, and only then runs git.
11
- - Codex / Gemini workers are NOT part of this profile's roster (see `Required workers:` above); the shared contract's `Gemini worker must always be attempted` clause does not apply to release-handoff.
4
+ - **Execution model: single-lead, no worker dispatch.** This phase is a thin orchestrator over `git` / `gh`; it does NOT run team-mode, does NOT call `TeamCreate`, does NOT dispatch analysis or drafter sub-agents, and does NOT run convergence. The Claude lead performs every step inline (drafting commit/PR text, asking the user, running git, writing the final report) — see "Lead-only contract" below.
5
+ - Required workers: *(none — this profile intentionally has no `- Required workers:` block; the run is executed entirely by the Claude lead)*
6
+ - Lead-only contract (replaces the shared team contract for this phase):
7
+ - The Claude lead is the sole agent for this run. No `Agent(...)` worker dispatch, no `TeamCreate`, no parallel sub-agents, no convergence loop.
8
+ - The lead drafts the commit message and PR body **inline** by reading the run brief, the cited final-verification report, and `git diff <base>..HEAD --stat`. No drafter worker is dispatched.
9
+ - The lead authors the final-report file directly (no `Report writer worker` dispatch). The report still conforms to the standard `okstra-final-report.template.md` structure, including the `## 4.6 Release Handoff Deliverables` section.
10
+ - The shared anti-escalation rule from the common contract still applies: do not start any other lifecycle phase from inside this run.
11
+ - The shared "authority & permissions assumption" rule from the common contract still applies: assume the user holds every permission needed; do not block on hypothetical approvals.
12
+ - The shared "MCP read-only" rule still applies if the brief lists MCP servers, though most release-handoff runs do not use MCP.
12
13
  - Pre-handoff entry gate (mandatory — refuse to start if any item fails):
13
14
  - the task brief MUST cite the originating `final-verification` final-report path under `## Source Verification Report`. The lead opens that file and confirms section `## 2. Final Verdict` contains exactly the token `accepted`.
14
15
  - if the verdict is `conditional-accept`, `blocked`, or any other token (including ambiguous phrasing like "looks good"), the run MUST end immediately with status `blocked` and a routing recommendation back to `error-analysis` or `implementation-planning`. Do NOT prompt the user; do NOT run any git command.
@@ -28,18 +29,17 @@
28
29
  - `dev`
29
30
  - `직접 입력` (free-form branch name; lead validates the name exists on origin via `git ls-remote --heads origin <name>` and re-asks on failure)
30
31
  The chosen base MUST NOT equal the feature branch. If it does, re-ask.
31
- 3. **Commit message + PR body confirmation** — show the `Claude worker` drafter's output verbatim and capture one of:
32
- - `use as-is` — proceed with the drafter's text.
32
+ 3. **Commit message + PR body confirmation** — show the lead's inline draft verbatim and capture one of:
33
+ - `use as-is` — proceed with the drafted text.
33
34
  - `edit then proceed` — accept inline edits from the user, then proceed with the edited text.
34
35
  - `cancel` — end the run without executing any git command; record the cancellation in the final report.
35
- - Drafter worker contract (`Claude worker`):
36
- - reads the run brief, the cited final-verification report, and `git diff <base>..HEAD --stat` to ground its output in actual changes.
37
- - produces **two artifacts** in its worker result:
38
- 1. **Commit message candidate** — a single message in Conventional Commits style (`<type>(<scope>): <subject>` + optional body + optional footer). When `commit + PR` will be opened against a `release-please`-managed repo, the type MUST match a configured changelog section (`feat` / `fix` / `perf` / `revert` / `deps` / `docs` / `refactor` / `build` / `ci` / `chore` / `test`).
39
- 2. **PR body candidate** — markdown with sections `## Summary`, `## Changes`, `## Test plan`, `## Linked issues` (omit a section only if it is genuinely empty).
40
- - is strictly **read-only**: MUST NOT run any git mutating command, MUST NOT call `gh pr create`, MUST NOT call `git push`.
41
- - if the drafter cannot produce output (no diff exists, repo is bare, etc.), it returns terminal status `not-run` with a reason; the lead falls back to a manually-authored message captured directly from the user.
42
- - Allowed actions during the run (Claude lead only — workers stay read-only):
36
+ - Inline drafting rules (Claude lead):
37
+ - read the run brief, the cited final-verification report, and `git diff <base>..HEAD --stat` to ground the drafted text in actual changes.
38
+ - produce **two artifacts** before showing them to the user:
39
+ 1. **Commit message** — a single message in Conventional Commits style (`<type>(<scope>): <subject>` + optional body + optional footer). When `commit + PR` will be opened against a `release-please`-managed repo, the type MUST match a configured changelog section (`feat` / `fix` / `perf` / `revert` / `deps` / `docs` / `refactor` / `build` / `ci` / `chore` / `test`).
40
+ 2. **PR body** — markdown with sections `## Summary`, `## Changes`, `## Test plan`, `## Linked issues` (omit a section only if it is genuinely empty).
41
+ - if the diff is empty or no commit can be produced (working tree already matches the base), record "no staged changes; commit skipped" in the final report and skip `git commit` while still proceeding to the PR step if requested.
42
+ - Allowed actions during the run (Claude lead only):
43
43
  - read-only inspection: `git status`, `git status --short`, `git diff`, `git log`, `git rev-parse`, `git ls-remote --heads origin <name>`, `gh pr list --head <branch>`, `gh pr view <url>`.
44
44
  - local commit: `git add -- <path>...` (prefer explicit file paths over `git add -A` / `git add .`), `git commit -m "<message>"`. Re-use the user-confirmed message exactly.
45
45
  - feature-branch push (only when the user picked `commit + PR`): `git push -u origin <current-branch>`. The pushed ref MUST be the feature branch — never the chosen base branch.
@@ -59,7 +59,7 @@
59
59
  - source-code edits, refactors, or any modification to files outside the run's own artifact directories (`reports/`, `prompts/`, `state/`, `manifests/`, `worker-results/`, `status/`, `sessions/`). The diff being shipped MUST be exactly what the prior `implementation` run produced; release-handoff packages it, it does not re-author it.
60
60
  - executing any mutating command the user did NOT select. Examples: opening a PR when the user picked `commit only`; running `git commit` when the user picked `skip`; switching the PR base branch silently after the user already chose one.
61
61
  - retrying a failed git / gh command with weaker safety flags. If `git push` fails with non-fast-forward, the lead MUST stop, explain the failure to the user, and ask for instructions — it MUST NOT add `--force`.
62
- - dispatching parallel sub-agents beyond the required worker roster.
62
+ - `TeamCreate`, `Agent(...)` dispatch of any kind, or any other parallel sub-agent fan-out. This phase runs entirely under the Claude lead.
63
63
  - silently treating an unrecognised user reply as one of the menu options. If the user's answer does not match a presented choice, re-ask the question verbatim.
64
64
  - Required deliverable shape (final report, in addition to the standard sections):
65
65
  - **Source Verification Report**: relative path of the originating `final-verification` final-report file plus the literal quoted `## 2. Final Verdict` line that read `accepted`.
@@ -67,7 +67,7 @@
67
67
  - **User Selections**: a block recording each prompt and the user's verbatim answer.
68
68
  - Q1 action: `commit only` | `commit + PR` | `skip`.
69
69
  - Q2 PR base (if applicable): the chosen branch and how it was selected (menu pick vs free-form input).
70
- - Q3 message/body: `use as-is` | `edit then proceed` (with a diff between the drafter's text and the final text) | `cancel`.
70
+ - Q3 message/body: `use as-is` | `edit then proceed` (with a diff between the lead's draft and the final text) | `cancel`.
71
71
  - **Executed Commands**: every git / gh command the lead actually ran, with its exit code and a one-line stdout/stderr summary. Read-only inspection commands MAY be summarised; mutating commands MUST be listed verbatim.
72
72
  - **Commit List**: each commit SHA (short and full), its subject line, and the files it touched. If no commit was produced (idempotent no-op), state `- No commit was produced (working tree had no staged changes).`
73
73
  - **Pull Request Outcome**: one of
@@ -76,7 +76,7 @@
76
76
  - `- PR reused: <url>` when an existing PR was found via `gh pr list`
77
77
  - `- PR creation skipped: <reason>` for any user-driven cancellation
78
78
  - **Routing recommendation**: explicit `done` token, since release-handoff is the terminal lifecycle phase. If the run ended in `skip` or `cancel`, the recommendation MUST also state whether re-entry into release-handoff is appropriate.
79
- - Self-review pass before finalising the report (`Claude lead` runs this; do not delegate to a generic subagent):
79
+ - Self-review pass before finalising the report (`Claude lead` runs this):
80
80
  1. **Entry-gate audit** — section 2 cites the originating final-verification report path and the literal `accepted` verdict line. If either is missing, the run is invalid and MUST be re-routed to `final-verification`.
81
81
  2. **User-selection traceability** — every executed mutating command maps to a user selection captured in the report. Any mutating command without a corresponding user answer is a contract violation.
82
82
  3. **Forbidden-action audit** — scan the run's session transcripts (`git`, `gh` invocations) for every entry in the Forbidden actions list above. Any occurrence means the run has crossed into unsafe territory and MUST be flagged as `contract-violated`.
@@ -86,4 +86,4 @@
86
86
  - re-litigating the final-verification verdict — release-handoff trusts the cited `accepted` verdict and does not reopen acceptance checks.
87
87
  - opening additional PRs, releases, or deployments beyond the single PR the user chose to create.
88
88
  - merging the PR. Merging is a separate, manual step performed by the user (or by repo automation) after release-handoff ends; the lead MUST NOT call `gh pr merge`.
89
- - escalating beyond the menu choices on user phrasing — every mutating action requires an explicit menu selection (the shared anti-escalation rule applies, with this phase-specific tightening).
89
+ - escalating beyond the menu choices on user phrasing — every mutating action requires an explicit menu selection.
@@ -986,7 +986,55 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
986
986
  else "- `Gemini worker` is not selected for this run, so no Gemini attempt is required."
987
987
  )
988
988
 
989
+ # Team creation gate block: phase-conditional.
990
+ # - release-handoff (and any other phase that renders with no workers
991
+ # selected) is single-lead and MUST NOT call `TeamCreate`. Emit a
992
+ # short notice instead of the BLOCKING gate.
993
+ # - All other phases keep the full team-creation contract.
994
+ task_type = ctx.get("ANALYSIS_TYPE", "")
995
+ if task_type == "release-handoff" or not selected:
996
+ team_creation_gate_block = (
997
+ "## Single-Lead Phase (no team creation)\n"
998
+ "\n"
999
+ "This run is single-lead. There is no worker roster, no\n"
1000
+ "`TeamCreate` call, no `Agent(...)` worker dispatch, and no\n"
1001
+ "convergence loop. The Claude lead performs every step inline\n"
1002
+ "(reading inputs, drafting commit / PR text when applicable,\n"
1003
+ "asking the user, running git / gh, and writing the final\n"
1004
+ "report). Do NOT call `TeamCreate` or dispatch any sub-agent\n"
1005
+ "from this run — that would be a contract violation."
1006
+ )
1007
+ else:
1008
+ team_creation_gate_block = (
1009
+ "## Team Creation Gate (BLOCKING)\n"
1010
+ "\n"
1011
+ "Before any `Agent` dispatch for workers, you MUST perform Phase 3 of the\n"
1012
+ "`okstra` skill (`agents/SKILL.md` → \"Phase 3 — Team creation\"). Skipping\n"
1013
+ "this gate silently degrades the run to in-process background dispatch and\n"
1014
+ "loses the Teams split-pane observability surface, even though worker\n"
1015
+ "outputs may still appear correct on disk.\n"
1016
+ "\n"
1017
+ "Required actions, in order, regardless of how many workers are selected\n"
1018
+ "for this run (roster comes from `resultContract.requiredWorkerRoles` in\n"
1019
+ "`task-manifest.json` — it may be 1, 2, 3, or more workers):\n"
1020
+ "\n"
1021
+ "1. Invoke the `okstra-team-contract` skill and verify the selected worker\n"
1022
+ " roster against `task-manifest.json`'s `resultContract.requiredWorkerRoles`.\n"
1023
+ f"2. Call `TeamCreate(team_name: \"okstra-{ctx.get('TASK_KEY', '')}\", description: ...)`.\n"
1024
+ "3. Record the outcome in team-state under\n"
1025
+ " `teamCreate: { attempted: true, status: \"ok\" | \"error\", error?: <msg> }`\n"
1026
+ " BEFORE any `Agent(...)` worker dispatch.\n"
1027
+ "4. Only after `teamCreate` is persisted may you dispatch workers — with\n"
1028
+ " `team_name` on success, or with `run_in_background: true` and no\n"
1029
+ " `team_name` ONLY when `teamCreate.status == \"error\"` was recorded.\n"
1030
+ "\n"
1031
+ "If the Agent tool rejects a dispatch with `\"team must be created first\"` /\n"
1032
+ "`\"team을 먼저 생성하거나 team_name 없이 호출해야 합니다\"`, the correct\n"
1033
+ "response is to go back to step 2 — NOT to strip `team_name` and retry."
1034
+ )
1035
+
989
1036
  mapping = {
1037
+ "{{TEAM_CREATION_GATE}}": team_creation_gate_block,
990
1038
  "{{PROJECT_ID}}": ctx.get("PROJECT_ID", ""),
991
1039
  "{{TASK_GROUP}}": ctx.get("TASK_GROUP", ""),
992
1040
  "{{TASK_ID}}": ctx.get("TASK_ID", ""),
@@ -51,10 +51,14 @@ from .seeding import (
51
51
  render_runtime_settings_file,
52
52
  verify_installation,
53
53
  )
54
- from .session import generate_claude_session_id, write_claude_resume_command_file
54
+ from .session import (
55
+ generate_claude_session_id,
56
+ resolve_inproc_lead_session_id,
57
+ write_claude_resume_command_file,
58
+ )
55
59
  from .workers import normalize_workers, resolve_profile_workers
56
60
  from .workflow import compute_workflow_state
57
- from .worktree import provision_implementation_worktree
61
+ from .worktree import provision_task_worktree
58
62
 
59
63
  APPROVED_PLAN_PATTERN = re.compile(
60
64
  r"^[ \t]*(?:[-*+][ \t]+)?(APPROVED([ \t]|:|$)|\[x\][ \t]*Approved|"
@@ -453,10 +457,17 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
453
457
  raise PrepareError(f"project.json upsert failed for {project_root}: {exc}") from exc
454
458
 
455
459
  # ---- workers resolution ----
456
- profile_workers_csv = ",".join(resolve_profile_workers(profile_file))
457
- workers = normalize_workers(inp.workers_override or profile_workers_csv)
458
- if not workers:
459
- raise PrepareError(f"no workers resolved for profile: {inp.task_type}")
460
+ # release-handoff is intentionally single-lead (no worker dispatch, no
461
+ # TeamCreate, no convergence). The profile has no `- Required workers:`
462
+ # block; force an empty roster regardless of any user override so the
463
+ # rendered task bundle stays consistent with the profile contract.
464
+ if inp.task_type == "release-handoff":
465
+ workers: list[str] = []
466
+ else:
467
+ profile_workers_csv = ",".join(resolve_profile_workers(profile_file))
468
+ workers = normalize_workers(inp.workers_override or profile_workers_csv)
469
+ if not workers:
470
+ raise PrepareError(f"no workers resolved for profile: {inp.task_type}")
460
471
  selected_reviewers = ",".join(workers)
461
472
 
462
473
  # ---- model assignments ----
@@ -513,21 +524,22 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
513
524
  task_type=inp.task_type, run_seq_override=run_seq_override,
514
525
  )
515
526
 
516
- # ---- executor worktree provisioning (implementation phase only) ----
517
- # The reports-seq is reused as the worktree-seq so the on-disk worktree
518
- # path is colocated with the artefacts produced by this run.
527
+ # ---- task worktree provisioning (every phase, every task-type) ----
528
+ # One worktree per task-key: requirements-discovery, error-analysis,
529
+ # implementation-planning and implementation phases of the same task
530
+ # all share this directory and branch. The global registry handles
531
+ # reservation across concurrent runs.
519
532
  try:
520
- worktree = provision_implementation_worktree(
533
+ worktree = provision_task_worktree(
521
534
  task_type=inp.task_type,
522
535
  project_root=project_root,
523
536
  project_id=inp.project_id,
524
537
  task_group_segment=ctx["TASK_GROUP_SEGMENT"],
525
538
  task_id_segment=ctx["TASK_ID_SEGMENT"],
526
- run_seq=int(ctx["RUN_REPORTS_SEQ"]),
527
539
  work_category=inp.work_category,
528
540
  )
529
541
  except RuntimeError as exc:
530
- raise PrepareError(f"executor worktree provisioning failed: {exc}") from exc
542
+ raise PrepareError(f"task worktree provisioning failed: {exc}") from exc
531
543
 
532
544
  ctx.update({
533
545
  "EXECUTOR_WORKTREE_PATH": worktree.path,
@@ -537,7 +549,16 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
537
549
  "EXECUTOR_WORKTREE_NOTE": worktree.note,
538
550
  })
539
551
 
540
- claude_session_id = "" if inp.render_only else generate_claude_session_id()
552
+ if inp.render_only:
553
+ # render-only entry path (e.g. okstra-run skill, in-session takeover):
554
+ # the calling Claude session itself becomes the lead, so we must NOT
555
+ # mint a fresh UUID — instead, best-effort detect the live session's
556
+ # jsonl under ~/.claude/projects/<encoded-cwd>/. Leaving this blank
557
+ # caused Phase 7 token-usage collection to fail (lead jsonl not
558
+ # found) and the validator to report `sessionId=`.
559
+ claude_session_id = resolve_inproc_lead_session_id(project_root)
560
+ else:
561
+ claude_session_id = generate_claude_session_id()
541
562
 
542
563
  # ---- material + related-tasks ----
543
564
  profile_content = _expand_profile_includes(profile_file)
@@ -15,6 +15,39 @@ def generate_claude_session_id() -> str:
15
15
  return str(uuid.uuid4())
16
16
 
17
17
 
18
+ def _claude_projects_dir_for(cwd: Path) -> Path:
19
+ """Mirror of `okstra_token_usage.paths.claude_project_dir` — kept local to
20
+ avoid a cross-package import inside the run path.
21
+ """
22
+ encoded = "-" + str(cwd).strip("/").replace("/", "-")
23
+ return Path.home() / ".claude" / "projects" / encoded
24
+
25
+
26
+ def resolve_inproc_lead_session_id(project_root: Path) -> str:
27
+ """Best-effort detection of the running Claude session's id when okstra is
28
+ invoked render-only from inside a live session (the `okstra-run` skill
29
+ path). The current session's jsonl is being actively written to under
30
+ `~/.claude/projects/<encoded-cwd>/`, so the most recently modified file
31
+ in that directory is, with very high probability, the calling session.
32
+
33
+ Returns the UUID stem on success, empty string on failure (directory
34
+ missing, no jsonl files, permission error). Callers must treat this as
35
+ best-effort — a failure does not invalidate the session, it just means
36
+ auto-detection could not confirm one.
37
+ """
38
+ try:
39
+ d = _claude_projects_dir_for(project_root)
40
+ if not d.exists():
41
+ return ""
42
+ candidates = [p for p in d.iterdir() if p.suffix == ".jsonl"]
43
+ if not candidates:
44
+ return ""
45
+ newest = max(candidates, key=lambda p: p.stat().st_mtime)
46
+ return newest.stem
47
+ except OSError:
48
+ return ""
49
+
50
+
18
51
  def write_claude_resume_command_file(
19
52
  *, resume_command_path: Path, project_root: Path, claude_session_id: str,
20
53
  ) -> None:
@@ -127,11 +127,11 @@ PHASE_RULES: dict[str, dict[str, str]] = {
127
127
  " - entering this phase only when the cited final-verification report's verdict is exactly `accepted`\n"
128
128
  " - asking the user (via `AskUserQuestion` / interactive prompt) which delivery action to take: `commit only`, `commit + PR`, or `skip` (end the run)\n"
129
129
  " - asking the user to pick a PR base branch from `staging` | `preprod` | `prod` | `main` | `dev` | a user-supplied branch name\n"
130
- " - dispatching the `Claude worker` (drafter) to produce candidate commit message(s) and PR body in markdown; the lead reviews and offers them to the user before any git command runs\n"
130
+ " - drafting commit message(s) and PR body **inline as the Claude lead** (no drafter worker, no `Agent` dispatch); the lead reviews its own draft with the user before any git command runs\n"
131
131
  " - local git operations: `git status`, `git diff`, `git log`, `git add`, `git commit -m`\n"
132
132
  " - pushing the current feature branch to its origin remote via `git push -u origin <current-branch>` (the feature branch only — NEVER the base branch)\n"
133
133
  " - creating a pull request via `gh pr create --base <chosen-base> --head <current-branch>`; if a PR with the same head already exists, surface its URL and skip creation\n"
134
- " - recording the executed actions, commit SHAs, PR URL, and user selections in the final report"
134
+ " - the lead writes the final report directly (no `Report writer worker` dispatch); the report still conforms to the standard final-report template"
135
135
  ),
136
136
  "forbidden": (
137
137
  " - entering this phase when the cited final-verification verdict is `conditional-accept` or `blocked`, or when no final-verification report is cited\n"
@@ -141,7 +141,7 @@ PHASE_RULES: dict[str, dict[str, str]] = {
141
141
  " - bypassing git hooks (`--no-verify`, `-n`), bypassing GPG signing, or otherwise disabling repo-configured safeguards\n"
142
142
  " - release-publishing commands: `gh release`, `npm publish`, `cargo publish`, `pip publish`, `docker push`, `terraform apply`, `kubectl apply` against non-local clusters\n"
143
143
  " - executing any command the user did NOT select (e.g. if the user picked `commit only`, opening a PR is forbidden; if the user picked `skip`, the run ends without git commands)\n"
144
- " - dispatching parallel sub-agents beyond the required worker roster (`Claude worker` drafter + `Report writer worker`)\n"
144
+ " - `TeamCreate`, `Agent(...)` worker dispatch, parallel sub-agent fan-out, or any team-mode orchestration this phase runs single-lead\n"
145
145
  " - silently retrying a failed git/gh command with weaker flags (e.g. retrying `git push` with `--force` after a non-fast-forward rejection)"
146
146
  ),
147
147
  },