okstra 0.17.0 → 0.18.1
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 +8 -8
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/SKILL.md +12 -3
- package/runtime/agents/workers/claude-worker.md +7 -5
- package/runtime/agents/workers/codex-worker.md +23 -16
- package/runtime/agents/workers/gemini-worker.md +23 -16
- package/runtime/prompts/launch.template.md +15 -0
- package/runtime/prompts/profiles/error-analysis.md +2 -1
- package/runtime/prompts/profiles/final-verification.md +2 -1
- package/runtime/prompts/profiles/implementation-planning.md +2 -1
- package/runtime/prompts/profiles/requirements-discovery.md +2 -1
- package/runtime/python/okstra_ctl/paths.py +16 -0
- package/runtime/python/okstra_ctl/render.py +10 -0
- package/runtime/python/okstra_ctl/workers.py +3 -2
- package/runtime/skills/okstra-team-contract/SKILL.md +18 -3
package/README.kr.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## 1. 용도
|
|
8
8
|
|
|
9
|
-
`okstra` 는 **Claude Code 안에서 lead + worker 모델로 작업을 cross-verify 하기 위한 정형화된 task 실행 러너**입니다. Claude lead 가 phase 진행을 주도하고, 독립된 분석 worker
|
|
9
|
+
`okstra` 는 **Claude Code 안에서 lead + worker 모델로 작업을 cross-verify 하기 위한 정형화된 task 실행 러너**입니다. Claude lead 가 phase 진행을 주도하고, 독립된 분석 worker — **기본 Claude · Codex** (Gemini 는 옵션으로 명시할 때만 추가) — 와 최종 보고서 작성을 전담하는 report-writer 를 dispatch 합니다.
|
|
10
10
|
|
|
11
11
|
설계의 세 가지 원칙:
|
|
12
12
|
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## 1. Purpose
|
|
8
8
|
|
|
9
|
-
`okstra` is a **structured task-execution runner for Claude Code that cross-verifies work with a lead + worker model**. The Claude lead drives phase progression and dispatches
|
|
9
|
+
`okstra` is a **structured task-execution runner for Claude Code that cross-verifies work with a lead + worker model**. The Claude lead drives phase progression and dispatches independent analysis workers — **Claude and Codex by default** (with **Gemini** available as an opt-in extra) — plus a dedicated report-writer for the final synthesis.
|
|
10
10
|
|
|
11
11
|
The design rests on three principles:
|
|
12
12
|
|
package/docs/kr/architecture.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
- **Run lifecycle**: 매 실행마다 per-run 디렉터리(`runs/<timestamp>/`)에 prompt snapshot, sessions/, expected-state, final report 템플릿, run manifest, timeline 이벤트를 저장합니다.
|
|
18
18
|
- **Single python authority**: 모든 prepare wiring(profile/workers/model 해소, path 계산, 9개 render, central record_start)이 [`okstra_ctl.run.prepare_task_bundle()`](scripts/okstra_ctl/run.py) 한 함수에 모여 있습니다. `okstra.sh` 와 `okstra-run` skill 은 같은 함수를 호출하는 thin caller 이며, 환경 변수로 상태를 전달하지 않습니다 — task 정체성·경로·workflow 상태는 모두 디스크 권위 파일에서 매번 계산됩니다.
|
|
19
19
|
- **Claude handoff (두 모드)**: (a) `okstra.sh` 가 새 `claude` 프로세스를 띄우는 전통 방식, (b) `okstra-run` skill 이 현재 claude 세션 안에서 prepare 후 lead 역할을 그대로 인계받는 in-session 모드. 둘 다 `prepare_task_bundle` 의 산출물(instruction-set 등)을 그대로 사용합니다.
|
|
20
|
-
- **Required team contract**: `Claude lead` +
|
|
20
|
+
- **Required team contract**: `Claude lead` + 기본 worker `Claude worker` · `Codex worker` · `Report writer worker` 와 Agent Teams 우선 시도를 강제합니다. `Gemini worker` 는 옵션 워커로, `--workers` 또는 프로필의 `- Workers:` 섹션에 명시할 때만 포함됩니다.
|
|
21
21
|
- **User-home install + project-local task bundles**: `npx okstra@latest install` 한 명령이 런타임(`~/.okstra/{lib/python, bin, templates}`) + 스킬 마크다운(`~/.claude/skills/<name>/SKILL.md`) 을 모두 깐다. 대상 프로젝트에는 task bundle 과 discovery metadata 가 `.project-docs/okstra/` 아래 저장되고, **추가로 `<PROJECT_ROOT>/.claude/settings.local.json` 이 `~/.okstra/templates/settings.local.json` 을 가리키는 symlink 로 provisioning** 됩니다 (`okstra setup` 또는 `okstra-ctl` prepare 가 idempotent 하게 관리; 기존에 일반 파일이 있었다면 `.bak.<timestamp>` 로 백업 후 교체). 이 symlink 가 host Claude Code 세션에 자동 로드되어 codex/gemini worker wrapper 호출 권한을 부여하므로, 사용자의 글로벌 `~/.claude/settings.json` 은 건드리지 않으며 별도 `--settings` CLI 주입도 필요 없습니다. (개발용으로는 `okstra-install.sh` 가 `--link` 모드 symlink 설치를 제공합니다.)
|
|
22
22
|
- **Resume and clarification**: `--task-key`, `--resume-clarification`, `--clarification-response`로 같은 task 재개와 lead의 추가 질문 응답 흐름을 지원합니다.
|
|
23
23
|
- **Optional integrations**: worker error sidecar, token usage / cost accounting을 옵션으로 제공합니다.
|
|
@@ -223,7 +223,7 @@ per-process 환경 변수에 task 정체성·경로·workflow 상태를 보관
|
|
|
223
223
|
|
|
224
224
|
두 모드 모두 동일한 산출물(task-manifest, run-manifest, timeline, instruction-set, central index 등록) 을 만들며, `okstra-ctl` 의 후속 명령(list / show / rerun / reconcile)은 산출물 차이를 알지 못한 채 일관되게 동작합니다.
|
|
225
225
|
- handoff된 메인 Claude는 `Claude lead`로 동작하며 orchestration과 final synthesis를 담당합니다.
|
|
226
|
-
- standard workflow의
|
|
226
|
+
- standard workflow의 기본 worker role은 `Claude worker`, `Codex worker`, `Report writer worker`이며, `Gemini worker`는 `--workers` 또는 프로필에서 명시할 때만 포함되는 옵션입니다.
|
|
227
227
|
- worker 역할 분담과 최종 판단은 Claude가 task bundle을 읽고 수행합니다.
|
|
228
228
|
- 사용자 홈에 설치된 okstra Claude assets(`~/.claude/skills`, `~/.claude/agents`) 는 Agent Teams 를 우선 시도하고, 팀 구성이 불가능할 때만 sequential/background fallback 을 사용하도록 Claude 를 유도합니다.
|
|
229
229
|
|
|
@@ -242,11 +242,11 @@ Claude launch prompt 본문은 항상 `prompts/launch.template.md` 템플릿에
|
|
|
242
242
|
표준 `okstra` workflow는 아래 팀 계약을 runtime prompt, profile, manifest, skill 문서에 공통으로 반영합니다.
|
|
243
243
|
|
|
244
244
|
- 메인 Claude는 항상 `Claude lead`이며 synthesis-only로 동작합니다.
|
|
245
|
-
- required worker role은 `Claude worker`, `Codex worker`, `Gemini worker
|
|
245
|
+
- 기본 required worker role은 `Claude worker`, `Codex worker`, `Report writer worker`입니다. `Gemini worker`는 옵션 워커로, `--workers` 또는 프로필의 `- Workers:` 섹션에 명시될 때만 required 로 포함됩니다.
|
|
246
246
|
- `Report writer worker`는 보고서 구조화와 근거 정리에 집중하지만 최종 synthesis owner는 여전히 `Claude lead`입니다.
|
|
247
|
-
- 기본 모델 계약은 중앙 기본값에서 계산합니다. 기본 fallback은 `Claude lead`=`opus`, `Claude worker`=`sonnet`, `Codex worker`=`gpt-5.5`, `Gemini worker`=`auto
|
|
248
|
-
- `Gemini worker`는
|
|
249
|
-
- 최종 판단 전에는 각 required
|
|
247
|
+
- 기본 모델 계약은 중앙 기본값에서 계산합니다. 기본 fallback은 `Claude lead`=`opus`, `Claude worker`=`sonnet`, `Codex worker`=`gpt-5.5`, `Gemini worker`=`auto`(opt-in 시 적용)이며, `Report writer worker`는 별도 override가 없으면 `Claude lead` 모델을 따릅니다(즉, 기본값에서는 `opus`).
|
|
248
|
+
- `Gemini worker`는 옵션이므로 명시 포함된 run에 한해서만 시도 대상이 됩니다.
|
|
249
|
+
- 최종 판단 전에는 현재 run의 worker roster 에 포함된 각 required role별로 결과 또는 명시적인 terminal status(`completed`, `timeout`, `error`, `not-run`)가 필요합니다.
|
|
250
250
|
- 시도된 worker(`completed`, `timeout`, `error`)는 현재 run의 `prompts/` 아래 assigned worker prompt history file을 반드시 가져야 합니다.
|
|
251
251
|
- 이름 없는 generic parallel worker는 required role 대체 수단으로 허용하지 않습니다.
|
|
252
252
|
|
|
@@ -587,7 +587,7 @@ canonical metadata는 항상 `task-manifest.json`을 기준으로 확인합니
|
|
|
587
587
|
9. `instruction-set/final-report-template.md`를 읽습니다.
|
|
588
588
|
10. current `manifests/run-manifest-<task-type>-<seq>.json`을 읽습니다.
|
|
589
589
|
11. 필요하면 `history/timeline.json`과 이전 run 결과를 참고합니다.
|
|
590
|
-
12. `Claude lead`로서
|
|
590
|
+
12. `Claude lead`로서 현재 run의 worker roster (기본 `Claude worker`, `Codex worker`, `Report writer worker`; `Gemini worker`는 명시 포함된 경우에만)에 따라 역할을 구성합니다.
|
|
591
591
|
13. 각 selected worker prompt를 assigned worker prompt history path로 현재 run의 `prompts/` 아래에 먼저 저장한 뒤 worker를 dispatch합니다.
|
|
592
592
|
14. 각 required worker에 대해 결과 또는 terminal status를 수집합니다.
|
|
593
593
|
15. brief이 더 구체적인 형식을 강제하지 않으면 `final-report-template.md` 구조로 Markdown 최종 보고서를 작성합니다.
|
|
@@ -850,7 +850,7 @@ Claude가 작성하는 최종 보고서는 brief에 더 구체적인 형식이
|
|
|
850
850
|
- 현재 run 세션의 resume helper는 `runs/<task-type>/sessions/claude-resume-<task-type>-<seq>.sh`에 생성됩니다.
|
|
851
851
|
- run directory 내부는 `manifests/`, `state/`, `prompts/`, `reports/`, `status/`, `sessions/`, `worker-results/`처럼 유형별 하위 폴더로 구성되고, prompt snapshot은 `prompts/` 아래에 먼저 준비됩니다.
|
|
852
852
|
- worker 생성과 결과 취합은 Claude가 수행합니다.
|
|
853
|
-
- standard workflow는 `Claude lead` +
|
|
853
|
+
- standard workflow는 `Claude lead` + 기본 worker `Claude worker`, `Codex worker`, `Report writer worker`를 사용하고, `Gemini worker`는 명시할 때만 포함되는 옵션입니다.
|
|
854
854
|
- worker 모델은 `--lead-model`, `--claude-model`, `--codex-model`, `--gemini-model`, `--report-writer-model`로 override할 수 있고, 기본값은 `OKSTRA_DEFAULT_*` 환경 변수에서 중앙 관리합니다. fallback 기본값은 `Claude lead`/`Report writer worker`=`opus`, `Claude worker`=`sonnet`, `Codex worker`=`gpt-5.5`, `Gemini worker`=`auto`입니다.
|
|
855
855
|
- `--task-type implementation` 에서는 Executor 역할을 맡을 provider 를 `--executor <claude|codex|gemini>` (또는 `OKSTRA_DEFAULT_EXECUTOR`, fallback `claude`) 로 선택합니다. Executor 만 프로젝트 파일을 mutate 할 수 있고, 나머지 두 provider 와 자기 자신의 provider 가 모두 별도 CLI 세션으로 verifier 로 dispatch 됩니다 (세션 분리만으로도 self-review 안전장치 유지). Executor 의 모델은 선택된 provider 의 worker 모델 플래그(`--claude-model` / `--codex-model` / `--gemini-model`) 를 그대로 재사용하며, run-manifest 의 `teamContract.executor` 블록에 provider / displayName / workerAgent / model 이 기록됩니다.
|
|
856
856
|
- Executor 별 worktree cwd 주입: codex / gemini executor 는 wrapper(`okstra-codex-exec.sh -C` / `okstra-gemini-exec.sh --include-directories`) 가 CLI layer 에서 cwd 를 worktree 로 고정합니다. Claude executor 는 Bash tool 에 per-call cwd 인자가 없어 cwd 민감 toolchain (`cargo`/`npm`/`pnpm`/`bun`/`pytest`/`make`/`go`) 호출을 같은 Bash invocation 안에서 `cd {{EXECUTOR_WORKTREE_PATH}} && <cmd>` 로 prefix 합니다 — `bash -lc`/`bash -c` 래핑은 금지되며 (`cd` leading token 이 가려져 permission auto-allow 우회 실패), 작업 디렉터리 플래그 (`git -C`, `cargo --manifest-path` 등) 가 있으면 그것을 우선합니다. 자세한 규약은 `prompts/profiles/implementation.md` 의 *Executor Worktree* 블록과 `agents/workers/claude-worker.md` 의 Executor exception 항목 참고.
|
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
package/runtime/agents/SKILL.md
CHANGED
|
@@ -175,12 +175,21 @@ Spawn **analysis workers only** in the same turn (Phase 4 in Teams mode; Phase 5
|
|
|
175
175
|
|
|
176
176
|
The no-`team_name` fallback (Phase 5) is only legal when team-state's `teamCreate.status` is `"error"` for this run. If `teamCreate` is missing or `attempted: false`, the correct action when an Agent dispatch is rejected for a missing team is to GO BACK to Phase 3 and call `TeamCreate` — never to strip `team_name` and continue.
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
### Errors log path wiring (BLOCKING)
|
|
179
|
+
|
|
180
|
+
The launch prompt's `## Run Logs (error-log wiring)` section gives Lead the resolved absolute paths for the run-level errors log and every per-worker sidecar. When Lead constructs each worker's dispatch prompt body, Lead MUST inject the matching two header lines verbatim:
|
|
181
|
+
|
|
182
|
+
- `**Errors log path:** <absolute run-level errors log path from launch prompt>`
|
|
183
|
+
- `**Errors sidecar path:** <absolute per-worker sidecar path matching the dispatched worker>`
|
|
184
|
+
|
|
185
|
+
Workers are contractually required to extract these two lines and abort with `<WORKER>_ERRORS_PATH_MISSING` if either is absent (see each worker definition's "Path extraction (BLOCKING)" block). Omitting these headers reproduces the historical bug class where every run's `errors-<task-type>-<seq>.jsonl` stayed empty because workers had only template placeholders to work from.
|
|
186
|
+
|
|
187
|
+
After each worker terminates (any terminal status), if its errors sidecar exists, dump it to the run error log using the same resolved paths from the launch prompt:
|
|
179
188
|
|
|
180
189
|
```bash
|
|
181
190
|
python3 scripts/okstra-error-log.py append-from-worker \
|
|
182
|
-
--sidecar <sidecar> \
|
|
183
|
-
--out <
|
|
191
|
+
--sidecar <absolute-sidecar-path-from-launch-prompt> \
|
|
192
|
+
--out <absolute-errors-log-path-from-launch-prompt> \
|
|
184
193
|
--task-key <taskKey> --agent <agent> --agent-role <role> --model <model>
|
|
185
194
|
```
|
|
186
195
|
|
|
@@ -92,13 +92,15 @@ If you find yourself thinking "let me double-check section 3" or "I should read
|
|
|
92
92
|
|
|
93
93
|
This agent is responsible for recording its own tool failures via `scripts/okstra-error-log.py`:
|
|
94
94
|
|
|
95
|
-
**
|
|
95
|
+
**Path extraction (BLOCKING).** Before recording anything, extract the absolute sidecar path from the lead's dispatch prompt body:
|
|
96
96
|
|
|
97
|
-
|
|
98
|
-
runs/<task-type>/worker-results/claude-worker-errors-<task-type>-<seq>.json
|
|
99
|
-
```
|
|
97
|
+
- `**Errors sidecar path:** <abs-path>` — this worker's per-run sidecar JSON.
|
|
100
98
|
|
|
101
|
-
|
|
99
|
+
If the header line is absent from the dispatch prompt, return `CLAUDE_WORKER_ERRORS_PATH_MISSING: lead prompt did not include **Errors sidecar path:** header` without proceeding. Do NOT synthesize the path from `runs/<task-type>/...` — that template syntax is documentation only and historically led to silent log loss.
|
|
100
|
+
|
|
101
|
+
**Tool failure (worker-reported)** — if `Write` of the prompt history file, `mkdir`, any MCP call, or any other pre-/in-analysis tool call fails (non-zero exit, exception, or empty result where data was required), append a `tool-failure` entry to the worker errors sidecar at the absolute path extracted from the `**Errors sidecar path:**` header. If the file does not exist, create it with `{"schemaVersion": 1, "errors": []}` then append.
|
|
102
|
+
|
|
103
|
+
The sidecar follows the schema in `skills/okstra-team-contract/SKILL.md` (Optional errors sidecar). Lead will dump it to the run error log after this subagent terminates.
|
|
102
104
|
|
|
103
105
|
There is NO `cli-failure` category for this worker — Claude worker has no external CLI to invoke. Treat MCP errors and Bash errors uniformly as `tool-failure`.
|
|
104
106
|
|
|
@@ -148,19 +148,26 @@ This contract mirrors the `okstra-team-contract` skill's Worker Output Contract
|
|
|
148
148
|
The wrapper agent (this Codex worker subagent) is responsible for recording
|
|
149
149
|
two kinds of errors via `scripts/okstra-error-log.py`:
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
`tool-failure` entry to the worker errors sidecar at:
|
|
151
|
+
**Path extraction (BLOCKING).** Before recording anything, extract the
|
|
152
|
+
following two absolute paths verbatim from the lead's dispatch prompt body:
|
|
154
153
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
```
|
|
154
|
+
- `**Errors log path:** <abs-path>` — the run-level errors JSONL.
|
|
155
|
+
- `**Errors sidecar path:** <abs-path>` — this worker's per-run sidecar JSON.
|
|
158
156
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
157
|
+
If either header line is absent from the dispatch prompt, return
|
|
158
|
+
`CODEX_ERRORS_PATH_MISSING: lead prompt did not include **Errors log path:** / **Errors sidecar path:** headers`
|
|
159
|
+
without proceeding. Do NOT synthesize the path from `<runDir>/logs/...` —
|
|
160
|
+
historical bug class: workers writing to a literally-named template path
|
|
161
|
+
and the run-level error log staying empty.
|
|
162
|
+
|
|
163
|
+
1. **Wrapper-internal tool failure (worker-reported)** — if `Write` of the
|
|
164
|
+
prompt history file, `mkdir`, or any pre-CLI tool call fails, append a
|
|
165
|
+
`tool-failure` entry to the worker errors sidecar at the absolute path
|
|
166
|
+
extracted from the `**Errors sidecar path:**` header. If the file does
|
|
167
|
+
not exist, create it with `{"schemaVersion": 1, "errors": []}` then
|
|
168
|
+
append. The sidecar follows the schema in
|
|
169
|
+
`skills/okstra-team-contract/SKILL.md` (Optional errors sidecar). Lead
|
|
170
|
+
will dump it to the run error log after this subagent terminates.
|
|
164
171
|
|
|
165
172
|
2. **CLI failure (lead-observed)** — if the wrapper's final `BashOutput`
|
|
166
173
|
reports a non-zero `exit_code`, the 30-minute polling cap is hit, or the
|
|
@@ -171,7 +178,7 @@ two kinds of errors via `scripts/okstra-error-log.py`:
|
|
|
171
178
|
|
|
172
179
|
```bash
|
|
173
180
|
python3 scripts/okstra-error-log.py append-observed \
|
|
174
|
-
--out "<
|
|
181
|
+
--out "<absolute-errors-log-path-from-lead-prompt>" \
|
|
175
182
|
--task-key "<task-key>" \
|
|
176
183
|
--phase "<phase>" \
|
|
177
184
|
--agent codex-worker --agent-role worker \
|
|
@@ -184,10 +191,10 @@ two kinds of errors via `scripts/okstra-error-log.py`:
|
|
|
184
191
|
--stderr-excerpt-file "<captured-stderr-path or omit>"
|
|
185
192
|
```
|
|
186
193
|
|
|
187
|
-
The lead prompt provides
|
|
188
|
-
the prompt history path. If any of these are
|
|
189
|
-
to the worker errors sidecar instead —
|
|
190
|
-
failure.
|
|
194
|
+
The lead prompt provides `**Errors log path:**`, `<task-key>`, and
|
|
195
|
+
`<phase>` alongside the prompt history path. If any of these are
|
|
196
|
+
missing, fall back to logging to the worker errors sidecar instead —
|
|
197
|
+
never silently swallow a CLI failure.
|
|
191
198
|
|
|
192
199
|
Do not record a `cli-failure` for `CODEX_NOT_INSTALLED` returns — that is a
|
|
193
200
|
pre-flight terminal status, not a runtime CLI error.
|
|
@@ -148,19 +148,26 @@ This contract mirrors the `okstra-team-contract` skill's Worker Output Contract
|
|
|
148
148
|
The wrapper agent (this Gemini worker subagent) is responsible for recording
|
|
149
149
|
two kinds of errors via `scripts/okstra-error-log.py`:
|
|
150
150
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
`tool-failure` entry to the worker errors sidecar at:
|
|
151
|
+
**Path extraction (BLOCKING).** Before recording anything, extract the
|
|
152
|
+
following two absolute paths verbatim from the lead's dispatch prompt body:
|
|
154
153
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
```
|
|
154
|
+
- `**Errors log path:** <abs-path>` — the run-level errors JSONL.
|
|
155
|
+
- `**Errors sidecar path:** <abs-path>` — this worker's per-run sidecar JSON.
|
|
158
156
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
157
|
+
If either header line is absent from the dispatch prompt, return
|
|
158
|
+
`GEMINI_ERRORS_PATH_MISSING: lead prompt did not include **Errors log path:** / **Errors sidecar path:** headers`
|
|
159
|
+
without proceeding. Do NOT synthesize the path from `<runDir>/logs/...` —
|
|
160
|
+
historical bug class: workers writing to a literally-named template path
|
|
161
|
+
and the run-level error log staying empty.
|
|
162
|
+
|
|
163
|
+
1. **Wrapper-internal tool failure (worker-reported)** — if `Write` of the
|
|
164
|
+
prompt history file, `mkdir`, or any pre-CLI tool call fails, append a
|
|
165
|
+
`tool-failure` entry to the worker errors sidecar at the absolute path
|
|
166
|
+
extracted from the `**Errors sidecar path:**` header. If the file does
|
|
167
|
+
not exist, create it with `{"schemaVersion": 1, "errors": []}` then
|
|
168
|
+
append. The sidecar follows the schema in
|
|
169
|
+
`skills/okstra-team-contract/SKILL.md` (Optional errors sidecar). Lead
|
|
170
|
+
will dump it to the run error log after this subagent terminates.
|
|
164
171
|
|
|
165
172
|
2. **CLI failure (lead-observed)** — if the wrapper's final `BashOutput`
|
|
166
173
|
reports a non-zero `exit_code`, the 30-minute polling cap is hit, or the
|
|
@@ -171,7 +178,7 @@ two kinds of errors via `scripts/okstra-error-log.py`:
|
|
|
171
178
|
|
|
172
179
|
```bash
|
|
173
180
|
python3 scripts/okstra-error-log.py append-observed \
|
|
174
|
-
--out "<
|
|
181
|
+
--out "<absolute-errors-log-path-from-lead-prompt>" \
|
|
175
182
|
--task-key "<task-key>" \
|
|
176
183
|
--phase "<phase>" \
|
|
177
184
|
--agent gemini-worker --agent-role worker \
|
|
@@ -184,10 +191,10 @@ two kinds of errors via `scripts/okstra-error-log.py`:
|
|
|
184
191
|
--stderr-excerpt-file "<captured-stderr-path or omit>"
|
|
185
192
|
```
|
|
186
193
|
|
|
187
|
-
The lead prompt provides
|
|
188
|
-
the prompt history path. If any of these are
|
|
189
|
-
to the worker errors sidecar instead —
|
|
190
|
-
failure.
|
|
194
|
+
The lead prompt provides `**Errors log path:**`, `<task-key>`, and
|
|
195
|
+
`<phase>` alongside the prompt history path. If any of these are
|
|
196
|
+
missing, fall back to logging to the worker errors sidecar instead —
|
|
197
|
+
never silently swallow a CLI failure.
|
|
191
198
|
|
|
192
199
|
Do not record a `cli-failure` for `GEMINI_NOT_INSTALLED` returns — that is a
|
|
193
200
|
pre-flight terminal status, not a runtime CLI error.
|
|
@@ -46,6 +46,21 @@ Invoke the `okstra` skill now. Read the manifests below for all task metadata, p
|
|
|
46
46
|
- Final status: `{{FINAL_STATUS_RELATIVE_PATH}}`
|
|
47
47
|
- Validator: `{{RUN_VALIDATOR_RELATIVE_PATH}}`
|
|
48
48
|
|
|
49
|
+
## Run Logs (error-log wiring)
|
|
50
|
+
|
|
51
|
+
- Run-level errors log (absolute): `{{RUN_ERRORS_LOG_PATH}}`
|
|
52
|
+
- Run-level errors log (relative): `{{RUN_ERRORS_LOG_RELATIVE_PATH}}`
|
|
53
|
+
- Worker error sidecars (absolute):
|
|
54
|
+
- Claude worker: `{{CLAUDE_WORKER_ERRORS_SIDECAR_PATH}}`
|
|
55
|
+
- Codex worker: `{{CODEX_WORKER_ERRORS_SIDECAR_PATH}}`
|
|
56
|
+
- Gemini worker: `{{GEMINI_WORKER_ERRORS_SIDECAR_PATH}}`
|
|
57
|
+
- Report writer worker: `{{REPORT_WRITER_WORKER_ERRORS_SIDECAR_PATH}}`
|
|
58
|
+
- When dispatching any worker you MUST inject **two header lines** into the dispatch prompt body so the worker subagent can record errors without guessing paths:
|
|
59
|
+
- `**Errors log path:** <absolute run-level errors log path>`
|
|
60
|
+
- `**Errors sidecar path:** <absolute per-worker sidecar path matching the dispatched worker>`
|
|
61
|
+
- These lines are the canonical contract — worker subagents extract them verbatim and pass them to `okstra-error-log.py append-observed --out ...` (run-level cli-failure / contract-violation events) and to their internal sidecar writes (worker-reported tool-failure events) respectively.
|
|
62
|
+
- After each worker terminates, dump its sidecar into the run-level errors log via `python3 scripts/okstra-error-log.py append-from-worker --sidecar <sidecar-path> --out <run-errors-log-path> --task-key {{TASK_KEY}} --agent <worker-id> --agent-role worker --model <assigned-model-execution-value>` (per `okstra-team-contract` Worker Output Contract).
|
|
63
|
+
|
|
49
64
|
## Executor Worktree
|
|
50
65
|
|
|
51
66
|
- Status: `{{EXECUTOR_WORKTREE_STATUS}}`
|
|
@@ -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 analyser set; omitted by default
|
|
9
10
|
{{INCLUDE:_common-contract.md}}
|
|
10
11
|
- Primary focus areas:
|
|
11
12
|
- symptom and trigger clarification
|
|
@@ -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 analyser set; omitted by default
|
|
9
10
|
{{INCLUDE:_common-contract.md}}
|
|
10
11
|
- Primary focus areas:
|
|
11
12
|
- requirement coverage
|
|
@@ -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 analyser set; omitted by default
|
|
9
10
|
{{INCLUDE:_common-contract.md}}
|
|
10
11
|
- Pre-planning context exploration (mandatory before option drafting):
|
|
11
12
|
- read the task brief, related-task briefs, and any cited spec / design doc end-to-end
|
|
@@ -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 analyser set; omitted by default
|
|
9
10
|
{{INCLUDE:_common-contract.md}}
|
|
10
11
|
- Primary focus areas:
|
|
11
12
|
- classify the work as bugfix, feature, improvement, refactor, or ops-change
|
|
@@ -143,6 +143,12 @@ def compute_run_paths(
|
|
|
143
143
|
gemini_worker_result = worker_results / f"gemini-worker{suffixes['worker_results']}.md"
|
|
144
144
|
report_writer_worker_result = worker_results / f"report-writer-worker{suffixes['worker_results']}.md"
|
|
145
145
|
|
|
146
|
+
run_errors_log = run_logs / f"errors-{task_type_segment}-{seqs['state']}.jsonl"
|
|
147
|
+
claude_worker_errors_sidecar = worker_results / f"claude-worker-errors{suffixes['worker_results']}.json"
|
|
148
|
+
codex_worker_errors_sidecar = worker_results / f"codex-worker-errors{suffixes['worker_results']}.json"
|
|
149
|
+
gemini_worker_errors_sidecar = worker_results / f"gemini-worker-errors{suffixes['worker_results']}.json"
|
|
150
|
+
report_writer_worker_errors_sidecar = worker_results / f"report-writer-worker-errors{suffixes['worker_results']}.json"
|
|
151
|
+
|
|
146
152
|
run_validator_script = workspace_root / "validators" / "validate-run.py"
|
|
147
153
|
|
|
148
154
|
abs_paths = {
|
|
@@ -193,6 +199,11 @@ def compute_run_paths(
|
|
|
193
199
|
"CODEX_WORKER_RESULT_FILE": str(codex_worker_result),
|
|
194
200
|
"GEMINI_WORKER_RESULT_FILE": str(gemini_worker_result),
|
|
195
201
|
"REPORT_WRITER_WORKER_RESULT_FILE": str(report_writer_worker_result),
|
|
202
|
+
"RUN_ERRORS_LOG_FILE": str(run_errors_log),
|
|
203
|
+
"CLAUDE_WORKER_ERRORS_SIDECAR_FILE": str(claude_worker_errors_sidecar),
|
|
204
|
+
"CODEX_WORKER_ERRORS_SIDECAR_FILE": str(codex_worker_errors_sidecar),
|
|
205
|
+
"GEMINI_WORKER_ERRORS_SIDECAR_FILE": str(gemini_worker_errors_sidecar),
|
|
206
|
+
"REPORT_WRITER_WORKER_ERRORS_SIDECAR_FILE": str(report_writer_worker_errors_sidecar),
|
|
196
207
|
"RUN_VALIDATOR_SCRIPT": str(run_validator_script),
|
|
197
208
|
"RUN_MANIFEST_FILENAME": run_manifest_file.name,
|
|
198
209
|
"RUN_PROMPT_SNAPSHOT_FILENAME": run_prompt_snapshot.name,
|
|
@@ -245,6 +256,11 @@ def compute_run_paths(
|
|
|
245
256
|
("CODEX_WORKER_RESULT_RELATIVE_PATH", codex_worker_result),
|
|
246
257
|
("GEMINI_WORKER_RESULT_RELATIVE_PATH", gemini_worker_result),
|
|
247
258
|
("REPORT_WRITER_WORKER_RESULT_RELATIVE_PATH", report_writer_worker_result),
|
|
259
|
+
("RUN_ERRORS_LOG_RELATIVE_PATH", run_errors_log),
|
|
260
|
+
("CLAUDE_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", claude_worker_errors_sidecar),
|
|
261
|
+
("CODEX_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", codex_worker_errors_sidecar),
|
|
262
|
+
("GEMINI_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", gemini_worker_errors_sidecar),
|
|
263
|
+
("REPORT_WRITER_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", report_writer_worker_errors_sidecar),
|
|
248
264
|
("LATEST_RUN_RELATIVE_PATH", run_dir),
|
|
249
265
|
]
|
|
250
266
|
rel_paths = {key: _rel(project_root, target) for key, target in rel_pairs}
|
|
@@ -1078,6 +1078,16 @@ def render_template_file(template_path: str, output_path: str, ctx: dict) -> Non
|
|
|
1078
1078
|
"{{CODEX_WORKER_RESULT_RELATIVE_PATH}}": ctx.get("CODEX_WORKER_RESULT_RELATIVE_PATH", ""),
|
|
1079
1079
|
"{{GEMINI_WORKER_RESULT_RELATIVE_PATH}}": ctx.get("GEMINI_WORKER_RESULT_RELATIVE_PATH", ""),
|
|
1080
1080
|
"{{REPORT_WRITER_WORKER_RESULT_RELATIVE_PATH}}": ctx.get("REPORT_WRITER_WORKER_RESULT_RELATIVE_PATH", ""),
|
|
1081
|
+
"{{RUN_ERRORS_LOG_PATH}}": ctx.get("RUN_ERRORS_LOG_FILE", ""),
|
|
1082
|
+
"{{RUN_ERRORS_LOG_RELATIVE_PATH}}": ctx.get("RUN_ERRORS_LOG_RELATIVE_PATH", ""),
|
|
1083
|
+
"{{CLAUDE_WORKER_ERRORS_SIDECAR_PATH}}": ctx.get("CLAUDE_WORKER_ERRORS_SIDECAR_FILE", ""),
|
|
1084
|
+
"{{CLAUDE_WORKER_ERRORS_SIDECAR_RELATIVE_PATH}}": ctx.get("CLAUDE_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""),
|
|
1085
|
+
"{{CODEX_WORKER_ERRORS_SIDECAR_PATH}}": ctx.get("CODEX_WORKER_ERRORS_SIDECAR_FILE", ""),
|
|
1086
|
+
"{{CODEX_WORKER_ERRORS_SIDECAR_RELATIVE_PATH}}": ctx.get("CODEX_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""),
|
|
1087
|
+
"{{GEMINI_WORKER_ERRORS_SIDECAR_PATH}}": ctx.get("GEMINI_WORKER_ERRORS_SIDECAR_FILE", ""),
|
|
1088
|
+
"{{GEMINI_WORKER_ERRORS_SIDECAR_RELATIVE_PATH}}": ctx.get("GEMINI_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""),
|
|
1089
|
+
"{{REPORT_WRITER_WORKER_ERRORS_SIDECAR_PATH}}": ctx.get("REPORT_WRITER_WORKER_ERRORS_SIDECAR_FILE", ""),
|
|
1090
|
+
"{{REPORT_WRITER_WORKER_ERRORS_SIDECAR_RELATIVE_PATH}}": ctx.get("REPORT_WRITER_WORKER_ERRORS_SIDECAR_RELATIVE_PATH", ""),
|
|
1081
1091
|
"{{LEAD_MODEL}}": lead_model,
|
|
1082
1092
|
"{{LEAD_MODEL_EXECUTION_VALUE}}": lead_model_execution,
|
|
1083
1093
|
"{{CLAUDE_WORKER_MODEL}}": ctx.get("CLAUDE_WORKER_MODEL_DISPLAY", ""),
|
|
@@ -9,6 +9,7 @@ from __future__ import annotations
|
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
|
|
11
11
|
ALLOWED_WORKERS = ["claude", "codex", "gemini", "report-writer"]
|
|
12
|
+
DEFAULT_WORKERS = ["claude", "codex", "report-writer"]
|
|
12
13
|
PROFILE_BULLET_HEADERS = {
|
|
13
14
|
"- Workers:",
|
|
14
15
|
"- Required workers:",
|
|
@@ -51,11 +52,11 @@ def normalize_workers(value: str) -> list[str]:
|
|
|
51
52
|
"""CSV 입력을 정규화한다.
|
|
52
53
|
|
|
53
54
|
- 공백 strip, 소문자화, 중복 제거(첫 출현 우선).
|
|
54
|
-
- 빈 입력이면 `
|
|
55
|
+
- 빈 입력이면 `DEFAULT_WORKERS` 를 default 로 사용 (gemini 제외).
|
|
55
56
|
- 허용 외 worker 가 포함되면 `WorkersError`.
|
|
56
57
|
"""
|
|
57
58
|
items = [v.strip().lower() for v in (value or "").split(",") if v.strip()]
|
|
58
|
-
source = items or
|
|
59
|
+
source = items or DEFAULT_WORKERS
|
|
59
60
|
unknown = [v for v in source if v not in ALLOWED_WORKERS]
|
|
60
61
|
if unknown:
|
|
61
62
|
raise WorkersError(f"unknown workers: {','.join(unknown)}")
|
|
@@ -263,12 +263,27 @@ Schema:
|
|
|
263
263
|
```
|
|
264
264
|
|
|
265
265
|
Workers MUST omit `source` / `recordedAt` / `agent` / `agentRole` / `model` /
|
|
266
|
-
`taskKey`. Claude lead fills those in when dumping the sidecar to
|
|
267
|
-
`runs/<task-type>/logs/errors-<task-type>-<seq>.jsonl`
|
|
268
|
-
`scripts/okstra-error-log.py append-from-worker`.
|
|
266
|
+
`taskKey`. Claude lead fills those in when dumping the sidecar to the
|
|
267
|
+
run-level errors log (`runs/<task-type>/logs/errors-<task-type>-<seq>.jsonl`)
|
|
268
|
+
via `scripts/okstra-error-log.py append-from-worker`.
|
|
269
269
|
|
|
270
270
|
Workers MUST use only `errorType: "tool-failure"` in the **sidecar file**.
|
|
271
271
|
|
|
272
|
+
**Path delivery contract (BLOCKING).** Workers do NOT synthesize the
|
|
273
|
+
run-level errors log path or their sidecar path from the
|
|
274
|
+
`runs/<task-type>/...` template syntax. Both absolute paths are delivered
|
|
275
|
+
by Lead via two dispatch-prompt header lines:
|
|
276
|
+
|
|
277
|
+
- `**Errors log path:** <absolute path>` — run-level JSONL (`okstra-error-log.py append-observed --out ...`)
|
|
278
|
+
- `**Errors sidecar path:** <absolute path>` — per-worker JSON (`{ "schemaVersion": 1, "errors": [...] }`)
|
|
279
|
+
|
|
280
|
+
Lead obtains both paths from the launch prompt's `## Run Logs (error-log
|
|
281
|
+
wiring)` section (resolved by the okstra runtime via `paths.py`). If Lead
|
|
282
|
+
omits either header, the worker MUST return `<WORKER>_ERRORS_PATH_MISSING`
|
|
283
|
+
without proceeding — this is the contractual replacement for the previous
|
|
284
|
+
"derive from template placeholders" behavior, which silently produced
|
|
285
|
+
empty run-level error logs in production.
|
|
286
|
+
|
|
272
287
|
- `cli-failure` events are recorded by the wrapper subagent itself (Codex / Gemini), but **directly to the run-level error log** via `okstra-error-log.py append-observed --error-type cli-failure ...` — NOT via the sidecar. The sidecar is an in-process tool-failure channel only.
|
|
273
288
|
- **Wrapper invocation arity.** Both `okstra-codex-exec.sh` and `okstra-gemini-exec.sh` accept four positional arguments: `<project-root> <model> <prompt-path> [<worktree-path>]`. The fourth (worktree) argument is **mandatory for implementation phase** and optional otherwise. For codex it becomes `--add-dir <worktree>` (sandbox write access); for gemini it is appended to `--include-directories`. Omitting it during implementation causes the codex sandbox to reject every Edit/Write targeting the worktree with EPERM. Workers extract the path from the `**Worktree:**` / `EXECUTOR_WORKTREE_PATH` / `cwd for every mutating command:` line in the lead prompt.
|
|
274
289
|
- **Background dispatch + polling contract (Codex / Gemini wrappers).** Both wrapper subagents MUST dispatch `okstra-codex-exec.sh` / `okstra-gemini-exec.sh` via `Bash(run_in_background: true)` and poll with `BashOutput(bash_id)` on a 60-second cadence, capped at 30 minutes (1800s). The legacy "single foreground `Bash` with 120000ms timeout" rule is retired — it forced workers into ad-hoc background dispatch that lost stdout and silently broke Phase 5 synthesis. The new rule applies in **every phase** (analysis runs typically complete in 1–2 polls, so there is no regression for short jobs). Recording responsibilities:
|