okstra 0.123.0 → 0.125.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.
Files changed (98) hide show
  1. package/README.md +1 -0
  2. package/docs/architecture/storage-model.md +3 -1
  3. package/docs/architecture.md +16 -2
  4. package/docs/cli.md +3 -1
  5. package/docs/for-ai/README.md +41 -35
  6. package/docs/for-ai/skills/okstra-brief-gen.md +105 -105
  7. package/docs/for-ai/skills/okstra-container-build.md +61 -61
  8. package/docs/for-ai/skills/okstra-graphify.md +64 -0
  9. package/docs/for-ai/skills/okstra-inspect.md +87 -86
  10. package/docs/for-ai/skills/okstra-manager.md +32 -32
  11. package/docs/for-ai/skills/okstra-memory.md +49 -50
  12. package/docs/for-ai/skills/okstra-pr-gen.md +48 -0
  13. package/docs/for-ai/skills/okstra-rollup.md +58 -58
  14. package/docs/for-ai/skills/okstra-run.md +95 -95
  15. package/docs/for-ai/skills/okstra-schedule-gen.md +106 -106
  16. package/docs/for-ai/skills/okstra-setup.md +63 -64
  17. package/docs/for-ai/skills/okstra-user-response.md +48 -0
  18. package/docs/performance-improvement-plan-v2.md +6 -6
  19. package/docs/pr-template-usage.md +34 -34
  20. package/docs/project-structure-overview.md +91 -70
  21. package/docs/task-process/README.md +33 -33
  22. package/docs/task-process/common-flow.md +26 -26
  23. package/docs/task-process/error-analysis.md +20 -21
  24. package/docs/task-process/final-verification.md +41 -41
  25. package/docs/task-process/implementation-planning.md +33 -33
  26. package/docs/task-process/implementation.md +38 -38
  27. package/docs/task-process/release-handoff.md +46 -46
  28. package/docs/task-process/requirements-discovery.md +22 -23
  29. package/package.json +1 -1
  30. package/runtime/BUILD.json +2 -2
  31. package/runtime/agents/workers/antigravity-worker.md +4 -4
  32. package/runtime/agents/workers/claude-worker.md +2 -2
  33. package/runtime/agents/workers/codex-worker.md +4 -4
  34. package/runtime/agents/workers/report-writer-worker.md +4 -4
  35. package/runtime/bin/lib/okstra/usage.sh +1 -1
  36. package/runtime/prompts/coding-preflight/frameworks/node-server.md +1 -1
  37. package/runtime/prompts/launch.template.md +1 -1
  38. package/runtime/prompts/lead/convergence.md +10 -20
  39. package/runtime/prompts/lead/okstra-lead-contract.md +15 -17
  40. package/runtime/prompts/lead/plan-body-verification.md +18 -18
  41. package/runtime/prompts/lead/report-writer.md +46 -44
  42. package/runtime/prompts/lead/team-contract.md +11 -122
  43. package/runtime/prompts/profiles/_common-contract.md +15 -22
  44. package/runtime/prompts/profiles/_implementation-deliverable.md +1 -1
  45. package/runtime/prompts/profiles/_implementation-executor.md +1 -1
  46. package/runtime/prompts/profiles/_implementation-verifier.md +28 -2
  47. package/runtime/prompts/profiles/error-analysis.md +2 -2
  48. package/runtime/prompts/profiles/final-verification.md +1 -1
  49. package/runtime/prompts/profiles/implementation-planning.md +13 -13
  50. package/runtime/prompts/profiles/implementation.md +2 -1
  51. package/runtime/prompts/profiles/improvement-discovery.md +1 -1
  52. package/runtime/prompts/profiles/release-handoff.md +3 -3
  53. package/runtime/prompts/profiles/requirements-discovery.md +17 -18
  54. package/runtime/python/okstra_ctl/models.py +4 -2
  55. package/runtime/python/okstra_ctl/run.py +8 -2
  56. package/runtime/python/okstra_ctl/stage_fix_carry.py +112 -0
  57. package/runtime/schemas/final-report-v1.0.schema.json +6 -6
  58. package/runtime/skills/_fragments/bash-invocation-rule.md +1 -0
  59. package/runtime/skills/_fragments/preflight-outdated-cli.md +1 -0
  60. package/runtime/skills/_fragments/python-bootstrap-note.md +1 -0
  61. package/runtime/skills/okstra-brief-gen/SKILL.md +117 -122
  62. package/runtime/skills/okstra-container-build/SKILL.md +24 -14
  63. package/runtime/skills/okstra-graphify/SKILL.md +12 -4
  64. package/runtime/skills/okstra-inspect/SKILL.md +39 -747
  65. package/runtime/skills/okstra-inspect/facets/cost.md +84 -0
  66. package/runtime/skills/okstra-inspect/facets/error-zip.md +41 -0
  67. package/runtime/skills/okstra-inspect/facets/errors.md +69 -0
  68. package/runtime/skills/okstra-inspect/facets/history.md +90 -0
  69. package/runtime/skills/okstra-inspect/facets/logs.md +89 -0
  70. package/runtime/skills/okstra-inspect/facets/recap.md +96 -0
  71. package/runtime/skills/okstra-inspect/facets/report.md +61 -0
  72. package/runtime/skills/okstra-inspect/facets/status.md +145 -0
  73. package/runtime/skills/okstra-inspect/facets/time.md +56 -0
  74. package/runtime/skills/okstra-manager/SKILL.md +1 -1
  75. package/runtime/skills/okstra-memory/SKILL.md +3 -3
  76. package/runtime/skills/okstra-pr-gen/SKILL.md +3 -3
  77. package/runtime/skills/okstra-rollup/SKILL.md +12 -6
  78. package/runtime/skills/okstra-run/SKILL.md +49 -88
  79. package/runtime/skills/okstra-schedule-gen/SKILL.md +36 -30
  80. package/runtime/skills/okstra-setup/SKILL.md +1 -1
  81. package/runtime/skills/okstra-setup/references/project-config.md +17 -16
  82. package/runtime/skills/okstra-usage/SKILL.md +5 -2
  83. package/runtime/skills/okstra-user-response/SKILL.md +15 -3
  84. package/runtime/templates/prd/brief.template.md +92 -92
  85. package/runtime/templates/project-docs/task-index.template.md +1 -1
  86. package/runtime/templates/reports/error-analysis-input.template.md +1 -1
  87. package/runtime/templates/reports/fan-out-unit.template.md +6 -6
  88. package/runtime/templates/reports/final-verification-input.template.md +6 -6
  89. package/runtime/templates/reports/implementation-input.template.md +1 -1
  90. package/runtime/templates/reports/implementation-planning-input.template.md +1 -1
  91. package/runtime/templates/reports/improvement-discovery-input.template.md +1 -1
  92. package/runtime/templates/reports/quick-input.template.md +1 -1
  93. package/runtime/templates/reports/release-handoff-input.template.md +1 -1
  94. package/runtime/templates/reports/schedule.template.md +22 -22
  95. package/runtime/templates/reports/task-brief.template.md +3 -3
  96. package/runtime/templates/reports/user-response.template.md +20 -20
  97. package/runtime/templates/worker-prompt-preamble.md +111 -13
  98. package/runtime/validators/validate-schedule.py +1 -1
@@ -1,28 +1,30 @@
1
1
  ---
2
2
  name: okstra-inspect
3
3
  description: >-
4
- Use this for everything that happens AFTER a single okstra task has already run — inspecting it or light bookkeeping on it, never launching new work. The tell is usually a named task id (PROD-1623, dev-9184), often dropped without the word "okstra." Reach for it when the user wants one task's: status, current/next phase, blockers, or approval gate; its final report — where it is or whether it passed (결론·통과); its elapsed time (소요 시간) or context/read cost (컨텍스트 비용); its run history, re-run, or resume; to mark it done / in-progress / blocked / todo; or a failed run's error logs gathered into a report (에러 리포트). Also bundles cross-project okstra errors into an anonymized feedback zip (에러 환류). Use it even for a bare "mark it done" or "where's the report." NOT for starting a run (okstra-run), rollups/schedules (okstra-rollup / okstra-schedule-gen), a brief (okstra-brief-gen), setup (okstra-setup), or cross-project management (okstra-manager).
4
+ Use this for everything that happens AFTER a single okstra task has already run — inspecting it or light bookkeeping on it, never launching new work. The tell is usually a named task id (PROD-1623, dev-9184), often dropped without the word "okstra." Reach for it when the user wants one task's: status, current/next phase, blockers, or approval gate; its final report — where it is or whether it passed (verdict / pass); its elapsed time or context/read cost; its run history, re-run, or resume; to mark it done / in-progress / blocked / todo; or a failed run's error logs gathered into a report (error report). Also bundles cross-project okstra errors into an anonymized feedback zip (error feedback). Use it even for a bare "mark it done" or "where's the report." NOT for starting a run (okstra-run), rollups/schedules (okstra-rollup / okstra-schedule-gen), a brief (okstra-brief-gen), setup (okstra-setup), or cross-project management (okstra-manager).
5
5
  ---
