okstra 0.13.0 → 0.13.2
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 +1 -1
- package/README.md +1 -1
- package/docs/kr/architecture.md +6 -3
- package/docs/kr/cli.md +2 -2
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/SKILL.md +1 -1
- package/runtime/agents/workers/codex-worker.md +2 -2
- package/runtime/bin/okstra-codex-exec.sh +1 -1
- package/runtime/bin/okstra.sh +1 -0
- package/runtime/prompts/profiles/implementation.md +10 -9
- package/runtime/python/lib/okstra/cli.sh +4 -0
- package/runtime/python/lib/okstra/globals.sh +2 -1
- package/runtime/python/lib/okstra/usage.sh +8 -1
- package/runtime/python/okstra_ctl/run.py +26 -0
- package/runtime/python/okstra_ctl/worktree.py +50 -7
- package/runtime/skills/okstra-run/SKILL.md +55 -1
- package/runtime/templates/reports/final-report.template.md +4 -3
package/README.kr.md
CHANGED
|
@@ -165,7 +165,7 @@ Claude Code 세션 밖에서 task 를 시작하려면:
|
|
|
165
165
|
|
|
166
166
|
0.8.0 이후 `main` 에 추가된 workflow 변경:
|
|
167
167
|
|
|
168
|
-
- **`--task-type implementation` 격리 worktree 자동 provisioning** — prepare 단계에서 `okstra-ctl` 이 `git worktree add ~/.okstra/worktrees/<project-id>/<task-group-segment>/<task-id-segment>-<run-seq>` 를 수행해 격리된 working tree 와 브랜치 `<work-category-prefix>-<task-id-segment>-<run-seq>` (예: `feat-dev-9436-001`, `fix-dev-7311-002`) 를 만듭니다. base ref 는
|
|
168
|
+
- **`--task-type implementation` 격리 worktree 자동 provisioning** — prepare 단계에서 `okstra-ctl` 이 `git worktree add ~/.okstra/worktrees/<project-id>/<task-group-segment>/<task-id-segment>-<run-seq>` 를 수행해 격리된 working tree 와 브랜치 `<work-category-prefix>-<task-id-segment>-<run-seq>` (예: `feat-dev-9436-001`, `fix-dev-7311-002`) 를 만듭니다. base ref 는 사용자가 `--base-ref` 로 지정 (release-handoff PR base picker 와 동일한 메뉴: `main` / `dev` / `staging` / `preprod` / `prod` / 직접 입력). 첫 phase 에서는 필수이며, okstra-run skill 이 `AskUserQuestion` 으로 수집합니다 — 비대화형 호출자는 `--base-ref` 플래그를 직접 전달해야 prepare 가 통과합니다. Executor 와 verifier 모두 이 worktree 안에서 동작하므로 caller 의 작업 디렉터리는 깨끗하게 유지되고, worktree 는 PR 작성 · rollback 검증의 권위 artefact 로 남습니다. caller 가 이미 다른 worktree 안에 있거나 project_root 가 git repo 가 아니면 provisioning 은 skip 됩니다. 수동 cleanup: `git worktree remove <path>` → `git branch -D <branch>`. 상세: [`docs/kr/architecture.md`](docs/kr/architecture.md) *Task type* 섹션, [`docs/kr/cli.md#--executor`](docs/kr/cli.md#--executor).
|
|
169
169
|
- **`release-handoff` lifecycle phase** — `final-verification` 이 `verdict=accepted` 를 반환한 직후에 실행되는 신규 phase. lead 가 Claude worker (drafter) 를 통해 commit message · PR body 후보를 만들고, `AskUserQuestion` 으로 사용자에게 (1) action (`commit only` / `commit + PR` / `skip`), (2) PR base branch (`staging` / `preprod` / `prod` / `main` / `dev` / 직접 입력), (3) message handling (`use as-is` / `edit then proceed` / `cancel`) 세 가지를 순서대로 묻습니다. 사용자가 메뉴로 선택한 git / gh 명령만 실행되고, force-push, base 브랜치 직접 push, hook bypass (`--no-verify`), release publish (`gh release`, `npm publish`, ...) 는 금지됩니다. 이 phase 에서는 소스 코드를 수정하지 않습니다. profile: [`prompts/profiles/release-handoff.md`](prompts/profiles/release-handoff.md).
|
|
170
170
|
|
|
171
171
|
### 3.5 운영 명령
|
package/README.md
CHANGED
|
@@ -164,7 +164,7 @@ Notable flags added in 0.7.0 / 0.8.0:
|
|
|
164
164
|
|
|
165
165
|
Recent workflow additions (post-0.8.0, on `main`):
|
|
166
166
|
|
|
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
|
|
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 **user-chosen base ref** (`--base-ref`, mirroring the `release-handoff` PR-base picker: `main` / `dev` / `staging` / `preprod` / `prod` / any local ref) the first time a task-key is seen. `--base-ref` is required on first phase; the okstra-run skill collects it via `AskUserQuestion`, non-interactive callers must pass the flag explicitly. 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).
|
|
168
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).
|
|
169
169
|
|
|
170
170
|
### 3.5 Ops commands
|
package/docs/kr/architecture.md
CHANGED
|
@@ -286,11 +286,14 @@ Claude launch prompt 본문은 항상 `prompts/launch.template.md` 템플릿에
|
|
|
286
286
|
"projectId": "sample-project-v2-api",
|
|
287
287
|
"projectRoot": "/Volumes/Workspaces/workspace/projects/sample-project",
|
|
288
288
|
"createdAt": "2026-05-10T00:00:00Z",
|
|
289
|
-
"updatedAt": "2026-05-10T00:00:00Z"
|
|
289
|
+
"updatedAt": "2026-05-10T00:00:00Z",
|
|
290
|
+
"worktreeSyncDirs": [".project-docs", ".scratch", "graphify-out", ".claude"]
|
|
290
291
|
}
|
|
291
292
|
```
|
|
292
293
|
|
|
293
|
-
처음 실행이면 위 4
|
|
294
|
+
처음 실행이면 위 4-필드(`projectId`, `projectRoot`, `createdAt`, `updatedAt`)를 새로 작성합니다. 이미 존재하면 `--project-id` 인자값과 저장된 `projectId` 가 일치하는지 검증한 뒤 `projectRoot`/`updatedAt` 만 갱신하고, 사용자가 추가한 알 수 없는 필드(`worktreeSyncDirs`, 향후 `mcpServers` 등)는 upsert 과정에서 보존됩니다. 불일치 시 즉시 종료해 동일 디렉토리에서 두 개의 ID 가 혼용되는 것을 막습니다.
|
|
295
|
+
|
|
296
|
+
`worktreeSyncDirs` (선택) 는 task worktree 로 symlink 할 project-root-relative 디렉토리 목록을 per-project 로 override 합니다. 해석 우선순위는 `OKSTRA_WORKTREE_SYNC_DIRS` 환경변수 → `project.json` → built-in default (`.project-docs`, `.scratch`, `graphify-out`, `.claude`). 빈 배열을 지정하면 sync 자체를 비활성화합니다.
|
|
294
297
|
|
|
295
298
|
`okstra-ctl` 의 reindex/backfill 도 신규 모델에서 권위 소스를 변경했습니다. 과거에는 `examples/projects/*.conf.sh` 를 source 했지만, 지금은 `~/.okstra/projects/<projectId>/meta.json` (record_start 가 위 project.json 정보를 mirror 한 결과) 을 스캔하여 (projectId, projectRoot) 매핑을 복원합니다. `OKSTRA_PROJECT_DEFINITION_DIR_OVERRIDE` 환경변수도 함께 폐기되었습니다.
|
|
296
299
|
|
|
@@ -321,7 +324,7 @@ Claude launch prompt 본문은 항상 `prompts/launch.template.md` 템플릿에
|
|
|
321
324
|
공통 제약:
|
|
322
325
|
|
|
323
326
|
- `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
|
-
- **모든 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/`
|
|
327
|
+
- **모든 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/`, `.claude/` 는 main worktree 에서 symlink 로 연결되어 모든 task 가 동일한 shared state 를 봅니다 (sync 대상 목록은 `project.json` 의 `worktreeSyncDirs` 또는 `OKSTRA_WORKTREE_SYNC_DIRS` 환경변수로 override 가능; 빈 배열이면 sync 비활성화). 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
328
|
- `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
329
|
- 사용자가 "다음 단계 진행해" 같은 표현을 보내도, 그 발화만으로 다음 phase가 자동 시작되지 않습니다. 다음 phase는 새 `okstra.sh` 실행으로만 시작합니다.
|
|
327
330
|
- **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
|
@@ -227,7 +227,7 @@ scripts/okstra.sh --task-type implementation-planning ... \
|
|
|
227
227
|
### `--workers`
|
|
228
228
|
|
|
229
229
|
이번 run에서 사용할 worker 목록을 직접 지정합니다.
|
|
230
|
-
생략하면 기본값 `claude,codex,gemini,report-writer
|
|
230
|
+
생략하면 기본값 `claude,codex,report-writer`를 사용합니다. Gemini worker는 옵션이며 필요할 때 `--workers claude,codex,gemini,report-writer` 와 같이 명시적으로 추가하세요.
|
|
231
231
|
지정하면 전달한 worker subset만 dedupe 후 기록됩니다.
|
|
232
232
|
|
|
233
233
|
예:
|
|
@@ -287,7 +287,7 @@ fallback 기본값은 아래와 같습니다.
|
|
|
287
287
|
- Executor 는 이 run 에서 **유일하게 프로젝트 파일을 mutate 할 수 있는 worker** 입니다. 나머지 두 provider 는 같은 run 에서 strict read-only verifier 로 dispatch 됩니다.
|
|
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
|
-
- 실제 파일 변경은 Codex/Gemini 의 경우 각 CLI 의 auto-edit 모드 (예: `codex exec --
|
|
290
|
+
- 실제 파일 변경은 Codex/Gemini 의 경우 각 CLI 의 auto-edit 모드 (예: `codex exec --sandbox workspace-write`) 를 통해 일어나며, Claude-side Edit/Write tool 을 거치지 않습니다.
|
|
291
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
|
예:
|
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
package/runtime/agents/SKILL.md
CHANGED
|
@@ -105,7 +105,7 @@ For `--task-type implementation` runs, the task bundle additionally pins one of
|
|
|
105
105
|
|
|
106
106
|
Lead MUST dispatch Edit/Write-bearing work only through the `workerAgent` declared there. The other two providers still run as read-only verifiers in the same run; the executor's own provider is *also* dispatched separately as a verifier (a fresh CLI session) so the diff is reviewed by a context-isolated session. Session isolation is the primary self-review safeguard — same-model executor and same-provider verifier is acceptable when running in distinct sessions. Selecting a different model variant (e.g. executor=opus / Claude verifier=sonnet) is recommended but no longer mandatory.
|
|
107
107
|
|
|
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 --
|
|
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 --sandbox workspace-write`), not through Claude-side Edit/Write tools.
|
|
109
109
|
|
|
110
110
|
#### Task worktree (BLOCKING for every task-type)
|
|
111
111
|
|
|
@@ -32,7 +32,7 @@ $HOME/.okstra/bin/okstra-codex-exec.sh "<absolute-project-root>" "<assigned-mode
|
|
|
32
32
|
|
|
33
33
|
The wrapper internally runs:
|
|
34
34
|
```bash
|
|
35
|
-
codex exec -C "<project-root>" --model "<model>" --
|
|
35
|
+
codex exec -C "<project-root>" --model "<model>" --sandbox workspace-write - < "<prompt-path>" 2>/dev/null
|
|
36
36
|
```
|
|
37
37
|
|
|
38
38
|
The wrapper exists because Claude Code's Bash permission matcher rejects simple-prefix matches when the command contains stdin/stderr redirects. Calling `codex exec ... - < <path> 2>/dev/null` directly triggers a permission prompt every dispatch even when `Bash(codex exec:*)` is allowlisted. The wrapper folds the redirects inside, so the harness sees a single non-redirect command that matches `Bash($HOME/.okstra/bin/okstra-codex-exec.sh:*)`.
|
|
@@ -71,7 +71,7 @@ The wrapper exists because Claude Code's Bash permission matcher rejects simple-
|
|
|
71
71
|
```bash
|
|
72
72
|
$HOME/.okstra/bin/okstra-codex-exec.sh "<absolute-project-root>" "<assigned-model-execution-value>" "<absolute-prompt-history-path>"
|
|
73
73
|
```
|
|
74
|
-
Substitute the literal extracted Project Root, model execution value, and prompt-history path in place of the placeholders above. The wrapper handles `-C`, `--model`, `--
|
|
74
|
+
Substitute the literal extracted Project Root, model execution value, and prompt-history path in place of the placeholders above. The wrapper handles `-C`, `--model`, `--sandbox workspace-write`, the stdin redirect from the prompt file, and stderr suppression internally. Calling `codex exec` directly (without the wrapper) is an error in this skill: the redirect tokens disqualify the prefix match against `Bash(codex exec:*)` and produce a permission prompt every dispatch.
|
|
75
75
|
|
|
76
76
|
8. Return the codex output as-is without modification.
|
|
77
77
|
|
|
@@ -50,4 +50,4 @@ fi
|
|
|
50
50
|
|
|
51
51
|
# stdin redirect and stderr suppression are intentionally inside the wrapper —
|
|
52
52
|
# this is the entire reason this script exists.
|
|
53
|
-
exec codex exec -C "$project_root" --model "$model" --
|
|
53
|
+
exec codex exec -C "$project_root" --model "$model" --sandbox workspace-write - < "$prompt_path" 2>/dev/null
|
package/runtime/bin/okstra.sh
CHANGED
|
@@ -119,6 +119,7 @@ PY_ARGS=(
|
|
|
119
119
|
[[ "$APPROVE_PLAN_ACK" == "true" ]] && PY_ARGS+=(--approve)
|
|
120
120
|
[[ -n "${CLARIFICATION_RESPONSE_PATH-}" ]] && PY_ARGS+=(--clarification-response "$CLARIFICATION_RESPONSE_PATH")
|
|
121
121
|
[[ -n "${WORK_CATEGORY-}" ]] && PY_ARGS+=(--work-category "$WORK_CATEGORY")
|
|
122
|
+
[[ -n "${BASE_REF-}" ]] && PY_ARGS+=(--base-ref "$BASE_REF")
|
|
122
123
|
[[ "$RENDER_ONLY" == "true" ]] && PY_ARGS+=(--render-only)
|
|
123
124
|
[[ "$REFRESH_OKSTRA_ASSETS" == "true" ]] && PY_ARGS+=(--refresh-assets)
|
|
124
125
|
|
|
@@ -4,8 +4,9 @@
|
|
|
4
4
|
- Required workers:
|
|
5
5
|
- claude
|
|
6
6
|
- codex
|
|
7
|
-
- gemini
|
|
8
7
|
- report-writer
|
|
8
|
+
- Optional workers (opt-in via `--workers`):
|
|
9
|
+
- gemini — when added to the roster it joins the verifier set; when omitted only the Claude+Codex verifiers participate (`--executor gemini` is therefore not selectable without explicitly listing `gemini` in `--workers`)
|
|
9
10
|
- **Executor binding (resolved at run-prep time, fixed for this run):**
|
|
10
11
|
- Executor display name: `{{EXECUTOR_DISPLAY_NAME}}`
|
|
11
12
|
- Executor provider: `{{EXECUTOR_PROVIDER}}` (one of: `claude` | `codex` | `gemini`; chosen via `--executor` or `OKSTRA_DEFAULT_EXECUTOR`, default `claude`)
|
|
@@ -13,12 +14,12 @@
|
|
|
13
14
|
- Executor model: `{{EXECUTOR_MODEL_DISPLAY}}` (launch value: `{{EXECUTOR_MODEL_EXECUTION_VALUE}}`)
|
|
14
15
|
- Wherever this profile mentions the `Executor`, it refers to the role bound above. The other two providers in the roster (`claude` / `codex` / `gemini` minus the executor) are dispatched as **verifiers only** for this run and remain strictly read-only.
|
|
15
16
|
{{INCLUDE:_common-contract.md}}
|
|
16
|
-
- Team contract (phase-specific overrides — `Claude worker` is replaced by `Executor` + verifier
|
|
17
|
-
- **Executor role:** the `Executor` (bound above) is the **only worker permitted to use Edit / Write / state-mutating Bash commands** on project files. All other workers run read-only. When the executor provider is `codex` or `gemini`, the actual file mutation happens inside the executor CLI's own auto-edit mode (e.g. `codex exec --
|
|
18
|
-
- **Verifier roles:** the
|
|
17
|
+
- Team contract (phase-specific overrides — `Claude worker` is replaced by `Executor` + verifier set in this phase):
|
|
18
|
+
- **Executor role:** the `Executor` (bound above) is the **only worker permitted to use Edit / Write / state-mutating Bash commands** on project files. All other workers run read-only. When the executor provider is `codex` or `gemini`, the actual file mutation happens inside the executor CLI's own auto-edit mode (e.g. `codex exec --sandbox workspace-write`, gemini's equivalent) — not through Claude-side Edit/Write tools — but the safety rules in this profile still apply identically.
|
|
19
|
+
- **Verifier roles:** the verifier slots are `Claude verifier` and `Codex verifier`, plus `Gemini verifier` **only when `gemini` is in the resolved `--workers` roster**. Every verifier in the resolved roster is dispatched regardless of which provider holds the executor role; the executor's own provider is run *separately* as a verifier (a fresh CLI session with no shared context) so that no verdict is produced from the same session that wrote the diff. Verifiers MUST NOT call Edit, Write, or any Bash command that mutates files outside the run's artifact directories. If a verifier wants a fix, it records the recommendation in its worker result; it does not apply the fix itself.
|
|
19
20
|
- Session isolation — not model-variant divergence — is the primary self-review safeguard: each verifier is a separate CLI invocation with its own context window, so reusing the same model variant for executor and same-provider verifier is acceptable. Different model variants (e.g. executor=opus / Claude verifier=sonnet) remain recommended when available.
|
|
20
|
-
- Phase-specific model defaults override the shared defaults: `Claude verifier`=`sonnet`, `Codex verifier`=`gpt-5.5`, `Gemini verifier`=`auto
|
|
21
|
-
- **All-verifier-failure policy**: if every verifier (`
|
|
21
|
+
- Phase-specific model defaults override the shared defaults: `Claude verifier`=`sonnet`, `Codex verifier`=`gpt-5.5`, `Gemini verifier`=`auto` (only when present in the roster). The `Executor`'s model is taken from the provider-specific worker model corresponding to `--executor`: claude→`--claude-model` (default `sonnet`, override to `opus` recommended when this run's executor is claude), codex→`--codex-model` (default `gpt-5.5`), gemini→`--gemini-model` (default `auto`).
|
|
22
|
+
- **All-verifier-failure policy**: if every verifier present in the resolved roster (`Claude verifier`, `Codex verifier`, and `Gemini verifier` when opted in) ends with a non-result terminal status (`timeout`, `error`, `not-run`) — i.e. zero independent verdicts were produced — the run MUST end with status `blocked` and route to a follow-up `error-analysis` run. `Claude lead` MUST NOT substitute its own verdict in place of the missing verifier outputs; synthesis requires at least one independent verifier's verdict. If one or more verifiers fail but at least one returns a verdict, the run proceeds with the surviving verdict(s) and the final report MUST explicitly notate which verifiers were unavailable, with the captured error / timeout evidence per failed verifier.
|
|
22
23
|
- Pre-implementation gate (mandatory — refuse to start if any item fails):
|
|
23
24
|
- the run brief MUST cite `--approved-plan <path>` pointing to a `final-report.md` produced by a prior `implementation-planning` run located under `runs/implementation-planning/.../reports/final-report.md`
|
|
24
25
|
- that file MUST contain a `User Approval Request` block (canonically placed at the **top of the report**, immediately under the metadata header) AND a recorded user approval marker. The canonical, recommended form is the single markdown checkbox line `- [x] Approved`. The runtime regex in `okstra_ctl.run._validate_approved_plan` also accepts (case-insensitive, line-anchored, optional leading `-`/`*`/`+` bullet): `APPROVED` (alone, followed by `:`, or end-of-line), `[x] Approved`, or `User Approval: APPROVED|granted|yes`. Free-form approvals such as "lgtm", "go ahead", or paraphrased confirmations are intentionally NOT accepted; if the user's approval is informal, re-edit the plan file to flip the top checkbox to `- [x] Approved` before invoking the implementation run.
|
|
@@ -34,7 +35,7 @@
|
|
|
34
35
|
- 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
36
|
- Provisioning note: `{{EXECUTOR_WORKTREE_NOTE}}`
|
|
36
37
|
- **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
|
-
- **Verifier behaviour**: all
|
|
38
|
+
- **Verifier behaviour**: all verifier roles in the resolved roster read from the SAME working tree path so they observe the exact diff the Executor produced. Verifiers remain strictly read-only there.
|
|
38
39
|
- **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
40
|
- **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
41
|
- **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.
|
|
@@ -84,7 +85,7 @@
|
|
|
84
85
|
- **Out-of-plan edits block**: every file edited that was not in the approved plan's file list, with rationale (empty block is acceptable and preferred)
|
|
85
86
|
- **Validation evidence**: actual command output (stdout/stderr) for every `pre / mid / post` validation command from the plan. Truncated output is acceptable but the command line and exit code MUST be exact. No paraphrasing of test results.
|
|
86
87
|
- **TDD evidence (when applicable)**: for steps that should be TDD-ordered, show the failing-test output BEFORE the implementation commit and the passing-test output AFTER, with commit SHAs framing the transition.
|
|
87
|
-
- **Verifier results**: a section per verifier (`
|
|
88
|
+
- **Verifier results**: a section per verifier present in the resolved roster (`Claude verifier`, `Codex verifier`, and `Gemini verifier` when opted in) containing their independent verdict (PASS / CONCERNS / FAIL), their cited diff snippets, and any fix recommendations they declined to apply. `Claude lead` synthesises a unified verdict but MUST preserve dissent — do not collapse opinions into one paragraph.
|
|
88
89
|
- **Rollback verification**: confirmation that the plan's rollback path is still valid after the changes. Strength of verification depends on the change category:
|
|
89
90
|
- **Pure code changes** (no persisted state, no infra mutation): a reachable revert SHA is sufficient. Record the exact `git revert <SHA>` command that would undo the change, and confirm `git rev-parse <SHA>` resolves.
|
|
90
91
|
- **Feature-flag-gated changes**: confirm the off-switch path was exercised in this run's validation evidence (i.e. one of the validation commands ran with the flag off and succeeded). A plan that ships a flag without exercising the off-path does NOT satisfy this requirement.
|
|
@@ -95,7 +96,7 @@
|
|
|
95
96
|
1. **Plan coverage** — every step in the approved plan's recommended option must point to a commit (or an explicit `Skipped: <reason>` entry). List gaps.
|
|
96
97
|
2. **Evidence completeness** — every `Validation evidence` and `TDD evidence` claim has the actual command line and exit code? No paraphrased "tests pass" without output?
|
|
97
98
|
3. **Out-of-plan honesty** — files in the diff that are NOT in the plan list must appear in the `Out-of-plan edits` block. Cross-check with `git diff --name-only`.
|
|
98
|
-
4. **Verifier dissent preserved** — if the
|
|
99
|
+
4. **Verifier dissent preserved** — if the verifiers in the resolved roster disagree, the disagreement is visible in the report? Synthesis hides nothing?
|
|
99
100
|
5. **Forbidden action audit** — `git push`, publish, deploy, migration, third-party write commands: scan the run's session transcripts for any occurrence and confirm none happened.
|
|
100
101
|
6. **Placeholder scan** — restrict the scan to lines this run actually introduced; pre-existing placeholders in unchanged regions of touched files are out of scope. Required command (substitute `<base>` with the parent of the first commit in this run's commit list):
|
|
101
102
|
```
|
|
@@ -144,6 +144,10 @@ while [[ $# -gt 0 ]]; do
|
|
|
144
144
|
WORK_CATEGORY="$(require_option_value --work-category "${2-}")"
|
|
145
145
|
shift 2
|
|
146
146
|
;;
|
|
147
|
+
--base-ref)
|
|
148
|
+
BASE_REF="$(require_option_value --base-ref "${2-}")"
|
|
149
|
+
shift 2
|
|
150
|
+
;;
|
|
147
151
|
--task-type)
|
|
148
152
|
ANALYSIS_TYPE="$(require_option_value --task-type "${2-}")"
|
|
149
153
|
shift 2
|
|
@@ -26,6 +26,7 @@ GEMINI_MODEL_OVERRIDE=""
|
|
|
26
26
|
REPORT_WRITER_MODEL_OVERRIDE=""
|
|
27
27
|
EXECUTOR_OVERRIDE=""
|
|
28
28
|
WORK_CATEGORY=""
|
|
29
|
+
BASE_REF=""
|
|
29
30
|
RELATED_TASKS_RAW=""
|
|
30
31
|
ANALYSIS_TYPE=""
|
|
31
32
|
BRIEF_PATH=""
|
|
@@ -150,7 +151,7 @@ GEMINI_WORKER_MODEL_DISPLAY=""
|
|
|
150
151
|
GEMINI_WORKER_MODEL_EXECUTION_VALUE=""
|
|
151
152
|
REPORT_WRITER_MODEL_DISPLAY=""
|
|
152
153
|
REPORT_WRITER_MODEL_EXECUTION_VALUE=""
|
|
153
|
-
DEFAULT_WORKERS="claude,codex,
|
|
154
|
+
DEFAULT_WORKERS="claude,codex,report-writer"
|
|
154
155
|
DEFAULT_LEAD_MODEL_NAME="${OKSTRA_DEFAULT_LEAD_MODEL:-opus}"
|
|
155
156
|
DEFAULT_CLAUDE_WORKER_MODEL_NAME="${OKSTRA_DEFAULT_CLAUDE_MODEL:-sonnet}"
|
|
156
157
|
DEFAULT_CODEX_WORKER_MODEL_NAME="${OKSTRA_DEFAULT_CODEX_MODEL:-gpt-5.5}"
|
|
@@ -65,7 +65,8 @@ options:
|
|
|
65
65
|
--refresh-assets Deprecated. okstra now installs skills/agents into ~/.claude and the codex
|
|
66
66
|
wrapper into ~/.okstra/bin via scripts/okstra-install.sh. Re-run that
|
|
67
67
|
installer with --refresh to update installed assets.
|
|
68
|
-
--workers Comma-separated worker list for this run. Default: claude,codex,
|
|
68
|
+
--workers Comma-separated worker list for this run. Default: claude,codex,report-writer
|
|
69
|
+
(Gemini worker is optional; add `gemini` explicitly, e.g. --workers claude,codex,gemini,report-writer)
|
|
69
70
|
--lead-model Model for Claude lead. Default: OKSTRA_DEFAULT_LEAD_MODEL or opus
|
|
70
71
|
--claude-model Model for Claude worker. Default: OKSTRA_DEFAULT_CLAUDE_MODEL or sonnet
|
|
71
72
|
--codex-model Model for Codex worker. Default: OKSTRA_DEFAULT_CODEX_MODEL or gpt-5.5
|
|
@@ -83,6 +84,12 @@ options:
|
|
|
83
84
|
Defaults to 'unknown' when omitted. Use this when the
|
|
84
85
|
lifecycle skipped --task-type=requirements-discovery
|
|
85
86
|
(where work-category would otherwise be inferred).
|
|
87
|
+
--base-ref Git ref (branch name, tag, or commit SHA) used as the
|
|
88
|
+
base of the per-task worktree on the FIRST phase of a
|
|
89
|
+
task-key. Required on first phase; ignored on
|
|
90
|
+
subsequent phases (the registered worktree is reused).
|
|
91
|
+
Typical values mirror the release-handoff PR-base picker:
|
|
92
|
+
main | dev | staging | preprod | prod | any local ref.
|
|
86
93
|
--task-type Set the task purpose for this run and select the matching profile file.
|
|
87
94
|
-h, --help Show this help.
|
|
88
95
|
|
|
@@ -90,6 +90,7 @@ class PrepareInputs:
|
|
|
90
90
|
executor: str = ""
|
|
91
91
|
related_tasks_raw: str = ""
|
|
92
92
|
work_category: str = ""
|
|
93
|
+
base_ref: str = ""
|
|
93
94
|
approved_plan_path: str = ""
|
|
94
95
|
clarification_response_path: str = "" # absolute or empty
|
|
95
96
|
render_only: bool = False
|
|
@@ -504,6 +505,15 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
|
|
|
504
505
|
raise PrepareError(
|
|
505
506
|
f"--executor must be one of: claude, codex, gemini (got: {executor_provider!r})"
|
|
506
507
|
)
|
|
508
|
+
if (
|
|
509
|
+
inp.task_type == "implementation"
|
|
510
|
+
and executor_provider not in workers
|
|
511
|
+
):
|
|
512
|
+
raise PrepareError(
|
|
513
|
+
f"--executor {executor_provider} requires {executor_provider!r} in "
|
|
514
|
+
f"--workers, but resolved roster is {workers!r}. "
|
|
515
|
+
f"Add it explicitly, e.g. --workers {','.join(sorted(set(workers + [executor_provider])))}."
|
|
516
|
+
)
|
|
507
517
|
executor_provider_to_meta = {
|
|
508
518
|
"claude": ("Claude executor", "claude-worker", cw),
|
|
509
519
|
"codex": ("Codex executor", "codex-worker", co),
|
|
@@ -537,6 +547,8 @@ def prepare_task_bundle(inp: PrepareInputs) -> PrepareOutputs:
|
|
|
537
547
|
task_group_segment=ctx["TASK_GROUP_SEGMENT"],
|
|
538
548
|
task_id_segment=ctx["TASK_ID_SEGMENT"],
|
|
539
549
|
work_category=inp.work_category,
|
|
550
|
+
base_ref=inp.base_ref,
|
|
551
|
+
require_base_ref=True,
|
|
540
552
|
)
|
|
541
553
|
except RuntimeError as exc:
|
|
542
554
|
raise PrepareError(f"task worktree provisioning failed: {exc}") from exc
|
|
@@ -818,6 +830,19 @@ def main(argv: list[str]) -> int:
|
|
|
818
830
|
"Falls back to `unknown` when omitted."
|
|
819
831
|
),
|
|
820
832
|
)
|
|
833
|
+
p.add_argument(
|
|
834
|
+
"--base-ref",
|
|
835
|
+
default="",
|
|
836
|
+
dest="base_ref",
|
|
837
|
+
help=(
|
|
838
|
+
"Git ref (branch name, tag, or commit SHA) used as the base of "
|
|
839
|
+
"the task worktree on the first phase of this task-key. "
|
|
840
|
+
"Required for first-phase prepare; ignored on subsequent phases "
|
|
841
|
+
"(the registered worktree is reused). Mirrors the PR-base picker "
|
|
842
|
+
"used by the `release-handoff` phase: typical values are "
|
|
843
|
+
"`main` / `dev` / `staging` / `preprod` / `prod` or any local ref."
|
|
844
|
+
),
|
|
845
|
+
)
|
|
821
846
|
args = p.parse_args(argv)
|
|
822
847
|
|
|
823
848
|
project_root = Path(args.project_root).expanduser().resolve()
|
|
@@ -854,6 +879,7 @@ def main(argv: list[str]) -> int:
|
|
|
854
879
|
executor=args.executor,
|
|
855
880
|
related_tasks_raw=args.related_tasks_raw,
|
|
856
881
|
work_category=args.work_category,
|
|
882
|
+
base_ref=args.base_ref,
|
|
857
883
|
approved_plan_path=args.approved_plan_path,
|
|
858
884
|
clarification_response_path=clarification_abs,
|
|
859
885
|
render_only=args.render_only,
|
|
@@ -161,6 +161,17 @@ def _head_sha(cwd: Path) -> str:
|
|
|
161
161
|
return res.stdout.strip()
|
|
162
162
|
|
|
163
163
|
|
|
164
|
+
def _resolve_commit_sha(cwd: Path, ref: str) -> str:
|
|
165
|
+
"""Resolve a user-supplied ref (branch, tag, short/long SHA) to a full
|
|
166
|
+
commit SHA in `cwd`. Returns empty string when the ref is not resolvable
|
|
167
|
+
so the caller can raise a contextual error.
|
|
168
|
+
"""
|
|
169
|
+
res = _git(cwd, "rev-parse", "--verify", "--quiet", f"{ref}^{{commit}}")
|
|
170
|
+
if res.returncode != 0:
|
|
171
|
+
return ""
|
|
172
|
+
return res.stdout.strip()
|
|
173
|
+
|
|
174
|
+
|
|
164
175
|
def _main_worktree_path(project_root: Path) -> Path:
|
|
165
176
|
"""Locate the repository's MAIN worktree (the original checkout).
|
|
166
177
|
|
|
@@ -292,6 +303,8 @@ def provision_task_worktree(
|
|
|
292
303
|
task_group_segment: str,
|
|
293
304
|
task_id_segment: str,
|
|
294
305
|
work_category: str,
|
|
306
|
+
base_ref: str = "",
|
|
307
|
+
require_base_ref: bool = False,
|
|
295
308
|
) -> WorktreeProvision:
|
|
296
309
|
"""Materialise (or reuse) the task worktree for this run.
|
|
297
310
|
|
|
@@ -299,6 +312,13 @@ def provision_task_worktree(
|
|
|
299
312
|
Subsequent phases of the same task-key look up the registry and
|
|
300
313
|
return the existing path + branch unchanged.
|
|
301
314
|
|
|
315
|
+
``base_ref`` is the ref (branch name, tag, or commit SHA) to branch
|
|
316
|
+
the new worktree from on first phase. When empty, the main worktree's
|
|
317
|
+
current ``HEAD`` is used (legacy default; the CLI enforces a
|
|
318
|
+
non-empty value on first phase so callers go through the
|
|
319
|
+
AskUserQuestion menu in the okstra-run skill). Subsequent phases
|
|
320
|
+
ignore ``base_ref`` — the registered entry's base is reused.
|
|
321
|
+
|
|
302
322
|
Raises:
|
|
303
323
|
RuntimeError when worktree creation fails (path clash on disk
|
|
304
324
|
that the registry does not know about, branch clash with a
|
|
@@ -369,16 +389,34 @@ def provision_task_worktree(
|
|
|
369
389
|
)
|
|
370
390
|
|
|
371
391
|
main_root = _main_worktree_path(project_root)
|
|
372
|
-
|
|
373
|
-
if not
|
|
392
|
+
requested_base = (base_ref or "").strip()
|
|
393
|
+
if not requested_base and require_base_ref:
|
|
374
394
|
raise RuntimeError(
|
|
375
|
-
"
|
|
395
|
+
"first-phase task worktree requires an explicit base ref; "
|
|
396
|
+
"pass `--base-ref <branch|tag|sha>` (or invoke through the "
|
|
397
|
+
"okstra-run skill which collects this interactively)"
|
|
376
398
|
)
|
|
399
|
+
if requested_base:
|
|
400
|
+
resolved_sha = _resolve_commit_sha(main_root, requested_base)
|
|
401
|
+
if not resolved_sha:
|
|
402
|
+
raise RuntimeError(
|
|
403
|
+
f"could not resolve base ref `{requested_base}` in main worktree "
|
|
404
|
+
f"({main_root}); ensure the branch/tag/SHA exists locally"
|
|
405
|
+
)
|
|
406
|
+
resolved_base_ref = resolved_sha
|
|
407
|
+
base_origin = requested_base
|
|
408
|
+
else:
|
|
409
|
+
resolved_base_ref = _head_sha(main_root)
|
|
410
|
+
if not resolved_base_ref:
|
|
411
|
+
raise RuntimeError(
|
|
412
|
+
"could not resolve HEAD sha in main worktree; cannot create task worktree"
|
|
413
|
+
)
|
|
414
|
+
base_origin = "HEAD"
|
|
377
415
|
|
|
378
416
|
worktree_path.parent.mkdir(parents=True, exist_ok=True)
|
|
379
417
|
res = _git(
|
|
380
418
|
main_root,
|
|
381
|
-
"worktree", "add", "-b", branch, str(worktree_path),
|
|
419
|
+
"worktree", "add", "-b", branch, str(worktree_path), resolved_base_ref,
|
|
382
420
|
)
|
|
383
421
|
if res.returncode != 0:
|
|
384
422
|
raise RuntimeError(
|
|
@@ -398,7 +436,7 @@ def provision_task_worktree(
|
|
|
398
436
|
task_id=safe_task,
|
|
399
437
|
worktree_path=str(worktree_path),
|
|
400
438
|
branch=branch,
|
|
401
|
-
base_ref=
|
|
439
|
+
base_ref=resolved_base_ref,
|
|
402
440
|
phase=task_type,
|
|
403
441
|
)
|
|
404
442
|
except RuntimeError:
|
|
@@ -408,13 +446,18 @@ def provision_task_worktree(
|
|
|
408
446
|
_git(main_root, "branch", "-D", branch)
|
|
409
447
|
raise
|
|
410
448
|
|
|
449
|
+
base_label = (
|
|
450
|
+
f"{base_origin} @ {resolved_base_ref[:12]}"
|
|
451
|
+
if base_origin != "HEAD"
|
|
452
|
+
else f"HEAD @ {resolved_base_ref[:12]}"
|
|
453
|
+
)
|
|
411
454
|
return WorktreeProvision(
|
|
412
455
|
status="created",
|
|
413
456
|
path=str(worktree_path),
|
|
414
457
|
branch=branch,
|
|
415
|
-
base_ref=
|
|
458
|
+
base_ref=resolved_base_ref,
|
|
416
459
|
note=(
|
|
417
460
|
f"task worktree created at {worktree_path} on branch {branch} "
|
|
418
|
-
f"(base {
|
|
461
|
+
f"(base {base_label}; phase {task_type}){linked_suffix}"
|
|
419
462
|
),
|
|
420
463
|
)
|
|
@@ -141,6 +141,58 @@ If `implementation` chosen, ask two more `AskUserQuestion` in order:
|
|
|
141
141
|
- `"Path to the approved final-report.md (must contain APPROVED marker)"` — the underlying python `prepare_task_bundle` re-validates the marker, but you can pre-check with `grep`.
|
|
142
142
|
- `"Executor provider for this run (claude | codex | gemini)?"` — only this provider mutates project files; the other two run as read-only verifiers. Default `claude` (or `OKSTRA_DEFAULT_EXECUTOR` if set). Pass the answer through `PrepareInputs.executor`.
|
|
143
143
|
|
|
144
|
+
## Step 4.6: Base ref for the task worktree (first phase only)
|
|
145
|
+
|
|
146
|
+
`okstra prepare` provisions a per-task git worktree on first phase of a task-key
|
|
147
|
+
and reuses it on every subsequent phase. The base ref of that worktree is the
|
|
148
|
+
**user's choice**, not the caller's current `HEAD`, so the worktree never
|
|
149
|
+
silently inherits an unrelated branch you happen to be checked out on.
|
|
150
|
+
|
|
151
|
+
First, decide whether to ask:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
python3 - <<PY
|
|
155
|
+
import sys
|
|
156
|
+
from pathlib import Path
|
|
157
|
+
sys.path.insert(0, "$OKSTRA_PYTHONPATH".split(":")[0])
|
|
158
|
+
from okstra_ctl import worktree_registry
|
|
159
|
+
from okstra_ctl.ids import _safe_fs_segment
|
|
160
|
+
entry = worktree_registry.lookup(
|
|
161
|
+
_safe_fs_segment("<project-id>"),
|
|
162
|
+
_safe_fs_segment("<task-group>"),
|
|
163
|
+
_safe_fs_segment("<task-id>"),
|
|
164
|
+
)
|
|
165
|
+
print("REUSE" if (entry and entry.status == "active") else "ASK")
|
|
166
|
+
PY
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
- `REUSE` → the registered worktree is reused; set `base_ref=""` and skip the
|
|
170
|
+
question (the registered base is authoritative).
|
|
171
|
+
- `ASK` → this is the first phase for this task-key. Continue.
|
|
172
|
+
|
|
173
|
+
`AskUserQuestion` (mirrors the `release-handoff` PR-base picker):
|
|
174
|
+
|
|
175
|
+
- **Label**: `"이 task worktree 의 base branch?"`
|
|
176
|
+
- **Options** (single-select):
|
|
177
|
+
1. `main` (recommended)
|
|
178
|
+
2. `dev`
|
|
179
|
+
3. `staging`
|
|
180
|
+
4. `preprod`
|
|
181
|
+
5. `prod`
|
|
182
|
+
6. Free text — let the user type any local ref (branch, tag, or full/short SHA).
|
|
183
|
+
|
|
184
|
+
Validate the chosen ref exists in the MAIN worktree before continuing:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
187
|
+
git -C "$(git -C "$PROJECT_ROOT" rev-parse --path-format=absolute --git-common-dir | xargs dirname)" \
|
|
188
|
+
rev-parse --verify --quiet "<chosen-ref>^{commit}" >/dev/null \
|
|
189
|
+
|| { echo "ref not found locally: <chosen-ref>"; exit 1; }
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Re-ask on failure. Echo the resolved short SHA back to the user
|
|
193
|
+
(`base 확정: <ref> (<short-sha>)`) and capture `base_ref=<chosen-ref>` for
|
|
194
|
+
Step 7.
|
|
195
|
+
|
|
144
196
|
## Step 5: Brief path
|
|
145
197
|
|
|
146
198
|
- New task: `AskUserQuestion` (free text) `"Path to the task brief markdown (relative to project root)"`. Verify file exists; re-ask on failure.
|
|
@@ -169,7 +221,7 @@ Do NOT ask for `workers_override` in implementation — the profile's required r
|
|
|
169
221
|
|
|
170
222
|
Ask each in turn (free text, blank = default):
|
|
171
223
|
|
|
172
|
-
1. `"참여 워커 목록 (쉼표 구분, 빈 칸 = 프로필 기본값). 선택지: claude, codex, gemini, report-writer"` → `workers_override`
|
|
224
|
+
1. `"참여 워커 목록 (쉼표 구분, 빈 칸 = 프로필 기본값 claude,codex,report-writer). 선택지: claude, codex, gemini, report-writer — gemini는 옵션이므로 필요할 때 명시"` → `workers_override`
|
|
173
225
|
2. `"리더(Claude lead) 모델? (빈 칸 = 기본값)"` → `lead_model`
|
|
174
226
|
3. `"claude 워커 모델? (빈 칸 = 기본값)"` → `claude_model`
|
|
175
227
|
4. `"codex 워커 모델? (빈 칸 = 기본값)"` → `codex_model`
|
|
@@ -189,6 +241,7 @@ Before calling `prepare_task_bundle`, echo the resolved selections back to the u
|
|
|
189
241
|
선택 확인:
|
|
190
242
|
task-type : implementation
|
|
191
243
|
task-key : <group>/<id>
|
|
244
|
+
base-ref : main (resolved <short-sha>) ← worktree base, first phase only
|
|
192
245
|
executor : codex
|
|
193
246
|
workers : (프로필 기본 — executor + verifier 2 + report-writer)
|
|
194
247
|
lead-model : default (opus)
|
|
@@ -231,6 +284,7 @@ out = prepare_task_bundle(PrepareInputs(
|
|
|
231
284
|
gemini_model="...", report_writer_model="...",
|
|
232
285
|
related_tasks_raw="...",
|
|
233
286
|
executor="<claude|codex|gemini or empty>", # implementation only; empty → default (claude / OKSTRA_DEFAULT_EXECUTOR)
|
|
287
|
+
base_ref="<chosen-ref-from-step-4.6 or empty when reusing existing worktree>",
|
|
234
288
|
approved_plan_path="<approved-plan-or-empty>",
|
|
235
289
|
clarification_response_path=str(clarification_abs) if clarification_abs else "",
|
|
236
290
|
render_only=True,
|
|
@@ -333,15 +333,16 @@ Empty-state placeholder, copy verbatim when nothing else applies:
|
|
|
333
333
|
규칙:
|
|
334
334
|
|
|
335
335
|
- 각 row의 `Auto-spawn?` 값이 `yes` 이면 Phase 7 의 `scripts/okstra-spawn-followups.py` 가 자동으로 후속 task 디렉터리(`tasks/<TASK_GROUP>/<New Task ID>/`)와 `task-manifest.json` (status: `todo`), stub task-brief 를 생성합니다. `no` 이면 사람이 따로 결정합니다.
|
|
336
|
+
- `Ticket ID` 는 본 보고서 상단 `Ticket Coverage` 규칙과 동일합니다. 후속 작업이 특정 외부 ticket(Jira/Linear/이슈 트래커 등)이나 본 보고서의 ticket 인덱스에 묶이면 그 키를 적고, 매핑이 없으면 `Task ID` 폴백 값을 prefix 없이 적습니다(예: `8852`). 어느 쪽으로도 식별 불가하면 `unknown`.
|
|
336
337
|
- `New Task ID` 는 알파숫자·하이픈만 사용하는 짧은 slug 입니다 (예: `8852-followup-logs`). 같은 task-group 안에서 유일해야 합니다.
|
|
337
338
|
- `Suggested task-type` 은 `requirements-discovery` / `error-analysis` / `implementation-planning` / `implementation` / `final-verification` / `release-handoff` 중 하나.
|
|
338
339
|
- `Scope` 는 영향 파일·영역을 콤마 또는 한 줄로 적습니다.
|
|
339
340
|
- `Reason / Why deferred` 는 본 run 에서 처리하지 못한 이유를 한 두 문장으로 적습니다. "시간이 없어서" 같은 모호한 사유는 거절됩니다.
|
|
340
341
|
- 동일 follow-up 이 여러 run 에 걸쳐 등장하면 `New Task ID` 를 동일하게 유지하여 중복 spawn 을 방지합니다 (script 가 기존 디렉터리 존재 여부로 idempotent 처리).
|
|
341
342
|
|
|
342
|
-
| ID | Origin | New Task ID | Title | Suggested task-type | Scope (files/areas) | Reason / Why deferred | Priority (P0/P1/P2) | Auto-spawn? |
|
|
343
|
-
|
|
344
|
-
| FU-001 | `<out-of-plan / verifier-concern / scope-boundary / open-question / manual>` | `<new-task-id-slug>` | <한 줄 제목> | `<task-type>` | `<files / areas>` | <한 두 문장 사유> | `P1` | `yes` |
|
|
343
|
+
| ID | Ticket ID | Origin | New Task ID | Title | Suggested task-type | Scope (files/areas) | Reason / Why deferred | Priority (P0/P1/P2) | Auto-spawn? |
|
|
344
|
+
|----|-----------|--------|-------------|-------|---------------------|---------------------|------------------------|---------------------|-------------|
|
|
345
|
+
| FU-001 | `<TICKET-or-fallback>` | `<out-of-plan / verifier-concern / scope-boundary / open-question / manual>` | `<new-task-id-slug>` | <한 줄 제목> | `<task-type>` | `<files / areas>` | <한 두 문장 사유> | `P1` | `yes` |
|
|
345
346
|
|
|
346
347
|
빈 상태 예시 (해당 없음):
|
|
347
348
|
|