okstra 0.110.0 → 0.112.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 (74) hide show
  1. package/README.kr.md +3 -2
  2. package/README.md +3 -2
  3. package/bin/okstra +7 -1
  4. package/docs/for-ai/README.md +2 -2
  5. package/docs/for-ai/skills/okstra-brief.md +2 -3
  6. package/docs/for-ai/skills/okstra-container-build.md +2 -3
  7. package/docs/for-ai/skills/okstra-inspect.md +5 -12
  8. package/docs/for-ai/skills/okstra-rollup.md +3 -4
  9. package/docs/for-ai/skills/okstra-run.md +10 -10
  10. package/docs/for-ai/skills/okstra-schedule.md +2 -7
  11. package/docs/for-ai/skills/okstra-setup.md +1 -1
  12. package/docs/kr/architecture/storage-model.md +1 -2
  13. package/docs/kr/architecture.md +11 -12
  14. package/docs/kr/cli.md +6 -4
  15. package/docs/project-structure-overview.md +14 -12
  16. package/docs/task-process/README.md +4 -4
  17. package/docs/task-process/common-flow.md +8 -5
  18. package/docs/task-process/implementation.md +4 -5
  19. package/docs/task-process/release-handoff.md +3 -3
  20. package/package.json +1 -1
  21. package/runtime/BUILD.json +2 -2
  22. package/runtime/agents/workers/antigravity-worker.md +1 -0
  23. package/runtime/agents/workers/codex-worker.md +1 -0
  24. package/runtime/prompts/coding-preflight/overview.md +3 -1
  25. package/runtime/prompts/launch.template.md +1 -5
  26. package/runtime/prompts/lead/context-loader.md +2 -4
  27. package/runtime/prompts/lead/convergence.md +11 -240
  28. package/runtime/prompts/lead/okstra-lead-contract.md +70 -34
  29. package/runtime/prompts/lead/plan-body-verification.md +240 -0
  30. package/runtime/prompts/lead/report-writer.md +7 -7
  31. package/runtime/prompts/lead/team-contract.md +15 -17
  32. package/runtime/prompts/profiles/_common-contract.md +2 -38
  33. package/runtime/prompts/profiles/_implementation-diff-review.md +43 -0
  34. package/runtime/prompts/profiles/_implementation-executor.md +9 -11
  35. package/runtime/prompts/profiles/_implementation-self-check.md +11 -5
  36. package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
  37. package/runtime/prompts/profiles/final-verification.md +1 -1
  38. package/runtime/prompts/profiles/implementation-planning.md +2 -2
  39. package/runtime/prompts/profiles/implementation.md +1 -1
  40. package/runtime/python/okstra_ctl/codex_dispatch.py +3 -0
  41. package/runtime/python/okstra_ctl/consumers.py +4 -0
  42. package/runtime/python/okstra_ctl/handoff.py +5 -23
  43. package/runtime/python/okstra_ctl/implementation_stage.py +11 -11
  44. package/runtime/python/okstra_ctl/path_hints.py +1 -0
  45. package/runtime/python/okstra_ctl/paths.py +3 -0
  46. package/runtime/python/okstra_ctl/render.py +8 -0
  47. package/runtime/python/okstra_ctl/report_views.py +76 -26
  48. package/runtime/python/okstra_ctl/set_work_status.py +147 -0
  49. package/runtime/python/okstra_ctl/stage_targets.py +152 -0
  50. package/runtime/python/okstra_ctl/team_reconcile.py +1 -1
  51. package/runtime/python/okstra_ctl/wizard.py +47 -0
  52. package/runtime/python/okstra_project/__init__.py +2 -0
  53. package/runtime/python/okstra_project/state.py +44 -2
  54. package/runtime/skills/okstra-brief/SKILL.md +32 -176
  55. package/runtime/skills/okstra-brief/references/reporter-confirmations.md +71 -0
  56. package/runtime/skills/okstra-brief/references/tracker-recursion.md +90 -0
  57. package/runtime/skills/okstra-container-build/SKILL.md +7 -20
  58. package/runtime/skills/okstra-graphify/SKILL.md +8 -8
  59. package/runtime/skills/okstra-inspect/SKILL.md +27 -43
  60. package/runtime/skills/okstra-rollup/SKILL.md +6 -6
  61. package/runtime/skills/okstra-run/SKILL.md +27 -32
  62. package/runtime/skills/okstra-schedule/SKILL.md +64 -419
  63. package/runtime/skills/okstra-setup/SKILL.md +25 -223
  64. package/runtime/skills/okstra-setup/references/project-config.md +188 -0
  65. package/runtime/templates/reports/schedule.template.md +2 -2
  66. package/runtime/templates/worker-prompt-preamble.md +1 -1
  67. package/runtime/validators/validate-run.py +7 -7
  68. package/src/cli-registry.mjs +14 -0
  69. package/src/commands/execute/wizard.mjs +3 -1
  70. package/src/commands/inspect/set-work-status.mjs +31 -0
  71. package/src/commands/inspect/task-show.mjs +11 -31
  72. package/src/commands/lifecycle/check-project.mjs +68 -56
  73. package/src/commands/lifecycle/install.mjs +1 -0
  74. package/src/commands/lifecycle/preflight.mjs +82 -0