6
6
 
7
7
  # OKSTRA Inspect
8
8
 
9
- Single read-side entry point for okstra runtime inspection plus the one status mutation that belongs here (`workStatus`) and read-derived artifact rendering (`errors` report). Sub-commands:
9
+ Single read-side entry point for okstra runtime inspection plus the one status mutation that belongs here (`workStatus`) and read-derived artifact rendering (`errors` report). Each sub-command's full procedure lives in a lazily loaded facet file — after dispatch, Read exactly the one facet you need.
10
10
 
11
- | Sub-command | What it does |
12
- |---|---|
13
- | `status` | Task / phase status, blockers, approval state. Also writes `workStatus` on user request. |
14
- | `history` | List past okstra runs; assemble re-run or resume command. |
15
- | `report` | Resolve final-report path for a task-key. Optionally read it. |
16
- | `time` | Per-task-type and per-worker duration breakdown for a task. |
17
- | `logs` | Inventory codex/antigravity wrapper `.log` sidecars; emit cleanup commands. |
18
- | `cost` | Estimate file/read context cost for a task bundle. |
19
- | `errors` | Aggregate okstra-run error logs for a task into a timestamped markdown report; print a summary. |
20
- | `error-zip` | Collect cross-project okstra error logs into an anonymized zip (report + raw) and summarize clusters. |
21
- | `recap` | Summarize a task's run-to-run phase transitions, then answer free-form questions over its `.okstra` artifacts. Appends each summary/Q&A to `recap/recap-log.jsonl`, and writes agent-authored notes to `notes/` for feeding into later runs. |
11
+ | Sub-command | Facet file | What it does |
12
+ |---|---|---|
13
+ | `status` | `facets/status.md` | Task / phase status, blockers, approval state. Also writes `workStatus` on user request. |
14
+ | `history` | `facets/history.md` | List past okstra runs; assemble re-run or resume command. |
15
+ | `report` | `facets/report.md` | Resolve final-report path for a task-key. Optionally read it. |
16
+ | `time` | `facets/time.md` | Per-task-type and per-worker duration breakdown for a task. |
17
+ | `logs` | `facets/logs.md` | Inventory codex/antigravity wrapper `.log` sidecars; emit cleanup commands. |
18
+ | `cost` | `facets/cost.md` | Estimate file/read context cost for a task bundle. |
19
+ | `errors` | `facets/errors.md` | Aggregate okstra-run error logs for a task into a timestamped markdown report; print a summary. |
20
+ | `error-zip` | `facets/error-zip.md` | Collect cross-project okstra error logs into an anonymized zip (report + raw) and summarize clusters. |
21
+ | `recap` | `facets/recap.md` | Summarize a task's run-to-run phase transitions, then answer free-form questions over its `.okstra` artifacts. Appends each summary/Q&A to `recap/recap-log.jsonl`, and writes agent-authored notes to `notes/` for feeding into later runs. |
22
22
 
23
23
  ## Step 0: Preflight (shared)
24
24
 
25
- Before any sub-command, run one Bash tool call, starting with the literal token `okstra` (never wrapped in `if`/`eval`/`export`/`$(...)`/`VAR=...`/`||`/`&&`/`npx` — a non-literal leading token defeats the `Bash(okstra:*)` permission match):
25
+ <!-- BEGIN FRAGMENT: bash-invocation-rule -->
26
+ Run one Bash tool call, starting with the literal token `okstra` (never wrapped in `if`/`eval`/`export`/`$(...)`/`VAR=...`/`||`/`&&`/`npx` — a non-literal leading token defeats the `Bash(okstra:*)` permission match):
27
+ <!-- END FRAGMENT: bash-invocation-rule -->
26
28
 
