okstra 0.62.0 → 0.64.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 (36) hide show
  1. package/docs/kr/architecture.md +1 -1
  2. package/docs/superpowers/plans/2026-06-09-implementation-run-artifact-stage-isolation.md +320 -0
  3. package/docs/superpowers/plans/2026-06-10-lead-worker-completion-polling-PROBE.md +42 -0
  4. package/docs/superpowers/plans/2026-06-10-lead-worker-completion-polling.md +337 -0
  5. package/docs/superpowers/specs/2026-06-09-executor-model-custom-id-cascade-design.md +66 -0
  6. package/docs/superpowers/specs/2026-06-09-implementation-run-artifact-stage-isolation-design.md +87 -0
  7. package/docs/superpowers/specs/2026-06-10-lead-worker-completion-polling-design.md +113 -0
  8. package/package.json +1 -1
  9. package/runtime/BUILD.json +2 -2
  10. package/runtime/agents/SKILL.md +5 -2
  11. package/runtime/agents/TODO.md +9 -2
  12. package/runtime/agents/workers/claude-worker.md +7 -3
  13. package/runtime/agents/workers/codex-worker.md +6 -2
  14. package/runtime/agents/workers/gemini-worker.md +6 -2
  15. package/runtime/agents/workers/report-writer-worker.md +6 -1
  16. package/runtime/bin/lib/okstra-ctl/cmd-rerun.sh +23 -4
  17. package/runtime/prompts/profiles/implementation-planning.md +1 -1
  18. package/runtime/prompts/wizard/prompts.ko.json +17 -1
  19. package/runtime/python/okstra_ctl/backfill.py +23 -4
  20. package/runtime/python/okstra_ctl/consumers.py +118 -1
  21. package/runtime/python/okstra_ctl/paths.py +11 -0
  22. package/runtime/python/okstra_ctl/run.py +147 -67
  23. package/runtime/python/okstra_ctl/run_context.py +2 -0
  24. package/runtime/python/okstra_ctl/wizard.py +127 -29
  25. package/runtime/skills/okstra-convergence/SKILL.md +3 -1
  26. package/runtime/skills/okstra-report-writer/SKILL.md +2 -0
  27. package/runtime/skills/okstra-run/SKILL.md +1 -1
  28. package/runtime/skills/okstra-team-contract/SKILL.md +37 -0
  29. package/runtime/templates/reports/final-report.template.md +6 -9
  30. package/runtime/templates/reports/i18n/en.json +1 -1
  31. package/runtime/templates/reports/i18n/ko.json +1 -1
  32. package/runtime/templates/worker-prompt-preamble.md +10 -0
  33. package/runtime/validators/validate-implementation-plan-stages.py +10 -5
  34. package/runtime/validators/validate-run.py +20 -3
  35. package/src/install.mjs +21 -0
  36. package/src/uninstall.mjs +17 -17
@@ -151,7 +151,7 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
151
151
  {%- endif %}
152
152
 
153
153
  {% if header.taskType == 'implementation-planning' %}
154
- ## 5.5 Implementation Plan Deliverables
154
+ ## 5.4 Implementation Plan Deliverables
155
155
 
156
156
  ### Option Candidates{% if t("sectionAside.optionCandidates") != "Option Candidates" %} ({{ t("sectionAside.optionCandidates") }}){% endif %}
157
157
 
@@ -235,18 +235,15 @@ implementation-option: {{ frontmatter.implementationOption | yaml_scalar }}
235
235
  {% for stage in implementationPlanning.stages %}
236
236
  ## 5.5.{{ stage.stage }} Stage {{ stage.stage }}: {{ stage.title }}
237
237
 
238
- Slice value: {{ stage.sliceValue }}
239
-
240
- Acceptance: {{ stage.acceptance }}
241
-
238
+ - **Slice value:** {{ stage.sliceValue }}
239
+ - **Acceptance:** {{ stage.acceptance }}
242
240
  {% if stage.conformanceTests %}