@@ -117,26 +117,15 @@ as source material, and never writes them. Decision files (when raised by
117
117
  via `implementation`):
118
118
  `<PROJECT_ROOT>/.okstra/decisions/<NNNN>-<slug>.md`.
119
119
 
120
- ## Step 0: Resolve project root
120
+ ## Step 0: Preflight
121
121
 
122
- Reuse the same literal-token-first rule as `okstra-run`. Each command below is a **separate Bash tool call** starting with the literal token `okstra` so the `Bash(okstra:*)` permission match succeeds. Do **not** wrap any of them in `if`, `eval`, `export`, `$(...)`, `VAR=...`, `||`, or `&&`, and do **not** introduce a `$OKSTRA_CMD` variable or an `npx -y okstra@latest` fallback — those leading tokens defeat the permission match and force a confirmation prompt on every call.
122
+ Every Bash command in this skill is a **separate 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).
123
123
 
124
124
  ```bash
125
- okstra ensure-installed --runtime claude-code
125
+ okstra preflight --runtime claude-code --json
126
126
  ```
127
127
 
128
- If this exits non-zero, tell the user: "okstra not installed — run `/okstra-setup` first." Then stop. Do **not** try to invoke `npx -y okstra@latest ...` as a fallback.
129
-
130
- ```bash
131
- okstra check-project --json
132
- ```
133
-
134
- Parse the JSON from stdout:
135
-
136
- - `ok: true` → use `projectRoot` from the JSON output. Carry it as a literal string into the steps below.
137
- - `ok: false` → tell the user: "this project has no okstra setup. Run `/okstra-setup` first." Then stop.
138
-
139
- > If the `okstra` binary is not on `PATH` at all, the commands above will not run. In that case tell the user verbatim: "okstra not installed — run `/okstra-setup` first." Do **not** try to invoke `npx -y okstra@latest ...` from this skill — `npx` is not on the literal-token allow-list this skill targets and will force a confirmation prompt on every subsequent call.
128
+ Parse the stdout JSON. `ok: true` → carry `projectRoot` as a literal string into the steps below. `ok: false` (or `okstra` not on `PATH` at all) → tell the user: "okstra not set up — 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). Do **not** try `npx -y okstra@latest ...` as a fallback.
140
129
 
141
130
  ## Step 1: Choose input source
142
131
 
