okstra 0.20.0 → 0.21.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 (39) hide show
  1. package/docs/kr/architecture.md +1 -1
  2. package/docs/kr/performance-improvement-plan-v2.md +330 -0
  3. package/docs/kr/performance-improvement-plan.md +125 -0
  4. package/docs/project-structure-overview.md +386 -0
  5. package/docs/superpowers/plans/2026-05-14-convergence-queue-pruning.md +1568 -0
  6. package/package.json +1 -1
  7. package/runtime/BUILD.json +2 -2
  8. package/runtime/agents/SKILL.md +7 -1
  9. package/runtime/agents/workers/codex-worker.md +6 -4
  10. package/runtime/agents/workers/gemini-worker.md +6 -4
  11. package/runtime/agents/workers/report-writer-worker.md +4 -0
  12. package/runtime/bin/okstra-codex-exec.sh +36 -6
  13. package/runtime/bin/okstra-gemini-exec.sh +6 -8
  14. package/runtime/prompts/profiles/final-verification.md +8 -2
  15. package/runtime/prompts/profiles/implementation-planning.md +1 -1
  16. package/runtime/prompts/profiles/release-handoff.md +26 -28
  17. package/runtime/prompts/profiles/requirements-discovery.md +1 -1
  18. package/runtime/python/okstra_ctl/render.py +78 -4
  19. package/runtime/python/okstra_ctl/run.py +0 -6
  20. package/runtime/python/okstra_ctl/run_context.py +5 -0
  21. package/runtime/python/okstra_ctl/workflow.py +8 -7
  22. package/runtime/python/okstra_ctl/worktree.py +155 -15
  23. package/runtime/python/okstra_token_usage/blocks.py +0 -2
  24. package/runtime/python/okstra_token_usage/claude.py +0 -2
  25. package/runtime/skills/okstra-brief/SKILL.md +523 -0
  26. package/runtime/skills/okstra-convergence/SKILL.md +149 -37
  27. package/runtime/skills/okstra-report-writer/SKILL.md +8 -6
  28. package/runtime/templates/prd/brief.template.md +12 -0
  29. package/runtime/templates/project-docs/task-index.template.md +12 -0
  30. package/runtime/templates/reports/error-analysis-input.template.md +12 -0
  31. package/runtime/templates/reports/final-report.template.md +39 -12
  32. package/runtime/templates/reports/final-verification-input.template.md +22 -0
  33. package/runtime/templates/reports/implementation-input.template.md +12 -0
  34. package/runtime/templates/reports/implementation-planning-input.template.md +12 -0
  35. package/runtime/templates/reports/quick-input.template.md +12 -0
  36. package/runtime/templates/reports/release-handoff-input.template.md +23 -10
  37. package/runtime/templates/reports/schedule.template.md +12 -0
  38. package/runtime/templates/reports/settings.template.json +83 -30
  39. package/runtime/templates/reports/task-brief.template.md +12 -0