27
29
  ```bash
28
30
  okstra preflight --runtime claude-code --json
@@ -32,758 +34,48 @@ The project check only sees the cwd of the Bash call. When the user is asking ab
32
34
 
33
35
  Branch on the stdout JSON:
34
36
  - `ok: true` → carry `projectRoot` as a literal string; it is the base for every sub-command step below.
35
- - `ok: false` → before concluding "no setup", ask whether the user pointed at a specific project directory. If they did, re-run targeting it: `okstra preflight --runtime claude-code --cwd <that-dir> --json` (`--cwd` is the sanctioned way to target a project — a leading `cd` would break the permission match). Only if this **also** returns `ok:false` do you tell the user: "this project has no okstra setup. Run `/okstra-setup` first." Then stop. If the call fails with `unknown command: preflight`, the `okstra` binary on PATH predates this skill — tell the user to update it (`npm i -g okstra@latest`), then stop (`/okstra-setup` does not update the binary).
36
-
37
- Carry the resolved `projectRoot` into the sub-commands: read file artifacts under `<projectRoot>/.okstra/...`, and for sub-command CLIs that accept it (e.g. `recap`, `context-cost`) pass `--cwd <projectRoot>` / `--project-root <projectRoot>` so they target the same project rather than the Bash cwd. Subsequent `okstra <subcmd>` calls self-bootstrap their Python path, so this skill never needs `okstra paths --shell` / `export PYTHONPATH=...`.
38
-
39
- ## Step 1: Dispatch by intent
40
-
41
- Classify the user's request into one sub-command using the trigger table above. If ambiguous (e.g. "okstra 보여줘"), ask which facet — do not silently default. After routing, the chosen sub-command's section below governs the rest of the response.
42
-
43
- When you ask which facet, the option list is **authoritative**: enumerate every sub-command in the `Sub-command | What it does` table above as a numbered text list, in table order, each with its one-line description, and let the user pick by number or name. Never present a hand-picked subset — the AskUserQuestion 4-option cap is not a reason to drop facets; render the full menu as text in the question body so no facet (e.g. a newly added one) is ever hidden. The table is the single source of truth for this menu; adding a row there is all it takes for the facet to appear here.
44
-
45
- When the user chains multiple facets in one message (e.g. "status, and then time for DEV-9047"), execute them sequentially — Step 0 runs once, each sub-command section runs once.
46
-
47
- ---
48
-
49
- ## status
50
-
51
- Trigger phrases: "okstra status", "task status", "current phase", "next phase", "what is pending", "resume point", "okstra status set", "okstra mark", "<task-id> done", "<task-id> in-progress", "<task-id> 진행중", "<task-id> 완료".
52
-
53
- ### status.1 — Overall project status
54
-
55
- Read `.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.
56
-
57
- | Field | Description |
58
- |------|------|
59
- | `taskKey` | `<project-id>:<task-group>:<task-id>` |
60
- | `taskType` | latest task type |
61
- | `workCategory` | bugfix / feature / improvement / refactor / ops / unknown. Display `unknown` as-is, but flag with `(unset)` annotation so the reader knows the requirements-discovery classification was skipped or no `--work-category` flag was passed. |
62
- | `currentStatus` | task-level status |
63
- | `currentPhase` | lifecycle current phase |
64
- | `currentPhaseState` | lifecycle phase state |
65
- | `nextRecommendedPhase` | next recommended phase |
66
- | `routingStatus` | routing decision status |
67
- | `awaitingApproval` | approval 대기 여부 |
68
- | `latestRunStatus` | latest run status |
69
- | `latestReportPath` | latest report path |
70
- | `latestResumeCommandPath` | latest resume command |
71
- | `workStatus` | user-managed work status (todo / in-progress / blocked / done; default in-progress) |
72
- | `updatedAt` | last update time |
73
-
74
- Sort by `updatedAt` desc, then `taskKey`.
75
-
76
- 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-inspect status <task-key>` for the detail view in `status.2`.
77
-
78
- If `awaitingApproval` is true OR `routingStatus == "pending"`, append a `*` to the `Next` cell and explain the marker once below the table.
79
-
80
- ```markdown
81
- ## okstra Status — <project-id>
82
-
83
- | # | Task Key | Category | Phase | workStatus | Next |
84
- |---|----------|----------|-------|------------|------|
85
- | 1 | proj:group:id | bugfix | error-analysis | in-progress | implementation-planning |
86
- | 2 | proj:group:id2 | feature | requirements-discovery | done | pending-routing-decision* |
87
-
88
- `*` = awaiting user approval or pending routing decision. Run `/okstra-inspect status <task-key>` for details.
89
- ```
90
-
91
- ### status.2 — Specific task status
92
-
93
- Given a specific `task-key` or `task-group + task-id`:
94
-
95
- 1. If possible, quickly look it up in `task-catalog.json`.
96
- 2. If necessary, read `.okstra/tasks/<task-group>/<task-id>/task-manifest.json` directly.
97
- 3. If you need the latest run information, read `history/timeline.json` along with the latest run manifest.
98
-
99
- Required fields: `taskKey`, `taskType`, `workCategory`, `currentStatus`, `latestRunStatus`, `workflow.{currentPhase, currentPhaseState, phaseStates, lastCompletedPhase, nextRecommendedPhase, awaitingApproval, routingStatus, lastSafeCheckpoint}`, `workStatus`, `workStatusUpdatedAt`, `workStatusNote`, `latestReportPath`, `latestResumeCommandPath`, `historyTimelinePath`.
100
-
101
- ```markdown
102
- ## okstra Task Status — <task-key>
103
-
104
- - Work category: `<category>`
105
- - Current phase: `<phase>`
106
- - Current phase state: `<phase-state>`
107
- - Last completed phase: `<phase-or-->`
108
- - Next recommended phase: `<phase>`
109
- - Awaiting approval: `<yes|no>`
110
- - Routing status: `<routing-status>`
111
- - Task status: `<task-status>`
112
- - Latest run status: `<run-status>`
113
- - Latest report: `<relative-path-or-->`
114
- - Resume command: `<relative-path-or-->`
115
- - workStatus: `<todo|in-progress|blocked|done>` (updated `<workStatusUpdatedAt-or-->`)
116
- - workStatus note: `<workStatusNote-or-->`
117
-
118
- ### Phase States
119
-
120
- - `requirements-discovery`: `<state>`
121
- - `error-analysis`: `<state>`
122
- - `implementation-planning`: `<state>`
123
- - `implementation`: `<state>`
124
- - `final-verification`: `<state>`
125
- - `release-handoff`: `<state>`
126
-
127
- ### Safe Resume Checkpoint
128
-
129
- - Label: `<checkpoint-label>`
130
- - Run manifest: `<relative-path-or-->`
131
- - Team state: `<relative-path-or-->`
132
- - Report: `<relative-path-or-->`
133
- - Resume command: `<relative-path-or-->`
134
- ```
135
-
136
- ### status.3 — Resume / next-step guidance
137
-
138
- The status response always includes one of:
37
+ - `ok: false` → before concluding "no setup", ask whether the user pointed at a specific project directory. If they did, re-run targeting it: `okstra preflight --runtime claude-code --cwd <that-dir> --json` (`--cwd` is the sanctioned way to target a project — a leading `cd` would break the permission match). Only if this **also** returns `ok:false` do you tell the user: "this project has no okstra setup. Run `/okstra-setup` first." Then stop.
139
38
 
140
- 1. **Resume current run** — if `latestResumeCommandPath` exists, display that path.
141
- 2. **Restart current phase** task can be re-run with the same `task-key` and current `taskType`.
142
- 3. **Start next phase** — if `workflow.nextRecommendedPhase` is one of `error-analysis`, `implementation-planning`, `implementation`, `final-verification`, `release-handoff`, that phase is proposed. If `nextRecommendedPhase` is `pending-release-handoff`, surface as verdict-gated and propose `release-handoff` only when the cited verdict is `accepted`.
143
- 4. **Need more information** — if `nextRecommendedPhase` is `pending-routing-decision` or `routingStatus` is `pending`.
144
- 5. **Task complete (terminal)** — 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.
39
+ <!-- BEGIN FRAGMENT: preflight-outdated-cli -->
40
+ If the call fails with `unknown command: preflight`, the `okstra` binary on PATH predates this skill — tell the user to update it (`npm i -g okstra@latest`), then stop (`/okstra-setup` does not update the binary).
41
+ <!-- END FRAGMENT: preflight-outdated-cli -->
145
42
 
146
- ### status.4 Update workStatus (write)
43
+ Carry the resolved `projectRoot` into the sub-commands: read file artifacts under `<projectRoot>/.okstra/...`, and for sub-command CLIs that accept it (e.g. `recap`, `context-cost`) pass `--cwd <projectRoot>` / `--project-root <projectRoot>` so they target the same project rather than the Bash cwd.
147
44
 
148
- Recognize requests to change a task's `workStatus` and update the corresponding `task-manifest.json`.
45
+ <!-- BEGIN FRAGMENT: python-bootstrap-note -->
46
+ Every subsequent `okstra <subcmd>` call self-bootstraps its Python path, so this skill never needs `okstra paths --shell` / `export PYTHONPATH=...`.
47
+ <!-- END FRAGMENT: python-bootstrap-note -->
149
48
 
150
- **Trigger patterns** (recognize both):
151
-
152
- Natural language: "DEV-6827 done으로 바꿔줘", "PROD-1623을 blocked로 표시", "DEV-9047 진행중", "Mark DEV-6827 as done".
153
-
154
- Explicit phrasing (recognized user utterances — normalize each to a `okstra set-work-status` call below):
155
- - `okstra status set <task-id> <status>`
156
- - `okstra status set <task-group> <task-id> <status>` (disambiguation)
157
- - `okstra mark <task-id> <status>`
158
- - `okstra status set <task-id> <status> --note "<note>"`
159
-
160
- Accepted `<status>` values: `todo`, `in-progress`, `blocked`, `done`.
161
-
162
- **Procedure:** run the update through the CLI (one Bash call, literal `okstra` token) — never edit `task-manifest.json` by hand:
163
-
164
- ```bash
165
- okstra set-work-status <token> <status> --project-root <projectRoot> --json
166
- ```
167
-
168
- - `<token>` is a full task-key or a bare task-id. Add `--task-group <group>` to scope a duplicated id, `--note "<note>"` to set `workStatusNote` (flag omitted → any existing note is left untouched).
169
- - Branch on the stdout JSON: `ok: true` → confirm below. `stage: "ambiguous"` → list `matches[]` (with `_matchedVia`) via a 3-option picker and retry with the chosen full task-key. `stage: "not-found"` → `<TASK-ID>를 찾을 수 없습니다.` An invalid `<status>` makes the CLI exit 2 with the allowed-values list — surface it verbatim; nothing was modified.
170
-
171
- Confirm in Korean:
172
- ```
173
- ✓ <TASK-ID> workStatus: <previousWorkStatus> → <workStatus>
174
- ✓ task-manifest.json 업데이트됨
175
- ```
176
- `<previousWorkStatus>` = `(없음)` when the JSON's `previousWorkStatus` is empty.
177
-
178
- **Default value convention:** if `workStatus` is missing or empty, infer the display value from lifecycle state (DO NOT default to a static `in-progress`):
179
-
180
- | Manifest state | Inferred display |
181
- |---|---|
182
- | `currentStatus == "completed"` AND `workflow.nextRecommendedPhase == "done-or-follow-up"` | `done` (inferred) |
183
- | `currentStatus == "completed"` AND `workflow.currentPhaseState == "completed"` | `phase-done` (inferred) |
184
- | `currentStatus == "contract-violated"` OR `workflow.currentPhaseState == "blocked"` | `blocked` (inferred) |
185
- | anything else | `in-progress` (default) |
186
-
187
- Annotate inferred values with `(inferred)` or `(default)`. Do not back-fill on read; only write when the user explicitly issues an update.
188
-
189
- **Catalog sync note:** the CLI updates `task-manifest.json` only. `discovery/task-catalog.json` may be stale until the next run regenerates it; downstream consumers (e.g. `okstra-schedule-gen`) re-read each manifest directly.
190
-
191
- ---
192
-
193
- ## history
194
-
195
- Trigger phrases: "okstra history", "past runs", "run history", "re-run", "list tasks", "다시 실행", "resume", "이어서".
196
-
197
- **Re-run vs Resume — decide upfront.** Re-run = start a fresh run (new run-seq, new manifest, new report) reusing an old run's parameters → `history.3`. Resume = continue an interrupted Claude session for an existing run, no new run-seq → `history.4`. If the user is ambiguous, ask — defaulting to the wrong one either wastes a fresh run-seq or silently abandons a recoverable session.
198
-
199
- ### history.1 — Read the task catalog
200
-
201
- 1. Read `.okstra/discovery/task-catalog.json`.
202
- 2. Apply filters from user input (all optional, AND-combined):
203
- - `--task-type <type>` → keep entries whose `taskType` matches.
204
- - `--latest-run-status <status>` → keep entries whose `latestRunStatus` matches (`completed`, `contract-violated`, `error`).
205
- - `--task-group <group>` → keep entries whose `taskGroup` matches.
206
- 3. Sort by `updatedAt` desc.
207
- 4. Page: default `--limit 20`. If truncated, add `... <N> more (pass --limit <N> to see all)`.
208
- 5. Extract: `taskKey`, `taskType`, `currentStatus`, `latestRunStatus`, `latestRunManifestPath`, `updatedAt`, `latestReportPath`, `latestResumeCommandPath`, `historyTimelinePath`.
209
-
210
- ```markdown
211
- ## okstra Task History — <project-id>
212
-
213
- | # | Task Key | Type | currentStatus | latestRunStatus | Last Run | Report |
214
- |---|----------|------|---------------|------------------|----------|--------|
215
- | 1 | proj:group:id | error-analysis | completed | completed | 2026-04-05 22:59 | .project-docs/.../final-report-*.md |
216
- | 2 | proj:group:id2 | final-verification | todo | error | 2026-04-04 15:30 | -- |
217
- ```
218
-
219
- **Catalog absent — fallback.** Do NOT bail out. Manifests on disk are the source of truth.
220
-
221
- 1. Glob `<projectRoot>/.okstra/tasks/*/*/task-manifest.json`.
222
- 2. For each manifest, read the same fields as above.
223
- 3. Apply the same filters/sort/limit and print the same table, prefixed with: `note: task-catalog.json missing; reconstructed from task manifests on disk.`
224
- 4. Only if the glob yields zero manifests: `There is no okstra execution history yet.`
225
-
226
- ### history.2 — Run history by task
227
-
228
- When a user selects a specific task:
229
-
230
- 1. Retrieve `historyTimelinePath`, read `runs` array.
231
- 2. Extract: `runTimestamp`, `runDateTimeSegment`, `taskType`, `status`, `runManifestPath`, `reportPath`, `resumeCommandPath`, `relatedTasks`.
232
-
233
- ```markdown
234
- ## Runs for <task-key>
235
-
236
- | # | Timestamp | Type | Status | Report |
237
- |---|-----------|------|--------|--------|
238
- | 1 | 2026-04-05 22:59 | error-analysis | completed | .../final-report-*.md |
239
- | 2 | 2026-04-04 15:30 | error-analysis | error | -- |
240
- ```
241
-
242
- ### history.3 — Re-run (NEW run from old parameters)
243
-
244
- Builds a fresh run — new run-seq, new manifest, new report — using parameters from a previous `run-manifest-*.json`. Does NOT touch old artifacts; use `history.4` to continue an interrupted session.
245
-
246
- 1. Pick the source run-manifest: `runManifestPath` from `history.2`, or task's `latestRunManifestPath` from `history.1`. Note: `latestRunManifestPath` is projected from the timeline's latest run, so it is empty when the task has no recorded run yet (timeline absent) — in that case fall back to a `history.2` per-run `runManifestPath`, or ask the user.
247
- 2. Read the manifest and extract required arguments:
248
- - `projectId` → `--project-id`
249
- - `taskGroup` → `--task-group`
250
- - `taskId` → `--task-id`
251
- - `taskType` → `--task-type`
252
- - `taskBriefPath` → `--task-brief`
253
- 3. Optional arguments (include only when present in source):
254
- - `recommendedWorkers` → `--workers` (comma-separated)
255
- - `relatedTasks` → `--related-tasks`
256
- - model overrides → `--claude-model`, `--codex-model`, `--antigravity-model`
257
- - for `taskType: implementation`: `teamContract.executor.provider` → `--executor <claude|codex|antigravity>` when different from `claude`.
258
- 4. **`taskType: implementation` only — resolve `--base-ref`:** base ref is NOT in the run-manifest; it lives in the worktree registry at `~/.okstra/worktrees/registry.json`. If a worktree is already registered, existing branch & base are reused — omit `--base-ref` unless the user explicitly wants a different starting point. If no worktree is registered (cleaned up), `--base-ref` is mandatory — ask the user before running.
259
- 5. Display the assembled command:
260
- ```bash
261
- ~/.okstra/bin/okstra.sh \
262
- --project-id <project-id> \
263
- --task-group <task-group> \
264
- --task-id <task-id> \
265
- --task-type <task-type> \
266
- --task-brief <brief-path> \
267
- --workers <worker-list>
268
- ```
269
- 6. Once the user confirms, execute it.
270
-
271
- ### history.4 — Resume (continue an interrupted run)
272
-
273
- Continues an existing Claude session for an unfinished run. Does NOT create a new run-seq — for a fresh dispatch use `history.3`.
274
-
275
- 1. Read `latestResumeCommandPath` from `history.1` (or `resumeCommandPath` from a `history.2` timeline entry).
276
- 2. Verify the file exists on disk.
277
- 3. If it exists: `bash <resume-command-path>`.
278
- 4. If the path is empty or the file is missing: `No resume script available for this run. Use 'history.3' to start a fresh run instead.`
279
-
280
- ---
281
-
282
- ## report
283
-
284
- Trigger phrases: "find report", "show report for", "read the okstra report", "continue from report".
285
-
286
- ### report.1 — Resolve report path
287
-
288
- task-key format: `<project-id>:<task-group>:<task-id>`.
289
-
290
- **Normalization:** task-key matching is lowercase. Disk segments are slugified (lowercase + non-alphanumeric runs → `-`) per `scripts/okstra_ctl/ids.py:88` (`slugify_task_segment`, the SSOT; `interactive.sh` consumes it via import). Catalog lookup is case-insensitive; file path assembly uses slugified segments.
291
-
292
- Lookup methods (in priority order):
293
-
294
- A. **`task-catalog.json` (fast):** read `.okstra/discovery/task-catalog.json`, match `taskKey` lowercase. `latestReportPath` is task-type-agnostic "most recent report".
295
-
296
- B. **`task-manifest.json` (direct):** if catalog missing, slugify task-group / task-id, read `.okstra/tasks/<group-segment>/<id-segment>/task-manifest.json`, use `latestReportPath` (task-type-agnostic).
297
-
298
- C. **`timeline.json` (specific run):** for a specific date or run, read `.okstra/tasks/<group-segment>/<id-segment>/history/timeline.json`, filter `runs[]` by `runTimestamp` / `status` / `taskType`, use `runs[].reportPath`.
299
-
300
- D. **Specific task-type (fallback):** `latestReportPath` is task-type-agnostic. For a specific task-type's latest report, look under `.okstra/tasks/<group-segment>/<id-segment>/runs/<task-type-segment>/reports/`, filename pattern `final-report-<task-type-segment>-<NNN>.md` per `scripts/okstra_ctl/sequence.py:30`. Highest seq is latest. Stage-isolated runs (`implementation`, single-stage `final-verification`) keep their reports one level deeper at `runs/<task-type-segment>/stage-<N>/reports/` — scan those subdirectories too; seq is independent per stage. Cross-verify with `timeline.json`'s `runs[].taskType` filter.
301
-
302
- ### report.2 — Confirm existence
303
-
304
- 1. Verify `latestReportPath` is non-empty AND the file exists on disk. Either signal indicates report presence (tolerant).
305
- 2. If present, display the path and ask the user whether to read it.
306
- 3. If absent, check `task-manifest.json` signals:
307
- - `latestReportPath` empty/missing AND `currentStatus != completed` AND `workStatus != done` AND `workflow.routingStatus` not completion-like → `이 task는 아직 완료되지 않았습니다 (currentStatus: <currentStatus>, workStatus: <workStatus>).`
308
- - Any signal indicates completion but the file is missing → `보고서 파일이 존재하지 않습니다: <path>`
309
-
310
- `workStatus` enum: `todo | in-progress | blocked | done`. `currentStatus`: `completed` / `contract-violated` etc. `"completed"` string does NOT exist in `workStatus` — do not confuse the two.
311
-
312
- ### report.3 — Read + next-step guidance
313
-
314
- Match the depth of read to the request — final reports routinely run 300+ lines / 50K+ tokens, so ingesting the whole file just to answer "핵심만 요약" is wasteful:
315
-
316
- - **Summary / verdict intent** ("요약", "핵심만", "결론", "통과했어?", "verdict"): do **not** read the whole report first. Read the `final-<task-type-segment>-<NNN>.status` sidecar under `runs/<task-type-segment>/status/` (stage-isolated: `runs/<task-type-segment>/stage-<N>/status/`) for the machine verdict, then read only the report's leading summary block (the first "종합 판정" / "Executive" / verdict heading and its body) to quote the gist. Offer to read the full report if the user wants detail.
317
- - **Full-read intent** ("전체", "다 읽어", "본문"): ingest the resolved report file.
318
-
319
- After reading, surface follow-up options:
320
-
321
- 1. **구현 진행:** based on the report's "권장 다음 단계" section.
322
- 2. **추가 검증:** to launch a new okstra run with the same task-key, assemble the command through the `history` sub-command — it offers the full option set (base-ref, workers, render-only) and renders a host-correct invocation.
323
- 3. **관련 task 확인:** if the report references related task-keys, fetch their reports too.
324
-
325
- ### report — Output template
326
-
327
- ```markdown
328
- ## Report for <task-key>
329
-
330
- | Field | Value |
331
- | ------------ | -------------------------------------------------- |
332
- | Status | `<status>` |
333
- | Task type | `<task-type>` |
334
- | Run seq | `<NNN>` |
335
- | Run date | `<runTimestamp ISO-8601>` |
336
- | Report (rel) | `<relative-path-from-project-root>` |
337
- | Report (abs) | `<absolute-path>` |
338
- ```
339
-
340
- ---
341
-
342
- ## time
343
-
344
- Trigger phrases: "작업 시간", "소요 시간", "time summary", "duration", "elapsed", "얼마나 걸렸", "시간 분석".
345
-
346
- Aggregate elapsed work time for a task, grouped by **task type** and broken down by **worker** (lead + each worker). The `time-report` CLI reads both data sources (`history/timeline.json` runs + each run's `team-state-*.json` usage blocks) and does **all** aggregation — cross-run sums, CPU-sum vs wall-clock, timestamp parsing, unavailable detection. You only resolve the task-key, call it, and render. **Never recompute durations by hand.**
347
-
348
- ### time.1 — Resolve the task-key
349
-
350
- Same standard as `cost.1`: full task-key → use directly; bare token (task-id or task-group) → `okstra resolve-task-key <token> --project-root <projectRoot> --json` and branch on `matches[]` (0 = not found, 1 = use, N = 3-option picker). If no okstra history exists, say `No okstra history found. Run /okstra-run first.`
351
-
352
- ### time.2 — Fetch aggregated data
353
-
354
- ```bash
355
- okstra time-report <task-key> --project-root <projectRoot> --json
356
- ```
357
-
358
- Returns (all durations are **raw milliseconds**):
359
- - `byTaskType[]` — `{taskType, runs, leadMs, workersMs, cpuSumMs}`, plus `grandTotal`
360
- - `perWorker` — `{<taskType>: [{workerId, agents[], runs, totalMs, avgMs}]}`; only workers with a nonzero run appear, and `agents[]` lists agent labels that differ from `workerId`
361
- - `perRunWallClock[]` — `{runTimestamp, taskType, wallClockMs}` (max `endedAt` − min `startedAt` per run)
362
- - `phaseTimelines[]` — `{runTimestamp, taskType, phases:[{phase, firstAt, wallMsToNext}]}`
363
- - `unavailable[]` — `{runTimestamp, taskType, reason}` for runs with no Phase-7 durations (never summed into totals)
364
-
365
- ### time.3 — Render
366
-
367
- Convert every `*Ms` to `HH:MM:SS` (zero-pad; never show raw ms). Task types in `byTaskType` are already in chronological (first-appearance) order.
368
-
369
- - **By task type** — `| Task type | Runs | CPU sum | Lead | Workers |` from `byTaskType`, plus a `grandTotal` row. `CPU sum` (= Lead + Workers) overlaps because workers run inside the lead's window — it is *not* wall-clock. Surface wall-clock only when the user explicitly asks, from `perRunWallClock`.
370
- - **Per worker** (per task type) — `| Worker | Runs | Total | Avg/run |` from `perWorker`. Render the worker as bare `workerId` when `agents` is empty, else `workerId (agent1, agent2)`.
371
- - **Phase breakdown** — "단계별"/"stage별"/"어느 단계가 오래" in okstra most often means the **lifecycle stage (task-type)** view, which the *By task type* table above already answers — lead with that table, do not treat the request as unanswerable. Render the intra-run phase timeline only when the user clearly means within-a-run phases ("어느 phase가", "Phase 1~7", "phase timeline"): one table per run from `phaseTimelines`: `| Phase | Start | Wall to next |` using `firstAt`/`wallMsToNext` (`null` → `--`). When `phaseTimelines` is empty, do **not** headline "측정 불가" — the By-task-type table is the stage answer; mention the missing intra-run markers only as a trailing footnote.
372
- - If `unavailable[]` is non-empty, append a trailing note listing each run with its reason. Never fold them into totals.
373
- - Show the resolved `<task-key>` in the heading.
374
-
375
- ```markdown
376
- ## Time summary — <task-key>
377
-
378
- ### By task type
379
-
380
- | Task type | Runs | CPU sum | Lead | Workers |
381
- |------------------------|------|-----------|----------|----------|
382
- | requirements-discovery | 2 | 00:33:12 | 00:12:08 | 00:21:04 |
383
- | **Grand total** | 6 | **03:03:06** | 01:05:49 | 01:57:17 |
384
-
385
- ### Per worker — requirements-discovery
386
-
387
- | Worker | Runs | Total | Avg/run |
388
- |----------------------|------|----------|----------|
389
- | lead | 2 | 00:12:08 | 00:06:04 |
390
- | claude (sonnet-eval) | 2 | 00:09:12 | 00:04:36 |
391
-
392
- > Unavailable: 1 run (implementation / 2026-04-30_03-03-48) — Phase 7 not reached
393
- ```
394
-
395
- ---
396
-
397
- ## cost
398
-
399
- Trigger phrases: "okstra context-cost", "context cost", "context-cost", "컨텍스트 비용", "읽기 비용", "산출물 비용", "task bundle cost", "agent read cost".
400
-
401
- Read-only estimate of how much file/context surface a prepared task bundle asks the lead, analysis workers, and report-writer to absorb. This sub-command does **not** mutate task artifacts.
402
-
403
- ### cost.1 — Resolve target
49
+ ## Step 1: Dispatch by intent
404
50
 
405
- Accepted target forms:
51
+ Classify the user's request into one sub-command using the trigger table above. If ambiguous (e.g. "show me okstra"), ask which facet — do not silently default. After routing, **Read the chosen row's facet file** (path relative to this skill's base directory) and follow it for the rest of the response. Load exactly one facet per sub-command — never preload the others.
406
52
 
407
- 1. Full task-key: `<project-id>:<task-group>:<task-id>`.
408
- 2. Bare token — a task-id (e.g. `DEV-9184`) **or** a task-group (e.g. `PROD`).
409
- 3. Task root path, e.g. `<projectRoot>/.okstra/tasks/<group>/<task-id>`.
53
+ When you ask which facet, the option list is **authoritative**: enumerate every sub-command in the dispatch table above as a numbered text list, in table order, each with its one-line description, and let the user pick by number or name. Never present a hand-picked subset — the AskUserQuestion 4-option cap is not a reason to drop facets; render the full menu as text in the question body so no facet (e.g. a newly added one) is ever hidden. The table is the single source of truth for this menu; adding a row there (plus its facet file) is all it takes for the facet to appear here.
410
54
 
411
- If the user gives a task root path, run `okstra context-cost <absolute-or-user-provided-path>` directly.
55
+ When the user chains multiple facets in one message (e.g. "status, and then time for DEV-9047"), execute them sequentially — Step 0 runs once, then load and run each facet in turn.
412
56
 
413
- If the user gives a full task-key, run:
57
+ ## Shared rules (referenced by facets)
414
58
 
415
- ```bash
416
- okstra context-cost <task-key> --project-root <projectRoot>
417
- ```
59
+ ### Standard task-key resolution (0/1/N)
418
60
 
419
- If the user gives a bare token, resolve it with the shared resolver (SSOT — `scripts/okstra_ctl/resolve_task_key.py` / `okstra_project.resolve_task_reference`). The resolver matches the token as a task-id **and** as a task-group, unioning both (a group expands to its member tasks); each entry carries `_matchedVia` (`"taskId"` | `"taskGroup"`):
61
+ Facets that accept a bare token (a task-id like `DEV-9184` **or** a task-group like `PROD`) resolve it with the shared resolver (SSOT — `scripts/okstra_ctl/resolve_task_key.py` / `okstra_project.resolve_task_reference`). The resolver matches the token as a task-id **and** as a task-group, unioning both (a group expands to its member tasks); each entry carries `_matchedVia` (`"taskId"` | `"taskGroup"`):
420
62
 
421
63
  ```bash