@@ -234,86 +223,15 @@ Order of operations:
234
223
  preserved)`).
235
224
 
236
225
  6. **Sub-ticket / child-issue / sub-task handling — recursive**:
237
- - Look at child references in the fetched ticket response:
238
- - Linear: `children` / `subIssues` field
239
- - Jira: `subtasks` (or `issuelinks` with the `is parent of` relation)
240
- - GitHub: `gh issue view --json title,body,comments,labels,state` only
241
- returns the issue body and comments child relations are NOT in that
242
- JSON. Resolve children via two paths:
243
- 1. **task-list parsing** scan the fetched body and comments for
244
- markdown task-list checkboxes referencing other issues
245
- (`- [ ] #NNN`, `- [x] owner/repo#NNN`, `- [ ] https://github.com/...`).
246
- Capture each as a child candidate.
247
- 2. **tracked / sub-issue GraphQL** — for GitHub Projects v2
248
- "tracked issues" / sub-issue relations, the REST `gh issue view`
249
- response does NOT carry them. Query GraphQL explicitly, e.g.
250
- `gh api graphql -f query='query($o:String!,$r:String!,$n:Int!){repository(owner:$o,name:$r){issue(number:$n){trackedIssues(first:50){nodes{number repository{nameWithOwner}}} subIssues:trackedInIssues(first:50){nodes{number repository{nameWithOwner}}}}}}' -f o=<owner> -f r=<repo> -F n=<num>`.
251
- If the GraphQL request fails (auth scope missing, feature
252
- unavailable on this repo, network error), **do NOT silently
253
- continue with full recursion**. Disable the "Generate the full
254
- tree recursively" option for this branch, default to
255
- `Parent only; list children in Related Artifacts and Related Task
256
- Graph`, and surface the GraphQL failure to the user in one line so
257
- they can decide whether to paste child bodies manually.
258
- - Notion: child pages / sub-pages
259
- - If there is **one or more** child, ask **once at the top parent**:
260
- - `AskUserQuestion` (single-select)
261
- - **Label**: `"This ticket has sub-tickets. How should the child tree be handled?"`
262
- - **Options**:
263
- 1. `Full tree` (recommended) — walk children, grandchildren, …
264
- via BFS/DFS and emit one brief per node.
265
- 2. `Parent only` — single brief. Children are not fetched; their
266
- keys/URLs are recorded under Related Artifacts and as `parent-of`
267
- rows in Related Task Graph.
268
- 3. `Selected` — multi-select the direct children; for each chosen
269
- child, apply option-1 policy recursively to that branch.
270
- - For options 1 / 3, **recurse into Step 1b sub-steps 3–5** for every
271
- descendant. No depth limit. Maintain a visited set of
272
- `<tracker>:<ticket-id>` to prevent cycles; on revisit, do not emit a
273
- new brief — only add a link back to the existing brief.
274
- - **Visited-set across re-runs (rebuild from disk)** — the in-memory
275
- visited set is per-run and disappears between invocations. When this
276
- skill runs again over a tree that already has briefs on disk, it MUST
277
- reseed the visited set by scanning
278
- `<PROJECT_ROOT>/.okstra/briefs/<task-group>/` recursively
279
- and reading each brief's frontmatter `ticket-id` + `source-type`.
280
- Reseed precedence:
281
- 1. On-disk frontmatter (`ticket-id` ≠ "") populates visited entries
282
- as `<source-type>:<ticket-id>` — these win.
283
- 2. Step 2c collision policy (`Skip` / `Append timestamp` /
284
- `Overwrite`) applies to any node whose path is already taken.
285
- 3. The fresh in-memory walk fills any gaps for tickets not yet on
286
- disk.
287
- A ticket present on disk is NOT refetched unless the user explicitly
288
- answers `Overwrite` for that path.
289
- - Each descendant's brief file at depth N is created under
290
- `<task-group>/<sub/ nested N times>/<ticket-id>-<file-title>.md` per
291
- Step 2b.
292
- - Each node's `Related Artifacts` should list:
293
- - The parent brief's relative path (`../<ticket-id>-<file-title>.md`)
294
- - All direct children briefs' relative paths
295
- (`sub/<ticket-id>-<file-title>.md`)
296
- bidirectionally. (Non-direct ancestors/descendants are tracked via the
297
- frontmatter `parent-id` chain.)
298
- - Each generated brief's `Related Task Graph` should contain the same full
299
- split topology, not only the current node's direct edges, so downstream
300
- phases can start from any child brief without losing order or dependency
301
- context. Use one row per source-backed edge with these columns:
302
- `From | Relation | To | Direction | Source | Impact`.
303
- - `From` / `To` use task key, brief id, tracker id, or URL. The edge
304
- direction is `From` → `To`.
305
- - `Relation` is one of `parent-of`, `child-of`, `depends-on`, `blocks`,
306
- `blocked-by`, `follow-up-of`, `split-from`, `duplicates`, `related-to`.
307
- - `Direction` is `directed` for parentage/ordering relations and
308
- `undirected` for `duplicates` / `related-to`.
309
- - `Source` names the evidence for the edge: tracker linked issue,
310
- markdown task-list checkbox, reporter statement, manual split, or prior
311
- okstra task.
312
- - `Impact` states what the next phase must preserve, such as "run after
313
- API contract" or "avoid duplicate implementation".
314
- Do not invent graph edges from filename similarity or topic overlap; if a
315
- relation is unclear, use `related-to` + `undirected` only when the source
316
- explicitly says the items are related.
226
+ - If the fetched ticket response shows **any** child reference (Linear
227
+ `children`/`subIssues`, Jira `subtasks`/`issuelinks`, GitHub task-list
228
+ checkboxes or tracked/sub-issues, Notion child pages), **read
229
+ `references/tracker-recursion.md` next to this SKILL.md and follow it**
230
+ before writing any briefit defines per-tracker child discovery
231
+ (including the GitHub GraphQL query and its failure handling), the
232
+ Full tree / Parent only / Selected picker, the cross-run visited-set
233
+ reseed from on-disk brief frontmatter, and the Related Artifacts /
234
+ Related Task Graph topology every emitted brief must carry.
317
235
  - If there are no children, proceed with a single brief (depth 0).
318
236
 
319
237
  ### 1c. `Link URL`
@@ -584,13 +502,11 @@ project's domain.
584
502
 
585
503
  ## Step 4: Fill in missing fields (NO full interview)
586
504
 
