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 +2 -0
- package/README.md +3 -1
- package/docs/kr/architecture.md +1 -1
- package/docs/kr/cli.md +1 -1
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/SKILL.md +14 -10
- package/runtime/prompts/launch.template.md +6 -3
- package/runtime/prompts/profiles/implementation.md +8 -8
- package/runtime/prompts/profiles/release-handoff.md +22 -22
- package/runtime/python/okstra_ctl/render.py +48 -0
- package/runtime/python/okstra_ctl/run.py +34 -13
- package/runtime/python/okstra_ctl/session.py +33 -0
- package/runtime/python/okstra_ctl/workflow.py +3 -3
- package/runtime/python/okstra_ctl/worktree.py +158 -75
- package/runtime/python/okstra_ctl/worktree_registry.py +211 -0
- package/runtime/skills/okstra-history/SKILL.md +8 -3
- package/runtime/skills/okstra-report-finder/SKILL.md +8 -3
- package/runtime/skills/okstra-report-writer/SKILL.md +3 -1
- package/runtime/skills/okstra-run/SKILL.md +11 -3
- package/runtime/skills/okstra-schedule/SKILL.md +8 -3
- package/runtime/skills/okstra-setup/SKILL.md +8 -2
- package/runtime/skills/okstra-status/SKILL.md +8 -3
- package/runtime/skills/okstra-time-summary/SKILL.md +8 -3
- package/runtime/validators/validate-run.py +22 -0
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
|
|
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
|
package/docs/kr/architecture.md
CHANGED
|
@@ -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
|
-
-
|
|
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
|
-
- **
|
|
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
package/runtime/BUILD.json
CHANGED
package/runtime/agents/SKILL.md
CHANGED
|
@@ -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
|
-
####
|
|
110
|
+
#### Task worktree (BLOCKING for every task-type)
|
|
111
111
|
|
|
112
|
-
|
|
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
|
-
-
|
|
115
|
-
-
|
|
116
|
-
-
|
|
117
|
-
-
|
|
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
|
|
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 `
|
|
164
|
-
4. If `TeamCreate`
|
|
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
|
|
48
|
-
-
|
|
49
|
-
-
|
|
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
|
-
-
|
|
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
|
|
33
|
-
- Branch: `{{EXECUTOR_WORKTREE_BRANCH}}` — empty when status is `skipped-*`. The branch name encodes `<work-category-prefix>-<task-id-segment
|
|
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)
|
|
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
|
|
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
|
-
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
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
|
|
32
|
-
- `use as-is` — proceed with the
|
|
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
|
-
-
|
|
36
|
-
-
|
|
37
|
-
-
|
|
38
|
-
1. **Commit message
|
|
39
|
-
2. **PR body
|
|
40
|
-
- is
|
|
41
|
-
|
|
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
|
-
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
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
|
-
# ----
|
|
517
|
-
#
|
|
518
|
-
#
|
|
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 =
|
|
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"
|
|
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
|
-
|
|
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
|
-
" -
|
|
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
|
-
" -
|
|
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
|
-
" -
|
|
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
|
},
|