243
- Conformance tests: stage-{{ stage.stage }} — {{ stage.conformanceTests }}
241
+ - **Conformance tests:** stage-{{ stage.stage }} — {{ stage.conformanceTests }}
244
242
  {% else %}
245
- Conformance exemption: {{ stage.conformanceExemption }}
243
+ - **Conformance exemption:** {{ stage.conformanceExemption }}
246
244
  {% endif %}
247
245
  {% if stage.tddExemption %}
248
-
249
- TDD exemption: {{ stage.tddExemption }}
246
+ - **TDD exemption:** {{ stage.tddExemption }}
250
247
  {% endif %}
251
248
 
252
249
  ### Carry-In
@@ -68,7 +68,7 @@
68
68
  "columnRelatedIds": "Related item IDs"
69
69
  },
70
70
  "finalVerdict": {
71
- "intro": "Final conclusion and recommended direction. `Direction` values: `continue-investigation / begin-implementation / approve / reject / hold`. When `task-type` is `final-verification`, `Verdict Token` is one of `accepted / conditional-accept / blocked` and serves as the `release-handoff` entry gate. For all other task-types: `not-applicable`."
71
+ "intro": "This run's final conclusion and next action. **`Direction`** is the recommended next action — one of `continue-investigation`, `begin-implementation`, `approve`, `reject`, or `hold` and is present for every task-type. **`Verdict Token`** is meaningful only for the `final-verification` task-type, where it is one of `accepted`, `conditional-accept`, or `blocked` and serves as the `release-handoff` entry gate. For every other task-type, `Verdict Token` is always `not-applicable`."
72
72
  },
73
73
  "evidence": {
74
74
  "sourceItemsColumnNote": "The `Source items` column is described in §6.1."
@@ -68,7 +68,7 @@
68
68
  "columnRelatedIds": "관련 항목 IDs"
69
69
  },
70
70
  "finalVerdict": {
71
- "intro": "최종 결론과 권장 방향입니다. `Direction` 값: `continue-investigation / begin-implementation / approve / reject / hold`. `task-type` `final-verification` `Verdict Token` `accepted / conditional-accept / blocked` 중 하나이며 `release-handoff` 진입 게이트로 쓰입니다. 그 외 task-type 에서는 `not-applicable`."
71
+ "intro": "이 run 의 최종 결론과 다음 행동입니다. **`Direction`** 권장 다음 행동으로 `continue-investigation`(조사 계속) · `begin-implementation`(구현 시작) · `approve`(승인) · `reject`(반려) · `hold`(보류) 중 하나이며, 모든 task-type 존재합니다. **`Verdict Token`** 은 `final-verification` task-type 에서만 의미를 가집니다 `accepted` · `conditional-accept` · `blocked` 중 하나로 `release-handoff` 진입 게이트로 쓰입니다. 그 외 task-type 에서 `Verdict Token` 은 항상 `not-applicable`(해당 없음) 입니다."
72
72
  },