587
- > **Verbatim-source rule**: Source Material collected in Step 1 must never
588
- > be paraphrased, summarized, or restructured. When supplementing missing
589
- > information, write only into the dedicated `Augmentation` section (see
590
- > Step 5); never modify Source Material by a single byte. **Anything
591
- > learned by grilling in this step lands in `Augmentation` or `Open
592
- > Questions` only.** brief is a pre-discovery artifact, not a PRD —
593
- > decisions belong to later phases.
505
+ > **Verbatim-source rule** (the first non-negotiable at the top of this
506
+ > file): never modify Source Material by a single byte. **Anything learned
507
+ > by grilling in this step lands in `Augmentation` or `Open Questions`
508
+ > only.** brief is a pre-discovery artifact, not a PRD decisions belong
509
+ > to later phases.
594
510
 
595
511
  Inspect the synthesized brief draft. For each REQUIRED section below that is
596
512
  empty or trivially thin **after** reading Source Material verbatim, ask **at
@@ -903,72 +819,16 @@ If the list is non-empty, run **one** `AskUserQuestion`:
903
819
  `implementation-planning`); see each phase profile's "Brief
904
820
  consumption" addendum.
905
821
 
906
- ### 6.5c. Batch the questions
907
-
908
- For each pending row, formulate one question. Because the
909
- `AskUserQuestion` tool caps options at 4 per call and questions per call
910
- also at 4, split into batches of ≤ 4 questions.
911
-
912
- **Cap — 12 questions per Step 6.5 run.** When the pending list
913
- exceeds 12 rows, sort by priority and ask only the top 12 this round:
914
-
915
- 1. `conversion-block:` rows first (translation failure — highest
916
- downstream risk).
917
- 2. `intent-check:` rows next, in the order they appear in `Open
918
- Questions`.
919
-
920
- Any rows beyond the cap are left unanswered in this run; set
921
- `reporter-confirmations: partial` in the frontmatter and tell the user
922
- which rows remain. The next `okstra-brief` re-run (or the downstream
923
- phase that consumes `Blocks=next-phase`) handles the remainder.
924
-
925
- Each question carries:
926
-
927
- - The reporter-facing phrasing (plain language, no jargon).
928
- - A recommended answer drawn from the brief's `intent-inference` /
929
- `conversion-block:` content, marked `(Recommended)`.
930
- - 2–3 alternative options when the answer space is naturally enumerable;
931
- otherwise a free-text fallback.
932
-
933
- ### 6.5d. Record verbatim
934
-
935
- Create a new top-level section `## Reporter Confirmations` in the brief
936
- (if not present) with one subsection per answered row:
937
-
938
- ```markdown
939
- ## Reporter Confirmations
940
-
941
- ### RC-1 — <intent-check: or conversion-block: row id / topic>
942
- - asked: <YYYY-MM-DD HH:MM>
943
- - linked-row: `<exact Open Questions row text>`
944
- - answer (verbatim):
945
-
946
- > <reporter's answer, byte-for-byte>
947
- ```
948
-
949
- Mark the linked `Open Questions` row by appending `[CONFIRMED <YYYY-MM-DD> → RC-N]` to the end of the row's line. Do NOT delete the row — the
950
- `CONFIRMED` marker keeps the audit trail intact.
951
-
952
- For every `intent-inference` augmentation whose paired `intent-check:`
953
- was confirmed, add one new line directly below the `> augmented:
954
- intent-inference …` blockquote:
955
-
956
- ```
957
- > confirmed-by-reporter: RC-N — <one-line summary of confirmation>
958
- ```
959
-
960
- This converts the inference from "unverified hypothesis" to "verified
961
- hypothesis" without rewriting the original augmentation.
962
-
963
- ### 6.5e. Frontmatter state
964
-
965
- Update `reporter-confirmations` in the brief frontmatter:
822
+ ### 6.5c–e. Collect, record, and set frontmatter state
966
823
 
967
- - `complete` every pending row was answered.
968
- - `partial` some rows answered, some skipped within this run.
969
- - `skipped` user picked option 2 in 6.5b.
970
- - `pending` should never appear at this point; if it does, treat the
971
- brief as not yet handed off and refuse to proceed.
824
+ When 6.5b answered "Yes collect now", **read
825
+ `references/reporter-confirmations.md` next to this SKILL.md and follow it**:
826
+ it defines the 4-question batching, the 12-question cap with
827
+ `conversion-block:`-first priority, the `## Reporter Confirmations` RC-N
828
+ record shape with `[CONFIRMED <date> RC-N]` row markers and
829
+ `> confirmed-by-reporter:` blockquote lines, and the final
830
+ `reporter-confirmations` frontmatter value (`complete` / `partial` /
831
+ `skipped`).
972
832
 
973
833
  Step 6.5 ends here. The brief now has either zero pending reporter-only
974
834
  rows (`complete` / `partial` with remainder in the same file) or an
@@ -1038,11 +898,11 @@ started.
1038
898
  stays under `<PROJECT_ROOT>/.okstra/` — briefs in `.okstra/briefs/`, glossary