422
64
  okstra resolve-task-key <token> --project-root <projectRoot> --json
423
65
  ```
424
66
 
425
- Branch on the stdout JSON `matches[]` — this **standard 0/1/N rule** is referenced by `errors`/`recap` below:
426
- - **0개** → report the task cannot be found. Do not guess.
427
- - **1개** → use that entry's `taskKey`.
428
- - **N개** → list the candidate `taskKey`s (with `updatedAt`, and `_matchedVia` so the user sees which came from a task-id vs a task-group) and ask via a 3-option picker (1~2 추천 + `직접 입력`), then use the chosen `taskKey`.
67
+ Branch on the stdout JSON `matches[]`:
68
+ - **0** → report the task cannot be found. Do not guess.
69
+ - **1** → use that entry's `taskKey`.
70
+ - **N** → list the candidate `taskKey`s (with `updatedAt`, and `_matchedVia` so the user sees which came from a task-id vs a task-group) and ask via a 3-option picker (12 recommendations + `Enter directly`), then use the chosen `taskKey`.
429
71
 
430
- If the user asks generally ("컨텍스트 비용 보여줘") and does not name a task:
72
+ ### No-task fallback
73
+
74
+ When a facet needs a task but the user did not name one:
431
75
 
432
76
  1. Read `.okstra/discovery/task-catalog.json`.
433
77
  2. If exactly one task exists, use it.
434
- 3. If multiple tasks exist, show the latest 10 by `updatedAt` and ask which task to measure. Do not guess.
435
-
436
- ### cost.2 — Run estimator
437
-
438
- Use the CLI output as the source of truth:
439
-
440
- ```bash
441
- okstra context-cost <resolved-target> --project-root <projectRoot>
442
- ```
443
-
444
- Do not re-count files manually unless the CLI fails and the user explicitly asks for manual fallback.
445
-
446
- ### cost.3 — Render
447
-
448
- Render the CLI JSON into the cost.4 template. Each row's Files / Size / ~Tokens come from the matching output block — `totals` (Task bundle, Current run, Legacy timestamp), `instructionSet`, `leadPhase1`, `analysisWorker`, `reportWriter`, `skillAssets`; their exact field names are in the CLI output (do not re-list them here). Format bytes as raw + rounded KB/MB.
449
-
450
- Field-specific notes (these affect interpretation, not just display):
451
- - Analysis worker uses per-worker fields (`bytesPerWorker`, `estimatedTokensPerWorker`). Use `analysisWorker.estimatedReductionPercent` directly for the worker-input reduction — do **not** recompute it from `bytesPerWorker` when `mode == "analysis-packet-primary"` (that value is already the packet-primary cost).
452
- - `estimatedTokens*` are a static ranking heuristic (~4 ASCII chars/token, non-ASCII ≈ 1 token/char), never billing numbers — actual cost is the token-usage collector's domain.
453
- - `skillAssets` = the per-run hot-path instruction assets loaded OUTSIDE the task bundle (skill bodies + agent specs), sorted by size — the prompt-diet target.
454
-
455
- ### cost.4 — Output template
456
-
457
- ```markdown
458
- ## okstra Context Cost — <task-key>
459
-
460
- | Surface | Files | Size | ~Tokens |
461
- |---|---:|---:|---:|
462
- | Task bundle | <N> | <bytes> (<human>) | - |
463
- | Current run | <N> | <bytes> (<human>) | - |
464
- | Instruction set | <N> | <bytes> (<human>) | <estimatedTokens> |
465
- | Lead Phase 1 (`<mode>`) | <N> | <bytes> (<human>) | <estimatedTokens> |
466
- | Analysis worker / worker (`<mode>`) | <N> | <bytes> (<human>) | <estimatedTokensPerWorker> |
467
- | Report writer synthesis | <N> | <bytes> (<human>) | <estimatedTokens> |
468
- | Skill assets (hot path) | <N> | <bytes> (<human>) | <estimatedTokens> |
469
-
470
- - Current run: `<currentRunPath-or-->`
471
- - Legacy timestamp artifacts: `<N>`
472
- - Legacy full worker contract: `<legacyFullContractBytesPerWorker>` bytes (`<human>`) per analysis worker
473
- - Packet estimate: `<estimatedPacketModeBytesPerWorker>` bytes (`<human>`) per analysis worker
474
- - Estimated worker-input reduction: `<percent>%`
475
-
476
- ### Reading
477
-
478
- <One or two Korean sentences explaining the main bottleneck and the next likely optimization target.>
479
- ```
480
-
481
- Interpretation rules:
482
-
483
- - `leadPhase1.mode == "active-run-context"` means the compact lead intake file is present and should be treated as the primary lead read surface.
484
- - `leadPhase1.mode == "legacy-five-file"` means this task was prepared before active-run-context, or the manifest does not reference it.
485
- - `analysisWorker.mode == "analysis-packet-primary"` means new workers should read `analysis-packet.md` first and open full source inputs only for evidence checks or missing detail.
486
- - If `analysisWorker.mode == "full-input-contract"` and `estimatedReductionPercent` is low, the next target is worker prompt/input contract slimming.
487
- - If `reportWriter.bytes` dominates, the next target is a compact `synthesis-input` artifact.
488
- - If `skillAssets.estimatedTokens` dominates the per-run fixed cost, the next target is slimming the largest `skillAssets.files[]` entries (prompt diet — perf plan v2 P2), e.g. thin-core + lazy-sidecar split.
489
- - If `legacyTimestampFileCount` is high, recommend current-view/cold-artifact separation or retention cleanup, not destructive deletion by default.
490
-
491
- ---
492
-
493
- ## logs
494
-
495
- Trigger phrases: "okstra logs", "로그 현황", "로그 파일", "log files", "log size", "log status", "로그 정리", "log cleanup".
496
-
497
- Read-only inventory of codex/antigravity wrapper log files written next to each prompt history file (`<prompt>.log`). Reports sizes, ages, totals, and suggests cleanup commands. **Does not delete** — the user runs whichever `find … -delete` line they like.
498
-
499
- **Background:** codex/antigravity wrappers (`okstra-codex-exec.sh`, `okstra-antigravity-exec.sh`) write a sidecar log next to each prompt history file:
500
-
501
- ```
502
- .okstra/tasks/<task-group>/<task-id>/runs/<phase>/prompts/
503
- <worker>-worker-prompt-<phase>-<seq>.md <-- prompt (git-tracked)
504
- <worker>-worker-prompt-<phase>-<seq>.log <-- live stdout+stderr mirror
505
- ```
506
-
507
- The log is truncated at each dispatch (`: > "$log_path"`) — only the latest run for a given seq is preserved. Different seqs keep separate files. Long-running implementation dispatches can produce multi-MB logs; analysis-phase dispatches are typically smaller.
508
-
509
- ### logs.1 — Inventory
510
-
511
- ```bash
512
- okstra log-report --project-root <projectRoot> --json
513
- ```
514
-
515
- Scans `<projectRoot>/.okstra/tasks/**/runs/*/prompts/*.log` and parses task/phase/worker/seq from each path. Returns (sizes are **raw bytes**, mtimes **epoch seconds**):
516
- - `topLargest[]` — `{path, sizeBytes, mtimeEpoch, taskKey, taskGroup, taskId, phase, worker, seq}`, size desc (widen with `--top <N>`)
517
- - `perTask[]` — `{taskKey, fileCount, totalBytes, oldestEpoch, newestEpoch}`, total-size desc
518
- - `totals` — `{fileCount, totalBytes, taskCount}`
519
-
520
- If `totals.fileCount` is 0, report `No wrapper log files found under <projectRoot>` and stop.
521
-
522
- ### logs.2 — Summary tables
523
-
524
- Render from the CLI output (format bytes → KB/MB; epoch → `Nd`/`Nh` relative to now):
525
-
526
- **Table A — Top largest logs** (from `topLargest`): `| # | Task | Phase | Worker | Seq | Size | Age | Path |`.
527
-
528
- **Table B — Per-task totals** (from `perTask`): `| Task Key | Files | Total Size | Oldest | Newest |`.
529
-
530
- **Footer:** `Total: <fileCount> files, <totalBytes→MB> across <taskCount> tasks under <PROJECT_ROOT>`.
531
-
532
- ### logs.3 — Suggested cleanup commands
533
-
534
- Emit a fenced bash block the user can copy-paste. Do NOT execute. Each block pairs a dry-run preview (`-print`) with the destructive (`-delete`) command:
535
-
536
- ```markdown
537
- ## Cleanup options (manual)
538
-
539
- # 7일 이상 된 로그만 삭제
540
- find <PROJECT_ROOT>/.okstra/tasks \
541
- -type f -path '*/runs/*/prompts/*.log' -mtime +7 -print # dry-run
542
- find <PROJECT_ROOT>/.okstra/tasks \
543
- -type f -path '*/runs/*/prompts/*.log' -mtime +7 -delete
544
-
545
- # 30일 이상 된 로그만 삭제
546
- find <PROJECT_ROOT>/.okstra/tasks \
547
- -type f -path '*/runs/*/prompts/*.log' -mtime +30 -print # dry-run
548
- find <PROJECT_ROOT>/.okstra/tasks \
549
- -type f -path '*/runs/*/prompts/*.log' -mtime +30 -delete
550
-
551
- # 특정 task-group 의 로그 일괄 삭제 (예: dev-9388)
552
- find <PROJECT_ROOT>/.okstra/tasks/dev-9388 \
553
- -type f -name '*.log' -print # dry-run
554
- find <PROJECT_ROOT>/.okstra/tasks/dev-9388 \
555
- -type f -name '*.log' -delete
556
-
557
- # 특정 task-id 의 로그 일괄 삭제 (예: dev-9428)
558
- find <PROJECT_ROOT>/.okstra/tasks/*/dev-9428 \
559
- -type f -name '*.log' -print # dry-run
560
- find <PROJECT_ROOT>/.okstra/tasks/*/dev-9428 \
561
- -type f -name '*.log' -delete
562
-
563
- # 전체 일괄 삭제 (주의)
564
- find <PROJECT_ROOT>/.okstra/tasks \
565
- -type f -path '*/runs/*/prompts/*.log' -print # dry-run
566
- find <PROJECT_ROOT>/.okstra/tasks \
567
- -type f -path '*/runs/*/prompts/*.log' -delete
568
- ```
569
-
570
- Substitute the literal `<PROJECT_ROOT>` with the resolved absolute path so the commands are directly copy-pasteable.
571
-
572
- ### logs.4 — Notes for the user
573
-
574
- - Logs are truncated on each re-dispatch of the same `seq`, so deleting an in-flight run's log will cause the wrapper to recreate an empty file on the next dispatch — no data loss beyond the current trace.
575
- - **If a dispatch is currently running, check the `status` sub-command first** and avoid deleting logs for tasks in `in-progress` state — you will lose the live trace for the active run.
576
- - Prompt history files (`.md`) are separate and are NOT touched by these commands — only `.log` sidecars.
577
- - This sub-command **does not modify any external files itself**, including `.gitignore`. If the project commits `.okstra/`, the user may want to add `.okstra/tasks/**/runs/**/prompts/*.log` to `.gitignore` manually to keep large logs out of git.
578
-
579
- ---
580
-
581
- ## errors
582
-
583
- Trigger phrases: "okstra errors", "error report", "에러 리포트", "에러 보고", "에러 모아줘", "실패 로그 정리".
584
-
585
- Aggregate a task's okstra-run error logs (`runs/*/logs/errors-*.jsonl`, lead-observed + worker-reported) into a timestamped markdown report and summarize it. This sub-command renders a **read-derived artifact** (a `.md` file) but never mutates task state (`task-manifest.json`, catalog, timeline).
586
-
587
- ### errors.1 — Resolve target
588
-
589
- Accepted target forms (same as `cost`):
590
-
591
- 1. Full task-key: `<project-id>:<task-group>:<task-id>`.
592
- 2. Bare token (task-id or task-group) — resolve via `okstra resolve-task-key <token> --project-root <projectRoot> --json` and branch on `matches[]` using the standard 0/1/N rule from `cost.1` (0 = not found, 1 = use, N = picker).
593
- 3. Task root path.
594
-
595
- 사용자가 task 를 지정하지 않고 그냥 에러 리포트를 요청하면, `cost.1` 과 동일한 no-task fallback 을 적용한다 — 플레이스홀더 형식만 나열하고 되묻지 않는다: `.okstra/discovery/task-catalog.json` 을 읽어 task 가 1개면 그대로 사용하고, 여러 개면 `updatedAt` 최신 10개를 실제 task-key 로 나열해 물어본다(추측 금지).
596
-
597
- ### errors.2 — Run the renderer
598
-
599
- Use the CLI output as the source of truth:
600
-
601
- ```bash
602
- okstra error-report <resolved-target> --project-root <projectRoot>
603
- ```
604
-
605
- For a task-root path, run `okstra error-report <path>` directly. Do not parse the jsonl files by hand unless the CLI fails and the user explicitly asks for a manual fallback.
606
-
607
- ### errors.3 — Summarize output
608
-
609
- Parse the stdout JSON and report:
610
-
611
- | Field | Source |
612
- |---|---|
613
- | Report file | `reportPath` (project-root-relative `.md`) |
614
- | Total errors | `totals.errorCount` |
615
- | Logs (runs) | `totals.runCount` |
616
- | By errorType | `totals.byErrorType` (tool-failure / cli-failure / contract-violation) |
617
- | By source | `totals.bySource` (lead-observed / worker-reported) |
618
- | By phase | `byPhase[]` |
619
- | By agent | `byAgent[]` |
620
- | Parse-skipped lines | `parseSkipped` |
621
-
622
- - If `reportPath` is empty AND `totals.errorCount == 0`: report `이 task 에는 기록된 에러 로그가 없습니다.` and do not claim a file was written.
623
- - Otherwise show the `.md` path and offer to read it.
624
- - If `parseSkipped > 0`, surface it (do not silently hide malformed lines).
625
-
626
- ### errors — Output template
627
-
628
- ```markdown
629
- ## okstra Error Report — <task-key>
630
-
631
- - Report: `<reportPath-or-->`
632
- - Total errors: <N> across <runCount> log(s)
633
- - By type: <tool-failure: a, cli-failure: b, ...>
634
- - By source: <lead-observed: x, worker-reported: y>
635
-
636
- | Phase | Count |
637
- |---|---:|
638
- | implementation | 3 |
639
-
640
- | Agent | Count |
641
- |---|---:|
642
- | codex-worker | 2 |
643
-
644
- <If parseSkipped > 0: "⚠ 파싱 건너뛴 줄: <N>">
645
- ```
646
-
647
- ---
648
-
649
- ## error-zip
650
-
651
- Trigger phrases: "okstra error-zip", "에러 zip", "에러 환류", "에러 번들", "cross-project 에러".
652
-
653
- 머신 내 모든 타겟 프로젝트의 okstra 실행 에러(`runs/*/logs/errors-*.jsonl`)를 글로벌 run-index 로 수집·익명화해 `.zip`(집계 리포트 + 익명화 JSONL)으로 묶는다. 모든 타겟의 `.okstra/` 에 대해 read-only.
654
-
655
- ### error-zip.1 — 출력 경로 결정 (picker)
656
-
657
- 이전 출력 경로를 `~/.okstra/error-zip.json` 의 `lastOutputPath` 에서 읽는다. 그 값이 있으면 3-옵션 picker 로 제시:
658
-
659
- 1. (이전 경로 있음) `<lastOutputPath>` 재사용 — 추천, 첫 옵션.
660
- (이전 경로 없음) 기본 경로 `~/okstra-error-feedback-<YYYY-MM-DD>.zip` 제안 — 추천.
661
- 2. 직접 입력 (항상 마지막 옵션).
662
-
663
- `~/.okstra/error-zip.json` 읽기는 Read 로 직접 수행한다(없으면 이전 경로 없음으로 간주).
664
-
665
- ### error-zip.2 — 렌더 실행
666
-
667
- ```bash
668
- okstra error-zip --out <결정된 경로>
669
- ```
670
-
671
- ### error-zip.3 — 요약 보고
672
-
673
- stdout JSON 을 파싱해 보고:
674
-
675
- | 필드 | 출처 |
676
- |---|---|
677
- | 출력 zip | `outPath` |
678
- | 총 에러 | `errorCount` |
679
- | 로그(run) | `runCount` |
680
- | 도달 불가 run | `unreachableRuns` |
681
- | 클러스터 수 | `clusterCount` |
682
- | 프로젝트 수 | `projectCount` |
683
-
684
- - `unreachableRuns > 0` 이면 표면화한다(침묵 누락 금지).
685
- - 끝에 다음 단계를 안내한다: "이 zip 으로 okstra 자신을 고치려면 `/okstra-brief-gen` 의 error-feedback variant 로 brief 를 만든 뒤 `okstra-run --task-type error-analysis` 를 okstra 레포에서 실행하세요."
686
-
687
- ---
688
-
689
- ## recap
690
-
691
- Trigger phrases: "okstra recap", "recap", "작업 요약", "이 task 요약", "전후 요약", "이 작업 설명해줘", "task 질문".
692
-
693
- task-id 하나에 쌓인 `.okstra` 산출물 위에서 (a) 처리 전/후 요약을 내고, (b) 그 작업에 대한 자유 질문에 답한다. 기본은 `.okstra/` 서브트리만 읽는다(artifact 모드). 사용자가 명시적으로 코드 변경까지 봐 달라고 요청할 때만 code 모드로 확장한다. 이 sub-command 는 `recap/recap-log.jsonl` append 와 `notes/` 노트 작성(recap.5)만 수행하고 `task-manifest.json` / catalog / timeline 은 절대 mutate 하지 않는다.
694
-
695
- ### recap.1 — Resolve target
696
-
697
- `cost` / `errors` 와 동일한 3형식: ① full task-key, ② bare token(task-id 또는 task-group → `okstra resolve-task-key <token> --project-root <projectRoot> --json`; `cost.1` 의 표준 0/1/N 분기), ③ task-root path.
698
-
699
- 사용자가 task 를 지정하지 않고 그냥 recap 을 요청하면(예: "이 작업 요약해줘"), `cost.1` 과 동일한 no-task fallback 을 적용한다 — 절대 `<task-group>` 같은 플레이스홀더 형식만 나열하고 되묻지 않는다:
700
-
701
- 1. `.okstra/discovery/task-catalog.json` 을 읽는다.
702
- 2. task 가 정확히 1개면 그 task 를 그대로 사용한다.
703
- 3. 여러 개면 `updatedAt` 최신 10개를 실제 task-key 로 나열하고 어느 task 인지 물어본다(추측 금지).
704
-
705
- ### recap.2 — Assemble the before/after summary
706
-
707
- CLI 출력을 source of truth 로 쓴다:
708
-
709
- ```bash
710
- okstra recap assemble <resolved-target> --project-root <projectRoot>
711
- ```
712
-
713
- stdout JSON(`{taskKey, runCount, transitions[], latestPhaseStates}`)을 파싱해 run 단위 전/후 전이를 서술한다. 각 `transitions[]` 항목의 `fromPhase → toPhase`(직전 run currentPhase → 이번 run currentPhase), `status`, `lastCompletedPhase`, `nextRecommendedPhase`, `reportPath` 를 시간순으로 보여준다. `runCount == 0` 이면 "이 task 에는 기록된 run 이 없습니다." 라고만 답하고 파일을 읽었다고 주장하지 않는다.
714
-
715
- `reportPath` 가 있는 run 은 사용자가 더 깊은 요약을 원할 때만 해당 final-report 를 읽어 보강한다(자동으로 모두 읽지 않는다).
716
-
717
- ### recap.3 — Free-form Q&A loop
718
-
719
- 요약 출력 뒤 사용자의 임의 질문을 받는다.
720
-
721
- - **artifact 모드(기본)**: `.okstra/` 산출물(timeline, task-manifest, 해당 run 의 final-report)만 근거로 답한다. 모든 사실 주장은 `.okstra` 파일 `path:line` 인용을 동반한다.
722
- - **code 모드(opt-in)**: 사용자가 "코드 변경까지 / diff 까지 봐줘" 류로 **명시적으로 요청**할 때만, 해당 task 의 worktree/브랜치에서 `git diff` · `git log` 를 추가로 읽어 코드 레벨로 답한다. recap 진입만으로는 절대 code 모드로 넘어가지 않는다.
723
-
724
- ### recap.4 — Persist each turn
725
-
726
- 요약 1건과 각 Q&A 답변을 로그에 남긴다(append-only):
727
-
728
- ```bash
729
- okstra recap record <resolved-target> --project-root <projectRoot> \
730
- --kind <summary|qa> --mode <artifact|code> \
731
- --question "<질문 또는 생략>" --answer "<한두 줄 요약>" \
732
- --citation "<path:line>" --citation "<path:line>"
733
- ```
734
-
735
- `--answer` 에는 전체 답변 전사가 아니라 한두 줄 요약을 넣는다. 인용이 여러 개면 `--citation` 을 반복한다. 기록 실패(비정상 종료)는 조용히 넘기지 말고 사용자에게 알린다.
736
-
737
- ### recap — Output template
738
-
739
- ```markdown
740
- ## okstra Recap — <task-key>
741
-
742
- 전/후 요약 (runs: <N>):
743
-
744
- | # | when | task-type | from → to | status | next |
745
- |---|---|---|---|---|---|
746
- | 1 | <ts> | requirements-discovery | (시작) → requirements-discovery | done | error-analysis |
747
-
748
- <이후 자유 질문을 받습니다. 코드 변경까지 보려면 "diff 까지 봐줘" 라고 요청하세요.>
749
- ```
750
-
751
- ### recap.5 — Agent 저작 노트 (`notes/`)
752
-
753
- recap 중에 네가 **okstra task 를 위해** 만든 산출물 — 검증 증거, 설계·decision 초안, 분석 노트 — 이 앞으로의 run 에 근거로 쓰일 수 있고, 그 내용이 **렌더된 report 도 아니고 사용자 결정도 아닐 때**만 `notes/` 에 남긴다.
754
-
755
- **소유권으로 라우팅한다.** 지금 쓰려는 것이 아래 어디에 해당하는지 먼저 판별한다:
756
-
757
- | 내용 | 레인(경로) | 저작 주체 |
758
- |---|---|---|
759
- | final report 본문 | `runs/<type>/reports/*.md` (`*.data.json` 에서 렌더) | okstra 렌더러 — 절대 수기 편집 금지 |
760
- | clarification 에 대한 사용자 답변 | `runs/<type>/user-responses/*.md` (`created-by: user`) | 사용자만 |
761
- | 확정된 ADR | `.okstra/decisions/NNNN-*.md` | 승격·관리됨 |
762
- | **Agent 증거 / 설계 초안 / 분석** | **`.okstra/tasks/<group>/<id>/notes/`** | 너(AI) |
763
-
764
- 렌더 산출물도, 사용자 결정도 아닌 **너 자신의 근거·초안**이면 `notes/` 로 간다. 노트는 손으로 찍지 말고 CLI 로 쓴다 — 경로·frontmatter·날짜를 코드가 보장하고, 다음 run 에 넘길 인자를 출력해 준다:
765
-
766
- ```bash
767
- okstra recap note <resolved-target> --project-root <projectRoot> \
768
- --kind <verification-evidence|decision-draft|analysis-note> \
769
- --slug <short-topic-slug> \
770
- --purpose "<한 줄 — 무엇이며 어느 run/decision 에 투입되는지>" \
771
- --scope-note "<한 줄 — 무엇이 아닌지, 예: C-00x 에 대한 사용자 결정이 아님>" \
772
- --body-file <노트 본문 마크다운 경로>
773
- ```
774
-
775
- 본문은 먼저 scratchpad 에 마크다운으로 쓴 뒤 `--body-file` 로 넘긴다(짧으면 `--body "<md>"` 인라인도 가능). CLI 는 `.okstra/tasks/<group>/<id>/notes/<slug>-<YYYY-MM-DD>.md` 를 생성하고 stdout JSON 으로 `notePath` 와 `clarificationResponseArg` 를 돌려준다.
776
-
777
- **self-check 규칙 (validator 가 없으니 스스로 지킨다):**
778
-
779
- 1. **렌더된 report 를 수기 편집하지 않는다** (`runs/*/reports/` 아래 `*.md` / `*.data.json` / `*.html`). `data.json` 에서 재생성되므로 편집은 덮어써지고 SSOT 와 어긋난다. 발견 내용은 `notes/` 에 쓴다.
780
- 2. **`user-responses/` 파일을 작성하거나 `created-by: user` 를 달지 않는다.** 그건 사용자의 결정이며, 대신 쓰면 사용자가 내리지 않은 결정을 위조하는 것이다. 네 증거가 특정 답을 뒷받침하면 `notes/` 에 그렇게 적고 결정은 사용자에게 맡긴다. 단, `okstra-user-response` 스킬의 에코백 확인 흐름은 예외다 — 그 흐름에서는 사용자가 세션에서 직접 결정을 내리고 CLI 는 전사 채널일 뿐이므로, `write` 로 `created-by: user` sidecar 를 기록하는 것이 정당하다. 이 예외는 사용자가 명시적으로 "맞음" 을 확인한 뒤에만, verbatim 입력에 대해서만 성립한다.
781
- 3. **okstra 관리 디렉토리에 쓰지 않는다** (`runs/`, `instruction-set/`, `history/`, `recap/`, `.okstra/decisions/`). `notes/` 만이 agent 소유 레인이다. `recap/recap-log.jsonl` 은 예외적으로 쓰되 손으로 편집하지 말고 `okstra recap record` CLI 로만 남긴다.
782
- 4. **`notes/` 는 okstra 에 inert 하다** — 어떤 run 도 자동으로 읽지 않는다. 작성 후 CLI 가 출력한 `clarificationResponseArg`(예: `--clarification-response <notePath>`)를 사용자에게 그대로 알려, 다음 run 을 그 인자와 함께 실행해야만 반영된다는 것을 전한다.
783
-
784
- **Guardrail:** `.okstra/` 는 gitignored 다 — `notes/` 는 로컬 scratch 로 취급하고 절대 `git add` 하지 않는다. 새 노트 생성은 되돌리기 쉬우니(파일 삭제), 생성물이나 사용자 소유 파일을 편집하는 것보다 항상 이쪽을 택한다.
785
-
786
- ---
78
+ 3. If multiple tasks exist, show the latest 10 by `updatedAt` as real task-keys and ask which one. Do not guess, and do not answer with placeholder forms only.
787
79
 
788
80
  ## Output Rules (shared)
789
81