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.
- package/docs/kr/architecture.md +1 -1
- package/docs/superpowers/plans/2026-06-09-implementation-run-artifact-stage-isolation.md +320 -0
- package/docs/superpowers/plans/2026-06-10-lead-worker-completion-polling-PROBE.md +42 -0
- package/docs/superpowers/plans/2026-06-10-lead-worker-completion-polling.md +337 -0
- package/docs/superpowers/specs/2026-06-09-executor-model-custom-id-cascade-design.md +66 -0
- package/docs/superpowers/specs/2026-06-09-implementation-run-artifact-stage-isolation-design.md +87 -0
- package/docs/superpowers/specs/2026-06-10-lead-worker-completion-polling-design.md +113 -0
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/SKILL.md +5 -2
- package/runtime/agents/TODO.md +9 -2
- package/runtime/agents/workers/claude-worker.md +7 -3
- package/runtime/agents/workers/codex-worker.md +6 -2
- package/runtime/agents/workers/gemini-worker.md +6 -2
- package/runtime/agents/workers/report-writer-worker.md +6 -1
- package/runtime/bin/lib/okstra-ctl/cmd-rerun.sh +23 -4
- package/runtime/prompts/profiles/implementation-planning.md +1 -1
- package/runtime/prompts/wizard/prompts.ko.json +17 -1
- package/runtime/python/okstra_ctl/backfill.py +23 -4
- package/runtime/python/okstra_ctl/consumers.py +118 -1
- package/runtime/python/okstra_ctl/paths.py +11 -0
- package/runtime/python/okstra_ctl/run.py +147 -67
- package/runtime/python/okstra_ctl/run_context.py +2 -0
- package/runtime/python/okstra_ctl/wizard.py +127 -29
- package/runtime/skills/okstra-convergence/SKILL.md +3 -1
- package/runtime/skills/okstra-report-writer/SKILL.md +2 -0
- package/runtime/skills/okstra-run/SKILL.md +1 -1
- package/runtime/skills/okstra-team-contract/SKILL.md +37 -0
- package/runtime/templates/reports/final-report.template.md +6 -9
- package/runtime/templates/reports/i18n/en.json +1 -1
- package/runtime/templates/reports/i18n/ko.json +1 -1
- package/runtime/templates/worker-prompt-preamble.md +10 -0
- package/runtime/validators/validate-implementation-plan-stages.py +10 -5
- package/runtime/validators/validate-run.py +20 -3
- package/src/install.mjs +21 -0
- 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.
|
|
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
|
|
239
|
-
|
|
240
|
-
Acceptance: {{ stage.acceptance }}
|
|
241
|
-
|
|
238
|
+
- **Slice value:** {{ stage.sliceValue }}
|
|
239
|
+
- **Acceptance:** {{ stage.acceptance }}
|
|
242
240
|
{% if stage.conformanceTests %}
|
|
243
|
-
Conformance tests
|
|
241
|
+
- **Conformance tests:** stage-{{ stage.stage }} — {{ stage.conformanceTests }}
|
|
244
242
|
{% else %}
|
|
245
|
-
Conformance exemption
|
|
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": "
|
|
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": "최종 결론과
|
|
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
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
|
763
|
-
#
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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);
|