1039
899
  in `.okstra/glossary.md` (Step 4.5). Paths outside that subtree are read-only
1040
900
  source material, and only when the reporter cited them.
1041
- - **Verbatim source**: never paraphrase, summarize, restructure, or reorder
1042
- the Source Material section. Only format conversion (ADF MD, HTML → MD)
1043
- is allowed, and the conversion must be annotated in the `format:` meta.
1044
- Augmentation / interpretation goes only into the `Augmentation` section
1045
- or a `> augmented:` blockquote inside the required section.
901
+ - **Verbatim source** (the two non-negotiables at the top of this file
902
+ govern): no line budget overrides them if source content is large,
903
+ preserve it exactly as separate `Source Material` entries; if a tool
904
+ returns a truncated body, ask the user for the missing content or emit a
905
+ `conversion-block:` row. Never silently excerpt or summarize.
1046
906
  - **No template author-guidance in the artifact body.** The Step 5
1047
907
  template carries author guidance in HTML comments (`<!-- ... -->`);
1048
908
  **strip every such comment when writing the brief** — the final artifact
@@ -1061,10 +921,6 @@ started.
1061
921
  paste.
1062
922
  - Never fabricate content for empty sections; use `_(none)_`.
1063
923
  - Echo each `AskUserQuestion` outcome on one short line.
1064
- - No line budget overrides the verbatim-source rule. If source content is
1065
- large, preserve it exactly as separate `Source Material` entries; if a
1066
- tool only returns a truncated body, ask the user for the missing content
1067
- or emit a `conversion-block:` row. Never silently excerpt or summarize.
1068
924
 
1069
925
  ## Failure Modes
1070
926
 
