okstra 0.26.0 → 0.28.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 +15 -0
- package/README.md +15 -0
- package/docs/kr/architecture.md +2 -6
- package/docs/kr/cli.md +40 -6
- package/docs/kr/performance-improvement-plan-v2.md +23 -0
- package/docs/kr/performance-improvement-plan.md +22 -0
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/claude-worker.md +4 -3
- package/runtime/agents/workers/codex-worker.md +4 -3
- package/runtime/agents/workers/gemini-worker.md +4 -3
- package/runtime/agents/workers/report-writer-worker.md +7 -2
- package/runtime/bin/okstra.sh +0 -1
- package/runtime/prompts/launch.template.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +36 -4
- package/runtime/prompts/profiles/error-analysis.md +12 -0
- package/runtime/prompts/profiles/implementation-planning.md +20 -0
- package/runtime/prompts/profiles/requirements-discovery.md +20 -0
- package/runtime/python/lib/okstra/cli.sh +1 -7
- package/runtime/python/lib/okstra/globals.sh +0 -1
- package/runtime/python/lib/okstra/usage.sh +1 -4
- package/runtime/python/okstra_ctl/render.py +3 -0
- package/runtime/python/okstra_ctl/run.py +0 -6
- package/runtime/python/okstra_ctl/run_context.py +1 -1
- package/runtime/python/okstra_ctl/wizard.py +25 -2
- package/runtime/python/okstra_token_usage/blocks.py +5 -1
- package/runtime/python/okstra_token_usage/claude.py +16 -1
- package/runtime/python/okstra_token_usage/cli.py +9 -2
- package/runtime/python/okstra_token_usage/collect.py +17 -3
- package/runtime/python/okstra_token_usage/pricing.py +159 -24
- package/runtime/python/okstra_token_usage/report.py +32 -3
- package/runtime/skills/okstra-brief/SKILL.md +532 -65
- package/runtime/skills/okstra-context-loader/SKILL.md +25 -11
- package/runtime/skills/okstra-convergence/SKILL.md +38 -14
- package/runtime/skills/okstra-history/SKILL.md +68 -37
- package/runtime/skills/okstra-logs/SKILL.md +26 -4
- package/runtime/skills/okstra-report-finder/SKILL.md +49 -22
- package/runtime/skills/okstra-report-writer/SKILL.md +62 -65
- package/runtime/skills/okstra-run/SKILL.md +35 -34
- package/runtime/skills/okstra-schedule/SKILL.md +51 -20
- package/runtime/skills/okstra-setup/SKILL.md +31 -12
- package/runtime/skills/okstra-status/SKILL.md +20 -8
- package/runtime/skills/okstra-team-contract/SKILL.md +41 -25
- package/runtime/skills/okstra-time-summary/SKILL.md +53 -16
- package/runtime/templates/reports/final-report.template.md +227 -207
- package/runtime/templates/reports/settings.template.json +7 -4
- package/runtime/validators/lib/fixtures.sh +47 -2
- package/runtime/validators/lib/validate-assets.sh +50 -24
- package/runtime/validators/validate-brief.py +385 -0
- package/runtime/validators/validate-brief.sh +35 -0
- package/runtime/validators/validate-run.py +313 -1
- package/runtime/validators/validate-workflow.sh +7 -33
|
@@ -40,7 +40,8 @@ npx -y okstra@latest install
|
|
|
40
40
|
This single command populates everything the user needs:
|
|
41
41
|
|
|
42
42
|
- `~/.okstra/{lib/python, bin, version}` — python + bash runtime
|
|
43
|
-
- `~/.claude/skills/<name>/SKILL.md` — all
|
|
43
|
+
- `~/.claude/skills/<name>/SKILL.md` — all okstra skills (canonical count
|
|
44
|
+
in `~/.okstra/installed-skills.json`)
|
|
44
45
|
- `~/.okstra/installed-skills.json` — manifest for safe uninstall
|
|
45
46
|
|
|
46
47
|
The skill should run this even if `~/.okstra/version` already exists —
|
|
@@ -72,10 +73,11 @@ them from the env vars.
|
|
|
72
73
|
## Step 3: Resolve PROJECT_ROOT
|
|
73
74
|
|
|
74
75
|
```bash
|
|
75
|
-
okstra check-project --cwd "$(pwd)"
|
|
76
|
+
PROJECT_ROOT=$(okstra check-project --cwd "$(pwd)" | jq -r '.projectRoot')
|
|
76
77
|
```
|
|
77
78
|
|
|
78
|
-
The JSON includes `projectRoot` on success.
|
|
79
|
+
The JSON includes `projectRoot` on success. Bind it into the shell so
|
|
80
|
+
Step 4 onwards can expand `$PROJECT_ROOT`. On failure (`ok: false`,
|
|
79
81
|
`stage: "resolve"`) ask the user (`AskUserQuestion`, free text) for an
|
|
80
82
|
absolute project root and rerun with `--cwd <their answer>`.
|
|
81
83
|
|
|
@@ -96,7 +98,11 @@ so overwriting requires manually deleting the file first.
|
|
|
96
98
|
If the file does NOT exist, ask via `AskUserQuestion`:
|
|
97
99
|
|
|
98
100
|
- **Question**: `"Project id for okstra (e.g. INV-1234, my-app, okstra)"`
|
|
99
|
-
- **Validate**:
|
|
101
|
+
- **Validate**: the answer must be non-empty AND contain at least one
|
|
102
|
+
alphanumeric character. Re-ask on empty input — `okstra setup --yes`
|
|
103
|
+
with no `--project-id` exits 1 with
|
|
104
|
+
`error: --project-id is required (no existing project.json, not a TTY)`,
|
|
105
|
+
so passing the user's empty answer through is a silent failure path.
|
|
100
106
|
|
|
101
107
|
Then create the file:
|
|
102
108
|
|
|
@@ -104,6 +110,12 @@ Then create the file:
|
|
|
104
110
|
okstra setup --yes --project-root "$PROJECT_ROOT" --project-id "$PROJECT_ID"
|
|
105
111
|
```
|
|
106
112
|
|
|
113
|
+
> After this, **Steps 4.5–4.8 are all optional**. The built-in defaults
|
|
114
|
+
> work for most projects — skip straight to Step 5 (`doctor`) unless the
|
|
115
|
+
> user explicitly asks to customise worktree sync, declare QA commands,
|
|
116
|
+
> or register a PR-body template. Step 4.7 (settings.local.json symlink)
|
|
117
|
+
> is automatic; it is documented for diagnostic purposes only.
|
|
118
|
+
|
|
107
119
|
## Step 4.5 (optional): customise worktree sync dirs
|
|
108
120
|
|
|
109
121
|
Each okstra run provisions a task-scoped git worktree under
|
|
@@ -114,14 +126,14 @@ every task sees the shared state. The built-in default is
|
|
|
114
126
|
|
|
115
127
|
To override per-project, add a `worktreeSyncDirs` array to
|
|
116
128
|
`project.json`. Empty array disables the feature; the field is
|
|
117
|
-
preserved across the runtime's auto-upserts (
|
|
118
|
-
|
|
129
|
+
preserved across the runtime's auto-upserts (see Step 4.6 for the
|
|
130
|
+
canonical list of runtime-owned fields).
|
|
119
131
|
|
|
120
132
|
```json
|
|
121
133
|
{
|
|
122
134
|
"projectId": "...",
|
|
123
135
|
"projectRoot": "...",
|
|
124
|
-
"worktreeSyncDirs": [".project-docs", ".scratch", ".claude", "my-custom-dir"]
|
|
136
|
+
"worktreeSyncDirs": [".project-docs", ".scratch", "graphify-out", ".claude", "my-custom-dir"]
|
|
125
137
|
}
|
|
126
138
|
```
|
|
127
139
|
|
|
@@ -130,7 +142,7 @@ field → built-in default. Only edit when defaults don't cover the
|
|
|
130
142
|
project's working files (e.g. additional cache or local-config dirs
|
|
131
143
|
that must follow the executor into the worktree).
|
|
132
144
|
|
|
133
|
-
## Step 4.
|
|
145
|
+
## Step 4.6 (optional but recommended): declare project QA commands
|
|
134
146
|
|
|
135
147
|
`implementation`-phase verifiers run an independent QA gate over the
|
|
136
148
|
executor's diff and need a project-wide baseline of check-only
|
|
@@ -178,7 +190,7 @@ The field is preserved across the runtime's auto-upserts of
|
|
|
178
190
|
`updatedAt` are runtime-owned, so manual edits to `qaCommands`
|
|
179
191
|
survive every subsequent `okstra setup` / `okstra run` invocation.
|
|
180
192
|
|
|
181
|
-
## Step 4.
|
|
193
|
+
## Step 4.7 (automatic): project-local Claude settings symlink
|
|
182
194
|
|
|
183
195
|
`okstra setup` (and `okstra run` on its first invocation per project)
|
|
184
196
|
provisions `<PROJECT_ROOT>/.claude/settings.local.json` as a symlink to
|
|
@@ -209,7 +221,7 @@ To opt out (advanced): replace the symlink with a regular file. okstra
|
|
|
209
221
|
will detect that it is no longer a symlink on its next setup call and
|
|
210
222
|
back it up as `.bak.<timestamp>` rather than overwriting silently.
|
|
211
223
|
|
|
212
|
-
## Step 4.8 (optional): register a project PR body template
|
|
224
|
+
## Step 4.8 (optional, opt-in): register a project PR body template
|
|
213
225
|
|
|
214
226
|
`release-handoff` fills the PR body from a template. By default it uses the
|
|
215
227
|
bundled skill template at
|
|
@@ -217,6 +229,11 @@ bundled skill template at
|
|
|
217
229
|
want their own — e.g. the repo's `.github/PULL_REQUEST_TEMPLATE.md` — to
|
|
218
230
|
keep PRs consistent with what the team already merges manually.
|
|
219
231
|
|
|
232
|
+
**Pre-registration during setup is opt-in.** The same prompt is also
|
|
233
|
+
offered on the first `release-handoff` run in this project, so it is
|
|
234
|
+
safe to defer this step (`나중에`) — most users should skip unless they
|
|
235
|
+
explicitly want to lock the template in now.
|
|
236
|
+
|
|
220
237
|
Ask the user with `AskUserQuestion` (fixed options, NOT free text — file
|
|
221
238
|
path entry happens in the follow-up plain text prompt per the
|
|
222
239
|
okstra-run prompt convention):
|
|
@@ -269,6 +286,8 @@ Inform the user with a short summary:
|
|
|
269
286
|
|---|---|---|
|
|
270
287
|
| `command not found: npx` | Node missing | Install node 18+. |
|
|
271
288
|
| `okstra ensure-installed` keeps reinstalling | `~/.okstra/version` write fails (permissions) | Check `~/.okstra` ownership and writability. |
|
|
272
|
-
| `
|
|
273
|
-
| `projectId 불일치` | `project.json` already exists with a different id | Decide which id is canonical; manually
|
|
289
|
+
| `error: --project-id is required (no existing project.json, not a TTY)` | `okstra setup --yes` invoked without `--project-id`, or with empty answer to Step 4 prompt | Re-ask Step 4 and pass a non-empty id via `--project-id`. |
|
|
290
|
+
| `projectId mismatch` / `projectId 불일치` | `project.json` already exists with a different id | Decide which id is canonical; manually delete `<PROJECT_ROOT>/.project-docs/okstra/project.json` to re-register, or re-run with the existing id. |
|
|
291
|
+
| `EACCES` writing under `.project-docs/okstra/` | directory owned by another user (e.g. created by a previous root-shell run) | `chown -R "$USER" <PROJECT_ROOT>/.project-docs` or delete and let setup recreate. |
|
|
292
|
+
| `warning: failed to provision .claude/settings.local.json symlink` | a non-symlink `.claude/settings.local.json` already exists and the backup-and-replace step failed | Inspect `<PROJECT_ROOT>/.claude/settings.local.json{,.bak.*}`; manually merge project-specific rules, then re-run setup. |
|
|
274
293
|
| `npx okstra@latest install` succeeds but `doctor` shows FAIL | runtime/{python,bin,skills} sync not yet performed (pre-release package) | Use dev install: clone the repo and run `node bin/okstra install --link <repo>`. |
|
|
@@ -40,7 +40,7 @@ parse and reuse it instead of re-resolving in the steps below.
|
|
|
40
40
|
|
|
41
41
|
## Step 1: Overall Project Status
|
|
42
42
|
|
|
43
|
-
To view the overall project status,
|
|
43
|
+
To view the overall project status, read `.project-docs/okstra/discovery/task-catalog.json`. The catalog is the authoritative source — every field listed below (including `workStatus`, `workStatusUpdatedAt`, `workStatusNote`) is projected directly from each `task-manifest.json` by `scripts/okstra_ctl/render.py :: render_task_catalog_discovery`. Do NOT re-open individual manifests for the overview.
|
|
44
44
|
|
|
45
45
|
Extract the following fields from each task.
|
|
46
46
|
|
|
@@ -68,13 +68,19 @@ Sort by:
|
|
|
68
68
|
|
|
69
69
|
출력 형식:
|
|
70
70
|
|
|
71
|
+
The overview table is intentionally narrow so it renders cleanly in a terminal. Only six columns are shown; for any task that needs a closer look (phase state, routing, approval gate, last run status, resume path, etc.) tell the user to run `okstra status <task-key>` for the detail view in Step 2.
|
|
72
|
+
|
|
73
|
+
If `awaitingApproval` is true OR `routingStatus == "pending"`, append a `*` to the `Next` cell as a visual marker and explain the marker once below the table.
|
|
74
|
+
|
|
71
75
|
```markdown
|
|
72
76
|
## okstra Status — <project-id>
|
|
73
77
|
|
|
74
|
-
| # | Task Key | Category | Phase |
|
|
75
|
-
|
|
76
|
-
| 1 | proj:group:id | bugfix | error-analysis |
|
|
77
|
-
| 2 | proj:group:id2 | feature | requirements-discovery |
|
|
78
|
+
| # | Task Key | Category | Phase | workStatus | Next |
|
|
79
|
+
|---|----------|----------|-------|------------|------|
|
|
80
|
+
| 1 | proj:group:id | bugfix | error-analysis | in-progress | implementation-planning |
|
|
81
|
+
| 2 | proj:group:id2 | feature | requirements-discovery | done | pending-routing-decision* |
|
|
82
|
+
|
|
83
|
+
`*` = awaiting user approval or pending routing decision. Run `okstra status <task-key>` for details.
|
|
78
84
|
```
|
|
79
85
|
|
|
80
86
|
## Step 2: Specific Task Status
|
|
@@ -153,10 +159,12 @@ The status response always includes one of the following options:
|
|
|
153
159
|
2. **Restart current phase**
|
|
154
160
|
- Indicates whether the task can be re-run with the same `task-key` and the current `taskType`.
|
|
155
161
|
3. **Start next phase**
|
|
156
|
-
- If `workflow.nextRecommendedPhase` is one of `error-analysis`, `implementation-planning`, `final-verification`, or `release-handoff`, that phase is proposed as the next candidate for the Okstra run.
|
|
162
|
+
- If `workflow.nextRecommendedPhase` is one of `error-analysis`, `implementation-planning`, `implementation`, `final-verification`, or `release-handoff`, that phase is proposed as the next candidate for the Okstra run.
|
|
157
163
|
- If `nextRecommendedPhase` is `pending-release-handoff`, the prior `final-verification` run completed but its verdict still has to be inspected before entering `release-handoff` — surface this as a verdict-gated next step and present `release-handoff` as a candidate only when the cited verdict is `accepted`.
|
|
158
164
|
4. **Need more information**
|
|
159
165
|
- If `nextRecommendedPhase` is `pending-routing-decision` or `routingStatus` is `pending`, this indicates that additional information is required.
|
|
166
|
+
5. **Task complete (terminal)**
|
|
167
|
+
- If `nextRecommendedPhase` is `done-or-follow-up`, the task lifecycle has reached its terminal signal. This is **not** a "next phase" — do not propose a new Okstra run. Surface the latest report and ask the user whether any follow-up task should be opened separately.
|
|
160
168
|
|
|
161
169
|
## Step 4: Update workStatus
|
|
162
170
|
|
|
@@ -195,7 +203,7 @@ Accepted `<status>` values: `todo`, `in-progress`, `blocked`, `done`.
|
|
|
195
203
|
Stop without modifying any file.
|
|
196
204
|
- If no match → output `<TASK-ID>를 찾을 수 없습니다.` and stop.
|
|
197
205
|
|
|
198
|
-
3. **Open the matching `task-manifest.json`** at `.project-docs/okstra/tasks/<task-group-segment>/<task-id-segment>/task-manifest.json`.
|
|
206
|
+
3. **Open the matching `task-manifest.json`** at `.project-docs/okstra/tasks/<task-group-segment>/<task-id-segment>/task-manifest.json`. Assemble the path from the catalog entry's `taskGroupPathSegment` and `taskIdPathSegment` fields (these are the filesystem-safe segments emitted by the renderer) — NOT from the raw user-supplied `<task-group>` / `<task-id>` strings, which may differ in case or contain characters that were normalized when the manifest was created. As a defensive shortcut, prefer the `taskManifestPath` field directly when present.
|
|
199
207
|
|
|
200
208
|
4. **Update fields at the manifest root**:
|
|
201
209
|
- `workStatus` ← new status value
|
|
@@ -224,7 +232,7 @@ If `workStatus` is missing or empty in any manifest, infer the display value fro
|
|
|
224
232
|
|
|
225
233
|
| Manifest state | Inferred display |
|
|
226
234
|
|---|---|
|
|
227
|
-
| `currentStatus == "completed"` AND `workflow.nextRecommendedPhase
|
|
235
|
+
| `currentStatus == "completed"` AND `workflow.nextRecommendedPhase == "done-or-follow-up"` | `done` (inferred) |
|
|
228
236
|
| `currentStatus == "completed"` AND `workflow.currentPhaseState == "completed"` | `phase-done` (inferred) |
|
|
229
237
|
| `currentStatus == "contract-violated"` OR `workflow.currentPhaseState == "blocked"` | `blocked` (inferred) |
|
|
230
238
|
| anything else | `in-progress` (default) |
|
|
@@ -244,3 +252,7 @@ This skill updates `task-manifest.json` only. `discovery/task-catalog.json` may
|
|
|
244
252
|
- If there is no recent report, display `--`.
|
|
245
253
|
- If a specific task does not exist, explicitly state that it cannot be found based on `task-catalog.json`.
|
|
246
254
|
- If `awaitingApproval` is true, clearly indicate that the task is awaiting user approval.
|
|
255
|
+
|
|
256
|
+
## Out-of-Scope Backlog
|
|
257
|
+
|
|
258
|
+
- **Step 0 boilerplate duplication.** The `ensure-installed` + `paths --shell` + `check-project --json` preamble is byte-identical across every user-facing okstra skill. The Claude Code skill framework has no include/snippet mechanism today, so each skill duplicates the block. A future change should either (a) extract the preamble into a single `okstra preflight` subcommand the skill can call in one line, or (b) ship the block as a shared SKILL fragment if the framework gains include support. Not actionable inside this skill alone.
|
|
@@ -11,6 +11,8 @@ user-invocable: false
|
|
|
11
11
|
- When verifying worker team composition and operational rules
|
|
12
12
|
- When applying model assignment rules
|
|
13
13
|
|
|
14
|
+
**Not applicable to `release-handoff`** — that profile is lead-only and intentionally has no `Required workers:` block (see `prompts/profiles/release-handoff.md`). The worker-dispatch contract in this document does not engage during `release-handoff` runs.
|
|
15
|
+
|
|
14
16
|
## Team Structure
|
|
15
17
|
|
|
16
18
|
okstra tasks are always operated using the `Claude lead` + required worker team structure.
|
|
@@ -19,24 +21,27 @@ okstra tasks are always operated using the `Claude lead` + required worker team
|
|
|
19
21
|
|
|
20
22
|
**All analysis workers (Claude / Codex / Gemini) 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.
|
|
21
23
|
|
|
22
|
-
| Role | Core responsibility | Specialization lens (Section 6 only) |
|
|
23
|
-
|
|
24
|
-
| Claude lead | orchestration + convergence supervision + final-report review/approval | — |
|
|
25
|
-
| 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 |
|
|
26
|
-
| Codex worker | Same core responsibility as Claude worker — identical questions, identical sections 1–5 | implementation realism, code-path implications, edge cases, technical trade-offs |
|
|
27
|
-
| Gemini worker | Same core responsibility as Claude worker — identical questions, identical sections 1–5 | requirement interpretation, consistency, safety, alternative viewpoints |
|
|
28
|
-
| Report writer worker | **Authors** the final-report file in Phase 6. NOT an analysis worker. | — |
|
|
24
|
+
| Role | Core responsibility | Specialization lens (Section 6 only) | subagent_type | Notes |
|
|
25
|
+
|------|------|------|---------------|------|
|
|
26
|
+
| Claude lead | orchestration + convergence supervision + final-report review/approval | — | -- | Does NOT author the final-report file when `Report writer worker` is in the roster |
|
|
27
|
+
| 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/claude-worker.md` |
|
|
28
|
+
| 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 | `agents/codex-worker.md` |
|
|
29
|
+
| Gemini worker | Same core responsibility as Claude worker — identical questions, identical sections 1–5 | requirement interpretation, consistency, safety, alternative viewpoints | gemini-worker | `agents/gemini-worker.md` |
|
|
30
|
+
| Report writer worker | **Authors** the final-report file in Phase 6. NOT an analysis worker. | — | report-writer-worker | `agents/report-writer-worker.md`. Excluded from Phase 4/5 and convergence |
|
|
31
|
+
|
|
32
|
+
**Model assignment has no default.** The model for every role comes from `resultContract.requiredWorkerRoles[*].modelExecutionValue` in `task-manifest.json` (and lead model metadata). There is no per-role hard-coded fallback — see "Model Assignment Rules" below.
|
|
29
33
|
|
|
30
34
|
**Dispatch-prompt invariant (BLOCKING).** Lead's dispatch prompt body for Claude / Codex / Gemini workers MUST be byte-identical except for the role label and any wrapper-specific path headers (e.g. `**Worktree:**`, `**Errors sidecar path:**`). Lead MUST NOT bias the brief by inserting per-worker emphasis sentences ("you focus on X") into the body. Bias-by-prompt reproduces the historical failure mode where Claude commented only on assumptions, Codex only on code paths, and Gemini only on requirements — leaving convergence with nothing to converge on.
|
|
31
35
|
|
|
32
36
|
### Model Assignment Rules
|
|
33
37
|
|
|
34
|
-
1.
|
|
35
|
-
2. If
|
|
36
|
-
3. If `modelExecutionValue` differs from `model`, use `modelExecutionValue` during execution.
|
|
38
|
+
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.
|
|
39
|
+
2. If `modelExecutionValue` differs from `model`, use `modelExecutionValue` during execution.
|
|
37
40
|
|
|
38
41
|
### Dynamic Worker Role Determination
|
|
39
42
|
|
|
43
|
+
**Roster canonical-source rule.** The profile's `Required workers:` block (in `prompts/profiles/<phase>.md`) is the **static roster definition** — the set of roles legal for that phase. `resultContract.requiredWorkerRoles` in `task-manifest.json` is the **per-run instance** — the actual roster materialized for this run, after recommendation, user selection, and any post-recommendation overrides. **On conflict, the task-manifest wins** — it is what the run was actually launched with, and what lead must dispatch against.
|
|
44
|
+
|
|
40
45
|
Only workers selected from `recommendedWorkers` in `task-manifest.json` and `resultContract.requiredWorkerRoles` become required roles.
|
|
41
46
|
|
|
42
47
|
- If one worker is selected: "`<role>` is the required worker role for this run."
|
|
@@ -127,19 +132,23 @@ Reading rules:
|
|
|
127
132
|
large for one read; if you must page, you MUST cover the entire file
|
|
128
133
|
before moving on, and you MUST state the page boundaries you used in your
|
|
129
134
|
Findings section.
|
|
130
|
-
- For the carry-in clarification response, read
|
|
131
|
-
|
|
135
|
+
- For the carry-in clarification response, read the conditional
|
|
136
|
+
`## 0. Clarification Response Carried In From Previous Run` section
|
|
137
|
+
(rendered only when carry-in is non-empty) and every row of
|
|
138
|
+
`## 5. Clarification Items` (`C-001`, `C-002`, ...) in full,
|
|
132
139
|
including rows whose `User input` cell is blank. The fact that you
|
|
133
140
|
will write your output into a file with a structurally similar
|
|
134
141
|
section 5 is NOT an excuse to skim — the prior `C-*` rows carry
|
|
135
|
-
context you cannot reconstruct from the new run alone.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
142
|
+
context you cannot reconstruct from the new run alone.
|
|
143
|
+
- Write the Reading Confirmation block to your **audit sidecar** at
|
|
144
|
+
`runs/<task-type>/worker-results/<worker>-audit-<task-type>-<seq>.md`
|
|
145
|
+
(sibling to the main worker-results file). One short line per input
|
|
146
|
+
file confirming end-to-end reading, e.g. "Read task-brief.md
|
|
147
|
+
end-to-end (147 lines)." Do NOT include a `## 0. Reading Confirmation`
|
|
148
|
+
heading in the main worker-results file — the validator now fails
|
|
149
|
+
worker-results that contain one. If you cannot truthfully confirm a
|
|
150
|
+
file end-to-end, record a `tool-failure` in the errors sidecar
|
|
151
|
+
instead of fabricating Findings.
|
|
143
152
|
- Do not collapse multiple input files into a single mental summary before
|
|
144
153
|
reading them all individually. Each file has its own canonical role
|
|
145
154
|
(brief = the user's request, profile = the lead's rules for this phase,
|
|
@@ -251,7 +260,11 @@ The same frontmatter contract applies to the `Report writer worker`'s final-repo
|
|
|
251
260
|
|
|
252
261
|
A successful worker result must include the following sections in this exact order, beneath the frontmatter block:
|
|
253
262
|
|
|
254
|
-
0. **Reading Confirmation** — one short line per input file
|
|
263
|
+
0. **Reading Confirmation** — one short line per input file stating that the worker read it end-to-end. Each line takes the form `- Read <file-name> end-to-end (<line-count> lines).`. The enumerated files are audience-scoped — they MUST match the recipient's row in the "Audience-scoped enumeration" table above:
|
|
264
|
+
- **Claude / Codex / Gemini analysis workers**: `task-brief.md`, `analysis-profile.md`, `analysis-material.md` (if present), `reference-expectations.md`, `clarification-response.md` (if a carry-in was provided). Analysis workers MUST NOT include `final-report-template.md` — it is not in their `[Required reading]` block.
|
|
265
|
+
- **Report writer worker (Phase 6)**: all of the above **plus** `final-report-template.md`.
|
|
266
|
+
|
|
267
|
+
If a file was skipped or only partially read, the worker MUST NOT produce sections 1–5; instead it records a `tool-failure` in the errors sidecar and stops. This section exists specifically to counteract the common failure mode where workers skim long inputs because they share structure with the file the run will eventually write into.
|
|
255
268
|
1. Findings
|
|
256
269
|
2. Missing Information or Assumptions
|
|
257
270
|
3. Safe or Reasonable Areas
|
|
@@ -341,7 +354,7 @@ without proceeding — this is the contractual replacement for the previous
|
|
|
341
354
|
empty run-level error logs in production.
|
|
342
355
|
|
|
343
356
|
- `cli-failure` events are recorded by the wrapper subagent itself (Codex / Gemini), but **directly to the run-level error log** via `okstra-error-log.py append-observed --error-type cli-failure ...` — NOT via the sidecar. The sidecar is an in-process tool-failure channel only.
|
|
344
|
-
- **Wrapper invocation arity.** Both `okstra-codex-exec.sh` and `okstra-gemini-exec.sh` accept four positional arguments
|
|
357
|
+
- **Wrapper invocation arity.** Both `okstra-codex-exec.sh` and `okstra-gemini-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 gemini it is appended to `--include-directories`. Omitting it during implementation causes the codex sandbox to reject every Edit/Write targeting the worktree with EPERM. Workers extract the path from the `**Worktree:**` / `EXECUTOR_WORKTREE_PATH` / `cwd for every mutating command:` line in the lead prompt. The optional fifth `<role>` is the trace-pane label suffix (e.g. `codex-<role>-trace`); always pass the literal string `worker` so the dispatch is self-describing (the wrapper defaults to `worker` if omitted).
|
|
345
358
|
- **Background dispatch + polling contract (Codex / Gemini wrappers).** Both wrapper subagents MUST dispatch `okstra-codex-exec.sh` / `okstra-gemini-exec.sh` via `Bash(run_in_background: true)` and poll with `BashOutput(bash_id)` until the shell reports `status == "completed"`, capped at 30 minutes (1800s) of wall-clock elapsed time. `BashOutput` itself is the wait primitive — call it back-to-back; do NOT insert a standalone `sleep` between polls. The Claude Code harness blocks `sleep` calls of 5 seconds or longer as a circumvention vector and explicitly forbids chaining shorter sleeps inside until-loops to work around the block. Workers that hit the contract bug must NOT self-recover with `until ...; do sleep 2; done` wrappers — that path violates the harness anti-circumvention rule, even though it superficially "works". The legacy "single foreground `Bash` with 120000ms timeout" rule, and the subsequent "60-second cadence with `sleep 60` between polls" rule, are both retired. The current rule applies in **every phase** (analysis runs typically complete in 1–2 `BashOutput` calls, so there is no regression for short jobs). Recording responsibilities:
|
|
346
359
|
- Successful completion: return the wrapper's accumulated stdout from the final `BashOutput`. No log entry.
|
|
347
360
|
- Non-zero `exit_code` reported by `BashOutput`: record a `cli-failure` to the run-level error log with the real `exit_code` and observed `duration-ms`.
|
|
@@ -356,7 +369,7 @@ empty run-level error logs in production.
|
|
|
356
369
|
2. Re-verification workers follow a constrained response format (verdict + brief explanation).
|
|
357
370
|
3. Workers cannot vote on their own findings (only verify other workers’ work).
|
|
358
371
|
4. The `report writer worker` does not participate in re-verification voting. It is responsible only for generating the final report.
|
|
359
|
-
5.
|
|
372
|
+
5. Division of labor: the Claude 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 `skills/okstra-convergence/SKILL.md`.
|
|
360
373
|
6. Batch processing is performed with one spawn per worker per round (not one spawn per finding).
|
|
361
374
|
7. These rules do not apply if Convergence is disabled.
|
|
362
375
|
|
|
@@ -376,10 +389,13 @@ Every worker result file under `worker-results/` must begin with a standardized
|
|
|
376
389
|
# <Role> Analysis — <task-key>
|
|
377
390
|
|
|
378
391
|
**Task:** <task-type>
|
|
392
|
+
**Target:** <path or scope> <!-- OPTIONAL: include when the run is scoped to a specific file/module -->
|
|
379
393
|
**Date:** <YYYY-MM-DD>
|
|
380
394
|
**Model:** <Role>, <AI model>
|
|
381
395
|
```
|
|
382
396
|
|
|
397
|
+
The `Target:` line is optional. Include it when the run is scoped to a specific path or module; omit it when the run spans the whole project. When included, place it between `Task:` and `Date:` as shown.
|
|
398
|
+
|
|
383
399
|
Examples:
|
|
384
400
|
|
|
385
401
|
```markdown
|
|
@@ -422,12 +438,12 @@ Token usage is collected from agent session transcripts after the run, NOT from
|
|
|
422
438
|
At the **start of Phase 7** (persistence), run the helper script with the path to this run's `team-state.json`:
|
|
423
439
|
|
|
424
440
|
```bash
|
|
425
|
-
python3
|
|
441
|
+
python3 "$HOME/.okstra/lib/python/okstra-token-usage.py" \
|
|
426
442
|
<runDirectoryPath>/state/team-state-<task-type>-<seq>.json \
|
|
427
443
|
--write --summary
|
|
428
444
|
```
|
|
429
445
|
|
|
430
|
-
|
|
446
|
+
The script is installed at `$HOME/.okstra/lib/python/okstra-token-usage.py` by `okstra install`. The previous repo-relative path (`scripts/okstra-token-usage.py`) only exists in a working clone of the okstra repo and is not appropriate for end-user-deployed runs.
|
|
431
447
|
|
|
432
448
|
The script reads:
|
|
433
449
|
- `~/.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/Gemini workers). Sessions are discovered by `teamName: okstra-<task-id>`, lead is identified by `lead.sessionId`, and other workers are identified by `agentName` (e.g. `claude-worker`, `codex-worker`, `gemini-worker`, `report-writer`).
|
|
@@ -6,7 +6,7 @@ user-invocable: false
|
|
|
6
6
|
|
|
7
7
|
# OKSTRA Time Summary
|
|
8
8
|
|
|
9
|
-
Aggregate elapsed work time for a given task, grouped by **task type** and broken down by **worker** (lead,
|
|
9
|
+
Aggregate elapsed work time for a given task, grouped by **task type** and broken down by **worker** (lead, claude, codex, gemini, report-writer).
|
|
10
10
|
|
|
11
11
|
## When to Use
|
|
12
12
|
|
|
@@ -19,7 +19,7 @@ Aggregate elapsed work time for a given task, grouped by **task type** and broke
|
|
|
19
19
|
Two sources, both already collected by `okstra`:
|
|
20
20
|
|
|
21
21
|
1. `.project-docs/okstra/tasks/<task-group>/<task-id>/history/timeline.json`
|
|
22
|
-
— lists every run with `runTimestamp`, `taskType`, `status`, `teamStatePath`.
|
|
22
|
+
— lists every run with `runTimestamp`, `taskType`, `status`, `teamStatePath`, and `taskRootPath`. Both path fields may be either project-root-relative or task-root-relative depending on which version of `render.py` wrote the manifest.
|
|
23
23
|
2. Each run's `.../runs/<task-type>/state/team-state-<suffix>.json`
|
|
24
24
|
— populated by `scripts/okstra-token-usage.py` at Phase 7. Contains:
|
|
25
25
|
- `leadUsage.{startedAt, endedAt, durationMs}`
|
|
@@ -64,12 +64,20 @@ If `task-catalog.json` is missing, respond: "No okstra history found. Run `scrip
|
|
|
64
64
|
|
|
65
65
|
For each entry in `timeline.json`'s `runs` array:
|
|
66
66
|
|
|
67
|
-
1.
|
|
67
|
+
1. Resolve the `team-state` file using a two-step lookup:
|
|
68
|
+
a. First try `<projectRoot>/<teamStatePath>`.
|
|
69
|
+
b. If that file does not exist, fall back to `<projectRoot>/<taskRootPath>/<teamStatePath>` (the manifest's `taskRootPath` field is the task-root relative to project root; `teamStatePath` written by `render.py` is task-root-relative in many runs).
|
|
70
|
+
Either path satisfies the lookup. If neither resolves to an existing file, treat the run as `unavailable`.
|
|
68
71
|
2. Extract:
|
|
69
72
|
- `taskType` from the timeline entry (authoritative).
|
|
70
73
|
- `leadUsage.durationMs` and `leadUsage.{startedAt,endedAt}`.
|
|
71
74
|
- For each `worker` in `workers[]`: `workerId`, `agent`, `usage.durationMs`.
|
|
72
|
-
|
|
75
|
+
Read defensively. `usage` (and `leadUsage`) may be:
|
|
76
|
+
- a normal `usage_block` with `durationMs >= 0`,
|
|
77
|
+
- a `na_block` with `{"source": "unavailable", "durationMs": 0, "note": ...}` when Phase 7 collection failed,
|
|
78
|
+
- missing entirely (older team-state files), or `None`.
|
|
79
|
+
Always normalize via `(block or {}).get("durationMs", 0) or 0`, and treat a `source == "unavailable"` block as zero contribution.
|
|
80
|
+
3. If the team-state file is missing, or every `durationMs` for the run is `0`/absent (i.e. `leadUsage` and every `workers[].usage` are zero or unavailable), record the run under `unavailable` with its `runTimestamp` and `taskType`.
|
|
73
81
|
|
|
74
82
|
## Step 3: Aggregate
|
|
75
83
|
|
|
@@ -82,20 +90,50 @@ For each distinct `taskType` across runs:
|
|
|
82
90
|
| Column | Computation |
|
|
83
91
|
|--------|-------------|
|
|
84
92
|
| `Runs` | count of runs of that task type that contributed any duration |
|
|
85
|
-
| `
|
|
93
|
+
| `CPU sum` | sum of (lead + all workers) across those runs — see note below |
|
|
86
94
|
| `Lead` | sum of `leadUsage.durationMs` |
|
|
87
95
|
| `Workers` | sum of all `workers[].usage.durationMs` |
|
|
88
96
|
|
|
89
97
|
Add a final `Grand total` row.
|
|
90
98
|
|
|
99
|
+
**Note on `CPU sum` vs wall-clock**: workers run as children of the lead session, so the lead's `durationMs` window OVERLAPS its workers' windows. `CPU sum = Lead + Workers` is therefore an additive CPU-style sum, not the wall-clock elapsed time the user actually waited.
|
|
100
|
+
|
|
101
|
+
Worked example for one run with three concurrent workers:
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
lead [================================] durationMs = 1800000 (30:00)
|
|
105
|
+
claude [============] durationMs = 720000 (12:00)
|
|
106
|
+
codex [==============] durationMs = 840000 (14:00)
|
|
107
|
+
gemini [========] durationMs = 480000 (08:00)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
- `CPU sum` for the run = `1800000 + 720000 + 840000 + 480000` = `3840000` (`01:04:00`)
|
|
111
|
+
- Wall-clock for the run = `max(endedAt) − min(startedAt)` ≈ `30:00`
|
|
112
|
+
|
|
113
|
+
Always report `CPU sum` in the by-task-type table. If the user explicitly asks for wall-clock, compute it per run as `max(leadUsage.endedAt, max(workers[].usage.endedAt)) − min(leadUsage.startedAt, min(workers[].usage.startedAt))` and surface it separately — never silently substitute it for `CPU sum`.
|
|
114
|
+
|
|
91
115
|
### B. Per worker breakdown (per task type)
|
|
92
116
|
|
|
93
|
-
For each task type, list one row per `workerId` actually present, plus `lead
|
|
117
|
+
For each task type, list one row per `workerId` actually present, plus `lead`. Aggregate `durationMs` across all runs of that task type.
|
|
94
118
|
|
|
95
119
|
| Worker | Runs | Total | Avg/run |
|
|
96
120
|
|--------|------|-------|---------|
|
|
97
121
|
|
|
98
|
-
|
|
122
|
+
- `Runs` denominator = number of runs of this task type in which this worker recorded a **nonzero** `durationMs`. A run where the worker's block was `na_block`, missing, or `0` does NOT count.
|
|
123
|
+
- If `Runs == 0` for a worker, **omit the row entirely** rather than dividing by zero.
|
|
124
|
+
- `Avg/run = Total / Runs` (integer ms, then format to `HH:MM:SS`).
|
|
125
|
+
|
|
126
|
+
Use the `workerId` from team-state. The valid worker enum is `lead, claude, codex, gemini, report-writer`.
|
|
127
|
+
|
|
128
|
+
Display rule for `workerId` vs `agent`:
|
|
129
|
+
- If every run of this task type used `agent == workerId` for this row, display the bare `workerId` (e.g. `claude`).
|
|
130
|
+
- If `agent` differs from `workerId` (e.g. a `claude` worker slot ran with `agent == "sonnet-eval"`), display `workerId (agent)` — and if multiple distinct agents were used across runs, comma-join them: `claude (sonnet-eval, opus-eval)`.
|
|
131
|
+
|
|
132
|
+
Never write `claude (claude)` — the parenthesized agent is shown only when it adds information.
|
|
133
|
+
|
|
134
|
+
### Timestamp parsing
|
|
135
|
+
|
|
136
|
+
When you need `startedAt` / `endedAt` (e.g. for wall-clock or chronological sort within a task type), normalize the ISO-8601 string before comparing: replace a trailing `Z` with `+00:00`, accept explicit offsets as-is, and parse via `datetime.fromisoformat(s.replace("Z", "+00:00"))`. Strings without an offset are assumed UTC. Mixed-form comparisons must be done as `datetime` objects, never as raw strings.
|
|
99
137
|
|
|
100
138
|
## Step 4: Format output
|
|
101
139
|
|
|
@@ -110,19 +148,20 @@ Use the `workerId` from team-state (e.g. `claude`, `codex`, `gemini`, `report-wr
|
|
|
110
148
|
|
|
111
149
|
### By task type
|
|
112
150
|
|
|
113
|
-
| Task type | Runs |
|
|
114
|
-
|
|
115
|
-
| requirements-discovery | 2 | 00:
|
|
116
|
-
| error-analysis | 1 | 00:18:45 | 00:08:11 |
|
|
117
|
-
| implementation | 3 | 02:11:09 | 00:45:30 |
|
|
118
|
-
| **Grand total** | 6 | **03:
|
|
151
|
+
| Task type | Runs | CPU sum | Lead | Workers |
|
|
152
|
+
|------------------------|------|-----------|----------|----------|
|
|
153
|
+
| requirements-discovery | 2 | 00:33:12 | 00:12:08 | 00:21:04 |
|
|
154
|
+
| error-analysis | 1 | 00:18:45 | 00:08:11 | 00:10:34 |
|
|
155
|
+
| implementation | 3 | 02:11:09 | 00:45:30 | 01:25:39 |
|
|
156
|
+
| **Grand total** | 6 | **03:03:06** | 01:05:49 | 01:57:17 |
|
|
157
|
+
|
|
158
|
+
`CPU sum` adds the lead window to each worker window even though they overlap; it is not a wall-clock total.
|
|
119
159
|
|
|
120
160
|
### Per worker — requirements-discovery
|
|
121
161
|
|
|
122
162
|
| Worker | Runs | Total | Avg/run |
|
|
123
163
|
|----------------|------|----------|----------|
|
|
124
164
|
| lead | 2 | 00:12:08 | 00:06:04 |
|
|
125
|
-
| intake | 1 | 00:01:00 | 00:01:00 |
|
|
126
165
|
| claude | 2 | 00:09:12 | 00:04:36 |
|
|
127
166
|
| codex | 2 | 00:07:40 | 00:03:50 |
|
|
128
167
|
| gemini | 2 | 00:03:12 | 00:01:36 |
|
|
@@ -134,8 +173,6 @@ Use the `workerId` from team-state (e.g. `claude`, `codex`, `gemini`, `report-wr
|
|
|
134
173
|
> Unavailable: 1 run (implementation / 2026-04-30_03-03-48) — team-state has no durationMs (Phase 7 not reached)
|
|
135
174
|
```
|
|
136
175
|
|
|
137
|
-
If the `Intake` column is all zero across every task type, omit that column entirely.
|
|
138
|
-
|
|
139
176
|
## Output Rules
|
|
140
177
|
|
|
141
178
|
- Always render durations as `HH:MM:SS`; never raw milliseconds.
|