okstra 0.22.0 → 0.24.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 +3 -0
- package/README.md +3 -0
- package/bin/okstra +5 -0
- package/docs/kr/architecture.md +2 -2
- package/docs/kr/cli.md +1 -0
- package/docs/project-structure-overview.md +4 -1
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +3 -1
- package/runtime/agents/workers/codex-worker.md +3 -1
- package/runtime/agents/workers/gemini-worker.md +3 -1
- package/runtime/agents/workers/report-writer-worker.md +17 -2
- package/runtime/prompts/profiles/release-handoff.md +16 -0
- package/runtime/python/okstra_ctl/render.py +25 -2
- package/runtime/python/okstra_ctl/wizard.py +1249 -0
- package/runtime/python/okstra_token_usage/collect.py +12 -1
- package/runtime/skills/okstra-report-writer/SKILL.md +1 -0
- package/runtime/skills/okstra-run/SKILL.md +115 -234
- package/runtime/skills/okstra-setup/SKILL.md +37 -0
- package/runtime/skills/okstra-team-contract/SKILL.md +47 -1
- package/runtime/templates/prd/brief.template.md +1 -0
- package/runtime/templates/project-docs/task-index.template.md +1 -0
- package/runtime/templates/reports/error-analysis-input.template.md +1 -0
- package/runtime/templates/reports/final-report.template.md +1 -0
- package/runtime/templates/reports/final-verification-input.template.md +1 -0
- package/runtime/templates/reports/implementation-input.template.md +1 -0
- package/runtime/templates/reports/implementation-planning-input.template.md +1 -0
- package/runtime/templates/reports/quick-input.template.md +1 -0
- package/runtime/templates/reports/release-handoff-input.template.md +1 -0
- package/runtime/templates/reports/schedule.template.md +1 -0
- package/runtime/templates/reports/task-brief.template.md +1 -0
- package/src/config.mjs +392 -0
- package/src/wizard.mjs +105 -0
package/README.kr.md
CHANGED
|
@@ -167,6 +167,8 @@ Claude Code 세션 밖에서 task 를 시작하려면:
|
|
|
167
167
|
|
|
168
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
|
+
- **PR 본문 템플릿 설정** (release-handoff) — PR 본문은 마크다운 템플릿에서 채워집니다. 해석 우선순위: 1회성 override (`--pr-template-path` 또는 okstra-run Step 6 prompt) → `<project_root>/.project-docs/okstra/project.json` 의 `prTemplatePath` → `~/.okstra/config.json` 의 `prTemplatePath` → 스킬 디폴트 `~/.claude/skills/okstra-run/templates/pr-body.template.md`. 템플릿 등록 명령: `okstra config set pr-template-path <path> [--scope project|global]` (project 스코프는 project root 기준 상대경로 허용, global 스코프는 절대경로 또는 `~/` 시작 경로만 허용). 현재 설정 확인: `okstra config get pr-template-path --scope all` 은 각 스코프 값 + 실제로 우승하는 경로(effective) 까지 보여줍니다. 디폴트 템플릿은 `## Summary` / `## Changes` / `## Test plan` / `## Linked issues` 4 섹션 + HTML 주석으로 lead 작성 가이드를 포함하며, PR 생성 직전에 lead 가 주석을 제거합니다.
|
|
171
|
+
- **프로파일 워커 로스터 검증** — `--workers <csv>` 와 okstra-run Step 6 의 워커 prompt 는 해당 프로파일의 `Required workers:` 블록에 선언된 워커 ID 만 허용합니다. 프로파일에 없는 워커 (예: `release-handoff` 에서 `codex` / `gemini`) 를 요청하면 명확한 에러로 거절되고, 인터랙티브 prompt 도 프로파일이 실제로 받는 워커만 보여줍니다.
|
|
170
172
|
|
|
171
173
|
### 3.5 운영 명령
|
|
172
174
|
|
|
@@ -177,6 +179,7 @@ Claude Code 세션 밖에서 task 를 시작하려면:
|
|
|
177
179
|
| `npx -y okstra@latest ensure-installed` | Idempotent 체크, stale 이면 자동 재설치 (스킬이 내부적으로 호출) |
|
|
178
180
|
| `npx -y okstra@latest setup --project-id <id>` | 현재 프로젝트를 등록 (`.project-docs/okstra/project.json`) |
|
|
179
181
|
| `npx -y okstra@latest check-project` | 현재 프로젝트가 `setup` 으로 등록됐는지 검증 |
|
|
182
|
+
| `npx -y okstra@latest config <get\|set\|unset\|show> [key] [value] [--scope project\|global\|all]` | okstra 설정 읽기/쓰기. 현재 지원 키: `pr-template-path` (project.json 또는 `~/.okstra/config.json` 의 `prTemplatePath` 갱신) |
|
|
180
183
|
| `npx -y okstra@latest uninstall` | 런타임 + 스킬 제거; 사용자 데이터(`recent.jsonl`, `projects/`, …)는 보존 |
|
|
181
184
|
| `npx -y okstra@latest uninstall --purge -y` | 사용자 데이터까지 모두 제거 |
|
|
182
185
|
|
package/README.md
CHANGED
|
@@ -166,6 +166,8 @@ Recent workflow additions (post-0.8.0, on `main`):
|
|
|
166
166
|
|
|
167
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
|
+
- **Configurable PR body template** (release-handoff) — the PR body is filled from a markdown template chosen in priority order: per-run override (`--pr-template-path` or the okstra-run Step 6 prompt) → `<project_root>/.project-docs/okstra/project.json` `prTemplatePath` → `~/.okstra/config.json` `prTemplatePath` → bundled skill default at `~/.claude/skills/okstra-run/templates/pr-body.template.md`. Register a template with `okstra config set pr-template-path <path> [--scope project|global]` (project scope accepts paths relative to the project root; global scope requires absolute or `~/`-prefixed). `okstra config get pr-template-path --scope all` reports every scope plus the effective winner. The bundled default ships `## Summary` / `## Changes` / `## Test plan` / `## Linked issues` with HTML comment guidance that the lead strips before opening the PR.
|
|
170
|
+
- **Profile-roster worker validation** — `--workers <csv>` (and the okstra-run Step 6 worker prompt) are now restricted to the worker IDs declared by the chosen profile's `Required workers:` block. Asking for `codex` / `gemini` on a profile that does not list them (e.g. `release-handoff`) is rejected with a clear error, and the interactive prompt only offers workers the profile actually accepts.
|
|
169
171
|
|
|
170
172
|
### 3.5 Ops commands
|
|
171
173
|
|
|
@@ -176,6 +178,7 @@ Recent workflow additions (post-0.8.0, on `main`):
|
|
|
176
178
|
| `npx -y okstra@latest ensure-installed` | Idempotent check, auto-reinstall if stale (skills call this internally) |
|
|
177
179
|
| `npx -y okstra@latest setup --project-id <id>` | Register the current project (`.project-docs/okstra/project.json`) |
|
|
178
180
|
| `npx -y okstra@latest check-project` | Verify the current project has been registered with `setup` |
|
|
181
|
+
| `npx -y okstra@latest config <get\|set\|unset\|show> [key] [value] [--scope project\|global\|all]` | Read / write okstra settings; initial key `pr-template-path` (writes `prTemplatePath` to project.json or `~/.okstra/config.json`) |
|
|
179
182
|
| `npx -y okstra@latest uninstall` | Remove runtime + skills; preserves user data (`recent.jsonl`, `projects/`, …) |
|
|
180
183
|
| `npx -y okstra@latest uninstall --purge -y` | Remove everything including user data |
|
|
181
184
|
|
package/bin/okstra
CHANGED
|
@@ -9,11 +9,13 @@ const COMMANDS = new Map([
|
|
|
9
9
|
["doctor", () => import("../src/doctor.mjs").then((m) => m.run)],
|
|
10
10
|
["setup", () => import("../src/setup.mjs").then((m) => m.run)],
|
|
11
11
|
["check-project", () => import("../src/check-project.mjs").then((m) => m.run)],
|
|
12
|
+
["config", () => import("../src/config.mjs").then((m) => m.run)],
|
|
12
13
|
["task-list", () => import("../src/task-list.mjs").then((m) => m.run)],
|
|
13
14
|
["task-show", () => import("../src/task-show.mjs").then((m) => m.run)],
|
|
14
15
|
["worktree-lookup", () => import("../src/worktree-lookup.mjs").then((m) => m.run)],
|
|
15
16
|
["plan-validate", () => import("../src/plan-validate.mjs").then((m) => m.run)],
|
|
16
17
|
["render-bundle", () => import("../src/render-bundle.mjs").then((m) => m.run)],
|
|
18
|
+
["wizard", () => import("../src/wizard.mjs").then((m) => m.run)],
|
|
17
19
|
]);
|
|
18
20
|
|
|
19
21
|
const USAGE = `okstra — multi-agent cross-verification orchestrator for Claude Code
|
|
@@ -40,6 +42,7 @@ Admin commands:
|
|
|
40
42
|
setup Register the current project (.project-docs/okstra/project.json)
|
|
41
43
|
doctor Diagnostic check of the installed runtime
|
|
42
44
|
check-project Verify the current project has been registered with setup
|
|
45
|
+
config Read / write okstra settings (e.g. PR template path)
|
|
43
46
|
paths Print runtime paths (workspace/agents/pythonpath/bin/home/version)
|
|
44
47
|
|
|
45
48
|
Introspection commands (JSON output, used by skills to avoid python heredocs):
|
|
@@ -49,6 +52,8 @@ Introspection commands (JSON output, used by skills to avoid python heredocs):
|
|
|
49
52
|
plan-validate Check an approved-plan file for the approval marker
|
|
50
53
|
render-bundle Preview prepare_task_bundle() output (forwards to
|
|
51
54
|
python3 -m okstra_ctl.run --render-only)
|
|
55
|
+
wizard Drive the okstra-run interactive input state machine
|
|
56
|
+
(init / step / render-args / confirmation)
|
|
52
57
|
|
|
53
58
|
Global options:
|
|
54
59
|
--version Print okstra version and exit
|
package/docs/kr/architecture.md
CHANGED
|
@@ -95,7 +95,7 @@ okstra 의 prepare 책임은 단일 python 진입점 [`okstra_ctl.run.prepare_ta
|
|
|
95
95
|
`prepare_task_bundle` 의 두 caller:
|
|
96
96
|
|
|
97
97
|
1. **`scripts/okstra.sh`**: CLI 인자를 파싱·확인하고 → `prepare_task_bundle` 호출 → `--render-only` 가 아니면 `claude --model ... --session-id ... "$PROMPT"` 를 `exec` 으로 띄움. ~160 줄의 thin wrapper.
|
|
98
|
-
2. **`okstra-run` skill**: 같은 claude 세션 안에서 `
|
|
98
|
+
2. **`okstra-run` skill**: 같은 claude 세션 안에서 [`okstra_ctl.wizard`](../../scripts/okstra_ctl/wizard.py) 상태머신(`okstra wizard init|step|...` CLI)을 돌려 사용자 입력을 모은 뒤 → `okstra render-bundle` (즉 `prepare_task_bundle(render_only=True)`) 호출 → 렌더된 lead prompt 를 현재 세션이 그대로 읽어 lead 역할 수행. 새 claude 프로세스를 띄우지 않음. 분기/검증/순서는 모두 wizard 가 결정하므로 skill 본문은 `Prompt.kind` 에 맞춰 `AskUserQuestion`(`pick`) 또는 평문 메시지(`text`)를 띄우는 ~30 줄짜리 루프이다.
|
|
99
99
|
|
|
100
100
|
판단 정책과 worker orchestration 은 lead claude 가 담당하고, okstra 의 prepare 단계는 그 lead 가 정확한 입력 묶음과 출력 골격을 받아 일을 시작할 수 있게 정형화된 자산을 준비할 뿐입니다.
|
|
101
101
|
|
|
@@ -167,7 +167,7 @@ okstra 의 prepare 단계는 디스크 권위 + 단일 python 진입점 모델
|
|
|
167
167
|
├────────────────────────────────────────────────────────────────┤
|
|
168
168
|
│ │
|
|
169
169
|
│ scripts/okstra.sh skills/okstra-run/SKILL.md │
|
|
170
|
-
│ (CLI: bash 인자 파싱) (
|
|
170
|
+
│ (CLI: bash 인자 파싱) (okstra_ctl.wizard 상태머신 루프) │
|
|
171
171
|
│ │ │ │
|
|
172
172
|
│ └─────────────┬────────────────┘ │
|
|
173
173
|
│ ▼ │
|
package/docs/kr/cli.md
CHANGED
|
@@ -492,6 +492,7 @@ chmod +x ~/.local/bin/okstra-ctl
|
|
|
492
492
|
| `okstra worktree-lookup <task-key>` | `worktree_registry.lookup` 결과 (예약된 path / branch / base ref / 현재 상태) |
|
|
493
493
|
| `okstra plan-validate <plan-path>` | `_validate_approved_plan` — approval marker 인식 결과와 sanitization 후 diff |
|
|
494
494
|
| `okstra render-bundle <args…>` | `prepare_task_bundle(render_only=True)` 의 thin shim — `python3 -m okstra_ctl.run --render-only` 와 동일 시그니처 |
|
|
495
|
+
| `okstra wizard <init\|step\|render-args\|confirmation> --state-file <path>` | okstra-run 인터랙티브 입력 상태머신 (`okstra_ctl.wizard`). `init` 으로 state file 을 시드한 뒤 skill 이 `step --answer <val>` 을 반복 호출하면 다음 `Prompt` JSON 을 받음. `render-args` 는 최종 `render-bundle` 인자 맵, `confirmation` 은 사용자 echo 블록을 반환 |
|
|
495
496
|
|
|
496
497
|
> 모든 subcommand 는 `bin/okstra` 가 spawn 하는 python 헬퍼 (`src/_python-helper.mjs`) 가 `PYTHONPATH` 와 `~/.okstra/lib/python` 을 wire 합니다. 직접 `python3 -m okstra_ctl.*` 으로 호출하면 `PYTHONPATH` 를 사용자가 직접 셋업해야 합니다.
|
|
497
498
|
|
|
@@ -101,6 +101,7 @@ okstra/
|
|
|
101
101
|
| `worktree-lookup.mjs` | 작업별 git worktree 경로 조회 |
|
|
102
102
|
| `plan-validate.mjs` | Approved plan 파일의 approval marker 검증 |
|
|
103
103
|
| `render-bundle.mjs` | `prepare_task_bundle()` 산출 미리보기 |
|
|
104
|
+
| `wizard.mjs` | okstra-run 인터랙티브 입력 상태머신 구동 (`init`/`step`/`render-args`/`confirmation`) |
|
|
104
105
|
| `uninstall.mjs` | `~/.okstra` 및 스킬 제거 |
|
|
105
106
|
|
|
106
107
|
**핵심 설계**: 모든 CLI 명령은 `okstra paths --shell` 결과를 환경변수로 로드한 뒤, Python의 단일 진입점 `okstra_ctl.run.prepare_task_bundle`을 호출한다. CLI(`okstra.sh`)와 skill(`okstra-run`)이 같은 Python 함수를 공유하는 **Rule of Two**를 강제한다.
|
|
@@ -126,6 +127,7 @@ okstra/
|
|
|
126
127
|
| `workers.py` | Worker 모델 할당 (Claude/Codex/Gemini) |
|
|
127
128
|
| `backfill.py` | 레거시 run 자동 마이그레이션 |
|
|
128
129
|
| `models.py` | `ModelAssignment`, `PrepareInputs`, `PrepareOutputs` 데이터클래스 |
|
|
130
|
+
| `wizard.py` | `WizardState`, `Prompt`, `STEPS` — okstra-run 입력 수집 상태머신 (단일 권위) |
|
|
129
131
|
|
|
130
132
|
#### 3.2.2 `scripts/okstra_project/` — 프로젝트 메타 리졸버
|
|
131
133
|
|
|
@@ -242,7 +244,7 @@ okstra/
|
|
|
242
244
|
|
|
243
245
|
### 3.8 `bin/okstra` — Node.js CLI
|
|
244
246
|
|
|
245
|
-
|
|
247
|
+
13개 명령(`paths`, `install`, `ensure-installed`, `uninstall`, `doctor`, `setup`, `check-project`, `task-list`, `task-show`, `worktree-lookup`, `plan-validate`, `render-bundle`, `wizard`) 라우터. 명령 파싱 → `src/<cmd>.mjs` 동적 로드 → `run(args)` 호출 → exit code 반환.
|
|
246
248
|
|
|
247
249
|
---
|
|
248
250
|
|
|
@@ -306,6 +308,7 @@ okstra/
|
|
|
306
308
|
| 프로젝트 | `setup.mjs`, `check-project.mjs` |
|
|
307
309
|
| Task 조회 | `task-list.mjs`, `task-show.mjs` |
|
|
308
310
|
| 실행 보조 | `worktree-lookup.mjs`, `plan-validate.mjs`, `render-bundle.mjs` |
|
|
311
|
+
| 스킬 구동 | `wizard.mjs` |
|
|
309
312
|
|
|
310
313
|
---
|
|
311
314
|
|
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -63,7 +63,9 @@ Before producing any output, you MUST read every input file enumerated in the `[
|
|
|
63
63
|
|
|
64
64
|
## Worker Output Structure
|
|
65
65
|
|
|
66
|
-
When returning results, organize into the following sections in this exact order
|
|
66
|
+
When returning results, start the file with a YAML frontmatter block, then organize the body into the following sections in this exact order.
|
|
67
|
+
|
|
68
|
+
**Frontmatter (mandatory)** — set `workerId: "claude"`. Copy `id`, `aliases`, `taskType`, `task-id`, `task-group`, `project-id`, `date` verbatim from the input files (`analysis-material.md` is canonical; if it lacks any field, record a `tool-failure` and stop). Full schema and a concrete example live in the `okstra-team-contract` skill's "Result Frontmatter" subsection.
|
|
67
69
|
|
|
68
70
|
0. **Reading Confirmation** - one short line per input file confirming end-to-end reading (e.g. `- Read task-brief.md end-to-end (147 lines).`). If any file was skipped, record a `tool-failure` and do NOT produce sections 1–5.
|
|
69
71
|
1. **Findings** - what you identified
|
|
@@ -130,7 +130,9 @@ Before producing any output, you MUST ensure the underlying Codex CLI run reads
|
|
|
130
130
|
|
|
131
131
|
## Worker Output Structure
|
|
132
132
|
|
|
133
|
-
When returning results, organize into the following sections in this exact order
|
|
133
|
+
When returning results, start the file with a YAML frontmatter block, then organize the body into the following sections in this exact order.
|
|
134
|
+
|
|
135
|
+
**Frontmatter (mandatory)** — set `workerId: "codex"`. Copy `id`, `aliases`, `taskType`, `task-id`, `task-group`, `project-id`, `date` verbatim from the input files (`analysis-material.md` is canonical; if it lacks any field, record a `tool-failure` and stop). Full schema and a concrete example live in the `okstra-team-contract` skill's "Result Frontmatter" subsection.
|
|
134
136
|
|
|
135
137
|
0. **Reading Confirmation** - one short line per input file confirming end-to-end reading (e.g. `- Read task-brief.md end-to-end (147 lines).`). If any file was skipped, record a `tool-failure` and do NOT produce sections 1–5.
|
|
136
138
|
1. **Findings** - what Codex identified
|
|
@@ -130,7 +130,9 @@ Before producing any output, you MUST ensure the underlying Gemini CLI run reads
|
|
|
130
130
|
|
|
131
131
|
## Worker Output Structure
|
|
132
132
|
|
|
133
|
-
When returning results, organize into the following sections in this exact order
|
|
133
|
+
When returning results, start the file with a YAML frontmatter block, then organize the body into the following sections in this exact order.
|
|
134
|
+
|
|
135
|
+
**Frontmatter (mandatory)** — set `workerId: "gemini"`. Copy `id`, `aliases`, `taskType`, `task-id`, `task-group`, `project-id`, `date` verbatim from the input files (`analysis-material.md` is canonical; if it lacks any field, record a `tool-failure` and stop). Full schema and a concrete example live in the `okstra-team-contract` skill's "Result Frontmatter" subsection.
|
|
134
136
|
|
|
135
137
|
0. **Reading Confirmation** - one short line per input file confirming end-to-end reading (e.g. `- Read task-brief.md end-to-end (147 lines).`). If any file was skipped, record a `tool-failure` and do NOT produce sections 1–5.
|
|
136
138
|
1. **Findings** - what Gemini identified
|
|
@@ -61,7 +61,7 @@ Hard rules:
|
|
|
61
61
|
- Include all four convergence categories (Full Consensus, Partial Consensus, Contested, Worker-Unique). Do not omit Contested or Worker-Unique findings.
|
|
62
62
|
- Include a Round History sub-table in Section 1 (one row per executed round) and a `round2SkippedReason` line below it. When convergence is disabled, omit both. The values are quoted verbatim from `state/convergence-<task-type>-<seq>.json` — do not recompute.
|
|
63
63
|
- Treat `verification-error` votes as their own verdict. They are listed in vote summaries as `verification-error`, not folded into AGREE/DISAGREE counts.
|
|
64
|
-
- Include the per-agent execution status table and the token-usage summary section.
|
|
64
|
+
- Include the per-agent execution status table and the token-usage summary section. **Leave the 10 token-related `{{...}}` placeholders verbatim** (`{{LEAD_TOTAL_TOKENS}}`, `{{LEAD_BILLABLE_TOKENS}}`, `{{LEAD_COST_USD}}`, `{{WORKER_TOTAL_TOKENS}}`, `{{WORKER_BILLABLE_TOKENS}}`, `{{WORKER_COST_USD}}`, `{{GRAND_TOTAL_TOKENS}}`, `{{GRAND_BILLABLE_TOKENS}}`, `{{GRAND_COST_USD}}`, `{{CLI_COST_USD}}`). You run in Phase 6, but `team-state-<task-type>-<seq>.json` is populated by `okstra-token-usage.py` at the start of Phase 7 and the same Phase 7 invocation substitutes the placeholders via `--substitute-final-report`. Never replace these cells with `not-collected`, `N/A`, `--`, `0`, or any other sentinel — doing so deletes the substitution target and the report ships with no token numbers. Likewise do NOT append a note like "Phase 7 has not run yet"; that statement is unfalsifiable at write-time and is wrong by the time the report is shipped.
|
|
65
65
|
- If only one analysis worker produced a usable result, perform a reduced-confidence write-up and say so explicitly.
|
|
66
66
|
- If evidence is missing, write `I don't know` rather than fabricating confidence.
|
|
67
67
|
- Cite file paths and line numbers for every code-evidence claim.
|
|
@@ -81,9 +81,22 @@ The validator (`validators/validate-run.py`) checks this file exists whenever th
|
|
|
81
81
|
|
|
82
82
|
The lead's prompt provides this path as a second result destination — extract it from the `**Worker Result Path:**` line (or, if absent, derive it as `runs/<task-type>/worker-results/report-writer-worker-<task-type>-<seq>.md` under `Project Root`).
|
|
83
83
|
|
|
84
|
-
The worker-results file
|
|
84
|
+
The worker-results file MUST begin with a YAML frontmatter block (set `workerId: "report-writer"`; copy `id`, `aliases`, `taskType`, `task-id`, `task-group`, `project-id`, `date` verbatim from `analysis-material.md` — full schema lives in the `okstra-team-contract` "Result Frontmatter" subsection), followed by the standard header:
|
|
85
85
|
|
|
86
86
|
```markdown
|
|
87
|
+
---
|
|
88
|
+
title: OKSTRA Report Writer Worker Result - <task-key>
|
|
89
|
+
id: "<task-key with ':' replaced by '-'>"
|
|
90
|
+
aliases: ["<id>-<task-type>"]
|
|
91
|
+
tags: ["obsidian", "okstra", "worker-result", "<task-type>"]
|
|
92
|
+
taskType: "<task-type>"
|
|
93
|
+
workerId: "report-writer"
|
|
94
|
+
task-id: "<task-id>"
|
|
95
|
+
task-group: "<task-group>"
|
|
96
|
+
project-id: "<project-id>"
|
|
97
|
+
date: <YYYY-MM-DD>
|
|
98
|
+
---
|
|
99
|
+
|
|
87
100
|
# Report Writer Worker Analysis — <task-key>
|
|
88
101
|
|
|
89
102
|
**Task:** <task-type>
|
|
@@ -92,6 +105,8 @@ The worker-results file must begin with the standard header from `okstra-team-co
|
|
|
92
105
|
**Model:** Report writer worker, opus
|
|
93
106
|
```
|
|
94
107
|
|
|
108
|
+
The same frontmatter (with `workerId: "report-writer"`) MUST also appear on the final-report file you assemble — the `final-report.template.md` already encodes it, so simply preserve the template's frontmatter block when filling sections.
|
|
109
|
+
|
|
95
110
|
Followed by a short body that:
|
|
96
111
|
1. Names the canonical final-report file path written by this worker (relative to project root).
|
|
97
112
|
2. Lists the input artifacts you reconciled (each analysis worker's result file path under `worker-results/`, plus the convergence-state file path if present).
|
|
@@ -28,6 +28,15 @@
|
|
|
28
28
|
- `main`
|
|
29
29
|
- `직접 입력` (free-form branch name; lead validates the name exists on origin via `git ls-remote --heads origin <name>` and re-asks on failure)
|
|
30
30
|
The chosen base MUST NOT equal the feature branch. If it does, re-ask.
|
|
31
|
+
2b. **Pre-merge conflict probe** (only when the user picked `push + PR`) — before the push/PR step, the lead MUST refresh the base ref and probe for merge conflicts against it:
|
|
32
|
+
- run `git fetch origin <chosen-base>` (read-only on the local working tree).
|
|
33
|
+
- run `git merge-tree --write-tree --merge-base origin/<chosen-base> HEAD origin/<chosen-base>`. A non-zero exit code OR conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) in the output indicate a merge conflict against the chosen base.
|
|
34
|
+
- If no conflict is detected, proceed silently to Q3 (do NOT add a confirmation prompt — keep the happy path frictionless).
|
|
35
|
+
- If a conflict IS detected, present the conflicting paths (parsed from the `merge-tree` output) and capture exactly one:
|
|
36
|
+
- `proceed anyway` — continue to Q3; the PR will be opened with conflicts and the final report MUST flag this in `Merge Conflict Probe`.
|
|
37
|
+
- `change base branch` — return to Q2 and re-ask the base selection.
|
|
38
|
+
- `cancel` — end the run without push or PR; record the cancellation in the final report.
|
|
39
|
+
- The probe is read-only. It MUST NOT run `git merge`, `git rebase`, `git pull`, or any command that mutates the working tree or local refs.
|
|
31
40
|
3. **PR title + PR body confirmation** — show the lead's inline draft verbatim and capture one of:
|
|
32
41
|
- `use as-is` — proceed with the drafted text.
|
|
33
42
|
- `edit then proceed` — accept inline edits from the user, then proceed with the edited text.
|
|
@@ -40,6 +49,7 @@
|
|
|
40
49
|
2. **PR body** — markdown filled from `PR_TEMPLATE_PATH`. The user-confirmation step's diff (Q3 `edit then proceed`) is computed against the filled template, not against the raw template file.
|
|
41
50
|
- Allowed actions during the run (Claude lead only):
|
|
42
51
|
- 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>`.
|
|
52
|
+
- merge-conflict probe (only when the user picked `push + PR`): `git fetch origin <chosen-base>` and `git merge-tree --write-tree --merge-base origin/<chosen-base> HEAD origin/<chosen-base>`. Both are non-mutating with respect to the working tree.
|
|
43
53
|
- feature-branch push (only when the user picked `push + PR`): `git push -u origin <current-branch>`. The pushed ref MUST be the feature branch — never the chosen base branch.
|
|
44
54
|
- PR creation (only when the user picked `push + PR` AND no PR with the same head already exists on origin): `gh pr create --base <chosen-base> --head <current-branch> --title "<title>" --body "<body>"`. The title and body are the user-confirmed PR draft.
|
|
45
55
|
- PR reuse: if `gh pr list --head <branch> --state open --json url --jq '.[0].url'` returns a URL, treat that PR as already existing — record the URL in the final report and SKIP `gh pr create`.
|
|
@@ -65,9 +75,14 @@
|
|
|
65
75
|
- **User Selections**: a block recording each prompt and the user's verbatim answer.
|
|
66
76
|
- Q1 action: `local only` | `push + PR` | `skip`.
|
|
67
77
|
- Q2 PR base (if applicable): the chosen branch and how it was selected (menu pick vs free-form input).
|
|
78
|
+
- Q2b merge-conflict probe (if applicable): `clean` (no conflict, no prompt shown) | `proceed anyway` | `change base branch` | `cancel`. When a conflict was detected, list the conflicting paths.
|
|
68
79
|
- Q3 title/body: `use as-is` | `edit then proceed` (with a diff between the lead's draft and the final text) | `cancel`.
|
|
69
80
|
- **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.
|
|
70
81
|
- **Commit List**: each existing implementation commit in `git log <base>..HEAD`, with short/full SHA, subject line, and touched files. Release-handoff MUST NOT create new commits.
|
|
82
|
+
- **Merge Conflict Probe**: one of
|
|
83
|
+
- `- Not run (user picked local only or skip).`
|
|
84
|
+
- `- Clean — no conflicts against <base> at <origin/base SHA>.`
|
|
85
|
+
- `- Conflicts detected against <base> at <origin/base SHA>; user chose <proceed anyway | change base branch | cancel>. Conflicting paths: <list>.`
|
|
71
86
|
- **Pull Request Outcome**: one of
|
|
72
87
|
- `- No PR action requested.` (user picked `local only` or `skip`)
|
|
73
88
|
- `- PR created: <url>` with title and base branch
|
|
@@ -80,6 +95,7 @@
|
|
|
80
95
|
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`.
|
|
81
96
|
4. **Push-target audit** — for every `git push` recorded, confirm the refspec resolves to the feature branch, not the base branch.
|
|
82
97
|
5. **Idempotency check** — if a PR with the same head already existed at run start, confirm the report records `PR reused` rather than a fresh `gh pr create` invocation.
|
|
98
|
+
6. **Merge-conflict probe audit** — for any `push + PR` run, confirm the report's `Merge Conflict Probe` section is present and either records `Clean` or records `Conflicts detected` with the user's verbatim choice. A missing or unparseable probe entry on a `push + PR` run is a contract violation.
|
|
83
99
|
- Non-goals:
|
|
84
100
|
- re-litigating the final-verification verdict — release-handoff trusts the cited `accepted` verdict and does not reopen acceptance checks.
|
|
85
101
|
- creating, amending, squashing, or rewriting commits. Commit production belongs to `implementation`.
|
|
@@ -96,6 +96,16 @@ def _doc_type_from_template_path(template_path: str) -> str:
|
|
|
96
96
|
return stem
|
|
97
97
|
|
|
98
98
|
|
|
99
|
+
def _frontmatter_id_from_task_key(task_key: str) -> str:
|
|
100
|
+
"""task_key (`project_id:task_group:task_id`) 를 ID 형식으로 변환.
|
|
101
|
+
|
|
102
|
+
`:` 를 `-` 로 치환한 단일 문자열. 예시:
|
|
103
|
+
``fontsninja-classifier-v2:DEV-9388:DEV-9429``
|
|
104
|
+
-> ``fontsninja-classifier-v2-DEV-9388-DEV-9429``
|
|
105
|
+
"""
|
|
106
|
+
return (task_key or "").strip().replace(":", "-")
|
|
107
|
+
|
|
108
|
+
|
|
99
109
|
def _frontmatter_mapping(ctx: dict) -> dict:
|
|
100
110
|
task_id = (ctx.get("TASK_ID") or "").strip()
|
|
101
111
|
project_id = (ctx.get("PROJECT_ID") or "").strip()
|
|
@@ -103,15 +113,28 @@ def _frontmatter_mapping(ctx: dict) -> dict:
|
|
|
103
113
|
task_key = (ctx.get("TASK_KEY") or "").strip()
|
|
104
114
|
task_date = (ctx.get("TASK_DATE") or "").strip()
|
|
105
115
|
doc_type = (ctx.get("DOC_TYPE") or "").strip()
|
|
116
|
+
# task_type 은 ctx 키가 두 곳에 분포 — 직접 키(`TASK_TYPE`), 또는
|
|
117
|
+
# `ANALYSIS_TYPE` fallback (workflow 의 render mapping 과 동일 우선순위).
|
|
118
|
+
task_type = (ctx.get("TASK_TYPE") or ctx.get("ANALYSIS_TYPE") or "").strip()
|
|
119
|
+
|
|
120
|
+
fm_id = _frontmatter_id_from_task_key(task_key)
|
|
121
|
+
fm_id_scalar = f'"{fm_id}"' if fm_id else f'"{_FM_DEFAULT}"'
|
|
122
|
+
alias_value = f"{fm_id}-{task_type}" if (fm_id and task_type) else fm_id
|
|
106
123
|
return {
|
|
107
124
|
"{{TASK_KEY}}": _fm_scalar(task_key),
|
|
108
125
|
"{{TASK_ID}}": _fm_scalar(task_id),
|
|
109
126
|
"{{PROJECT_ID}}": _fm_scalar(project_id),
|
|
110
127
|
"{{TASK_GROUP}}": _fm_scalar(task_group),
|
|
111
128
|
"{{TASK_DATE}}": _fm_scalar(task_date),
|
|
112
|
-
|
|
113
|
-
"
|
|
129
|
+
# task_key 의 `:` 를 `-` 로 치환한 단일 스칼라.
|
|
130
|
+
# 예: "fontsninja-classifier-v2-DEV-9388-DEV-9429"
|
|
131
|
+
"{{FM_ID}}": fm_id_scalar,
|
|
132
|
+
# id 와 task-type 을 `-` 로 연결한 단일 alias 를 array 한 칸에 담는다
|
|
133
|
+
# (Obsidian aliases 컨벤션).
|
|
134
|
+
"{{FM_ALIASES}}": _fm_array([alias_value]) if alias_value else "[]",
|
|
114
135
|
"{{FM_TAGS}}": _fm_tags(doc_type),
|
|
136
|
+
# 신규: 모든 okstra 산출물의 frontmatter 가 task type 을 명시한다.
|
|
137
|
+
"{{FM_TASK_TYPE}}": _fm_scalar(task_type),
|
|
115
138
|
}
|
|
116
139
|
|
|
117
140
|
|