okstra 0.121.0 → 0.122.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/docs/architecture.md +4 -2
- package/docs/project-structure-overview.md +1 -1
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +1 -1
- package/runtime/bin/lib/okstra/usage.sh +2 -2
- package/runtime/prompts/launch.template.md +4 -4
- package/runtime/prompts/lead/adapters/claude-code.md +104 -0
- package/runtime/prompts/lead/adapters/codex.md +45 -0
- package/runtime/prompts/lead/adapters/external.md +46 -0
- package/runtime/prompts/lead/context-loader.md +5 -5
- package/runtime/prompts/lead/convergence.md +11 -28
- package/runtime/prompts/lead/okstra-lead-contract.md +42 -92
- package/runtime/prompts/lead/plan-body-verification.md +21 -5
- package/runtime/prompts/lead/report-writer.md +50 -51
- package/runtime/prompts/lead/team-contract.md +33 -106
- package/runtime/prompts/profiles/_common-contract.md +2 -2
- package/runtime/prompts/profiles/_implementation-executor.md +1 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
- package/runtime/prompts/profiles/implementation-planning.md +3 -3
- package/runtime/prompts/profiles/requirements-discovery.md +1 -1
- package/runtime/python/okstra_ctl/lead_runtime.py +4 -0
- package/runtime/python/okstra_ctl/models.py +1 -1
- package/runtime/python/okstra_ctl/render.py +76 -194
- package/runtime/python/okstra_ctl/report_views.py +22 -7
- package/runtime/python/okstra_ctl/team_reconcile.py +3 -1
- package/runtime/schemas/final-report-v1.0.schema.json +3 -0
- package/runtime/skills/okstra-schedule/SKILL.md +60 -18
- package/runtime/templates/reports/final-report.template.md +4 -4
- package/runtime/templates/reports/i18n/en.json +1 -1
- package/runtime/templates/reports/i18n/ko.json +1 -1
- package/runtime/validators/validate-run.py +89 -0
- package/runtime/validators/validate-schedule.py +7 -3
- package/runtime/validators/validate_session_conformance.py +58 -33
- package/src/cli-registry.mjs +7 -0
- package/src/commands/inspect/stage-map.mjs +100 -0
- package/src/commands/lifecycle/install.mjs +3 -0
- package/src/commands/lifecycle/uninstall.mjs +8 -23
- package/src/lib/skill-catalog.mjs +8 -0
|
@@ -2,15 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## File-author ownership (BLOCKING)
|
|
4
4
|
|
|
5
|
-
The final-report
|
|
5
|
+
The final-report data.json is authored by `Report writer worker` when that role is in the roster. The lead reviews both rendered artifacts but does not write them. Lead-authored fallback is legal only after a real `dispatch_worker` attempt records `error`, `timeout`, or `not-run` with a concrete reason. `release-handoff` remains the intentional single-lead exception.
|
|
6
|
+
|
|
7
|
+
The JSON SSOT path is `runs/<task-type>/reports/final-report-<task-type>-<seq>.data.json`. The user-facing markdown at `runs/<task-type>/reports/final-report-<task-type>-<seq>.md` is produced by `scripts/okstra-render-final-report.py` from the data.json so both files land on disk before the worker returns.
|
|
6
8
|
|
|
7
9
|
The data.json schema is `schemas/final-report-v1.0.schema.json`. The renderer + the run-validator both consume that schema, so a data.json that validates is guaranteed to render into a markdown that passes the contract checks.
|
|
8
10
|
|
|
9
11
|
Two `frontmatter` approval fields are always emitted with their unset default — never pre-fill them: `frontmatter.approved` is emitted as `false`, and `frontmatter.implementationOption` is emitted as an empty string `""`. The user later flips `approved` to `true` (via `--approve` or manual edit) and fills `implementationOption` with the chosen Option Candidate name (via `--implementation-option <name>` or manual edit) to authorise and scope the next `implementation` run.
|
|
10
12
|
|
|
11
|
-
If you are reading this contract **as the report-writer
|
|
13
|
+
If you are reading this contract **as the report-writer worker**, YOU are the one writing the data.json and invoking the renderer through the available execution interface. Do not return either artifact inline — the files on disk are the canonical record.
|
|
12
14
|
|
|
13
|
-
If you are reading this contract **as
|
|
15
|
+
If you are reading this contract **as the lead**, your job in Phase 6 is to prepare the report-writer prompt, dispatch the Report writer worker per the Phase 6 dispatch template in okstra-lead-contract.md, and review both files in Phase 7. Do not call `write_artifact` against either report path yourself when Report writer worker is in the roster.
|
|
14
16
|
|
|
15
17
|
## When to Use
|
|
16
18
|
|
|
@@ -20,21 +22,13 @@ If you are reading this contract **as Claude lead**, your job in Phase 6 is to (
|
|
|
20
22
|
|
|
21
23
|
## Phase 6 dispatch template (Report writer worker)
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
prompt: "<report-writer prompt: see this contract + Required reading clause + Available MCP Servers section>",
|
|
29
|
-
name: "report-writer",
|
|
30
|
-
subagent_type: "report-writer-worker",
|
|
31
|
-
run_in_background: true, # no team_name — implicit team; see team-contract Operating Rule 0
|
|
32
|
-
model: "<family token of Report writer worker's modelExecutionValue>", # opus/sonnet/haiku — NOT hardcoded; see below
|
|
33
|
-
mode: "auto"
|
|
34
|
-
)
|
|
35
|
-
```
|
|
25
|
+
1. Resolve the Report writer worker assignment and all required prompt/result/error paths from the manifests.
|
|
26
|
+
2. Persist the exact prompt history with the required anchor headers and audience-specific reading list.
|
|
27
|
+
3. Emit the Phase 6 checkpoint.
|
|
28
|
+
4. Call `dispatch_worker(report_writer_assignment, prompt)` through the selected adapter.
|
|
29
|
+
5. Call `await_workers([handle])` and verify both the data.json Result Path and worker-results audit path.
|
|
36
30
|
|
|
37
|
-
The
|
|
31
|
+
The assignment's `modelExecutionValue` feeds both adapter dispatch and the prompt header in item 7 below, so the execution model and recorded `**Model:**` header always agree. Missing or unsupported model resolution is a pre-dispatch contract failure; the common contract does not choose a runtime fallback.
|
|
38
32
|
|
|
39
33
|
The prompt MUST include, in this order at the top:
|
|
40
34
|
|
|
@@ -58,36 +52,32 @@ The prompt MUST include, in this order at the top:
|
|
|
58
52
|
before the dispatch is constructed. The worker copies this verbatim
|
|
59
53
|
into `data.json.meta.reportLanguage`.
|
|
60
54
|
12. For implementation-planning runs: a literal block listing the 8 required English section headings the validator scans for (`Option Candidates`, `Trade-off`, `Recommended Option`, `Stepwise Execution Order`, `Dependency`, `Validation Checklist`, `Rollback`, `User Approval Request`). The writer must use these exact substrings as section headings (Korean translation in parentheses is allowed).
|
|
61
|
-
13. An explicit instruction: `You are the author of TWO files: (a) the final-report data.json at <Result Path>, (b) the worker-results audit file at <Worker Result Path>. After writing the data.json, invoke "okstra render-final-report <Result Path>"
|
|
55
|
+
13. An explicit instruction: `You are the author of TWO files: (a) the final-report data.json at <Result Path>, (b) the worker-results audit file at <Worker Result Path>. After writing the data.json, invoke "okstra render-final-report <Result Path>" through the available execution interface so the markdown sibling is rendered before you return. Do not return the report inline. The validator fails the run when (a)'s schema validation fails, when the rendered markdown is absent, or when (b) is missing.`
|
|
62
56
|
|
|
63
|
-
**Completion detection after dispatch (BLOCKING).**
|
|
57
|
+
**Completion detection after dispatch (BLOCKING).** A dispatch acknowledgement is NOT completion — detect completion via the SSOT protocol in [team-contract](./team-contract.md) "Worker-completion detection", with a one-entry pending set covering the data.json (Result Path) and the worker-results file (Worker Result Path). Do NOT end the turn with a prose "waiting for the report" statement.
|
|
64
58
|
|
|
65
59
|
### Resume-safe dispatch
|
|
66
60
|
|
|
67
|
-
A resumed lead
|
|
68
|
-
|
|
69
|
-
- Dispatch with `Agent(name: "report-writer", run_in_background: true)`; the worker's session is recoverable by `agentName: "report-writer"` in `okstra-token-usage.py`.
|
|
70
|
-
- Do NOT skip dispatch because of any team-related concern. Record `teamCreate` in team-state and proceed.
|
|
61
|
+
A resumed lead can always call `redispatch_worker` for a fresh Report writer worker. Resume/session mechanics are adapter-owned and are never a reason to bypass the role.
|
|
71
62
|
|
|
72
63
|
### Lead-authored fallback (only if dispatch failed)
|
|
73
64
|
|
|
74
65
|
Except for `release-handoff` (which is single-lead by design and never dispatches a Report writer worker — see "Release-handoff section contract" below), lead-authored fallback is permitted only if all of the following are true and recorded in team-state:
|
|
75
66
|
|
|
76
|
-
1. A Report writer worker dispatch was actually attempted
|
|
67
|
+
1. A Report writer worker dispatch was actually attempted through `dispatch_worker`.
|
|
77
68
|
2. The attempt recorded a terminal status of `error`, `timeout`, or `not-run` with a concrete reason (tool error message, timeout duration, or external blocker).
|
|
78
69
|
3. The reason is logged via `okstra error-log append-observed --error-type cli-failure ...` (or `tool-failure` if the failure was internal).
|
|
79
70
|
|
|
80
|
-
Speculative reasons such as "session resume constraint", "
|
|
81
|
-
|
|
82
|
-
For `tmux-pane` backend runs, do not use the Agent template. Create a one-job jobs file for the `report-writer` with `dispatchKind: "report-writer"` and the same field set as reverify jobs files ([convergence](./convergence.md) "Re-verification Agent Dispatch", tmux-pane branch — the single source for the jobs-file fields). Then call `okstra team dispatch --project-root <dir> --run-manifest <path> --jobs-file <jobs-file>` followed by `okstra team await --project-root <dir> --run-manifest <path>`. Completion requires both the report data.json and the report-writer worker-results audit file.
|
|
71
|
+
Speculative reasons such as "session resume constraint", "runtime state is unavailable", or "lead can do it faster" are NOT valid.
|
|
83
72
|
|
|
84
73
|
|
|
85
74
|
## Phase 6 → Phase 7 execution sequence (BLOCKING order)
|
|
86
75
|
|
|
87
|
-
|
|
76
|
+
Phase 6 first produces the final-report data.json at `runs/<task-type>/reports/final-report-<task-type>-<seq>.data.json` and its rendered markdown sibling. Token Usage cells are `null` at this point, and Section 3 does not yet include auto-spawned follow-ups.
|
|
77
|
+
|
|
78
|
+
The four Phase 7 steps below MUST execute in this exact order. Reordering them is the recurring root cause of reports shipping with `--` token cells, Section 3 missing follow-up entries, or Section 4 rows never spawning.
|
|
88
79
|
|
|
89
|
-
1. **
|
|
90
|
-
2. **Phase 7 step 1 — Token-usage collector with `--substitute-data`** (BLOCKING). One invocation aggregates `leadUsage` / `workers[].usage` / `usageSummary` into team-state AND populates `tokenUsage` + `executionStatus[].totalTokens` etc. in the data.json AND re-invokes the renderer so the sibling markdown carries the real numbers. Skipping the flag ships a markdown full of `--` cells.
|
|
80
|
+
1. **Collect usage.** Call `collect_usage` through the selected adapter with substitution enabled by the existing `okstra token-usage` workflow. One invocation aggregates `leadUsage` / `workers[].usage` / `usageSummary` into team-state, populates `tokenUsage` and the execution-status usage fields in data.json, and re-invokes the renderer so the markdown carries real numbers.
|
|
91
81
|
|
|
92
82
|
```bash
|
|
93
83
|
okstra token-usage \
|
|
@@ -97,7 +87,7 @@ The four steps below MUST execute in this exact order. Reordering them is the re
|
|
|
97
87
|
```
|
|
98
88
|
|
|
99
89
|
The data.json paths populated: `tokenUsage.lead.{totalTokens,billableTokens,costUsd}`, the `worker` / `grand` rows, `tokenUsage.cli.costUsd`, and each `executionStatus[].{totalTokens,billableTokens,costUsd,durationMs,cliTotalTokens,cliCostUsd}` for rows whose role matches a team-state worker. The data.json MUST already exist (Phase 6 output).
|
|
100
|
-
|
|
90
|
+
2. **Validate and render the report artifacts.** Always invoke the report-view renderer after the substituted data.json passes its schema/run checks; the renderer decides whether an html sibling is warranted:
|
|
101
91
|
|
|
102
92
|
```bash
|
|
103
93
|
okstra render-views \
|
|
@@ -109,8 +99,8 @@ The four steps below MUST execute in this exact order. Reordering them is the re
|
|
|
109
99
|
- implementation-planning 보고서에는 본문 끝에 **Plan Approval** 섹션(구현 옵션 `<select>` + 승인 체크박스)이 렌더된다 — §1 `Blocks: approval` 행이 미해소면 disabled. 승인을 체크하고 Export 하면 sidecar 본문에 `## APPROVAL` 블록이 실리고, implementation 시작 마법사의 approve-confirm 단계가 그것을 감지해 사용자 확인 후 기존 `--approve` / `--implementation-option` 경로로 적용한다.
|
|
110
100
|
- When the report has **no** `C-*` clarification rows and is **not** a Plan Approval widget target, the html carries no interactive forms (it would only duplicate the MD), so the renderer prints `html: skipped (...)` and writes nothing. This is the expected state for such runs — `validators/validate-report-views.py` treats "no C-* rows + no approval target + no html" as a pass, not a missing artifact.
|
|
111
101
|
|
|
112
|
-
|
|
113
|
-
|
|
102
|
+
This step runs after usage collection so token placeholders are substituted in any rendered html and before routing persistence so the html artifact, when generated, exists for the validator step that checks it.
|
|
103
|
+
3. **Complete routing and follow-up persistence.** Run the follow-up task spawner when Section 4 is non-empty. It turns the report's `## 4. Follow-up Tasks (후속 작업)` rows into `tasks/<task-group>/<new-task-id>/` stubs.
|
|
114
104
|
|
|
115
105
|
```bash
|
|
116
106
|
okstra spawn-followups \
|
|
@@ -126,15 +116,14 @@ The four steps below MUST execute in this exact order. Reordering them is the re
|
|
|
126
116
|
- Rows whose `origin` is `phase-continuation` are reported as `skipped (no new task dir)` and never spawn — they advance the same task-key via `/okstra-run` instead.
|
|
127
117
|
- An invalid `origin`, `suggestedTaskType`, missing `title`, missing `reason`, or missing `newTaskId` exits `1`. (Schema validation in Phase 6 catches most of these before the spawner runs.)
|
|
128
118
|
- **Canonical spawn rule (single source of truth):** the spawner runs when `task-type` ∈ {`implementation`, `final-verification`, `release-handoff`}, OR when `followUpTasks` is non-empty for any other task-type. For the listed task-types `followUpTasks` must be present (schema enforces the phase-continuation row for non-terminal task-types); an empty array is permitted only for `release-handoff`. Missing arrays are no-ops (exit `0`). All other references to this rule (including the Persistence Checklist) defer to this statement.
|
|
129
|
-
|
|
119
|
+
After the spawner, the report writer MUST append one row per newly spawned task-key with its entry command:
|
|
130
120
|
|
|
131
121
|
```
|
|
132
|
-
- Follow-up: `<task-group>/<new-task-id>` —
|
|
122
|
+
- Follow-up: `<task-group>/<new-task-id>` — in-session `/okstra-run task-key=<task-group>/<new-task-id> task-type=<suggested>` / standalone `scripts/okstra.sh --task-key <task-group>/<new-task-id> --task-type <suggested>`
|
|
133
123
|
```
|
|
134
124
|
|
|
135
|
-
The status file is written after
|
|
136
|
-
|
|
137
|
-
**Run-end teammate teardown follows this whole sequence.** Token-usage collection (step 1) reads the worker session jsonls, so the lead MUST NOT disband the team until every step above is done. Only then — after the combined pane/team disposition prompt and only when the user approves cleanup — does the lead dismiss the worker teammates via `SendMessage` shutdown_request per [okstra-lead-contract](./okstra-lead-contract.md) "Run-scoped pane & teammate lifecycle" → "Run-end teammate teardown" (split-pane runs only; silent-skip when split-pane is off; if the user keeps panes/teammates, leave them intact and surface the manual Teams/FleetView cleanup path).
|
|
125
|
+
The status file is written after routing and follow-up persistence completes.
|
|
126
|
+
4. **Execute the run-scoped cleanup gate.** Call `shutdown_workers` only after usage collection, all persistence work, and explicit user approval under [okstra-lead-contract](./okstra-lead-contract.md) "Run-scoped worker-resource lifecycle". If the user keeps resources, leave the selected adapter's resources intact and surface its manual cleanup guidance.
|
|
138
127
|
|
|
139
128
|
## Final Report Structure
|
|
140
129
|
|
|
@@ -142,13 +131,15 @@ The final report follows the structure encoded in `schemas/final-report-v1.0.sch
|
|
|
142
131
|
|
|
143
132
|
### Report Header
|
|
144
133
|
|
|
134
|
+
Milestone 1 keeps the final-report schema unchanged. Read the exact permitted values for `header.reportOwner` and `header.reportAuthor` from the task bundle's `instruction-set/final-report-schema.json` excerpt of `schemas/final-report-v1.0.schema.json`, then write those schema v1 compatibility values according to the actual authorship path. Do not derive either header field from the selected runtime's lead role. Runtime identity remains visible in the execution-status row and team-state audit fields.
|
|
135
|
+
|
|
145
136
|
```markdown
|
|
146
137
|
# <task-key> - Multi-Agent Cross Verification Final Report
|
|
147
138
|
- Date: <ISO 8601 timestamp>
|
|
148
139
|
- Task Key: <task-key>
|
|
149
140
|
- Task Type: <task-type>
|
|
150
|
-
- Report Owner:
|
|
151
|
-
- Report Author: `<
|
|
141
|
+
- Report Owner: `<exact schema header.reportOwner compatibility value>`
|
|
142
|
+
- Report Author: `<exact schema header.reportAuthor compatibility value for the actual authorship path>`
|
|
152
143
|
- Lead model: `<lead-model>`
|
|
153
144
|
- Preparation Method: Final report authored by Report writer worker (or lead-authored fallback — record the documented dispatch failure reason here when applicable)
|
|
154
145
|
```
|
|
@@ -158,22 +149,19 @@ The final report follows the structure encoded in `schemas/final-report-v1.0.sch
|
|
|
158
149
|
```markdown
|
|
159
150
|
| Agent | Role | Model | Status | 처리 토큰 | 환산 토큰 | 비용 (USD) | Duration | Summary of Key Findings |
|
|
160
151
|
|-------|------|-------|--------|-----------|-----------|------------|----------|------------------------|
|
|
161
|
-
|
|
|
162
|
-
|
|
|
163
|
-
| Codex | Codex worker | gpt-5.5 | completed | 2,274,011 (CLI: 5,261,833) | 586,223 | $8.79 (+ CLI $4.20) | 22m 06s | Key findings summary |
|
|
164
|
-
| Antigravity | Antigravity worker | auto | completed | 3,107,795 | 746,623 | $11.20 | 22m 06s | Key findings summary |
|
|
165
|
-
| Claude Code | Report writer | opus-4-6 | completed | 665,497 | 267,210 | $4.01 | 4m 20s | Report organization |
|
|
152
|
+
| <team-state.lead.agent> | <team-state.lead.role> | <model> | completed | <tokens> | <billable> | <cost> | <duration> | Final synthesis status |
|
|
153
|
+
| <team-state.workers[].agent> | <team-state.workers[].role> | <model> | <status> | <tokens> | <billable> | <cost> | <duration> | Key findings summary |
|
|
166
154
|
```
|
|
167
155
|
|
|
168
156
|
Table Generation Rules:
|
|
169
|
-
- The first row
|
|
170
|
-
-
|
|
157
|
+
- The first row derives its agent identifier and role from `team-state.lead.agent` / `team-state.lead.role` and its usage data from `leadUsage`; subsequent rows follow `recommendedWorkers` / `resultContract.requiredWorkerRoles` order.
|
|
158
|
+
- Every worker agent identifier and role derives from its persisted team-state entry; the common contract does not infer labels from provider names.
|
|
171
159
|
- **처리 토큰** = `usage.totalTokens` (input + output + cache_creation + cache_read; the raw volume processed).
|
|
172
160
|
- **환산 토큰** = `usage.billableEquivalentTokens` (cache reads weighted at 0.1×, cache_creation 1.25×, output 5×; useful as a single number for "how big was this session in cost terms").
|
|
173
161
|
- **비용 (USD)** = `usage.estimatedCostUsd`. For Codex/Antigravity workers that actually invoked the CLI, append `(+ CLI $X.XX)` from `usage.cliEstimatedCostUsd`.
|
|
174
162
|
- For Codex/Antigravity workers, append `(CLI: <cliTotalTokens>)` to the 처리 토큰 cell when `usage.cliTotalTokens` is set.
|
|
175
163
|
- Status values are retrieved from team-state; format duration as `Xm Ys` from `usage.durationMs`.
|
|
176
|
-
- Workers with status `not-run` or
|
|
164
|
+
- Workers with status `not-run` or unavailable usage evidence show `--` for tokens/cost/duration; quote the `note` underneath the table if useful.
|
|
177
165
|
|
|
178
166
|
### Token Usage Summary Section
|
|
179
167
|
|
|
@@ -206,7 +194,7 @@ Token Summary Generation Rules:
|
|
|
206
194
|
- **Grand total** (ko: `전체 합계`) row: `usageSummary.grandTotalTokens` / `usageSummary.grandBillableEquivalentTokens` / sum of `lead + claudeWorkers`.
|
|
207
195
|
- **Codex/Antigravity CLI add-on** (ko: `Codex/Antigravity CLI 추가 비용`) row: `usageSummary.estimatedCostUsd.cliWorkers`. If 0, still show the row so the reader sees that no CLI work was billed under those providers (or that CLI fallback occurred).
|
|
208
196
|
- Format tokens with comma separators (e.g., `32,500`); format USD with two decimals (e.g., `$1.43`).
|
|
209
|
-
- If `lead` or any `worker.usage`
|
|
197
|
+
- If `lead` or any `worker.usage` records unavailable evidence, show `--` for that row and append a one-line note (`reason: <note>`).
|
|
210
198
|
- If pricing for a model is unknown, the script omits `estimatedCostUsd` for that block — show `N/A` in that column and add a note like `pricing missing for model <model>`.
|
|
211
199
|
|
|
212
200
|
### Implementation-planning section heading contract (BLOCKING)
|
|
@@ -229,6 +217,17 @@ The Korean translation in parentheses is optional but the English keyword is man
|
|
|
229
217
|
|
|
230
218
|
The final-report template `templates/reports/final-report.template.md` Section 5.5 already encodes this contract — copy that block verbatim and fill in.
|
|
231
219
|
|
|
220
|
+
### Self-fix rewrite (plan-body self-fix 라운드)
|
|
221
|
+
|
|
222
|
+
lead 가 self-fix 라운드를 지시하면(대상 `P-*` 목록 + 각 결함 사유 전달), 지정된 planner-fixable 항목이 가리키는 **계획 섹션만** 교정한다 — 전체 draft 를 다시 쓰지 않는다:
|
|
223
|
+
|
|
224
|
+
- 경로 축약 → `<PROJECT_ROOT>` 기준 전체 상대경로로 교체(File Structure / Stepwise `files`).
|
|
225
|
+
- prose command → 작업 디렉터리·인자·task-type 이 포함된 실행 가능한 명령 시퀀스로 구체화.
|
|
226
|
+
- placeholder(`stage-<N>` 등) → 구체 파일명·재생성 계획으로 교체.
|
|
227
|
+
- requirement-coverage 매핑 오류 → 실재하는 option/stage/step 또는 XP 행으로 재매핑.
|
|
228
|
+
|
|
229
|
+
교정 후 해당 `planItems[].selfFixNote` 에 `self-fixed in round <N>: <무엇을 고쳤는지>` 를 남기고, `planBodyVerification.selfFixRoundApplied = true` 로 표시한다. `needs-user-input` 항목은 절대 교정 대상이 아니다 — 그대로 clarification 으로 승격된다.
|
|
230
|
+
|
|
232
231
|
### Final-verification verdict token contract (BLOCKING)
|
|
233
232
|
|
|
234
233
|
When the run's `task-type` is `final-verification`, the report's `## 7. Final Verdict` table MUST contain a `Verdict Token` row whose value is **exactly one of** the literal strings below. The `release-handoff` profile reads this row as its entry gate; any other value blocks the next phase.
|
|
@@ -247,13 +246,13 @@ The final-report template `templates/reports/final-report.template.md` Section 7
|
|
|
247
246
|
|
|
248
247
|
When the run's `task-type` is `release-handoff`, the final report MUST include Section `## 5.6 Release Handoff Deliverables` with all eight sub-sections (`5.6.1` Source Verification Report, `5.6.2` Feature Branch & Working-Tree State, `5.6.3` User Selections, `5.6.4` Executed Commands, `5.6.5` Commit List, `5.6.6` Merge Conflict Probe, `5.6.7` Pull Request Outcome, `5.6.8` Routing Recommendation). Every entry is dictated by the lead's recorded git/gh command log and the user's verbatim answers to the H1/H2/H3 menu prompts. H1 choices are `local checkout`, `push + PR`, or `skip`; release-handoff records existing implementation commits and MUST NOT create new commits. If the user picked `skip` (H1) or `cancel` (H3), keep 5.6.3 populated but leave 5.6.4–5.6.6 explicitly empty per the template's empty-state lines.
|
|
249
248
|
|
|
250
|
-
**Single-lead authorship (release-handoff only):** release-handoff has no worker roster
|
|
249
|
+
**Single-lead authorship (release-handoff only):** release-handoff has no worker roster. The lead authors the final-report file directly — there is no `Report writer worker` dispatch to perform in Phase 6, no resume-safe dispatch concern, and no mandatory worker-results file for a report-writer role. The rest of this contract's dispatch / resume / fallback machinery applies ONLY when `Report writer worker` is in the roster (i.e. every task-type other than `release-handoff`).
|
|
251
250
|
|
|
252
251
|
The final-report template `templates/reports/final-report.template.md` Section 5.6 already encodes this contract — copy that block verbatim and fill in. For non-`release-handoff` runs, omit Section 5.6 entirely.
|
|
253
252
|
|
|
254
253
|
### Mandatory worker-results file (BLOCKING)
|
|
255
254
|
|
|
256
|
-
You (the report-writer
|
|
255
|
+
You (the report-writer worker) MUST also write a worker-results audit file at the path the lead provides as `**Worker Result Path:**`, defaulting to:
|
|
257
256
|
|
|
258
257
|
```
|
|
259
258
|
runs/<task-type>/worker-results/report-writer-worker-<task-type>-<seq>.md
|
|
@@ -9,15 +9,15 @@
|
|
|
9
9
|
|
|
10
10
|
## Team Structure
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Okstra tasks use one lead plus the exact worker assignments selected in the prepared bundle. The lead runtime and the worker providers are independent axes.
|
|
13
13
|
|
|
14
14
|
### Role Definitions
|
|
15
15
|
|
|
16
16
|
**All analysis workers (Claude / Codex / Antigravity) share an identical core responsibility.** Specialization is additive — it lives in optional Section 6 of the worker output, NOT in differentiated core questions. This is intentional: cross-verification only converges if all three workers are answering the same questions against the same brief. Disjoint per-worker scopes produce union-of-perspectives, not triangulation.
|
|
17
17
|
|
|
18
|
-
| Role | Core responsibility | Specialization lens (Section 6 only) |
|
|
18
|
+
| Role | Core responsibility | Specialization lens (Section 6 only) | Assignment key | Notes |
|
|
19
19
|
|------|------|------|---------------|------|
|
|
20
|
-
|
|
|
20
|
+
| Lead | orchestration + convergence supervision + final-report review/approval | runtime-specific | -- | Does not author the final report when `Report writer worker` is rostered |
|
|
21
21
|
| Claude worker | Answer every brief question across feasibility, requirement interpretation, hidden assumptions, and alternatives — with file:line evidence | broad reasoning depth, hidden assumptions, execution-risk surfacing | claude-worker | `agents/workers/claude-worker.md` |
|
|
22
22
|
| Codex worker | Same core responsibility as Claude worker — identical questions, identical sections 1–5 | implementation realism, code-path implications, edge cases, technical trade-offs | codex-worker | generated from `agents/workers/_cli-wrapper-template.md` + `codex-worker.params.json` |
|
|
23
23
|
| Antigravity worker | Same core responsibility as Claude worker — identical questions, identical sections 1–5 | requirement interpretation, consistency, safety, alternative viewpoints | antigravity-worker | generated from `agents/workers/_cli-wrapper-template.md` + `antigravity-worker.params.json` |
|
|
@@ -31,9 +31,7 @@ okstra tasks are always operated using the `Claude lead` + required worker team
|
|
|
31
31
|
|
|
32
32
|
1. `resultContract.requiredWorkerRoles` in `task-manifest.json` (and the lead model metadata) is the canonical source. There is no role-level fallback — a missing assignment is a manifest defect, not a license to invent one.
|
|
33
33
|
2. If `modelExecutionValue` differs from `model`, use `modelExecutionValue` during execution.
|
|
34
|
-
3. **
|
|
35
|
-
4. **`modelExecutionValue` → Agent `model:` family token.** The Agent tool's `model` parameter accepts family tokens only — `fable` / `opus` / `sonnet` / `haiku` (an exact version such as `claude-opus-4-7` is NOT a valid value). Map by prefix: a `modelExecutionValue` of `fable*` / `claude-fable*` → `"fable"`, `opus*` / `claude-opus*` → `"opus"`, `sonnet*` / `claude-sonnet*` → `"sonnet"`, `haiku*` / `claude-haiku*` → `"haiku"`. This enforces the assignment at **family granularity** (fable vs opus vs sonnet vs haiku); the exact version within a family is still inherited from the lead session and cannot be pinned via this parameter.
|
|
36
|
-
5. **Codex / Antigravity wrappers are out of scope for the Agent `model:` rule.** `Codex worker` / `Antigravity worker` subagents are Claude wrappers that shell out to an external CLI; the role's `modelExecutionValue` is already applied via the CLI's own `--model <modelExecutionValue>` argument (see `agents/workers/_cli-wrapper-template.md`). The Agent `model:` parameter for these wrappers would only set the wrapper's own orchestration model, not the external CLI's model — leave it at `inherit` and do NOT map it from `modelExecutionValue`.
|
|
34
|
+
3. **Dispatch-time enforcement (BLOCKING).** The selected adapter receives each role's `modelExecutionValue` and must apply its documented native mapping. The adapter must fail before dispatch if it cannot apply the exact assignment; it must not inherit the lead model, change provider, or choose a nearby alias silently.
|
|
37
35
|
|
|
38
36
|
### Dynamic Worker Role Determination
|
|
39
37
|
|
|
@@ -48,10 +46,10 @@ Only workers selected from `recommendedWorkers` in `task-manifest.json` and `res
|
|
|
48
46
|
|
|
49
47
|
## Operating Rules
|
|
50
48
|
|
|
51
|
-
0. **
|
|
52
|
-
1.
|
|
49
|
+
0. **Adapter-owned dispatch (BLOCKING).** Every worker start, await, retry, and shutdown goes through the selected runtime adapter. Core state records the outcome but never guesses a host primitive.
|
|
50
|
+
1. The lead is responsible for orchestration, convergence supervision, and final-report review/approval. It never overrides worker analysis and never bypasses a rostered Report writer worker.
|
|
53
51
|
2. `Report writer worker` is NOT an analysis worker. It is excluded from Phase 4/5 (initial analysis) and Phase 5.5 (convergence re-verification). It is spawned only in Phase 6 and is the **author** of the final-report file at `runs/<task-type>/reports/final-report-<task-type>-<seq>.md`.
|
|
54
|
-
3. When `Report writer worker` is in the roster, Lead MUST dispatch it in Phase 6. The only legal lead-authored fallback is when a dispatch was attempted and recorded a terminal status of `error` / `timeout` / `not-run` with a concrete logged reason. Speculative reasons such as "session resume constraint" or "team is no longer alive" are NOT valid —
|
|
52
|
+
3. When `Report writer worker` is in the roster, Lead MUST dispatch it in Phase 6. The only legal lead-authored fallback is when a dispatch was attempted and recorded a terminal status of `error` / `timeout` / `not-run` with a concrete logged reason. Speculative reasons such as "session resume constraint" or "team is no longer alive" are NOT valid — `dispatch_worker` can start a fresh one-shot assignment through the selected adapter.
|
|
55
53
|
4. The assigned model for each role is maintained based on `resultContract.requiredWorkerRoles` in task-manifest.json and the lead model metadata.
|
|
56
54
|
5. Required roles must not be replaced by unnamed generic parallel workers.
|
|
57
55
|
6. Before dispatching any required worker, persist the exact worker prompt to the assigned current-run prompt history path under `runs/<task-type>/prompts/`.
|
|
@@ -101,8 +99,6 @@ Audience-scoped file enumeration (performance optimization — mandatory):
|
|
|
101
99
|
| Report writer worker (Phase 6) | task-brief, analysis-profile, analysis-material, reference-expectations, clarification-response (if carry-in), **plus** the instruction-set-local `final-report-template.md` (phase-stripped) and `final-report-schema.json` (per-task-type excerpt) — NOT the full `templates/reports/...` / `schemas/...` sources |
|
|
102
100
|
| Reverify dispatches | none — the lead provides only the items to reverify |
|
|
103
101
|
|
|
104
|
-
Asymmetry note: `claude-worker` runs in-process and the Agent SDK auto-loads its agent definition; lead's dispatch prompt body for claude-worker can therefore be shorter than for codex/antigravity. The Worker Preamble pointer is still emitted for all three so the contract source is identical regardless of dispatch path.
|
|
105
|
-
|
|
106
102
|
## Terminal Statuses
|
|
107
103
|
|
|
108
104
|
Terminal statuses that can be recorded for a worker:
|
|
@@ -114,58 +110,13 @@ Terminal statuses that can be recorded for a worker:
|
|
|
114
110
|
| `error` | Execution error, reason recorded; prompt history file must exist |
|
|
115
111
|
| `not-run` | Not executed, reason recorded |
|
|
116
112
|
|
|
117
|
-
## Worker-completion detection
|
|
118
|
-
|
|
119
|
-
**SSOT.** This section is the single source of truth for how Lead detects worker completion across all phases and all worker kinds (Claude teammate, Codex / Antigravity wrappers). Other documents (`prompts/lead/okstra-lead-contract.md`, `report-writer`, `convergence`) reference this section by name; they MUST NOT restate the algorithm.
|
|
120
|
-
|
|
121
|
-
Lead dispatches workers asynchronously: an `Agent` call with `run_in_background: true` returns `Spawned successfully` **immediately** — that ack is NOT a completion. Lead MUST NOT treat the spawn ack as completion, and MUST NOT end its turn with a prose "waiting for ..." statement (that path stalls the run — the Agent Teams idle-notification is experimental and can be dropped, leaving Lead parked until the user manually nudges it). Instead:
|
|
122
|
-
|
|
123
|
-
For `tmux-pane` backend runs, Lead MUST use exactly one background wait command instead of raw result-file polling:
|
|
124
|
-
|
|
125
|
-
```bash
|
|
126
|
-
okstra team await --project-root <dir> --run-manifest <path>
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
This command checks both result files and wrapper status sidecars. Raw result-file-only polling is forbidden for `tmux-pane` because pane creation and file presence alone are not terminal worker completion.
|
|
130
|
-
|
|
131
|
-
For the Claude Code `team` backend, use the self-scheduled polling protocol below.
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
1. Record the dispatched workers' Result Paths as the **pending set** (resolved to absolute from each launch prompt's `**Result Path:**` anchor header against `**Project Root:**`; the same paths recorded in run-manifest / team-state).
|
|
136
|
-
2. Arm a SINGLE self-wakeup: one `Bash(run_in_background: true)` poll covering ALL dispatched workers (not one background task per worker):
|
|
137
|
-
|
|
138
|
-
```bash
|
|
139
|
-
deadline=$((SECONDS + <per-worker-deadline-seconds>))
|
|
140
|
-
until [ -f "<result_A>" ] && [ -f "<result_B>" ]; do
|
|
141
|
-
[ $SECONDS -ge $deadline ] && { echo "POLL_TIMEOUT"; exit 1; }
|
|
142
|
-
sleep 5
|
|
143
|
-
done
|
|
144
|
-
echo "ALL_WORKERS_DONE"
|
|
145
|
-
```
|
|
113
|
+
## Worker-completion detection
|
|
146
114
|
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
- the poll exited `POLL_TIMEOUT` for a worker past its deadline → record terminal status `timeout` for that worker, remove it from the pending set, then redispatch-once (per "Lead Redispatch Policy on Result-Missing" below) or proceed.
|
|
153
|
-
- any error / abort path → no zombie schedule exists, because the background task is self-terminating.
|
|
154
|
-
6. **Per-worker soft timeout.** Use 2× the task-type expected per-worker duration in the table below as `<per-worker-deadline-seconds>`. The background poll's `deadline` IS the lead-side worker soft-timeout safety net.
|
|
155
|
-
|
|
156
|
-
| Task type | Expected per-worker | Deadline (2×) |
|
|
157
|
-
|---|---|---|
|
|
158
|
-
| requirements-discovery | 10 min | 20 min |
|
|
159
|
-
| error-analysis | 15 min | 30 min |
|
|
160
|
-
| implementation-planning | 20 min | 40 min |
|
|
161
|
-
| implementation | 20 min | 40 min |
|
|
162
|
-
| final-verification | 10 min | 20 min |
|
|
163
|
-
|
|
164
|
-
Relationship to the Codex / Antigravity wrapper polling contract: that contract (in the errors-sidecar section below) governs how a *wrapper subagent* waits on its own external CLI via `BashOutput`. This section governs how *Lead* waits on the worker subagents themselves. The two compose — Lead's background poll watches the result files; each wrapper independently watches its CLI — and neither imposes a timeout on the other (see "No external timeout on wrapper subagents").
|
|
165
|
-
|
|
166
|
-
### Claude-worker heartbeat staleness (lead-side liveness check)
|
|
167
|
-
|
|
168
|
-
The claude-worker appends a `- PROGRESS: <stage> <ISO-UTC>` line to its audit sidecar (`runs/<task-type>/worker-results/claude-worker-audit-<task-type>-<seq>.md`) at least every 5 minutes (`agents/workers/claude-worker.md` "Heartbeat"). While the result file is still pending, if that sidecar is absent or its mtime is >5 minutes stale, treat the dispatch as `timeout` and redispatch once with a byte-identical prompt (one-retry budget shared with "Lead Redispatch Policy on Result-Missing" below); after a second silent hang, record terminal status `timeout` with the missing-sidecar reason in team-state. The heartbeat is an auxiliary liveness signal layered on top of the completion protocol above — the authoritative completion signal remains the result file — and a missing sidecar after the result file appears is itself a contract violation per the heartbeat rule.
|
|
115
|
+
1. Build the pending set from the manifest-assigned completion paths.
|
|
116
|
+
2. Call `await_workers(handles)` through the selected adapter.
|
|
117
|
+
3. On every wake/return, read terminal dispatch records and verify every required completion path.
|
|
118
|
+
4. A process exit, spawn acknowledgement, pane creation, or Result Path alone is insufficient when the backend contract requires additional status/audit artifacts.
|
|
119
|
+
5. Apply the single shared retry budget through `redispatch_worker`; after the second failure, record the terminal status and proceed with reduced confidence.
|
|
169
120
|
|
|
170
121
|
## Lead Redispatch Policy on Result-Missing
|
|
171
122
|
|
|
@@ -179,12 +130,12 @@ After each worker subagent returns (regardless of role), Lead MUST verify the ca
|
|
|
179
130
|
|
|
180
131
|
**One-retry policy:**
|
|
181
132
|
|
|
182
|
-
1. On the FIRST result-missing trigger for a given role within a single run, Lead MUST
|
|
133
|
+
1. On the FIRST result-missing trigger for a given role within a single run, Lead MUST call `redispatch_worker` with the byte-identical prompt — same `**Result Path:**`, same `**Prompt History Path:**`, same model assignment, and same adapter-native assignment identity. The redispatch counts as a second attempt against the existing role slot; do NOT create a new role-id, do NOT change the result file path, do NOT switch to a different model as a "workaround".
|
|
183
134
|
2. If the SECOND attempt also fails the same check, Lead records the role's terminal status as `error` with `--message "result-missing after 1 retry"` and proceeds to Phase 5.5 / Phase 6 with the remaining workers' results. Lead MUST NOT retry a third time — convergence and the report writer are designed to operate on reduced-confidence single-or-two-analyser mode when one role is absent (`prompts/lead/okstra-lead-contract.md` "If only one worker result is usable: reduced-confidence synthesis").
|
|
184
135
|
3. The retry counter is **per-run, per-role** and is NOT preserved across runs. A subsequent okstra run for the same task-key starts each role's counter fresh.
|
|
185
136
|
4. Convergence reverify rounds (Phase 5.5) inherit the same one-retry budget — a reverify dispatch that triggers result-missing may be re-dispatched once.
|
|
186
137
|
|
|
187
|
-
**Logging.** Lead records the first attempt's `cli-failure` (already emitted by the wrapper sub-agent) as-is. The retry, on success, is logged via the normal worker-completion path; on failure (second `*_RESULT_MISSING`), Lead records a single `contract-violation` entry with `--message "result-missing after 1 retry"` referencing both
|
|
138
|
+
**Logging.** Lead records the first attempt's `cli-failure` (already emitted by the wrapper sub-agent) as-is. The retry, on success, is logged via the normal worker-completion path; on failure (second `*_RESULT_MISSING`), Lead records a single `contract-violation` entry with `--message "result-missing after 1 retry"` referencing both adapter dispatch-attempt ids and prompt-history paths.
|
|
188
139
|
|
|
189
140
|
**Diagnostic sidecar (advisory).** Both codex/antigravity wrappers also write a heartbeat sidecar at `<prompt-path>.status.json` recording `started_ts`, `ended_ts`, `exit_code`, `duration_ms`, and the canonical `log_path` (see `scripts/okstra-wrapper-status.py` for the schema). Lead MAY read this sidecar when deciding whether the first attempt actually launched the CLI (stage=`exited`, `exit_code=0`, non-zero `duration_ms`) versus failed before reaching it (sidecar absent, or stage=`started` with no exit fields). The sidecar is best-effort — its absence is NOT by itself a reason to skip the retry; the canonical trigger remains the missing result file.
|
|
190
141
|
|
|
@@ -310,7 +261,7 @@ Schema:
|
|
|
310
261
|
```
|
|
311
262
|
|
|
312
263
|
Workers MUST omit `source` / `recordedAt` / `agent` / `agentRole` / `model` /
|
|
313
|
-
`taskKey`.
|
|
264
|
+
`taskKey`. The lead fills those in when dumping the sidecar to the
|
|
314
265
|
run-level errors log (`runs/<task-type>/logs/errors-<task-type>-<seq>.jsonl`)
|
|
315
266
|
via `okstra error-log append-from-worker`.
|
|
316
267
|
|
|
@@ -326,13 +277,13 @@ omits either header, the worker MUST return `<WORKER>_ERRORS_PATH_MISSING`
|
|
|
326
277
|
without proceeding.
|
|
327
278
|
|
|
328
279
|
- `cli-failure` events are recorded by the wrapper subagent itself (Codex / Antigravity), but **directly to the run-level error log** via `okstra error-log append-observed --error-type cli-failure ...` — NOT via the sidecar. The sidecar is an in-process tool-failure channel only.
|
|
329
|
-
- **Wrapper invocation arity.** Both `okstra-codex-exec.sh` and `okstra-antigravity-exec.sh` accept four required positional arguments plus an optional fifth `<role>`: `<project-root> <model> <prompt-path> <worktree-path> [<role>]`. The fourth (worktree) argument is **mandatory for implementation phase** and optional otherwise. For codex it becomes `--add-dir <worktree>` (sandbox write access); for antigravity 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. The optional fifth `<role>` is folded into both the caller (worker) pane title `<cli>-<role>-<pid>` and the sibling trace-pane title `<cli>-<role>-<pid>-tail` (e.g. `codex-executor-93421` ↔ `codex-executor-93421-tail`). `<pid>` is the wrapper's own PID and disambiguates concurrent dispatches of the same role. The
|
|
330
|
-
- **Background dispatch + polling contract (Codex / Antigravity wrappers).** Both wrapper subagents MUST
|
|
331
|
-
- Successful completion: return the wrapper's accumulated stdout from the
|
|
332
|
-
- Non-zero `exit_code
|
|
333
|
-
- Polling cap reached:
|
|
334
|
-
-
|
|
335
|
-
- **No external timeout on wrapper subagents.** The codex/antigravity wrapper subagent's polling loop (with optional mtime grace) is the SINGLE timeout authority for its dispatch. Lead MUST NOT impose a separate `
|
|
280
|
+
- **Wrapper invocation arity.** Both `okstra-codex-exec.sh` and `okstra-antigravity-exec.sh` accept four required positional arguments plus an optional fifth `<role>`: `<project-root> <model> <prompt-path> <worktree-path> [<role>]`. The fourth (worktree) argument is **mandatory for implementation phase** and optional otherwise. For codex it becomes `--add-dir <worktree>` (sandbox write access); for antigravity 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. The optional fifth `<role>` is folded into both the caller (worker) pane title `<cli>-<role>-<pid>` and the sibling trace-pane title `<cli>-<role>-<pid>-tail` (e.g. `codex-executor-93421` ↔ `codex-executor-93421-tail`). `<pid>` is the wrapper's own PID and disambiguates concurrent dispatches of the same role. The selected adapter maps the functional assignment identity to this value; when the value is absent, pass the literal `worker` (the wrapper also defaults to `worker` if the argument is omitted).
|
|
281
|
+
- **Background dispatch + polling contract (Codex / Antigravity wrappers).** Both wrapper subagents MUST start their CLI through the selected adapter's asynchronous execution mapping and await the same handle until it reports terminal completion, capped at 30 minutes (1800s) of wall-clock elapsed time. The adapter's await operation is the wait primitive; do not add a standalone sleep or build shorter-sleep loops to bypass a host constraint. This rule applies in **every phase**. Recording responsibilities:
|
|
282
|
+
- Successful completion: return the wrapper's accumulated stdout from the terminal await result. No log entry.
|
|
283
|
+
- Non-zero `exit_code`: record a `cli-failure` to the run-level error log with the real `exit_code` and observed `duration-ms`.
|
|
284
|
+
- Polling cap reached: perform a one-shot **mtime-grace check** on the wrapper's live log (`<prompt>.log`). If the log was written within the last 90 seconds and grace has not yet been applied, extend the cap from 1800s to 2100s and continue awaiting. Otherwise call the selected adapter's termination mapping, record `cli-failure` with `--exit-code 124 --duration-ms <observed_ms> --message "<wrapper> exceeded polling cap (grace=<applied|not-applied>, last_mtime_age=<n>s)"`, then return the language-specific `*_CLI_TIMEOUT` sentinel.
|
|
285
|
+
- The selected adapter owns runtime-session accounting for the full wrapper window; core retains only the observed start/end event boundaries.
|
|
286
|
+
- **No external timeout on wrapper subagents.** The codex/antigravity wrapper subagent's polling loop (with optional mtime grace) is the SINGLE timeout authority for its dispatch. Lead MUST NOT impose a separate `dispatch_worker` timeout, an outer `Bash` wall-clock deadline, or any other mechanism that terminates the subagent before its own polling cap is reached. Doing so reproduces the historical failure mode that motivated this rule: Lead aborts the subagent at e.g. 18 minutes, the subagent returns nothing, and Lead classifies the role as "no response" while the underlying CLI was actively working. The wrapper's polling cap (30min + optional 5min grace) is calibrated so that, combined with Lead's redispatch policy (see "Lead Redispatch Policy on Result-Missing"), a recoverable single-run failure costs at most ~70 minutes of wall-clock — predictable enough to plan around. If a specific run requires a tighter cap, lower it in the wrapper subagent's polling contract (single source of truth), NOT by layering Lead-side timeouts.
|
|
336
287
|
- `contract-violation` events (C) are recorded by Lead via `okstra error-log append-observed --error-type contract-violation ...` after inspecting worker outputs.
|
|
337
288
|
- Lead's responsibility regarding the sidecar is to dump it to the run-level error log via `okstra error-log append-from-worker` after each worker terminates; Lead does not write into the sidecar.
|
|
338
289
|
|
|
@@ -342,7 +293,7 @@ without proceeding.
|
|
|
342
293
|
2. Re-verification workers follow a constrained response format (verdict + brief explanation).
|
|
343
294
|
3. Workers cannot vote on their own findings (only verify other workers’ work).
|
|
344
295
|
4. The `report writer worker` does not participate in re-verification voting. It is responsible only for generating the final report.
|
|
345
|
-
5. Division of labor: the
|
|
296
|
+
5. Division of labor: the lead performs **finding-to-finding matching** (deciding which worker-A finding maps to which worker-B finding for cross-review) and mediates the round protocol; **workers cast the AGREE / DISAGREE / SUPPLEMENT votes** that determine consensus. The lead does NOT vote on substance and does NOT collapse worker disagreements by fiat — disagreements flow into the `contested` / `partial-consensus` classifications defined in `prompts/lead/convergence.md`.
|
|
346
297
|
6. Batch processing is performed with one spawn per worker per round (not one spawn per finding).
|
|
347
298
|
7. These rules do not apply if Convergence is disabled.
|
|
348
299
|
|
|
@@ -382,22 +333,7 @@ The header is followed by the standard worker output contract sections (Findings
|
|
|
382
333
|
|
|
383
334
|
## Usage Tracking
|
|
384
335
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
### How to Collect
|
|
388
|
-
|
|
389
|
-
At the **start of Phase 7** (persistence), run the helper via the okstra CLI with the path to this run's `team-state.json`. Substitute `<runDirectoryPath>` with a literal absolute path (no shell variables, no `$(...)`) so the `Bash(okstra:*)` permission match holds:
|
|
390
|
-
|
|
391
|
-
```bash
|
|
392
|
-
okstra token-usage /abs/path/to/run/state/team-state-<task-type>-<seq>.json --write --summary
|
|
393
|
-
```
|
|
394
|
-
|
|
395
|
-
`okstra token-usage` is a thin Node-side wrapper around the python helper installed at `~/.okstra/bin/okstra-token-usage.py`. Calling the python script directly with `python3 "$HOME/..."` is forbidden — the `$HOME` expansion breaks the literal-token permission match and forces a confirmation prompt every call.
|
|
396
|
-
|
|
397
|
-
The script reads:
|
|
398
|
-
- `~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl` for the lead and every Claude-side worker (Claude worker, Report writer worker, plus the Claude wrappers around Codex/Antigravity workers). Sessions are discovered by the recorded team-state `teamName`, lead is identified by `lead.sessionId`, and other workers are identified by `agentName` (e.g. `claude-worker`, `codex-worker`, `antigravity-worker`, `report-writer`). **For this `agentName` match to work, Lead MUST set the Agent `name` arg to `<workerId>-worker` on every dispatch** (see [agents okstra-lead-contract.md Phase 4 — "Agent `name` on dispatch"](./okstra-lead-contract.md)); a worker dispatched without `name` carries no `agentName`, so the collector cannot attribute its session and records it `unavailable` (now surfaced as a `usageSummary.unattributedTeamSessions` entry rather than dropped silently).
|
|
399
|
-
- `~/.agent/sessions/Y/M/D/rollout-*.jsonl` for the underlying Codex CLI session (matched by `cwd` and timestamp window of the wrapper subagent). Last `event_msg.token_count.total_token_usage.total_tokens` is the session total.
|
|
400
|
-
- `~/.antigravity/tmp/<project>/chats/session-*.json` for the underlying Antigravity CLI session. Sum of per-message `tokens.total`.
|
|
336
|
+
At the start of Phase 7, call `collect_usage(source)` through the selected adapter. `source` is `team-state.leadAdapter.sessionAccounting` plus the dispatch backend's recorded event source. Never read another runtime's session store as a fallback. Persist the resulting `leadUsage`, worker usage, and `usageSummary` in team-state before report substitution and cleanup.
|
|
401
337
|
|
|
402
338
|
### Resulting team-state shape
|
|
403
339
|
|
|
@@ -411,8 +347,8 @@ The script reads:
|
|
|
411
347
|
"cacheReadTokens": 9842949,
|
|
412
348
|
"toolUses": 47,
|
|
413
349
|
"durationMs": 7253000,
|
|
414
|
-
"source": "
|
|
415
|
-
"sessionId": "<
|
|
350
|
+
"source": "<adapter-accounting-source>",
|
|
351
|
+
"sessionId": "<adapter-session-id>",
|
|
416
352
|
"collectedAt": "<utc>"
|
|
417
353
|
},
|
|
418
354
|
"workers": [
|
|
@@ -420,11 +356,8 @@ The script reads:
|
|
|
420
356
|
"workerId": "codex",
|
|
421
357
|
"usage": {
|
|
422
358
|
"totalTokens": 2274011,
|
|
423
|
-
"source": "
|
|
424
|
-
"sessionId": "<
|
|
425
|
-
"cliSessionPaths": ["/Users/.../.agent/sessions/.../rollout-*.jsonl"],
|
|
426
|
-
"cliTotalTokens": 5261833,
|
|
427
|
-
"cliModel": "gpt-5.5"
|
|
359
|
+
"source": "<dispatch-event-source>",
|
|
360
|
+
"sessionId": "<worker-session-id>"
|
|
428
361
|
}
|
|
429
362
|
}
|
|
430
363
|
],
|
|
@@ -440,20 +373,14 @@ The script reads:
|
|
|
440
373
|
|
|
441
374
|
### Notes
|
|
442
375
|
|
|
443
|
-
-
|
|
444
|
-
-
|
|
445
|
-
-
|
|
446
|
-
- If the lead jsonl is missing (e.g. if `lead.sessionId` was never persisted), the script records an "unavailable" block with the searched path. Always populate `lead.sessionId` in team-state at Phase 3 — the okstra.sh launcher already passes it as `CLAUDE_SESSION_ID`.
|
|
447
|
-
- Convergence re-verification agents are dispatched as fresh subagents under the same `teamName` and will be discovered automatically. If you want them split out, set a distinct `agentName` on dispatch and post-process.
|
|
448
|
-
|
|
449
|
-
### Lead Duration
|
|
450
|
-
|
|
451
|
-
`durationMs` for the lead is computed from the first to last timestamp in the lead jsonl. If you need wall-clock from run-start to report-completion instead, override `leadUsage.durationMs` after running the script.
|
|
376
|
+
- If a worker has status `not-run`, `collect_usage` records an `unavailable` block.
|
|
377
|
+
- Adapter-native and underlying provider usage are separate accounting sources and must not be added together without an explicit pricing model.
|
|
378
|
+
- `durationMs` uses the selected adapter's documented session boundary.
|
|
452
379
|
|
|
453
380
|
## Team State Persistence
|
|
454
381
|
|
|
455
382
|
Information to be recorded in the team-state JSON file:
|
|
456
|
-
- `teamName` — record the audit label set in Phase 3 (`okstra-<task-key>` + stage suffix)
|
|
383
|
+
- `teamName` — record the manifest-provided audit label set in Phase 3 (`okstra-<task-key>` + stage suffix) and preserve that exact value consistently within the run. The selected adapter owns any runtime-session matching derived from this audit field.
|
|
457
384
|
- Current status of each worker role
|
|
458
385
|
- Start/end times for each worker
|
|
459
386
|
- Prompt history path for each worker
|
|
@@ -30,7 +30,7 @@ profile document.
|
|
|
30
30
|
- This rule does NOT relax any phase-specific Forbidden actions list; safety rules in the per-profile document remain in force regardless of the user's authority.
|
|
31
31
|
- Anti-escalation rule (shared):
|
|
32
32
|
- treating "다음 단계 진행해" or equivalent user phrases as authorisation to start a *different* lifecycle phase is forbidden. The next phase begins only in a separate okstra run launched with the new `--task-type`. Per-profile documents may further restrict this within their own scope.
|
|
33
|
-
- Run-scoped
|
|
33
|
+
- Run-scoped worker-resource lifecycle (shared — run-start resource setup, per-batch cleanup, wrap-up resource disposition, run-end shutdown): these are lead-only operating duties and live in `prompts/lead/okstra-lead-contract.md` "Run-scoped worker-resource lifecycle". Profiles do not restate them.
|
|
34
34
|
- Brief handoff contract (shared — applies whenever the run consumes a task brief produced by `okstra-brief-gen`):
|
|
35
35
|
- the brief is a **pre-discovery artifact**: it converts a domain-reporter's words (non-expert *or* developer) into expert-consumable form so this and later phases can run with zero fill-in questions to the operator. The brief is **not** authoritative on solution decisions; it is authoritative on the reporter's intent.
|
|
36
36
|
- **Reporter confirmation precondition (BLOCKING)**: the brief's frontmatter carries `reporter-confirmations: <complete | partial | pending | skipped>` set by `okstra-brief-gen` Step 6.5. Every phase that consumes the brief MUST read this field before doing analysis. The handling matrix is:
|
|
@@ -71,7 +71,7 @@ profile document.
|
|
|
71
71
|
- each row's `Kind` column picks one of `{material, decision, data-point}`: `material` for files / snapshots / logs / screenshots the user must attach (the `User input` cell will hold a path or URL); `decision` for choices and yes/no confirmations only the user can make; `data-point` for a single number, ID, date, or short string the user can answer inline. A `decision` alternative must be a terminal choice the user can pick as-is; if acting on an alternative still requires the user to supply a concrete value (a path, string, number, or file), that value is its own `data-point` / `material` row — never phrase a data-entry action (e.g. "경로 구체화", "값 입력") as a selectable `decision` option, because the rendered `<select>` cannot capture the value the option demands. Items that mix "yes/no + file path if yes" are one row of `Kind=material` with the combined expectation written into `Expected form`.
|
|
72
72
|
- each row's `Blocks` column picks one of `{approval, next-phase, none}`. `approval` is reserved for items that gate an approval action, especially the `implementation-planning` `approved:` frontmatter flip; outside `implementation-planning`, unresolved brief reporter-confirmation rows use `next-phase` instead. `next-phase` blocks the next run from starting cleanly. `none` is informational/audit-only.
|
|
73
73
|
- write every entry in full, descriptive sentences that a non-developer can act on without further context. Avoid abbreviations and internal jargon. The `Statement` cell must state *what* is needed, *why* the answer / attachment changes the next step, and (for `material`) *where* the user can find it and *where* to place it. The `Expected form` cell must state the answer shape (예/아니오, 보기 중 하나, 숫자/날짜, 파일 경로, 짧은 서술 등); supply concrete option choices when applicable.
|
|
74
|
-
- if a phase requires a recommended answer, alternatives, or an evidence-check note, encode it inside the existing 4-column schema: put evidence notes in `Statement` as `Evidence checked: <path:line>` or `Evidence checked: none — <human-only reason>`, and put recommendations/options in `Expected form` as `Recommended: <answer> — <rationale>; Alternatives: <
|
|
74
|
+
- if a phase requires a recommended answer, alternatives, or an evidence-check note, encode it inside the existing 4-column schema: put evidence notes in `Statement` as `Evidence checked: <path:line>` or `Evidence checked: none — <human-only reason>`, and put recommendations/options in `Expected form` as `Recommended: (a) <answer> — <rationale>; Alternatives: (b) <option> (c) <option>`. The recommended answer is always the first option and MUST carry the `(a)` label; alternatives continue the same letter sequence from `(b)` (a lone alternative is `(b) <option>`, never restart at `(a)`), so the full option set reads `(a) (b) (c) …` in order and renders each as its own selectable option. Do **not** append a pick-one answer-space summary such as `(A / B 중 택1)` or `(… 중 택N)` to `<options>` — the rendered `<select>` already enforces single choice, and that annotation leaks verbatim into an option label. Do not add `Recommended`, `Evidence`, `Alternatives`, or `evidence-checked` columns, and do not break the merged record-meta cell back into separate columns.
|
|
75
75
|
- the same `final-report.md` file is the canonical artifact carried into the next run; the user appends answers inline before rerunning. The preferred turn-around is `scripts/okstra.sh --resume-clarification --task-key <project-id>:<task-group>:<task-id>` (opens the latest report in `$EDITOR`, then auto-reruns the same phase with `--clarification-response` carry-in). The lower-level form `--clarification-response <path>` remains available for scripted runs.
|
|
76
76
|
- if a clarification response was carried in for this run, render the conditional `## 0. Clarification Response Carried In From Previous Run` section (the template's `RENDER_IF` guard activates it), walk every `C-*` row of the prior report's `## 1. Clarification Items` table, reconcile each one against new evidence, and update its `Status` to `resolved` or `obsolete` before issuing the next decision/verdict. When no carry-in path was provided, omit the `## 0.` heading entirely — the validator fails reports that emit an empty Section 0 stub (e.g. "No prior clarification response was provided for this run.").
|
|
77
77
|
- Verdict Card (shared — applies to every final-report regardless of profile):
|
|
@@ -11,7 +11,7 @@ until Phase 5 ends, then drop from active context for Phase 6/7.
|
|
|
11
11
|
|
|
12
12
|
## Executor role binding (carried over from the thin core)
|
|
13
13
|
|
|
14
|
-
- **Executor dispatch labelling.**
|
|
14
|
+
- **Executor dispatch labelling.** The core functional role label is `<provider>-executor` (e.g. `codex-executor`). Provider, role, and model identity are owned by `prompts/lead/okstra-lead-contract.md` "Model assignments"; the selected runtime adapter owns provider-native dispatch-label mapping (including any `name` / `**Pane role:**` fields) and token-attribution wiring under its "Semantic operation mapping".
|
|
15
15
|
- The `Executor` (bound in `implementation.md` thin core) is the **only worker permitted to use Edit / Write / state-mutating Bash commands** on project files. All other workers run read-only. When the executor provider is `codex` or `antigravity`, the actual file mutation happens inside the executor CLI's own auto-edit mode (e.g. `codex exec --sandbox workspace-write`, antigravity's equivalent) — not through Claude-side Edit/Write tools — but the safety rules in this sidecar still apply identically.
|
|
16
16
|
- When the thin core's Task worktree block resolves status to `created` or `reused`, the Executor MUST run every Edit / Write / build / test / commit command with the worktree path 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 genuinely needed, treat it as a planning gap: record it in `Out-of-plan edits` and continue.
|
|
17
17
|
- **How to set cwd per Bash call**: the Claude Bash tool inherits its cwd from the lead session, which is NOT the worktree. To put cwd-sensitive toolchains (`cargo`, `npm`, `pnpm`, `bun`, `pytest`, `make`, `go`) into the worktree, prefix the command with `cd {{EXECUTOR_WORKTREE_PATH}} && ` inside the same Bash invocation — e.g. `cd {{EXECUTOR_WORKTREE_PATH}} && cargo test -p foo`. **Never wrap in `bash -lc "..."` or `bash -c "..."`** — the wrapper hides the leading `cd` token from Claude Code's permission auto-allow layer (causing prompts on every call) without any safety benefit. For tools that accept an explicit working-directory flag (`git -C <path>`, `cargo --manifest-path`, `pytest --rootdir`), prefer that form over the `cd && ` chain. Edit / Write / Read tool calls already use absolute paths and need no cwd handling. The codex / antigravity executor CLI wrappers (`okstra-codex-exec.sh -C`, `okstra-antigravity-exec.sh --include-directories`) already inject worktree cwd at the CLI layer, so this rule applies primarily to the Claude executor.
|