okstra 0.71.1 → 0.72.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 +14 -1
- package/docs/kr/cli.md +7 -1
- package/docs/superpowers/plans/2026-06-11-fix-cycle.md +1290 -0
- package/docs/superpowers/specs/2026-06-11-fix-cycle-design.md +94 -0
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/SKILL.md +1 -1
- package/runtime/bin/lib/okstra/cli.sh +5 -1
- package/runtime/bin/lib/okstra/globals.sh +1 -0
- package/runtime/bin/lib/okstra/usage.sh +6 -1
- package/runtime/bin/okstra.sh +1 -0
- package/runtime/prompts/profiles/_implementation-executor.md +1 -1
- package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
- package/runtime/prompts/profiles/implementation-planning.md +2 -2
- package/runtime/prompts/wizard/prompts.ko.json +9 -0
- package/runtime/python/okstra_ctl/analysis_packet.py +17 -0
- package/runtime/python/okstra_ctl/conformance.py +5 -0
- package/runtime/python/okstra_ctl/fix_cycles.py +172 -0
- package/runtime/python/okstra_ctl/render.py +45 -5
- package/runtime/python/okstra_ctl/run.py +93 -0
- package/runtime/python/okstra_ctl/run_context.py +15 -9
- package/runtime/python/okstra_ctl/wizard.py +64 -4
- package/runtime/python/okstra_token_usage/claude.py +64 -8
- package/runtime/python/okstra_token_usage/collect.py +30 -1
- package/runtime/schemas/final-report-v1.0.schema.json +25 -0
- package/runtime/skills/okstra-brief/SKILL.md +8 -0
- package/runtime/skills/okstra-report-writer/SKILL.md +2 -0
- package/runtime/skills/okstra-run/SKILL.md +2 -1
- package/runtime/templates/project-docs/task-index.template.md +1 -0
- package/runtime/templates/reports/final-report.template.md +14 -0
- package/runtime/validators/validate-run.py +81 -4
- package/runtime/validators/validate_session_conformance.py +7 -1
- package/src/render-bundle.mjs +4 -1
|
@@ -334,8 +334,29 @@ def effective_run_task_type(run_manifest: dict, task_manifest: dict) -> str:
|
|
|
334
334
|
).strip()
|
|
335
335
|
|
|
336
336
|
|
|
337
|
+
def _is_legal_concurrent_run_skip(
|
|
338
|
+
team_create: object, concurrent_run_authorized: bool
|
|
339
|
+
) -> bool:
|
|
340
|
+
"""prepare 가 run-manifest 에 동시-run 을 기록한 run 에서만, 렌더 게이트
|
|
341
|
+
("Concurrent-run: no-team background")가 지시한 teamCreate skipped 형태를
|
|
342
|
+
legal 터미널 상태로 인정한다. lead 의 team-state 자기 선언만으로는(앵커
|
|
343
|
+
없이) 열리지 않는다 — 선언이 아닌 prepare-측 사실이 강제 근거다."""
|
|
344
|
+
if not concurrent_run_authorized or not isinstance(team_create, dict):
|
|
345
|
+
return False
|
|
346
|
+
return (
|
|
347
|
+
team_create.get("attempted") is False
|
|
348
|
+
and str(team_create.get("status", "")).strip() == "skipped"
|
|
349
|
+
and str(team_create.get("reason", "")).strip() == "concurrent-run"
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
|
|
337
353
|
def validate_team_state(
|
|
338
|
-
team_state: dict,
|
|
354
|
+
team_state: dict,
|
|
355
|
+
project_root: Path,
|
|
356
|
+
contract: dict,
|
|
357
|
+
failures: list[str],
|
|
358
|
+
*,
|
|
359
|
+
concurrent_run_authorized: bool = False,
|
|
339
360
|
) -> None:
|
|
340
361
|
artifacts = team_state.get("artifacts")
|
|
341
362
|
if not isinstance(artifacts, dict):
|
|
@@ -377,12 +398,18 @@ def validate_team_state(
|
|
|
377
398
|
)
|
|
378
399
|
if any_dispatched:
|
|
379
400
|
team_create = team_state.get("teamCreate")
|
|
380
|
-
if
|
|
401
|
+
if _is_legal_concurrent_run_skip(team_create, concurrent_run_authorized):
|
|
402
|
+
pass
|
|
403
|
+
elif not isinstance(team_create, dict) or not team_create.get("attempted"):
|
|
381
404
|
failures.append(
|
|
382
405
|
"team-state.teamCreate.attempted must be true once any worker has "
|
|
383
406
|
"been dispatched (status in completed/timeout/error/in-progress). "
|
|
384
407
|
"Phase 3 (TeamCreate) was skipped — workers ran in-process without "
|
|
385
|
-
"the Teams split-pane surface. See agents/SKILL.md Phase 3."
|
|
408
|
+
"the Teams split-pane surface. See agents/SKILL.md Phase 3. "
|
|
409
|
+
"(The no-team concurrent-run path is legal ONLY when the "
|
|
410
|
+
"run-manifest carries prepare-recorded `concurrentRun.detected: "
|
|
411
|
+
'true` AND team-state records `teamCreate: { attempted: false, '
|
|
412
|
+
'status: "skipped", reason: "concurrent-run" }`.)'
|
|
386
413
|
)
|
|
387
414
|
else:
|
|
388
415
|
tc_status = str(team_create.get("status", "")).strip()
|
|
@@ -1342,6 +1369,19 @@ def _data_path_for(report_path: Path) -> Path:
|
|
|
1342
1369
|
return report_path.with_suffix(".data.json")
|
|
1343
1370
|
|
|
1344
1371
|
|
|
1372
|
+
def _load_final_report_data(report_path: Path) -> dict:
|
|
1373
|
+
"""Best-effort parse of the final-report data.json sibling. Returns {} when
|
|
1374
|
+
absent or unparseable — those conditions are already surfaced as failures by
|
|
1375
|
+
validate_final_report_data; this loader only feeds cross-field checks."""
|
|
1376
|
+
data_path = _data_path_for(report_path)
|
|
1377
|
+
if not data_path.is_file():
|
|
1378
|
+
return {}
|
|
1379
|
+
try:
|
|
1380
|
+
return json.loads(data_path.read_text(encoding="utf-8"))
|
|
1381
|
+
except (OSError, json.JSONDecodeError):
|
|
1382
|
+
return {}
|
|
1383
|
+
|
|
1384
|
+
|
|
1345
1385
|
def validate_final_report_data(report_path: Path, failures: list[str]) -> None:
|
|
1346
1386
|
"""Validate the final-report data.json against the v1.0 schema.
|
|
1347
1387
|
|
|
@@ -1721,6 +1761,31 @@ def _validate_improvement_discovery(
|
|
|
1721
1761
|
failures.append(f"improvement-discovery: {err}")
|
|
1722
1762
|
|
|
1723
1763
|
|
|
1764
|
+
def _validate_fix_cycle(run_manifest: dict, data: dict, failures: list[str]) -> None:
|
|
1765
|
+
"""Enforce: when the run-manifest carries a fixCycleId, the final-report
|
|
1766
|
+
data.json MUST contain a fixCycle block whose ``cycle`` matches it.
|
|
1767
|
+
|
|
1768
|
+
Direction is one-way: a fixCycle block present without a run-manifest
|
|
1769
|
+
fixCycleId is NOT rejected — same posture as the schema-optional block.
|
|
1770
|
+
This check owns only the run→report direction (no missing/mismatched
|
|
1771
|
+
block when the run is attached), never the reverse."""
|
|
1772
|
+
cycle_id = (run_manifest or {}).get("fixCycleId", "")
|
|
1773
|
+
if not cycle_id:
|
|
1774
|
+
return
|
|
1775
|
+
block = (data or {}).get("fixCycle")
|
|
1776
|
+
if not isinstance(block, dict):
|
|
1777
|
+
failures.append(
|
|
1778
|
+
f"fix-cycle: run-manifest fixCycleId={cycle_id} but data.json has "
|
|
1779
|
+
"no fixCycle block"
|
|
1780
|
+
)
|
|
1781
|
+
return
|
|
1782
|
+
if block.get("cycle") != cycle_id:
|
|
1783
|
+
failures.append(
|
|
1784
|
+
f"fix-cycle: data.json fixCycle.cycle={block.get('cycle')!r} does "
|
|
1785
|
+
f"not match run-manifest fixCycleId={cycle_id!r}"
|
|
1786
|
+
)
|
|
1787
|
+
|
|
1788
|
+
|
|
1724
1789
|
def _validate_session_conformance(
|
|
1725
1790
|
team_state: dict,
|
|
1726
1791
|
team_state_path: Path,
|
|
@@ -2114,13 +2179,25 @@ def main() -> int:
|
|
|
2114
2179
|
if autofix_state == "accuracy-failed":
|
|
2115
2180
|
failures.extend(autofix_messages)
|
|
2116
2181
|
contract = extract_contract(run_manifest, task_manifest, failures)
|
|
2117
|
-
|
|
2182
|
+
concurrent_run_authorized = bool(
|
|
2183
|
+
(run_manifest.get("concurrentRun") or {}).get("detected")
|
|
2184
|
+
)
|
|
2185
|
+
validate_team_state(
|
|
2186
|
+
team_state,
|
|
2187
|
+
project_root,
|
|
2188
|
+
contract,
|
|
2189
|
+
failures,
|
|
2190
|
+
concurrent_run_authorized=concurrent_run_authorized,
|
|
2191
|
+
)
|
|
2118
2192
|
# Schema validation runs BEFORE markdown substring checks: if the
|
|
2119
2193
|
# data.json is well-formed, the rendered markdown is guaranteed to
|
|
2120
2194
|
# contain every required section. Substring checks below are a
|
|
2121
2195
|
# safety net for hand-edited or pre-v1.0 reports.
|
|
2122
2196
|
task_type = effective_run_task_type(run_manifest, task_manifest)
|
|
2123
2197
|
validate_final_report_data(report_path, failures)
|
|
2198
|
+
_validate_fix_cycle(
|
|
2199
|
+
run_manifest, _load_final_report_data(report_path), failures
|
|
2200
|
+
)
|
|
2124
2201
|
validate_report(report_path, contract["required_agent_status_entries"], failures)
|
|
2125
2202
|
validate_team_state_usage(team_state, failures)
|
|
2126
2203
|
|
|
@@ -271,7 +271,13 @@ def _check_progress_checkpoints(
|
|
|
271
271
|
str(w.get("status", "")).strip() in _DISPATCHED_STATUSES for w in workers
|
|
272
272
|
)
|
|
273
273
|
require("phase-2-prompts", bool(workers), "before any Write to assigned prompt paths")
|
|
274
|
-
require(
|
|
274
|
+
require(
|
|
275
|
+
"phase-3-team-create",
|
|
276
|
+
any_dispatched,
|
|
277
|
+
"immediately before the TeamCreate call — or, in the authorized no-team "
|
|
278
|
+
"concurrent-run path, the `phase-3-team-create skipped (concurrent-run)` "
|
|
279
|
+
"variant emitted right after recording teamCreate skipped in team-state",
|
|
280
|
+
)
|
|
275
281
|
_check_worker_checkpoint_lines(by_phase, analysis_workers, errors)
|
|
276
282
|
require(
|
|
277
283
|
"phase-5.5-convergence",
|
package/src/render-bundle.mjs
CHANGED
|
@@ -18,11 +18,14 @@ Usage:
|
|
|
18
18
|
[--gemini-model <m>] [--report-writer-model <m>] \\
|
|
19
19
|
[--related-tasks <list>] [--base-ref <ref>] \\
|
|
20
20
|
[--clarification-response <path>] [--work-category <cat>] \\
|
|
21
|
-
[--stage <auto|N>] [--stages <csv>] [--pr-template-path <path>]
|
|
21
|
+
[--stage <auto|N>] [--stages <csv>] [--pr-template-path <path>] \\
|
|
22
|
+
[--fix-cycle <yes|no>]
|
|
22
23
|
|
|
23
24
|
--stage implementation / final-verification only
|
|
24
25
|
--stages release-handoff only: PR stage bundle csv (empty = whole-task)
|
|
25
26
|
--pr-template-path release-handoff only
|
|
27
|
+
--fix-cycle entry phase only: record this re-entry of a done task as a
|
|
28
|
+
bug-fix cycle (yes opens/continues the cycle)
|
|
26
29
|
|
|
27
30
|
release-handoff takes NO --task-brief (briefs belong to entry phases) —
|
|
28
31
|
prepare generates the run's input document from the cited verification
|