@@ -0,0 +1,523 @@
1
+ ---
2
+ name: okstra-brief
3
+ description: Use when the user wants to generate a task brief file for okstra from a requirements document, an existing markdown file, an issue-tracker ticket (Linear / Jira / GitHub / Notion), a link URL, conversation context, or short user input. Produces the markdown brief consumed by `okstra-run` Step 5 (task-brief). Trigger words include "okstra brief", "make a brief", "brief 생성", "요구사항 brief 만들어", "okstra 입력 만들어", "task brief 작성", "이 티켓으로 brief", "이 링크로 brief".
4
+ ---
5
+
6
+ # okstra-brief
7
+
8
+ Generate a single `task brief` markdown file under the okstra project domain
9
+ (`.project-docs/okstra/briefs/<task-group>/<task-id>.md`) so it can be fed to
10
+ [`okstra-run`](../okstra-run/SKILL.md) as `--task-brief`.
11
+
12
+ This skill produces **only the brief** — a structured "request slip". It does
13
+ NOT write a PRD, decompose work into vertical slices, or decide modules.
14
+ Those belong to later okstra phases (`requirements-discovery`,
15
+ `implementation-planning`) which use cross-verification and would conflict
16
+ with anything decided here.
17
+
18
+ ## Intended chain
19
+
20
+ ```
21
+ okstra-brief
22
+ → okstra-run (requirements-discovery | error-analysis)
23
+ → okstra-run (implementation-planning)
24
+ → okstra-run (implementation)
25
+ → okstra-run (final-verification)
26
+ → okstra-run (release-handoff)
27
+ ```
28
+
29
+ ## When to use
30
+
31
+ - The user pastes / points at a requirements doc, support ticket, bug report,
32
+ or rough description and wants to start the okstra pipeline.
33
+ - The user asks to "make a brief", "okstra 입력 만들어", "brief 생성", etc.
34
+ - A previous okstra task produced findings the user wants to convert into a
35
+ new follow-up task's brief.
36
+
37
+ ## When NOT to use
38
+
39
+ - The user already has a brief file at a known path → skip to `okstra-run`.
40
+ - The user wants to write a full PRD with user stories and implementation
41
+ decisions → that's `to-prd`, not this. okstra phases will generate the
42
+ equivalent artifacts with cross-verification.
43
+ - The user wants to split work into issues → that's `to-issues`, and within
44
+ okstra it's the job of `implementation-planning`.
45
+
46
+ ## Authority files
47
+
48
+ - `<PROJECT_ROOT>/.project-docs/okstra/project.json` — must exist; if not,
49
+ ask the user to run `okstra-setup` first.
50
+ - `<PROJECT_ROOT>/.project-docs/okstra/briefs/<task-group>/<ticket-id>-<file-title>.md`
51
+ (single / parent) or
52
+ `<PROJECT_ROOT>/.project-docs/okstra/briefs/<task-group>/sub/<ticket-id>-<file-title>.md`
53
+ (tracker child ticket; at depth N, `sub/` is nested N times) — output
54
+ location for this skill. `<ticket-id>` is the raw issue-id when the source
55
+ is a tracker, otherwise free-text supplied by the user. `<file-title>` is
56
+ auto-slugified from the tracker title, otherwise from user input. Never
57
+ write outside this tree. Never use `.scratch/` (that belongs to `to-prd` /
58
+ `to-issues`).
59
+
60
+ ## Step 0: Resolve project root
61
+
62
+ Reuse the same disk-only resolution rule as `okstra-run`:
63
+
64
+ ```bash
65
+ if command -v okstra >/dev/null 2>&1; then
66
+ OKSTRA_CMD="okstra"
67
+ else
68
+ OKSTRA_CMD="npx -y okstra@latest"
69
+ fi
70
+ $OKSTRA_CMD check-project --cwd "$(pwd)"
71
+ ```
72
+
73
+ - `ok: true` → use `projectRoot`.
74
+ - `ok: false` → `AskUserQuestion` (free text) for an absolute project root, then
75
+ re-run `okstra check-project --cwd <input>`. If still not ok, tell the user
76
+ to run `okstra-setup` and stop.
77
+
78
+ ## Step 1: Choose input source
79
+
80
+ `AskUserQuestion` (tool constraint: max 4 options):
81
+
82
+ - **Label**: "Source of this brief?"
83
+ - **Options** (single-select):
84
+ 1. `File` — path to an existing markdown / txt / issue-export file
85
+ 2. `Issue tracker ticket` — Linear / Jira / GitHub Issue / Notion key or URL
86
+ 3. `Link URL` — general web page, blog, spec doc, design doc, etc.
87
+ 4. `User input` — synthesize from the current conversation context, or
88
+ accept a short free-text from the user
89
+ (default: conversation synthesis. If context is thin, fall back to a
90
+ single free-text question.)
91
+
92
+ If the user wants to bundle multiple sources into one brief (e.g. "this
93
+ Linear ticket + this PR link"), repeat this step to collect them. Preserve
94
+ each source's raw content **separately** under the `Source Material
95
+ (verbatim)` section in Step 5.
96
+
97
+ ### 1a. `File`
98
+
99
+ Take an absolute or project-relative path via free-text. Read the entire
100
+ file. Re-ask if it doesn't exist. Captured content is preserved **verbatim**
101
+ in Source Material.
102
+
103
+ ### 1b. `Issue tracker ticket`
104
+
105
+ Order of operations:
106
+
107
+ 1. `AskUserQuestion` (free text):
108
+ `"Ticket key or URL (e.g. LIN-1234, PROJ-42, https://linear.app/..., https://your.atlassian.net/browse/...)"` →
109
+ `ticket_ref`.
110
+ 2. Auto-detect the tracker by URL host or key prefix (`linear.app` → Linear,
111
+ `atlassian.net` or `JIRA_*` pattern → Jira, `github.com/.../issues/` →
112
+ GitHub, `notion.so` → Notion). If detection fails, ask once more via
113
+ `AskUserQuestion`.
114
+ 3. Check whether the tracker's **MCP tools** are loaded in the current
115
+ session and prefer them when available:
116
+ - Linear → `mcp__linear__*` family (e.g. `get_issue` /
117
+ `getIssueByIdentifier`). If absent, try `ToolSearch` with the keyword
118
+ `linear` — it auto-waits for connecting MCP servers.
119
+ - Jira → `mcp__jira__*` (or `mcp__atlassian__*`). Same `ToolSearch`
120
+ fallback.
121
+ - GitHub → `gh issue view <num> --repo <owner>/<repo> --json title,body,comments,labels,state`
122
+ (an authenticated `gh` CLI is the default path — usable even when MCP
123
+ is present). If `gh` is missing or unauthenticated, try `ToolSearch`
124
+ for `mcp__github` family; otherwise fall through to step 4 (paste /
125
+ skip).
126
+ - Notion → `mcp__notion__API-retrieve-a-page` /
127
+ `API-get-block-children`.
128
+ 4. If **no** MCP or CLI path is available:
129
+ - Ask the user via `AskUserQuestion`:
130
+ `"<tracker> MCP is not connected. (a) Paste the ticket body here / (b) Skip this source"`.
131
+ - Never invent ticket content.
132
+ 5. Preserve the retrieved fields (summary/title, description body, comments,
133
+ status, labels, assignee, linked issues) **verbatim** in Source Material.
134
+ When format conversion is required (e.g. Jira ADF → MD), **do not
135
+ paraphrase semantics** — format-only conversion is allowed. Note the
136
+ conversion on one line (`format: Jira ADF → Markdown (semantics
137
+ preserved)`).
138
+
139
+ 6. **Sub-ticket / child-issue / sub-task handling — recursive**:
140
+ - Look at child references in the fetched ticket response:
141
+ - Linear: `children` / `subIssues` field
142
+ - Jira: `subtasks` (or `issuelinks` with the `is parent of` relation)
143
+ - GitHub: task-list checkbox `#NNN` references + `tracked_issues`
144
+ (when present)
145
+ - Notion: child pages / sub-pages
146
+ - If there is **one or more** child, ask **once at the top parent**:
147
+ - `AskUserQuestion` (single-select)
148
+ - **Label**: `"This ticket has sub-tickets. How should the child tree be handled?"`
149
+ - **Options**:
150
+ 1. `Generate the full tree recursively (recommended)` — walk
151
+ children, grandchildren, … via BFS/DFS and emit one brief per
152
+ node.
153
+ 2. `Parent only; list children in Related Artifacts` — single
154
+ brief. Children are not fetched, only their keys/URLs are
155
+ recorded.
156
+ 3. `Generate selected children only` — multi-select the direct
157
+ children; for each chosen child, apply option-1 policy
158
+ recursively to that branch.
159
+ - For options 1 / 3, **recurse into Step 1b sub-steps 3–5** for every
160
+ descendant. No depth limit. Maintain a visited set of
161
+ `<tracker>:<ticket-id>` to prevent cycles; on revisit, do not emit a
162
+ new brief — only add a link back to the existing brief.
163
+ - Each descendant's brief file at depth N is created under
164
+ `<task-group>/<sub/ nested N times>/<ticket-id>-<file-title>.md` per
165
+ Step 2b.
166
+ - Each node's `Related Artifacts` should list:
167
+ - The parent brief's relative path (`../<ticket-id>-<file-title>.md`)
168
+ - All direct children briefs' relative paths
169
+ (`sub/<ticket-id>-<file-title>.md`)
170
+ bidirectionally. (Non-direct ancestors/descendants are tracked via the
171
+ frontmatter `parent-id` chain.)
172
+ - If there are no children, proceed with a single brief (depth 0).
173
+
174
+ ### 1c. `Link URL`
175
+
176
+ Order of operations:
177
+
178
+ 1. `AskUserQuestion` (free text): `"Link URL"`.
179
+ 2. Fetch directly via `WebFetch`. If `WebFetch` is deferred, load its schema
180
+ first via `ToolSearch` with `select:WebFetch`.
181
+ 3. On fetch failure or auth required (login wall, intranet-only, etc.):
182
+ - Ask the user via `AskUserQuestion` to paste the body.
183
+ - Never invent URL content.
184
+ 4. Preserve the core content (title + body + quotes/examples) **verbatim**
185
+ in Source Material. For very large pages, respect the ~400-line per-brief
186
+ guideline and **excerpt key paragraphs only**, but do not modify or
187
+ summarize the excerpted text by a single character. Annotate the excerpt
188
+ with `[excerpt — full: <URL>]`.
189
+
190
+ ### 1d. `User input`
191
+
192
+ Two sub-modes combined under one option.
193
+
194
+ - **Conversation synthesis**: do not re-interview. Synthesize from the
195
+ current conversation (mirrors `to-prd`'s "synthesize, don't interview").
196
+ Label Source Material as `conversation synthesis` and quote key
197
+ utterances verbatim wherever possible.
198
+ - **Inline input**: if conversation context is empty or thin, take one
199
+ free-text `AskUserQuestion`. The received text is preserved in Source
200
+ Material without changing a character.
201
+
202
+ When both are used, record them as two separate sub-sources under Source
203
+ Material.
204
+
205
+ ## Step 2: Identify task key & filename
206
+
207
+ ### 2a. Task group
208
+
209
+ `AskUserQuestion` (free text):
210
+
211
+ - `"Task group (e.g. backend-api, INV-1234, refactor)"` → `task_group`
212
+
213
+ Slug rule: same as `okstra-run` Step 3 — slugify, must have at least one
214
+ alphanumeric character.
215
+
216
+ ### 2b. Filename per source type
217
+
218
+ Final brief path rule (fixed; one extra `sub/` per depth):
219
+
220
+ ```
221
+ depth 0 (parent / single) : <PROJECT_ROOT>/.project-docs/okstra/briefs/<task-group>/<ticket-id>-<file-title>.md
222
+ depth 1 (child) : <PROJECT_ROOT>/.project-docs/okstra/briefs/<task-group>/sub/<ticket-id>-<file-title>.md
223
+ depth 2 (grandchild) : <PROJECT_ROOT>/.project-docs/okstra/briefs/<task-group>/sub/sub/<ticket-id>-<file-title>.md
224
+ depth N : <PROJECT_ROOT>/.project-docs/okstra/briefs/<task-group>/<sub/ nested N times>/<ticket-id>-<file-title>.md
225
+ ```
226
+
227
+ `<sub/ nested N times>` is a meta-notation; the actual path concatenates
228
+ `sub/` N times (e.g. depth 3 → `sub/sub/sub/`). Create the directories
229
+ recursively as needed. No upper bound on tree depth. All parent-child
230
+ relations are also tracked via the brief's `parent-id` frontmatter
231
+ (directory layout + frontmatter both).
232
+
233
+ `<ticket-id>` resolution:
234
+
235
+ - **Issue-tracker sources (Step 1b)**: the **raw issue-id / ticket-id**
236
+ assigned by the tracker.
237
+ - Linear `LIN-1234` → `LIN-1234`
238
+ - Jira `PROJ-42` → `PROJ-42`
239
+ - GitHub `owner/repo#123` → `gh-<repo>-123`
240
+ - Notion (no canonical id) → `notion-<8-char-page-short-id>`
241
+ - **File / URL / User-input sources**: `AskUserQuestion` (free text):
242
+ - `"Ticket / identifier (e.g. dev-9043, INV-1234, login-error)"` →
243
+ `ticket_id`
244
+
245
+ `<file-title>` resolution:
246
+
247
+ - **Issue-tracker sources**: auto-generated from the ticket title / summary —
248
+ do not ask the user.
249
+ 1. Keep alphanumerics, spaces, Hangul; drop everything else
250
+ 2. Replace spaces with `-`
251
+ 3. Lowercase (Hangul untouched)
252
+ 4. Cut to 60 chars at a word boundary (hard-cut at 60 if no boundary)
253
+ 5. Trim leading/trailing `-`
254
+ - Example: Linear `LIN-1234 "Fix flaky login retry on 5xx"` →
255
+ `LIN-1234-fix-flaky-login-retry-on-5xx.md`
256
+ - **Other sources**: `AskUserQuestion` (free text):
257
+ - `"File title (short summary, e.g. fix-login-retry, dev-9043-brief)"` →
258
+ `file_title`
259
+ - Apply rules 1–5 above. Re-ask on empty input.
260
+
261
+ Validate the full `<ticket-id>-<file-title>` slug: must have at least one
262
+ alphanumeric character after slugification. Apply Step 2c on collision.
263
+
264
+ ### 2c. Collision handling
265
+
266
+ If a brief file already exists, `AskUserQuestion` with (`Skip if exists` /
267
+ `Append timestamp suffix` / `Overwrite`). Default recommendation: `Skip if
268
+ exists` — protects briefs the user has already edited.
269
+
270
+ When multiple collisions occur in tracker multi-generation mode:
271
+
272
+ - First echo the colliding file list to the user.
273
+ - Ask **once** with `AskUserQuestion` for a bulk policy.
274
+ - **Even when `Overwrite` is selected, confirm one more time for each
275
+ colliding file**, or warn that downgrading to `Append timestamp suffix`
276
+ is safer. Silent bulk overwrite is forbidden.
277
+
278
+ ## Step 3: Light context scan (optional but recommended)
279
+
280
+ Only when the source material references project-specific terms or files:
281
+
282
+ - Read `CONTEXT.md` / `docs/adr/` at repo root if present (domain vocabulary).
283
+ - Resolve any file paths or symbols mentioned in the source so the brief uses
284
+ the project's domain language, not generic phrasing.
285
+
286
+ Skip this step for purely external request material.
287
+
288
+ ## Step 4: Fill in missing fields (NO full interview)
289
+
290
+ > **Verbatim-source rule**: Source Material collected in Step 1 must never
291
+ > be paraphrased, summarized, or restructured. When supplementing missing
292
+ > information, write only into the dedicated `Augmentation` section (see
293
+ > Step 5); never modify Source Material by a single byte.
294
+
295
+ Inspect the synthesized brief draft. For each REQUIRED section below that is
296
+ empty or trivially thin **after** reading Source Material verbatim, ask **at
297
+ most one** `AskUserQuestion` (free text) to fill it. Never ask about sections
298
+ already covered by the source material.
299
+
300
+ Augmentation content must always be confined to the `Augmentation` section
301
+ or to a `> augmented:` blockquote inside the required section, so it is
302
+ clear that the content is the skill's / user's added interpretation rather
303
+ than the original source.
304
+
305
+ ### Step 4 policy under multi-brief (tracker recursion) mode
306
+
307
+ When emitting a parent + N children at once, repeating the Step 4 interview
308
+ per brief ruins the UX. In this mode:
309
+
310
+ - **Run Step 4 interview only on the parent (depth 0) brief.**
311
+ - **Skip Step 4 prompts for children (depth ≥ 1)** — fill only their Source
312
+ Material. If required sections are empty, leave them as `_(none)_`. The
313
+ safer default is to let `requirements-discovery` fill child gaps later.
314
+ - Only when the user explicitly says "interview the children too" do we run
315
+ per-child Step 4.
316
+
317
+ Required sections:
318
+
319
+ - **Context** — background; what system/feature, who's affected, why now.
320
+ - **Problem / Symptom** — current state. For bugs: repro + observed/expected;
321
+ for greenfield: gap between current and desired.
322
+ - **Desired Outcome** — success shape, not a solution prescription.
323
+ - **Constraints** — deadlines, compatibility, technical/operational limits.
324
+ - **Related Artifacts** — files, URLs, issues, prior task-keys.
325
+ - **Open Questions** — anything the user already flagged as undecided
326
+ (becomes raw material for `requirements-discovery`).
327
+
328
+ Sections **deliberately omitted** (do NOT add them, do NOT prompt for them):
329
+
330
+ - User stories
331
+ - Module decomposition / implementation decisions
332
+ - Testing strategy
333
+ - Vertical-slice breakdown
334
+
335
+ Those belong to later okstra phases.
336
+
337
+ ## Step 5: Write the brief(s)
338
+
339
+ Use the same template per brief file. In tracker mode producing a parent
340
+ plus N children, you write **N+1 files** (each carries only its own Source
341
+ Material).
342
+
343
+ Use this exact template. Leave a section's body as `_(none)_` rather than
344
+ fabricating content. (The outer fence uses 4 backticks so it does not
345
+ collide with the inner 3-backtick code block.)
346
+
347
+ ````markdown
348
+ ---
349
+ type: brief
350
+ brief-id: <ticket-id>-<file-title> # equals the filename stem
351
+ parent-id: self # `self` at root, otherwise parent's brief-id
352
+ ticket-id: <LIN-1234 | PROJ-42 | gh-repo-123 | notion-abcdef12 | "">
353
+ source-type: <file | linear | jira | github | notion | url | user-input>
354
+ task-group: <task-group>
355
+ depth: 0 # 0=parent/single, 1=child, 2=grandchild, ...
356
+ created: <YYYY-MM-DD>
357
+ generator: okstra-brief
358
+ ---
359
+
360
+ # Task Brief: <task_group>/<filename-without-ext>
361
+
362
+ > Generated: okstra-brief · <YYYY-MM-DD>
363
+ > Source type: <file | linear | jira | github | notion | url | user-input>
364
+ > Tracker key (if any): <LIN-1234 | PROJ-42 | gh-repo-123 | notion-abcdef12>
365
+ > Parent brief (child briefs only): <relative path>
366
+ > Recommended next phase: <requirements-discovery | error-analysis> ← from Step 6
367
+
368
+ ## Source Material (verbatim — do not modify)
369
+
370
+ Paste each source separately and as-is. No paraphrasing, summarizing, or
371
+ restructuring. Format conversion (e.g. Jira ADF → Markdown) is allowed and
372
+ must be annotated in the header meta.
373
+
374
+ ### Source 1 — <type: file | linear | jira | github | notion | url | user-input>
375
+
376
+ - ref: <abs file path | LIN-1234 | https://... | "conversation synthesis">
377
+ - fetched-via: <Read | mcp__linear__getIssue | mcp__notion__... | gh issue view | WebFetch | user-paste>
378
+ - fetched-at: <YYYY-MM-DD HH:MM>
379
+ - format: <as-is | "Jira ADF → Markdown (semantics preserved)" | "excerpt — full: <URL>">
380
+
381
+ ```
382
+ <Paste the raw source here without changing a single character.>
383
+ ```
384
+
385
+ ### Source 2 — ...
386
+
387
+ (Repeat as needed.)
388
+
389
+ ## Context
390
+
391
+ <Background / scope / why now. If self-evident from Source Material, quote
392
+ it briefly and stop. Use the blockquote below when augmentation is needed.>
393
+
394
+ > augmented: <Interpretation added by the skill or user. Do NOT add any
395
+ > extra interpretation outside this blockquote.>
396
+
397
+ ## Problem / Symptom
398
+
399
+ <Current state. For bugs: repro / observed / expected. For greenfield: gap
400
+ between current and desired. Same source-quote + `> augmented:` rule as
401
+ above.>
402
+
403
+ ## Desired Outcome
404
+
405
+ <Shape of success. Do NOT prescribe a solution — that belongs to
406
+ implementation-planning.>
407
+
408
+ ## Constraints
409
+
410
+ <Deadlines, compatibility, technical/operational limits. Use _(none)_ if
411
+ none.>
412
+
413
+ ## Related Artifacts
414
+
415
+ - <file path / URL / issue / prior task-key>
416
+
417
+ ## Open Questions
418
+
419
+ - <Unresolved questions the user flagged. _(none)_ if none.>
420
+
421
+ ## Augmentation
422
+
423
+ Cross-references / interpretation / context added by the user or skill that
424
+ is not in the original source. May be empty. Keep this section visually
425
+ separated from Source Material — never inline it inside Source Material.
426
+
427
+ - _(none)_ or `- <augmentation>`
428
+ ````
429
+
430
+ ### Frontmatter rules
431
+
432
+ - Every brief starts on line 1 with a YAML frontmatter delimited by `---`.
433
+ - `type: brief` is a fixed value.
434
+ - `brief-id` must match the filename stem (`<ticket-id>-<file-title>`)
435
+ exactly. If the file is moved/renamed, update both.
436
+ - `parent-id`:
437
+ - The root (depth 0) brief uses `self` (or its own `brief-id`).
438
+ - Descendant briefs use the direct parent's `brief-id`.
439
+ - `depth` must equal the number of `sub/` segments in the path (consistency
440
+ check point). On mismatch, the file location wins — correct the
441
+ frontmatter.
442
+ - `ticket-id` is populated only for tracker sources; leave it as an empty
443
+ string otherwise.
444
+
445
+ Echo the file path back on one line. Show the rendered brief to the user
446
+ inline and ask:
447
+
448
+ `AskUserQuestion`: `"Proceed with this brief?"` — options `Save` / `Edit`.
449
+ On `Edit`, return to Step 4 for the section to revise.
450
+
451
+ ## Step 6: Recommend next okstra phase
452
+
453
+ In multi-brief mode, **evaluate each brief independently** with the rules
454
+ below (parent and child recommendations may differ).
455
+
456
+ Inspect the finalized brief and recommend the next `okstra-run` task-type:
457
+
458
+ | Heuristic | Recommendation |
459
+ |---|---|
460
+ | `Problem / Symptom` describes a repro / log / stack trace / observable error | `error-analysis` |
461
+ | `Open Questions` is large or `Desired Outcome` is ambiguous / needs classification | `requirements-discovery` |
462
+ | Both / ambiguous | `requirements-discovery` (safe default — the phase itself decides branching) |
463
+
464
+ Write the chosen recommendation into the `Recommended next phase:` header
465
+ line of the brief file (Step 5). Do NOT auto-spawn `okstra-run` — leave the
466
+ trigger to the user.
467
+
468
+ ## Step 7: Hand off
469
+
470
+ Single brief:
471
+
472
+ ```
473
+ brief saved: <abs-path>
474
+ next: /okstra-run (recommended task-type: <…>)
475
+ ```
476
+
477
+ Multi-brief (tracker parent + children) — echo all in one block. Keep the
478
+ `sub/` prefix visible by emitting the absolute path verbatim:
479
+
480
+ ```
481
+ briefs saved (N):
482
+ - <abs>/<task-group>/<ticket-id>-<title>.md (depth 0 · parent · recommended: <…>)
483
+ - <abs>/<task-group>/sub/<ticket-id>-<title>.md (depth 1 · child · recommended: <…>)
484
+ - <abs>/<task-group>/sub/sub/<ticket-id>-<title>.md (depth 2 · grand · recommended: <…>)
485
+ next: /okstra-run (run a separate task-key per brief — use the filename stem as task-id)
486
+ ```
487
+
488
+ Then stop. Do not invoke `okstra-run` directly — the user chooses when to
489
+ proceed, and they may want to edit the brief externally first. In the
490
+ multi-brief case the user also decides the order in which tasks are
491
+ started.
492
+
493
+ ## Output Rules
494
+
495
+ - Never write outside `<PROJECT_ROOT>/.project-docs/okstra/briefs/`.
496
+ - Never write to `.scratch/` — that path belongs to `to-prd` / `to-issues`.
497
+ - **Verbatim source**: never paraphrase, summarize, restructure, or reorder
498
+ the Source Material section. Only format conversion (ADF → MD, HTML → MD)
499
+ is allowed, and the conversion must be annotated in the `format:` meta.
500
+ Augmentation / interpretation goes only into the `Augmentation` section
501
+ or a `> augmented:` blockquote inside the required section.
502
+ - If the tracker MCP is not connected, do not guess — ask the user to paste
503
+ the body or skip the source.
504
+ - If a URL fetch fails or hits an auth wall, do not guess — ask for a
505
+ paste.
506
+ - Never fabricate content for empty sections; use `_(none)_`.
507
+ - Echo each `AskUserQuestion` outcome on one short line.
508
+ - Aim for ~400 lines per brief total. If the source body is longer, **do
509
+ not modify** it — instead excerpt key paragraphs with the
510
+ `[excerpt — full: <URL>]` marker and record the original location in
511
+ `Related Artifacts`.
512
+
513
+ ## Failure Modes
514
+
515
+ | Symptom | Cause | Fix |
516
+ |---|---|---|
517
+ | `project.json not found` | okstra not set up in this project | Tell user to run `okstra-setup`; stop. |
518
+ | `briefs/<group>/<id>.md already exists` | duplicate task-key | Step 2c overwrite prompt. |
519
+ | Source file not found | bad path in Step 1 | Re-ask. |
520
+ | Linear/Jira MCP not connected | tracker MCP missing from the session | Try `ToolSearch` once → if still missing, ask the user to paste the body. Never guess. |
521
+ | URL fetch fails or 401 / login wall | intranet, paywall, JS-rendered page | Ask the user to paste the core body. Never guess. |
522
+ | Tracker body is in non-MD format (ADF / HTML) | Jira description, Notion blocks | Convert format only, preserve semantics. Annotate the conversion in the `format:` meta. |
523
+ | Brief comes out skeletal after synthesis | source too thin AND user picked `User input` with little context | Switch to inline input mode and ask one or two targeted questions. |