73
73
  "evidence": {
74
74
  "sourceItemsColumnNote": "`Source items` 열 설명은 §6.1 과 동일합니다."
@@ -114,6 +114,16 @@ For task types `requirements-discovery`, `error-analysis`, `implementation-plann
114
114
 
115
115
  See `skills/okstra-team-contract/SKILL.md` "Worker Output Contract" for the full frontmatter schema and section ordering rules. This preamble is consistent with that contract; the team-contract document is authoritative if the two ever diverge.
116
116
 
117
+ ## Return message to the lead (all workers)
118
+
119
+ The short message you return inline to the lead — the text shown to the user as your agent box — MUST begin with one line identifying the model you ran under, copied verbatim from the `**Model:** <Role>, <modelExecutionValue>` line in your dispatch prompt:
120
+
121
+ ```
122
+ **Model:** <Role>, <modelExecutionValue>
123
+ ```
124
+
125
+ Emit that line first, then your normal status / summary text on the following lines. This keeps the acting model visible in every worker box — across every provider (Claude / Codex / Gemini / report-writer) and every dispatch type (initial analysis, convergence reverify, report authoring). Copy the value exactly; never guess, abbreviate, or substitute a provider default. If your prompt carries no `**Model:**` line, say so in the return message rather than inventing one. Each worker spec applies this rule at its own concrete return point — see `agents/workers/claude-worker.md` "Stop Condition", `agents/workers/_cli-wrapper-template.md` step 8d, and `agents/workers/report-writer-worker.md` "Authoring Contract".
126
+
117
127
  ## Writing style (all prose output — analysis workers + report-writer)
118
128
 
119
129
  When you write in Korean (`Report Language: ko` / `meta.reportLanguage = "ko"`), write so a Korean developer understands it on first read. Translate the **meaning**, never the dictionary word.
@@ -162,11 +162,16 @@ def _check_each_stage_section(text: str, stages: List[StageMeta]) -> List[Valida
162
162
  return errs
163
163
 
164
164
 
165
- SLICE_VALUE = re.compile(r"^\s*Slice value\s*:\s*(.+?)\s*$", re.M)
166
- ACCEPTANCE = re.compile(r"^\s*Acceptance\s*:\s*(.+?)\s*$", re.M)
167
- TDD_EXEMPTION = re.compile(r"^\s*TDD exemption\s*:\s*\S", re.M)
168
- CONFORMANCE_TESTS = re.compile(r"^\s*Conformance tests\s*:\s*\S", re.M)
169
- CONFORMANCE_EXEMPTION = re.compile(r"^\s*Conformance exemption\s*:\s*\S", re.M)
165
+ # Each label line may be plain (`Slice value: …`) or rendered as a bold-label
166
+ # bullet (`- **Slice value:** …`). The optional `(?:[-*]\s+)?` bullet marker and
167
+ # the two optional `\*\*` groups accept both forms; the bold wraps the colon, so
168
+ # the closing `**` sits AFTER the colon.
169
+ _LABEL_PREFIX = r"^\s*(?:[-*]\s+)?(?:\*\*)?"
170
+ SLICE_VALUE = re.compile(_LABEL_PREFIX + r"Slice value\s*:\s*(?:\*\*)?\s*(.+?)\s*$", re.M)
171
+ ACCEPTANCE = re.compile(_LABEL_PREFIX + r"Acceptance\s*:\s*(?:\*\*)?\s*(.+?)\s*$", re.M)
172
+ TDD_EXEMPTION = re.compile(_LABEL_PREFIX + r"TDD exemption\s*:\s*(?:\*\*)?\s*\S", re.M)
173
+ CONFORMANCE_TESTS = re.compile(_LABEL_PREFIX + r"Conformance tests\s*:\s*(?:\*\*)?\s*\S", re.M)
174
+ CONFORMANCE_EXEMPTION = re.compile(_LABEL_PREFIX + r"Conformance exemption\s*:\s*(?:\*\*)?\s*\S", re.M)
170
175
 
171
176
 
172
177
  def _check_slice_tdd(text: str, stages: List[StageMeta]) -> List[ValidationError]:
@@ -748,6 +748,20 @@ def _parse_diff_summary_files(content: str) -> list[str]:
748
748
  return _DIFF_ROW_PATH_RE.findall(section.group(0))
749
749
 
750
750
 
751
+ def _task_root_from_run_dir(run_dir: Path) -> Path:
752
+ """run_dir 에서 `runs` 디렉터리를 앵커로 task_root 를 복원한다.
753
+
754
+ 일반 task-type: run_dir = task_root/runs/<task-type> → task_root.
755
+ implementation(stage 격리): run_dir = task_root/runs/implementation/stage-<N>
756
+ → 같은 task_root. `runs` 를 위로 탐색하므로 stage-<N> 레벨이 추가돼도
757
+ 안전하다. `runs` 가 조상에 없으면 기존 동작(두 단계 위)로 폴백한다.
758
+ """
759
+ for parent in run_dir.parents:
760
+ if parent.name == "runs":
761
+ return parent.parent
762
+ return run_dir.parent.parent
763
+
764
+
751
765
  def _validate_conformance(report_path: Path, failures: list[str],
752
766
  surface_patterns: object = None) -> None:
753
767
  """Tier 3 conformance 게이트(implementation / final-verification).
@@ -759,10 +773,13 @@ def _validate_conformance(report_path: Path, failures: list[str],
759
773
  """
760
774
  # conformance 산출물은 task-level(<task_root>/qa)에 있어 planning/
761
775
  # implementation/final-verification 가 공유한다. report_path 는
762
- # task_root/runs/<task-type>/reports/final-report.md 이므로 task_root 는
763
- # run_dir(=report_path.parent.parent)에서 단계 위.
776
+ # task_root/runs/<task-type>/reports/final-report.md (implementation
777
+ # stage 격리로 runs/implementation/stage-<N>/reports/...) 이므로 고정 parent
778
+ # 카운트 대신 `runs` 디렉터리를 앵커로 task_root 를 찾는다 — stage-<N> 레벨이
779
+ # 있어도 task_root/qa 로 정확히 떨어진다.
764
780
  run_dir = report_path.parent.parent
765
- qa_dir = run_dir.parent.parent / "qa"
781
+ task_root = _task_root_from_run_dir(run_dir)
782
+ qa_dir = task_root / "qa"
766
783
  manifest_path = qa_dir / "conformance-manifest.json"
767
784
  if not manifest_path.is_file():
768
785
  return
package/src/install.mjs CHANGED
@@ -44,6 +44,7 @@ Usage:
44
44
 
45
45
  Effect (copy mode):
46
46
  ${"$HOME"}/.okstra/lib/python <- runtime/python
47
+ ${"$HOME"}/.okstra/lib/validators <- runtime/validators (stage/report structure validators)
47
48
  ${"$HOME"}/.okstra/bin <- runtime/bin
48
49
  ${"$HOME"}/.okstra/templates <- runtime/templates (report.css / report.js / *.template.md)
49
50
  ${"$HOME"}/.okstra/templates/settings.local.json <- runtime/templates/reports/settings.template.json
@@ -577,12 +578,27 @@ export async function runInstall(args) {
577
578
  join(paths.home, "schemas"),
578
579
  { refresh: opts.refresh, dryRun: opts.dryRun, mode: 0o644 },
579
580
  );
581
+ // validators/ tree — validate-implementation-plan-stages.py (+ lib/) is
582
+ // resolved at runtime by run.py / validate-run.py via the installed package's
583
+ // `parents[2]/validators`, i.e. ~/.okstra/lib/validators. Without this step a
584
+ // copy-mode UPGRADE bumps the version stamp but never refreshes the
585
+ // validators, so a stale validator (e.g. a pre-renumber section schema)
586
+ // survives and render-bundle's stage-structure check diverges from the
587
+ // current template (observed: render-bundle demanding `## 4.5 Stage Map`
588
+ // while the report + wizard validator use `## 5.5`). Mode 0o755 preserves the
589
+ // executable .sh helpers in the tree.
590
+ const validatorsResult = await copyTreeIfChanged(
591
+ join(runtimeRoot, "validators"),
592
+ join(paths.home, "lib", "validators"),
593
+ { refresh: opts.refresh, dryRun: opts.dryRun, mode: 0o755 },
594
+ );
580
595
 
581
596
  if (!opts.quiet) {
582
597
  summarise("python", pythonResult, paths.pythonpath);
583
598
  summarise("bin", binResult, paths.bin);
584
599
  summarise("templates", templatesResult, join(paths.home, "templates"));
585
600
  summarise("schemas", schemasResult, join(paths.home, "schemas"));
601
+ summarise("validators", validatorsResult, join(paths.home, "lib", "validators"));
586
602
  }
587
603
 
588
604
  if (pythonResult.missingSource && binResult.missingSource) {
@@ -600,6 +616,11 @@ export async function runInstall(args) {
600
616
  "warning: runtime/schemas is empty. final-report schema validation + excerpt generation will be unavailable — re-run the build step.\n",
601
617
  );
602
618
  }
619
+ if (validatorsResult.missingSource) {
620
+ process.stderr.write(
621
+ "warning: runtime/validators is empty. stage-structure / report validation will use stale or missing validators — re-run the build step.\n",
622
+ );
623
+ }
603
624
 
604
625
  const skillResult = await installSkillsCopy(runtimeRoot, opts);
605
626
  await writeSkillsManifest(paths.home, skillResult.installed, { dryRun: opts.dryRun });
package/src/uninstall.mjs CHANGED
@@ -53,10 +53,10 @@ const AGENTS_MANIFEST_REL = "installed-agents.json";
53
53
  const USAGE = `okstra uninstall — remove installed runtime from ~/.okstra, ~/.claude/skills, ~/.claude/agents
54
54
 
55
55
  Usage:
56
- okstra uninstall Remove ~/.okstra/{lib, bin/<known>, version,
56
+ okstra uninstall Remove ~/.okstra/{lib (python + validators),
57
+ bin/<known>, schemas, templates, version,
57
58
  dev-link, installed-skills.json,
58
- installed-agents.json,
59
- templates/settings.local.json} AND
59
+ installed-agents.json} AND
60
60
  ~/.claude/skills/<name> AND
61
61
  ~/.claude/agents/<worker>.md for every
62
62
  entry in the install manifests (fallback:
@@ -156,6 +156,12 @@ export async function runUninstall(args) {
156
156
  process.stdout.write(` home: ${paths.home}\n`);
157
157
  }
158
158
  await removePath(paths.pythonpath, opts);
159
+ // lib/validators — installed wholesale by copy mode (runtime/validators).
160
+ // Without removing it, `lib` stays non-empty so the rmdir below is skipped and
161
+ // a stale validator survives uninstall → reinstall, diverging from the current
162
+ // template's section schema (observed: a pre-renumber `## 4.5 Stage Map`
163
+ // validator outliving every later install).
164
+ await removePath(join(paths.home, "lib", "validators"), opts);
159
165
  for (const name of BIN_ENTRYPOINTS) {
160
166
  await removePath(join(paths.bin, name), opts);
161
167
  }
@@ -174,6 +180,10 @@ export async function runUninstall(args) {
174
180
  }
175
181
  }