@@ -0,0 +1,71 @@
1
+ # Reporter batch confirmation — collection procedure (Step 6.5c–e)
2
+
3
+ Read this only when Step 6.5b answered "Yes — collect now" for a non-empty
4
+ pending list of `intent-check:` / `conversion-block:` rows.
5
+
6
+ ## 6.5c. Batch the questions
7
+
8
+ For each pending row, formulate one question. Because the `AskUserQuestion`
9
+ tool caps options at 4 per call and questions per call also at 4, split into
10
+ batches of ≤ 4 questions.
11
+
12
+ **Cap — 12 questions per Step 6.5 run.** When the pending list exceeds 12
13
+ rows, sort by priority and ask only the top 12 this round:
14
+
15
+ 1. `conversion-block:` rows first (translation failure — highest downstream
16
+ risk).
17
+ 2. `intent-check:` rows next, in the order they appear in `Open Questions`.
18
+
19
+ Any rows beyond the cap are left unanswered in this run; set
20
+ `reporter-confirmations: partial` in the frontmatter and tell the user which
21
+ rows remain. The next `okstra-brief` re-run (or the downstream phase that
22
+ consumes `Blocks=next-phase`) handles the remainder.
23
+
24
+ Each question carries:
25
+
26
+ - The reporter-facing phrasing (plain language, no jargon).
27
+ - A recommended answer drawn from the brief's `intent-inference` /
28
+ `conversion-block:` content, marked `(Recommended)`.
29
+ - 2–3 alternative options when the answer space is naturally enumerable;
30
+ otherwise a free-text fallback.
31
+
32
+ ## 6.5d. Record verbatim
33
+
34
+ Create a new top-level section `## Reporter Confirmations` in the brief (if
35
+ not present) with one subsection per answered row:
36
+
37
+ ```markdown
38
+ ## Reporter Confirmations
39
+
40
+ ### RC-1 — <intent-check: or conversion-block: row id / topic>
41
+ - asked: <YYYY-MM-DD HH:MM>
42
+ - linked-row: `<exact Open Questions row text>`
43
+ - answer (verbatim):
44
+
45
+ > <reporter's answer, byte-for-byte>
46
+ ```
47
+
48
+ Mark the linked `Open Questions` row by appending
49
+ `[CONFIRMED <YYYY-MM-DD> → RC-N]` to the end of the row's line. Do NOT delete
50
+ the row — the `CONFIRMED` marker keeps the audit trail intact.
51
+
52
+ For every `intent-inference` augmentation whose paired `intent-check:` was
53
+ confirmed, add one new line directly below the `> augmented: intent-inference …`
54
+ blockquote:
55
+
56
+ ```
57
+ > confirmed-by-reporter: RC-N — <one-line summary of confirmation>
58
+ ```
59
+
60
+ This converts the inference from "unverified hypothesis" to "verified
61
+ hypothesis" without rewriting the original augmentation.
62
+
63
+ ## 6.5e. Frontmatter state
64
+
65
+ Update `reporter-confirmations` in the brief frontmatter:
66
+
67
+ - `complete` — every pending row was answered.
68
+ - `partial` — some rows answered, some skipped within this run.
69
+ - `skipped` — user picked option 2 in 6.5b.
70
+ - `pending` — should never appear at this point; if it does, treat the brief
71
+ as not yet handed off and refuse to proceed.
@@ -0,0 +1,90 @@
1
+ # Sub-ticket / child-issue recursion (tracker sources)
2
+
3
+ Read this only when a fetched tracker ticket shows one or more child references
4
+ (okstra-brief Step 1b sub-step 6). It defines how the child tree is discovered,
5
+ which briefs are emitted, and how re-runs stay idempotent.
6
+
7
+ ## 1. Discover child references per tracker
8
+
9
+ - Linear: `children` / `subIssues` field.
10
+ - Jira: `subtasks` (or `issuelinks` with the `is parent of` relation).
11
+ - GitHub: `gh issue view --json title,body,comments,labels,state` only returns
12
+ the issue body and comments — child relations are NOT in that JSON. Resolve
13
+ children via two paths:
14
+ 1. **task-list parsing** — scan the fetched body and comments for markdown
15
+ task-list checkboxes referencing other issues (`- [ ] #NNN`,
16
+ `- [x] owner/repo#NNN`, `- [ ] https://github.com/...`). Capture each as a
17
+ child candidate.
18
+ 2. **tracked / sub-issue GraphQL** — for GitHub Projects v2 "tracked issues" /
19
+ sub-issue relations, query GraphQL explicitly, e.g.
20
+ `gh api graphql -f query='query($o:String!,$r:String!,$n:Int!){repository(owner:$o,name:$r){issue(number:$n){trackedIssues(first:50){nodes{number repository{nameWithOwner}}} subIssues:trackedInIssues(first:50){nodes{number repository{nameWithOwner}}}}}}' -f o=<owner> -f r=<repo> -F n=<num>`.
21
+ If the GraphQL request fails (auth scope missing, feature unavailable,
22
+ network error), do NOT silently continue with full recursion: disable the
23
+ "Full tree" option for this branch, default to `Parent only`, and surface
24
+ the GraphQL failure to the user in one line so they can decide whether to
25
+ paste child bodies manually.
26
+ - Notion: child pages / sub-pages.
27
+
28
+ ## 2. Ask once at the top parent
29
+
30
+ If there is one or more child, `AskUserQuestion` (single-select):
31
+
32
+ - **Label**: `"This ticket has sub-tickets. How should the child tree be handled?"`
33
+ - **Options**:
34
+ 1. `Full tree` (recommended) — walk children, grandchildren, … via BFS/DFS
35
+ and emit one brief per node.
36
+ 2. `Parent only` — single brief. Children are not fetched; their keys/URLs are
37
+ recorded under Related Artifacts and as `parent-of` rows in Related Task
38
+ Graph.
39
+ 3. `Selected` — multi-select the direct children; for each chosen child,
40
+ apply option-1 policy recursively to that branch.
41
+
42
+ For options 1 / 3, recurse into Step 1b sub-steps 3–5 for every descendant. No
43
+ depth limit. Maintain a visited set of `<tracker>:<ticket-id>` to prevent
44
+ cycles; on revisit, do not emit a new brief — only add a link back to the
45
+ existing brief.
46
+
47
+ ## 3. Visited-set across re-runs (rebuild from disk)
48
+
49
+ The in-memory visited set is per-run. When this skill runs again over a tree
50
+ that already has briefs on disk, reseed the visited set by scanning
51
+ `<PROJECT_ROOT>/.okstra/briefs/<task-group>/` recursively and reading each
52
+ brief's frontmatter `ticket-id` + `source-type`. Reseed precedence:
53
+
54
+ 1. On-disk frontmatter (`ticket-id` ≠ "") populates visited entries as
55
+ `<source-type>:<ticket-id>` — these win.
56
+ 2. Step 2c collision policy (`Skip` / `Append timestamp` / `Overwrite`) applies
57
+ to any node whose path is already taken.
58
+ 3. The fresh in-memory walk fills any gaps for tickets not yet on disk.
59
+
60
+ A ticket present on disk is NOT refetched unless the user explicitly answers
61
+ `Overwrite` for that path.
62
+
63
+ ## 4. Per-node output
64
+
65
+ - Each descendant's brief file at depth N is created under
66
+ `<task-group>/<sub/ nested N times>/<ticket-id>-<file-title>.md` per Step 2b.
67
+ - Each node's `Related Artifacts` lists, bidirectionally:
68
+ - the parent brief's relative path (`../<ticket-id>-<file-title>.md`)
69
+ - all direct children briefs' relative paths
70
+ (`sub/<ticket-id>-<file-title>.md`)
71
+ (Non-direct ancestors/descendants are tracked via the frontmatter
72
+ `parent-id` chain.)
73
+ - Each generated brief's `Related Task Graph` contains the same **full split
74
+ topology**, not only the current node's direct edges, so downstream phases
75
+ can start from any child brief without losing order or dependency context.
76
+ One row per source-backed edge with columns
77
+ `From | Relation | To | Direction | Source | Impact`:
78
+ - `From` / `To` use task key, brief id, tracker id, or URL; edge direction is
79
+ `From` → `To`.
80
+ - `Relation` is one of `parent-of`, `child-of`, `depends-on`, `blocks`,
81
+ `blocked-by`, `follow-up-of`, `split-from`, `duplicates`, `related-to`.
82
+ - `Direction` is `directed` for parentage/ordering relations, `undirected`
83
+ for `duplicates` / `related-to`.
84
+ - `Source` names the evidence for the edge: tracker linked issue, markdown
85
+ task-list checkbox, reporter statement, manual split, or prior okstra task.
86
+ - `Impact` states what the next phase must preserve (e.g. "run after API
87
+ contract", "avoid duplicate implementation").
88
+ Do not invent graph edges from filename similarity or topic overlap; if a
89
+ relation is unclear, use `related-to` + `undirected` only when the source
90
+ explicitly says the items are related.
@@ -16,18 +16,15 @@ Single entry point for the okstra user-test container runtime. okstra provisions
16
16
  | `stop-watcher` | Reap the watcher/tail panes only — the containers keep running. |
17
17
  | `down` | Tear down the containers (label query) and reap orphan watcher panes; `--all` covers every container group in the project. |
18
18
 
19
- ## Step 0: Verify okstra runtime + project setup (shared)
19
+ ## Step 0: Preflight (shared)
20
20
 
21
- Before any sub-command, run each of the following commands as a **separate Bash tool call**. Each command starts with the literal token `okstra` so the `Bash(okstra:*)` permission match succeeds. Do **not** wrap any of them in `if`, `eval`, `export`, `$(...)`, `VAR=...`, `||`, or `&&`, and do **not** introduce a `$OKSTRA_CMD` variable or an `npx -y okstra@latest` fallback — those leading tokens defeat the permission match and force a confirmation prompt on every call. The LLM (you) inspects each command's output and decides what to do next in natural language — never in shell.
21
+ 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):
22
22
 
