okstra 0.186.4 → 0.186.6
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/architecture.md +1 -1
- package/docs/cli.md +3 -3
- package/docs/for-ai/skills/okstra-user-response.md +1 -1
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/bin/okstra-render-report-views.py +6 -5
- package/runtime/prompts/launch.template.md +14 -0
- package/runtime/prompts/lead/convergence.md +2 -2
- package/runtime/prompts/lead/okstra-lead-contract.md +4 -14
- package/runtime/prompts/lead/plan-body-verification.md +4 -3
- package/runtime/prompts/lead/report-writer.md +3 -1
- package/runtime/prompts/profiles/_coverage-critic.md +1 -1
- package/runtime/prompts/profiles/error-analysis.md +2 -2
- package/runtime/prompts/profiles/final-verification.md +2 -2
- package/runtime/prompts/profiles/implementation-planning.md +3 -3
- package/runtime/prompts/profiles/requirements-discovery.md +2 -2
- package/runtime/prompts/wizard/prompts.ko.json +2 -4
- package/runtime/python/okstra_ctl/adapters/hosts/grok/adapter.py +2 -5
- package/runtime/python/okstra_ctl/agent_activity.py +6 -0
- package/runtime/python/okstra_ctl/clarification_items.py +67 -11
- package/runtime/python/okstra_ctl/next_phase.py +6 -3
- package/runtime/python/okstra_ctl/plan_items.py +32 -6
- package/runtime/python/okstra_ctl/plan_items_cli.py +57 -3
- package/runtime/python/okstra_ctl/render_final_report.py +3 -1
- package/runtime/python/okstra_ctl/report_assembly.py +65 -3
- package/runtime/python/okstra_ctl/report_html/run_usage.py +5 -1
- package/runtime/python/okstra_ctl/report_projections.py +45 -4
- package/runtime/python/okstra_ctl/run.py +21 -6
- package/runtime/python/okstra_ctl/usage_cells.py +15 -0
- package/runtime/python/okstra_ctl/user_response.py +72 -6
- package/runtime/python/okstra_ctl/wizard.py +1 -5
- package/runtime/python/okstra_token_usage/codex.py +32 -3
- package/runtime/python/okstra_token_usage/collect.py +148 -15
- package/runtime/python/okstra_token_usage/grok.py +24 -5
- package/runtime/python/okstra_token_usage/report.py +12 -2
- package/runtime/skills/okstra-user-response/SKILL.md +4 -2
- package/runtime/templates/reports/html/assets/base.css +3 -9
- package/runtime/templates/reports/html/assets/base.js +0 -21
- package/runtime/templates/reports/html/base.template.html +1 -4
- package/runtime/templates/reports/html/tasks/implementation-planning.template.html +9 -28
- package/runtime/validators/lib/runners.sh +5 -1
- package/runtime/validators/validate-report-views.py +2 -1
- package/runtime/validators/validate-run.py +240 -102
- package/runtime/validators/validate_session_conformance.py +71 -18
|
@@ -47,6 +47,7 @@ from okstra_ctl.lead_events import ( # noqa: E402
|
|
|
47
47
|
)
|
|
48
48
|
from okstra_ctl.agent_activity import ACTIVITY_FIELDS # noqa: E402
|
|
49
49
|
from okstra_ctl.domain.host import HostNotRegistered # noqa: E402
|
|
50
|
+
from okstra_ctl.final_report_paths import final_report_data_path # noqa: E402
|
|
50
51
|
from okstra_ctl.registry.host_registry import default_host_registry # noqa: E402
|
|
51
52
|
from okstra_ctl.wrapper_status import read_wrapper_status # noqa: E402
|
|
52
53
|
|
|
@@ -624,16 +625,7 @@ def _plan_body_state_path(run_dir: Path, suffix: str) -> Path:
|
|
|
624
625
|
|
|
625
626
|
def _ids_reported_as_asked(report_path: Path) -> list[str]:
|
|
626
627
|
"""리포트가 "사용자에게 물었다"고 기록한 열린 승인 차단 행의 id."""
|
|
627
|
-
|
|
628
|
-
if not name.endswith(".md"):
|
|
629
|
-
return []
|
|
630
|
-
data_path = report_path.with_name(name.removesuffix(".md") + ".data.json")
|
|
631
|
-
try:
|
|
632
|
-
doc = json.loads(data_path.read_text())
|
|
633
|
-
except (OSError, json.JSONDecodeError):
|
|
634
|
-
return []
|
|
635
|
-
if not isinstance(doc, dict):
|
|
636
|
-
return []
|
|
628
|
+
doc = _read_report_data(report_path)
|
|
637
629
|
asked: list[str] = []
|
|
638
630
|
for row in doc.get("clarificationItems") or []:
|
|
639
631
|
if not isinstance(row, dict):
|
|
@@ -666,12 +658,13 @@ def _activity_index(events: list[LeadEvent]) -> dict[str, list[LeadEvent]]:
|
|
|
666
658
|
|
|
667
659
|
|
|
668
660
|
def _read_report_data(report_path: Path) -> Mapping[str, Any]:
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
661
|
+
"""`--report` 가 data.json · markdown · html 이어도 같은 레코드를 연다.
|
|
662
|
+
|
|
663
|
+
Phase 7 는 data.json 을 넘긴다. `.md` 만 받던 동안 투영과 self-fix 횟수가
|
|
664
|
+
빈 객체에서 나와 `projected=<missing>` / `selfFixRoundsApplied=0` 이 됐다.
|
|
665
|
+
"""
|
|
673
666
|
try:
|
|
674
|
-
data = json.loads(
|
|
667
|
+
data = json.loads(final_report_data_path(report_path).read_text(encoding="utf-8"))
|
|
675
668
|
except (OSError, json.JSONDecodeError):
|
|
676
669
|
return {}
|
|
677
670
|
return data if isinstance(data, Mapping) else {}
|
|
@@ -758,7 +751,16 @@ def _check_projected_agent_activity(
|
|
|
758
751
|
{field: event.details.get(field) for field in ACTIVITY_FIELDS}
|
|
759
752
|
for event in events
|
|
760
753
|
]
|
|
761
|
-
|
|
754
|
+
raw_projected = report_data.get("agentActivity")
|
|
755
|
+
projected = (
|
|
756
|
+
[
|
|
757
|
+
{field: row.get(field) for field in ACTIVITY_FIELDS}
|
|
758
|
+
for row in raw_projected
|
|
759
|
+
if isinstance(row, Mapping)
|
|
760
|
+
]
|
|
761
|
+
if isinstance(raw_projected, list)
|
|
762
|
+
else raw_projected
|
|
763
|
+
)
|
|
762
764
|
if projected == expected:
|
|
763
765
|
return
|
|
764
766
|
mismatch = "length"
|
|
@@ -850,6 +852,43 @@ def _allowed_automatic_rounds(self_fix_rounds: int, *, gating: bool = True) -> i
|
|
|
850
852
|
return 2 + max(self_fix_rounds, 0)
|
|
851
853
|
|
|
852
854
|
|
|
855
|
+
def _self_fix_rounds_from_state(run_dir: Path, suffix: str | None) -> int | None:
|
|
856
|
+
"""이번 런 상태 파일의 자가수정 횟수. 없으면 None — 호출자가 리포트로 폴백.
|
|
857
|
+
|
|
858
|
+
리포트 칸은 이어진 seq 의 누적이라, 이번 창의 `self-fix-applied` 건수와
|
|
859
|
+
비교하면 어긋난다.
|
|
860
|
+
"""
|
|
861
|
+
if not suffix:
|
|
862
|
+
return None
|
|
863
|
+
try:
|
|
864
|
+
doc = json.loads(_plan_body_state_path(run_dir, suffix).read_text())
|
|
865
|
+
except (OSError, json.JSONDecodeError):
|
|
866
|
+
return None
|
|
867
|
+
if not isinstance(doc, dict):
|
|
868
|
+
return None
|
|
869
|
+
projection = doc.get("planBodyVerification")
|
|
870
|
+
value = (
|
|
871
|
+
projection.get("selfFixRoundsApplied")
|
|
872
|
+
if isinstance(projection, dict)
|
|
873
|
+
else None
|
|
874
|
+
)
|
|
875
|
+
if isinstance(value, int) and value >= 0:
|
|
876
|
+
return value
|
|
877
|
+
value = doc.get("selfFixRoundsApplied")
|
|
878
|
+
return value if isinstance(value, int) and value >= 0 else None
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
def _is_plan_body_verification_activity(event: LeadEvent) -> bool:
|
|
882
|
+
"""`verification-round-completed` 가 계획 본문 배치인지.
|
|
883
|
+
|
|
884
|
+
같은 kind 로 적대 재검증 라운드도 남는다. 그 건을 본문 라운드에 넣으면
|
|
885
|
+
recorded 가 roundCount 보다 커진다. 요약이 `adversarial reverify` 이면
|
|
886
|
+
본문이 아니다. 픽스처 요약(`verification-round-completed for …`)은 본문이다.
|
|
887
|
+
"""
|
|
888
|
+
summary = str(event.details.get("summary") or "").lower()
|
|
889
|
+
return "adversarial reverify" not in summary and "adversarial re-verify" not in summary
|
|
890
|
+
|
|
891
|
+
|
|
853
892
|
def _plan_body_verification(report_data: Mapping[str, Any]) -> Mapping[str, Any] | None:
|
|
854
893
|
planning = report_data.get("implementationPlanning")
|
|
855
894
|
verification = (
|
|
@@ -970,14 +1009,23 @@ def _check_activity_round_counts(
|
|
|
970
1009
|
errors: list[str],
|
|
971
1010
|
) -> None:
|
|
972
1011
|
verification_rounds = _plan_body_rounds_ran(run_dir, suffix)
|
|
973
|
-
recorded_verifications = len(
|
|
1012
|
+
recorded_verifications = len([
|
|
1013
|
+
event
|
|
1014
|
+
for event in indexed.get("verification-round-completed", [])
|
|
1015
|
+
if _is_plan_body_verification_activity(event)
|
|
1016
|
+
])
|
|
974
1017
|
user_reverification_rounds = _resolved_correctness_reverification_rounds(
|
|
975
1018
|
indexed,
|
|
976
1019
|
report_data,
|
|
977
1020
|
verification_rounds,
|
|
978
1021
|
)
|
|
979
1022
|
automatic_rounds = verification_rounds - len(user_reverification_rounds)
|
|
980
|
-
|
|
1023
|
+
state_self_fix = _self_fix_rounds_from_state(run_dir, suffix)
|
|
1024
|
+
self_fix_rounds = (
|
|
1025
|
+
state_self_fix
|
|
1026
|
+
if state_self_fix is not None
|
|
1027
|
+
else _self_fix_rounds_applied(report_data)
|
|
1028
|
+
)
|
|
981
1029
|
allowed_rounds = _allowed_automatic_rounds(
|
|
982
1030
|
self_fix_rounds, gating=_plan_body_gating(report_data),
|
|
983
1031
|
)
|
|
@@ -1435,6 +1483,11 @@ def _check_cmux_adapter_read(
|
|
|
1435
1483
|
return
|
|
1436
1484
|
if evidence.sidecar_reads.get(CMUX_ADAPTER_BASENAME):
|
|
1437
1485
|
return
|
|
1486
|
+
dispatch_mode = str(team_state.get("dispatchMode", "")).strip()
|
|
1487
|
+
if dispatch_mode in _WORKER_DISPATCH_MODES:
|
|
1488
|
+
# grok 같은 artifact-only 호스트는 Read 도구 기록이 없다. 워커가
|
|
1489
|
+
# okstra 디스패치로 나갔으면 어댑터가 가리키는 경로를 탄 것이다.
|
|
1490
|
+
return
|
|
1438
1491
|
errors.append(
|
|
1439
1492
|
f"cmux adapter: no read of `{CMUX_ADAPTER_BASENAME}` (a `Read` call or a "
|
|
1440
1493
|
f"shell command naming it) found in the "
|