176
182
 
183
+ // schemas/ tree — installed by copy mode (runtime/schemas). Older uninstall
184
+ // never removed it, leaving a stale final-report schema behind.
185
+ await removePath(join(paths.home, "schemas"), opts);
186
+
177
187
  // Remove the skills we own. Manifest is authoritative; fall back to the
178
188
  // hard-coded okstra-* names if it is missing (e.g. an install from an old
179
189
  // version that did not write the manifest).
@@ -196,24 +206,14 @@ export async function runUninstall(args) {
196
206
  }
197
207
  await removePath(join(paths.home, AGENTS_MANIFEST_REL), opts);
198
208
 
199
- await removePath(join(paths.home, "templates", "settings.local.json"), opts);
209
+ // templates/ tree — installed wholesale by copy mode (runtime/templates),
210
+ // including the seeded settings.local.json sidecar. Remove the whole tree so
211
+ // an upgrade/reinstall never serves a stale report.css / *.template.md.
212
+ await removePath(join(paths.home, "templates"), opts);
200
213
  // Per-project <PROJECT>/.claude/settings.local.json symlinks are NOT removed
201
214
  // here — uninstall is machine-level and does not know which projects opted
202
215
  // in. They will dangle until the user removes them manually or re-runs
203
216
  // okstra install + okstra setup.
204
- // Remove templates/ if now empty.
205
- const templatesDir = join(paths.home, "templates");
206
- if (await pathExists(templatesDir)) {
207
- try {
208
- const entries = await fs.readdir(templatesDir);
209
- if (entries.length === 0) {
210
- if (!opts.dryRun) await fs.rmdir(templatesDir);
211
- if (!opts.quiet) process.stdout.write(` removed empty: ${templatesDir}\n`);
212
- }
213
- } catch {
214
- /* ignore */
215
- }
216
- }
217
217
 
218
218
  await removePath(join(paths.home, "version"), opts);
219
219
  await removePath(join(paths.home, "dev-link"), opts);