23
- 1. `okstra ensure-installed --runtime claude-code`
24
- If this exits non-zero, tell the user: "okstra not installed — run `/okstra-setup` first." Then stop. Do **not** try to invoke `npx -y okstra@latest ...` as a fallback.
25
-
26
- 2. `okstra check-project --json`
27
- Reads the project from the current working directory. Parse the JSON from stdout. The shape is `{ok, projectRoot, projectJsonPath, projectId}`.
23
+ ```bash
24
+ okstra preflight --runtime claude-code --json
25
+ ```
28
26
 
29
- - `ok: false` → tell the user: "this project has no okstra setup. Run `/okstra-setup` first." Then stop.
30
- - `ok: true` → carry `projectRoot` as a literal string and pass it as `--project-root <projectRoot>` to every `okstra container` call below.
27
+ Parse the stdout JSON. `ok: true` → carry `projectRoot` as a literal string and pass it as `--project-root <projectRoot>` to every `okstra container` call below. `ok: false` → tell the user to 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).
31
28
 
32
29
  Docker itself must be installed and running for `up`/`status`/`down` to work. If a sub-command fails with a docker connection error, tell the user to start Docker (Desktop / daemon) and retry — do not try to start docker yourself.
33
30
 
@@ -35,7 +32,7 @@ Subsequent `okstra container <subcmd>` calls self-bootstrap their Python path, s
35
32
 
36
33
  ## Step 1: Dispatch by intent
37
34
 
38
- Classify the user's request into one sub-command using the trigger table above.
35
+ Classify the user's request into one sub-command using the sub-command table above.
39
36
 
40
37
  - Clear verbs route directly: "띄워/올려/up" → `up`; "상태/status" → `status`; "로그/logs" → `logs`; "watcher 멈춰/stop-watcher" → `stop-watcher`; "내려/down" → `down`.
41
38
  - If the request is ambiguous (e.g. "okstra container 좀 봐줘"), present a numbered picker. Recommend the 1–2 most likely facets first, and make the **last option always "직접 입력"** so the user can name any facet:
@@ -61,8 +58,6 @@ Every sub-command needs a **task-key** (`<project-id>:<task-group>:<task-id>`)
61
58
 
62
59
  ## up
63
60
 
64
- Trigger phrases: "okstra container up", "컨테이너 띄워", "컨테이너 올려", "유저 테스트 환경 띄워", "docker compose 올려줘", "이 task 컨테이너로 띄워".
65
-
66
61
  Brings up the task's container group: integrates the implementation stages into the task worktree, synthesizes the compose env override, runs `docker compose up -d`, polls healthchecks, and attaches a tmux watcher pane.
67
62
 
68
63
  **Preconditions** (state them if unmet, do not guess):
@@ -84,8 +79,6 @@ After a successful `up`, tell the user how to reach the running build (the compo
84
79
 
85
80
  ## status
86
81
 
87
- Trigger phrases: "okstra container status", "컨테이너 상태", "컨테이너 떠 있어?", "container status".
88
-
89
82
  Reports the running containers (queried by run-trace label — the source of truth for "is it up") plus the watcher metadata recorded in the registry.
90
83
 
91
84
  ```bash
@@ -106,8 +99,6 @@ If `containers` is empty, say the group is not running and offer `up`. Treat the
106
99
 
107
100
  ## logs
108
101
 
109
- Trigger phrases: "okstra container logs", "컨테이너 로그", "container logs", "로그 어디서 봐".
110
-
111
102
  The live log stream lives in the tmux watcher pane, not in a file. This sub-command points at the watcher findings directory and the per-service watcher state.
112
103
 
113
104
  ```bash
@@ -126,8 +117,6 @@ Parse the stdout JSON (`{watchersDir, watchers}`). Report the `watchersDir` host
126
117
 
127
118
  ## stop-watcher
128
119
 
129
- Trigger phrases: "okstra container stop-watcher", "watcher 멈춰", "감시 종료", "stop watcher", "watcher 만 꺼줘".
130
-
131
120
  Reaps the watcher/tail tmux panes for the task **without stopping the containers**. Use when the user wants to silence the monitor but keep the build running.
132
121
 
133
122
  ```bash
@@ -140,8 +129,6 @@ Parse the stdout JSON (`{reapedPanes, note}`). Confirm which panes were reaped a
140
129
 
141
130
  ## down
142
131
 
143
- Trigger phrases: "okstra container down", "컨테이너 내려", "컨테이너 종료", "container down", "전부 내려줘".
144
-
145
132
  Tears down the container group (removes containers found by the run-trace label) and reaps orphan watcher panes.
146
133
 
147
134
  Single task:
@@ -20,17 +20,17 @@ Only `.okstra/**/*.md` (final reports, `decisions/*.md`, `glossary.md`, briefs)
20
20
  | `wiki` | Agent-crawlable wiki (`index.md` + one article per community). |
21
21
  | `mcp` | Start a stdio MCP server exposing the graph to other agents (opt-in — additionally needs `pip install mcp`). |
22
22
 
23
- ## Step 0: Verify okstra runtime + project setup (shared)
23
+ ## Step 0: Preflight (shared)
24
24
 
25
- Before any sub-command, run each command below as a **separate Bash tool call**. Each starts with the literal token `okstra` so the `Bash(okstra:*)` permission match succeeds. Do **not** wrap them in `if`, `eval`, `export`, `$(...)`, `VAR=...`, `||`, `&&`, and do **not** introduce an `$OKSTRA_CMD` variable or an `npx -y okstra@latest` fallback — leading tokens defeat the permission match. You (the LLM) inspect each command's output and decide the next step in natural language, never in shell.
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):
26
26
 
27
- 1. `okstra ensure-installed --runtime claude-code`
28
- If this exits non-zero, tell the user: "okstra not installed — run `/okstra-setup` first." Then stop. Do **not** try `npx -y okstra@latest ...` as a fallback.
27
+ ```bash
28
+ okstra preflight --runtime claude-code --json
29
+ ```
29
30
 
30
- 2. `okstra check-project --json`
31
- Resolves PROJECT_ROOT from the Bash call's cwd, looks for `.okstra/project.json`, and prints `{ok, projectRoot, projectJsonPath, projectId}` (plus a `stage` on failure). Parse the JSON from stdout.
32
- - `ok: true` → carry `projectRoot` as a literal string; it is the `--project-root` value for every sub-command below.
33
- - `ok: false` → before concluding "no setup", ask whether the user pointed at a specific project directory. If so, re-run `okstra check-project --cwd <that-dir> --json` (the leading `okstra` token keeps 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.
31
+ Parse the stdout JSON:
32
+ - `ok: true` carry `projectRoot` as a literal string; it is the `--project-root` value for every sub-command below.
33
+ - `ok: false` → before concluding "no setup", ask whether the user pointed at a specific project directory. If so, re-run `okstra preflight --runtime claude-code --cwd <that-dir> --json`. 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).
34
34
 
35
35
  Every `okstra graphify` call below passes `--project-root <projectRoot>` — `--project-root` is REQUIRED and there is no cwd injection; a bare `okstra graphify build` fails with a usage error.
36
36