okstra 0.146.1 → 0.148.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/README.md +23 -9
- package/docs/architecture/storage-model.md +39 -65
- package/docs/architecture.md +68 -60
- package/docs/cli.md +40 -23
- package/docs/for-ai/skills/okstra-run.md +13 -34
- package/docs/performance-improvement-plan-v2.md +2 -2
- package/docs/pr-template-usage.md +1 -1
- package/docs/project-structure-overview.md +26 -22
- package/docs/task-process/README.md +4 -4
- package/docs/task-process/common-flow.md +12 -12
- package/docs/task-process/final-verification.md +2 -2
- package/docs/task-process/implementation.md +1 -1
- package/docs/task-process/release-handoff.md +1 -1
- package/package.json +2 -2
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/antigravity-worker.md +2 -2
- package/runtime/agents/workers/claude-worker.md +1 -1
- package/runtime/agents/workers/codex-worker.md +2 -2
- package/runtime/agents/workers/grok-worker.md +256 -0
- package/runtime/agents/workers/kimi-worker.md +256 -0
- package/runtime/agents/workers/report-writer-worker.md +12 -12
- package/runtime/bin/lib/okstra/cli.sh +13 -1
- package/runtime/bin/lib/okstra/globals.sh +3 -0
- package/runtime/bin/lib/okstra/usage.sh +17 -12
- package/runtime/bin/okstra-grok-exec.sh +5 -0
- package/runtime/bin/okstra-kimi-exec.sh +5 -0
- package/runtime/bin/okstra-provider-exec.py +235 -0
- package/runtime/bin/okstra-render-final-report.py +4 -4
- package/runtime/bin/okstra-render-report-views.py +100 -12
- package/runtime/bin/okstra.sh +3 -0
- package/runtime/prompts/lead/adapters/antigravity.md +48 -0
- package/runtime/prompts/lead/adapters/claude-code.md +13 -11
- package/runtime/prompts/lead/adapters/codex.md +7 -7
- package/runtime/prompts/lead/okstra-lead-contract.md +5 -5
- package/runtime/prompts/lead/report-writer.md +16 -12
- package/runtime/prompts/profiles/_coding-conventions-preflight.md +1 -1
- package/runtime/prompts/profiles/_common-contract.md +16 -10
- package/runtime/prompts/profiles/_implementation-deliverable.md +2 -2
- package/runtime/prompts/profiles/_implementation-diff-review.md +1 -1
- package/runtime/prompts/profiles/_implementation-executor.md +12 -12
- package/runtime/prompts/profiles/_implementation-self-check.md +4 -4
- package/runtime/prompts/profiles/_implementation-verifier.md +3 -3
- package/runtime/prompts/profiles/change-impact-analysis.md +2 -0
- package/runtime/prompts/profiles/error-analysis.md +2 -0
- package/runtime/prompts/profiles/feature-analysis.md +2 -0
- package/runtime/prompts/profiles/final-verification.md +3 -1
- package/runtime/prompts/profiles/forbidden-actions.json +4 -4
- package/runtime/prompts/profiles/implementation-planning.md +3 -1
- package/runtime/prompts/profiles/implementation.md +2 -2
- package/runtime/prompts/profiles/improvement-discovery.md +6 -2
- package/runtime/prompts/profiles/project-analysis.md +2 -0
- package/runtime/prompts/profiles/release-handoff.md +7 -7
- package/runtime/prompts/profiles/requirements-discovery.md +2 -0
- package/runtime/prompts/wizard/prompts.ko.json +9 -1
- package/runtime/python/okstra_ctl/codex_dispatch.py +68 -87
- package/runtime/python/okstra_ctl/dispatch_core.py +4 -22
- package/runtime/python/okstra_ctl/final_report_schema.py +37 -12
- package/runtime/python/okstra_ctl/lead_events.py +1 -1
- package/runtime/python/okstra_ctl/lead_runtime.py +13 -2
- package/runtime/python/okstra_ctl/models.py +156 -8
- package/runtime/python/okstra_ctl/path_hints.py +9 -25
- package/runtime/python/okstra_ctl/paths.py +1 -1
- package/runtime/python/okstra_ctl/render.py +172 -74
- package/runtime/python/okstra_ctl/render_final_report.py +136 -28
- package/runtime/python/okstra_ctl/report_contract.py +124 -0
- package/runtime/python/okstra_ctl/report_finalize.py +1 -1
- package/runtime/python/okstra_ctl/report_html/__init__.py +10 -0
- package/runtime/python/okstra_ctl/report_html/common.py +86 -0
- package/runtime/python/okstra_ctl/report_html/filters.py +104 -0
- package/runtime/python/okstra_ctl/report_html/models.py +59 -0
- package/runtime/python/okstra_ctl/report_html/render.py +76 -0
- package/runtime/python/okstra_ctl/report_html/router.py +40 -0
- package/runtime/python/okstra_ctl/report_html/view_models/__init__.py +1 -0
- package/runtime/python/okstra_ctl/report_html/view_models/change_impact_analysis.py +39 -0
- package/runtime/python/okstra_ctl/report_html/view_models/error_analysis.py +49 -0
- package/runtime/python/okstra_ctl/report_html/view_models/feature_analysis.py +39 -0
- package/runtime/python/okstra_ctl/report_html/view_models/final_verification.py +47 -0
- package/runtime/python/okstra_ctl/report_html/view_models/implementation.py +47 -0
- package/runtime/python/okstra_ctl/report_html/view_models/implementation_planning.py +103 -0
- package/runtime/python/okstra_ctl/report_html/view_models/improvement_discovery.py +43 -0
- package/runtime/python/okstra_ctl/report_html/view_models/project_analysis.py +54 -0
- package/runtime/python/okstra_ctl/report_html/view_models/release_handoff.py +54 -0
- package/runtime/python/okstra_ctl/report_html/view_models/requirements_discovery.py +55 -0
- package/runtime/python/okstra_ctl/report_html/visualizations.py +139 -0
- package/runtime/python/okstra_ctl/report_view_artifacts.py +4 -1
- package/runtime/python/okstra_ctl/report_views.py +15 -43
- package/runtime/python/okstra_ctl/run.py +276 -51
- package/runtime/python/okstra_ctl/runner_resolution.py +103 -0
- package/runtime/python/okstra_ctl/schema_excerpt.py +7 -17
- package/runtime/python/okstra_ctl/team.py +2 -7
- package/runtime/python/okstra_ctl/wizard.py +194 -21
- package/runtime/python/okstra_ctl/worker_artifacts.py +46 -0
- package/runtime/python/okstra_ctl/workers.py +3 -1
- package/runtime/python/okstra_ctl/workflow.py +4 -2
- package/runtime/python/okstra_token_usage/__init__.py +1 -0
- package/runtime/python/okstra_token_usage/collect.py +32 -23
- package/runtime/python/okstra_token_usage/pricing.py +35 -3
- package/runtime/schemas/final-report-v2.0.schema.json +3923 -0
- package/runtime/skills/okstra-run/SKILL.md +31 -42
- package/runtime/templates/prd/pr-body.template.md +1 -1
- package/runtime/templates/reports/final-report-v2.template.md +66 -0
- package/runtime/templates/reports/html/assets/base.css +41 -0
- package/runtime/templates/reports/html/assets/base.js +5 -0
- package/runtime/templates/reports/html/base.template.html +79 -0
- package/runtime/templates/reports/html/macros/forms.html +47 -0
- package/runtime/templates/reports/html/macros/layout.html +19 -0
- package/runtime/templates/reports/html/macros/visualizations.html +27 -0
- package/runtime/templates/reports/html/tasks/change-impact-analysis.template.html +40 -0
- package/runtime/templates/reports/html/tasks/error-analysis.template.html +40 -0
- package/runtime/templates/reports/html/tasks/feature-analysis.template.html +40 -0
- package/runtime/templates/reports/html/tasks/final-verification.template.html +39 -0
- package/runtime/templates/reports/html/tasks/implementation-planning.template.html +47 -0
- package/runtime/templates/reports/html/tasks/implementation.template.html +40 -0
- package/runtime/templates/reports/html/tasks/improvement-discovery.template.html +29 -0
- package/runtime/templates/reports/html/tasks/project-analysis.template.html +57 -0
- package/runtime/templates/reports/html/tasks/release-handoff.template.html +36 -0
- package/runtime/templates/reports/html/tasks/requirements-discovery.template.html +37 -0
- package/runtime/templates/reports/report.js +21 -4
- package/runtime/templates/reports/settings.template.json +4 -0
- package/runtime/templates/reports/task-brief.template.md +7 -7
- package/runtime/validators/validate-report-views.py +86 -4
- package/runtime/validators/validate-run.py +73 -15
- package/runtime/validators/validate_improvement_report.py +55 -0
- package/runtime/validators/validate_session_conformance.py +2 -1
- package/src/cli-registry.mjs +4 -4
- package/src/commands/execute/codex-dispatch.mjs +7 -10
- package/src/commands/execute/render-bundle.mjs +3 -3
- package/src/commands/execute/run.mjs +17 -52
- package/src/commands/execute/wizard.mjs +4 -1
- package/src/commands/lifecycle/doctor.mjs +6 -3
- package/src/commands/lifecycle/install.mjs +49 -21
- package/src/commands/report/finalize.mjs +2 -3
- package/src/commands/report/render-final-report.mjs +4 -2
- package/src/commands/report/render-views.mjs +8 -8
- package/src/lib/runtime-manifest.mjs +1 -1
- package/src/lib/runtime-resolver.mjs +2 -2
- package/src/lib/worker-agent-render.mjs +50 -0
|
@@ -30,12 +30,12 @@ for _ssot_dir in (_VALIDATORS_DIR.parent / "scripts", _VALIDATORS_DIR.parent / "
|
|
|
30
30
|
try:
|
|
31
31
|
from okstra_ctl.final_report_schema import (
|
|
32
32
|
SchemaError,
|
|
33
|
-
|
|
33
|
+
load_schema_for_data,
|
|
34
34
|
validate as schema_validate,
|
|
35
35
|
)
|
|
36
36
|
except ImportError: # pragma: no cover — runtime guarantees this import
|
|
37
37
|
SchemaError = None # type: ignore[assignment]
|
|
38
|
-
|
|
38
|
+
load_schema_for_data = None # type: ignore[assignment]
|
|
39
39
|
schema_validate = None # type: ignore[assignment]
|
|
40
40
|
|
|
41
41
|
from okstra_project import project_json_path # noqa: E402
|
|
@@ -51,6 +51,7 @@ from okstra_ctl.conformance import ( # noqa: E402
|
|
|
51
51
|
validate_conformance_manifest,
|
|
52
52
|
)
|
|
53
53
|
from okstra_ctl.paths import RunRef # noqa: E402
|
|
54
|
+
from okstra_ctl.lead_runtime import ARTIFACT_ONLY_LEAD_RUNTIMES # noqa: E402
|
|
54
55
|
from okstra_ctl.build_tools import ( # noqa: E402
|
|
55
56
|
command_invokes_build_tool,
|
|
56
57
|
resolve_build_tool_tokens,
|
|
@@ -156,7 +157,7 @@ def _session_accounting(team_state: dict) -> str:
|
|
|
156
157
|
return value
|
|
157
158
|
return (
|
|
158
159
|
"artifact-only"
|
|
159
|
-
if team_state.get("leadRuntime") in
|
|
160
|
+
if team_state.get("leadRuntime") in ARTIFACT_ONLY_LEAD_RUNTIMES
|
|
160
161
|
else "claude-jsonl"
|
|
161
162
|
)
|
|
162
163
|
|
|
@@ -415,20 +416,24 @@ def extract_contract(
|
|
|
415
416
|
required_worker_roles = []
|
|
416
417
|
failures.append("requiredWorkerRoles is missing from run/task manifest")
|
|
417
418
|
|
|
419
|
+
lead_role = (
|
|
420
|
+
run_contract.get("leadRole")
|
|
421
|
+
or task_contract.get("leadRole")
|
|
422
|
+
or "Okstra lead"
|
|
423
|
+
)
|
|
424
|
+
|
|
418
425
|
required_agent_status_entries = run_contract.get("requiredAgentStatusEntries")
|
|
419
426
|
if not isinstance(required_agent_status_entries, list):
|
|
420
427
|
required_agent_status_entries = task_contract.get("requiredAgentStatusEntries")
|
|
421
428
|
if not isinstance(required_agent_status_entries, list):
|
|
422
|
-
required_agent_status_entries = [
|
|
429
|
+
required_agent_status_entries = [lead_role] + [
|
|
423
430
|
item.get("role", "")
|
|
424
431
|
for item in required_worker_roles
|
|
425
432
|
if isinstance(item, dict) and item.get("role")
|
|
426
433
|
]
|
|
427
434
|
|
|
428
435
|
return {
|
|
429
|
-
"lead_role":
|
|
430
|
-
or task_contract.get("leadRole")
|
|
431
|
-
or "Claude lead",
|
|
436
|
+
"lead_role": lead_role,
|
|
432
437
|
"lead_agent": run_contract.get("leadAgent")
|
|
433
438
|
or task_contract.get("leadAgent")
|
|
434
439
|
or "claude",
|
|
@@ -722,7 +727,7 @@ def validate_team_state(
|
|
|
722
727
|
dispatch_mode = str(team_state.get("dispatchMode", "")).strip()
|
|
723
728
|
if (
|
|
724
729
|
dispatch_mode in WORKER_DISPATCH_MODES
|
|
725
|
-
or team_state.get("leadRuntime") in
|
|
730
|
+
or team_state.get("leadRuntime") in ARTIFACT_ONLY_LEAD_RUNTIMES
|
|
726
731
|
):
|
|
727
732
|
if dispatch_mode not in WORKER_DISPATCH_MODES:
|
|
728
733
|
expected = ", ".join(sorted(WORKER_DISPATCH_MODES))
|
|
@@ -1021,6 +1026,48 @@ def _scan_token_usage_summary(
|
|
|
1021
1026
|
# a section heading line (not as inline text inside a paragraph or table).
|
|
1022
1027
|
_VERDICT_CARD_HEADING_RE = re.compile(r"^##[ \t]+Verdict Card\b", re.MULTILINE)
|
|
1023
1028
|
|
|
1029
|
+
_V2_AI_HANDOFF_HEADINGS = (
|
|
1030
|
+
"## AI Handoff Summary",
|
|
1031
|
+
"## Clarification and User Decisions",
|
|
1032
|
+
"## Evidence Ledger",
|
|
1033
|
+
"## Task Deliverable:",
|
|
1034
|
+
"## Cross Verification Audit",
|
|
1035
|
+
"## Execution Audit",
|
|
1036
|
+
"## Token and Cost Audit",
|
|
1037
|
+
)
|
|
1038
|
+
|
|
1039
|
+
|
|
1040
|
+
def _validate_v2_ai_handoff(content: str, failures: list[str]) -> None:
|
|
1041
|
+
positions: list[int] = []
|
|
1042
|
+
for heading in _V2_AI_HANDOFF_HEADINGS:
|
|
1043
|
+
count = content.count(heading)
|
|
1044
|
+
if count != 1:
|
|
1045
|
+
failures.append(
|
|
1046
|
+
"schema-v2 AI handoff markdown requires exactly one "
|
|
1047
|
+
f"`{heading}` heading; found {count}."
|
|
1048
|
+
)
|
|
1049
|
+
continue
|
|
1050
|
+
positions.append(content.index(heading))
|
|
1051
|
+
if len(positions) == len(_V2_AI_HANDOFF_HEADINGS) and positions != sorted(
|
|
1052
|
+
positions
|
|
1053
|
+
):
|
|
1054
|
+
failures.append(
|
|
1055
|
+
"schema-v2 AI handoff markdown heading order does not match "
|
|
1056
|
+
"templates/reports/final-report-v2.template.md."
|
|
1057
|
+
)
|
|
1058
|
+
for human_only_field in ('"humanSummary"', '"userNarrative"'):
|
|
1059
|
+
if human_only_field in content:
|
|
1060
|
+
failures.append(
|
|
1061
|
+
"schema-v2 AI handoff markdown contains human-only field "
|
|
1062
|
+
f"{human_only_field}; render it only in the task-specific HTML."
|
|
1063
|
+
)
|
|
1064
|
+
if _READING_CONFIRMATION_HEADING_RE.search(content) is not None:
|
|
1065
|
+
failures.append(
|
|
1066
|
+
"final report contains a `## 0. Reading Confirmation` heading — "
|
|
1067
|
+
"Reading Confirmation lives in the worker audit sidecar, never "
|
|
1068
|
+
"in the AI handoff markdown."
|
|
1069
|
+
)
|
|
1070
|
+
|
|
1024
1071
|
# Top-of-report Index block. The renderer
|
|
1025
1072
|
# (scripts/okstra_ctl/render_final_report.py) injects `<a id="report-index">`
|
|
1026
1073
|
# into the index heading; a missing anchor means the markdown was produced
|
|
@@ -2229,6 +2276,10 @@ def validate_report(
|
|
|
2229
2276
|
"run `okstra-token-usage.py ... --substitute-data <report-path>` during Phase 7"
|
|
2230
2277
|
)
|
|
2231
2278
|
|
|
2279
|
+
if (report_data or {}).get("schemaVersion") == "2.0":
|
|
2280
|
+
_validate_v2_ai_handoff(content, failures)
|
|
2281
|
+
return
|
|
2282
|
+
|
|
2232
2283
|
# Catch the "workers typed `0` / `pending` instead of the placeholder"
|
|
2233
2284
|
# failure mode that bypasses the placeholder check above.
|
|
2234
2285
|
_scan_token_usage_summary(
|
|
@@ -2995,7 +3046,7 @@ def validate_final_report_data(
|
|
|
2995
3046
|
project_root: Path | None = None,
|
|
2996
3047
|
clarification_text: str = "",
|
|
2997
3048
|
) -> Mapping[str, Any] | None:
|
|
2998
|
-
"""Validate
|
|
3049
|
+
"""Validate final-report data.json against its declared versioned schema.
|
|
2999
3050
|
|
|
3000
3051
|
The data.json is the source-of-truth that the renderer reads to
|
|
3001
3052
|
produce the markdown. If schema validation passes here, the rendered
|
|
@@ -3010,7 +3061,7 @@ def validate_final_report_data(
|
|
|
3010
3061
|
The returned mapping is the exact loaded snapshot consumed by later
|
|
3011
3062
|
finalization checks and workflow persistence.
|
|
3012
3063
|
"""
|
|
3013
|
-
if schema_validate is None or
|
|
3064
|
+
if schema_validate is None or load_schema_for_data is None:
|
|
3014
3065
|
# Module-load fallback path; should never fire in a real install.
|
|
3015
3066
|
failures.append(
|
|
3016
3067
|
"validate-run: okstra_ctl.final_report_schema is not importable — "
|
|
@@ -3028,13 +3079,11 @@ def validate_final_report_data(
|
|
|
3028
3079
|
return
|
|
3029
3080
|
|
|
3030
3081
|
try:
|
|
3031
|
-
|
|
3082
|
+
data = json.loads(data_path.read_text(encoding="utf-8"))
|
|
3083
|
+
schema = load_schema_for_data(data)
|
|
3032
3084
|
except SchemaError as exc:
|
|
3033
3085
|
failures.append(f"final-report schema could not be loaded: {exc}")
|
|
3034
3086
|
return
|
|
3035
|
-
|
|
3036
|
-
try:
|
|
3037
|
-
data = json.loads(data_path.read_text(encoding="utf-8"))
|
|
3038
3087
|
except json.JSONDecodeError as exc:
|
|
3039
3088
|
failures.append(f"final-report data.json is not valid JSON: {exc}")
|
|
3040
3089
|
return
|
|
@@ -6035,6 +6084,8 @@ def validate_phase_boundary(
|
|
|
6035
6084
|
task_type: str,
|
|
6036
6085
|
report_path: Path,
|
|
6037
6086
|
failures: list[str],
|
|
6087
|
+
*,
|
|
6088
|
+
report_data: Mapping[str, Any] | None = None,
|
|
6038
6089
|
) -> None:
|
|
6039
6090
|
"""Phase-specific contract checks.
|
|
6040
6091
|
|
|
@@ -6050,6 +6101,8 @@ def validate_phase_boundary(
|
|
|
6050
6101
|
MUST be absent (lead converted findings into Clarification rows
|
|
6051
6102
|
instead of opening the gate).
|
|
6052
6103
|
"""
|
|
6104
|
+
if (report_data or {}).get("schemaVersion") == "2.0":
|
|
6105
|
+
return
|
|
6053
6106
|
if not report_path.exists():
|
|
6054
6107
|
return
|
|
6055
6108
|
content = report_path.read_text()
|
|
@@ -7167,7 +7220,12 @@ def main() -> int:
|
|
|
7167
7220
|
)
|
|
7168
7221
|
validate_team_state_usage(team_state, failures)
|
|
7169
7222
|
|
|
7170
|
-
validate_phase_boundary(
|
|
7223
|
+
validate_phase_boundary(
|
|
7224
|
+
task_type,
|
|
7225
|
+
report_path,
|
|
7226
|
+
failures,
|
|
7227
|
+
report_data=validation_data,
|
|
7228
|
+
)
|
|
7171
7229
|
_validate_phase_boundary_error_log(
|
|
7172
7230
|
report_path.parent.parent,
|
|
7173
7231
|
task_type,
|
|
@@ -7,6 +7,7 @@ Called by validators/validate-run.py when task_type == "improvement-discovery".
|
|
|
7
7
|
"""
|
|
8
8
|
from __future__ import annotations
|
|
9
9
|
|
|
10
|
+
import json
|
|
10
11
|
import re
|
|
11
12
|
import sys
|
|
12
13
|
from dataclasses import dataclass, field
|
|
@@ -25,6 +26,10 @@ from okstra_ctl.improvement_lenses import (
|
|
|
25
26
|
ABSOLUTE_CANDIDATE_CAP,
|
|
26
27
|
SOURCE_WORKERS,
|
|
27
28
|
)
|
|
29
|
+
from okstra_ctl.final_report_schema import (
|
|
30
|
+
load_schema_version,
|
|
31
|
+
validate as validate_final_report_data,
|
|
32
|
+
)
|
|
28
33
|
from okstra_ctl.md_table import split_pipe_row
|
|
29
34
|
|
|
30
35
|
|
|
@@ -290,6 +295,47 @@ def _check_verdict_card(
|
|
|
290
295
|
errors.append("Verdict Card row must byte-match `## 7. Final Verdict` row")
|
|
291
296
|
|
|
292
297
|
|
|
298
|
+
def _v2_candidate_rows(data: dict) -> list[list[str]]:
|
|
299
|
+
candidates = data["improvementDiscovery"]["candidates"]
|
|
300
|
+
return [
|
|
301
|
+
[
|
|
302
|
+
row["id"],
|
|
303
|
+
",".join(row["lens"]),
|
|
304
|
+
row["title"],
|
|
305
|
+
",".join(row["scope"]),
|
|
306
|
+
row["severity"],
|
|
307
|
+
row["effort"],
|
|
308
|
+
row["consensus"],
|
|
309
|
+
",".join(row["sourceWorkers"]),
|
|
310
|
+
row["recommendedNextPhase"],
|
|
311
|
+
row["expectedBehaviorAfter"],
|
|
312
|
+
",".join(row["evidence"]),
|
|
313
|
+
]
|
|
314
|
+
for row in candidates
|
|
315
|
+
]
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def _validate_v2_report(
|
|
319
|
+
data: dict,
|
|
320
|
+
run_dir: Path,
|
|
321
|
+
brief_frontmatter: dict,
|
|
322
|
+
) -> ValidationResult:
|
|
323
|
+
errors = validate_final_report_data(data, load_schema_version("2.0"))
|
|
324
|
+
_check_grilling_log(run_dir, errors)
|
|
325
|
+
if errors:
|
|
326
|
+
return ValidationResult(ok=False, errors=errors)
|
|
327
|
+
|
|
328
|
+
rows = _v2_candidate_rows(data)
|
|
329
|
+
_check_candidates_cap(len(rows), brief_frontmatter, errors)
|
|
330
|
+
_check_candidate_rows(
|
|
331
|
+
rows,
|
|
332
|
+
brief_frontmatter.get("scan-scope") or [],
|
|
333
|
+
brief_frontmatter.get("out-of-scope") or [],
|
|
334
|
+
errors,
|
|
335
|
+
)
|
|
336
|
+
return ValidationResult(ok=not errors, errors=errors)
|
|
337
|
+
|
|
338
|
+
|
|
293
339
|
def validate_improvement_report(
|
|
294
340
|
report_path: Path, run_dir: Path, brief_frontmatter: dict
|
|
295
341
|
) -> ValidationResult:
|
|
@@ -299,6 +345,15 @@ def validate_improvement_report(
|
|
|
299
345
|
errors.append(f"report file not found: {report_path}")
|
|
300
346
|
return ValidationResult(ok=False, errors=errors)
|
|
301
347
|
|
|
348
|
+
data_path = report_path.with_suffix(".data.json")
|
|
349
|
+
if data_path.exists():
|
|
350
|
+
try:
|
|
351
|
+
data = json.loads(data_path.read_text(encoding="utf-8"))
|
|
352
|
+
except (OSError, json.JSONDecodeError) as exc:
|
|
353
|
+
return ValidationResult(ok=False, errors=[f"invalid report data: {exc}"])
|
|
354
|
+
if data.get("schemaVersion") == "2.0":
|
|
355
|
+
return _validate_v2_report(data, run_dir, brief_frontmatter)
|
|
356
|
+
|
|
302
357
|
body = report_path.read_text(encoding="utf-8")
|
|
303
358
|
|
|
304
359
|
_check_grilling_log(run_dir, errors)
|
|
@@ -38,6 +38,7 @@ from okstra_ctl.worker_heartbeat import ( # noqa: E402
|
|
|
38
38
|
HEARTBEAT_LINE_RE,
|
|
39
39
|
max_gap_seconds_after,
|
|
40
40
|
)
|
|
41
|
+
from okstra_ctl.lead_runtime import ARTIFACT_ONLY_LEAD_RUNTIMES # noqa: E402
|
|
41
42
|
from okstra_ctl.wrapper_status import read_wrapper_status # noqa: E402
|
|
42
43
|
|
|
43
44
|
_DISPATCHED_STATUSES = {"completed", "timeout", "error", "in-progress"}
|
|
@@ -366,7 +367,7 @@ def _conformance_evidence_source(
|
|
|
366
367
|
return None, f"unsupported sessionAccounting: `{accounting}`"
|
|
367
368
|
|
|
368
369
|
legacy_runtime = str(team_state.get("leadRuntime", "") or "claude-code")
|
|
369
|
-
if legacy_runtime in
|
|
370
|
+
if legacy_runtime in ARTIFACT_ONLY_LEAD_RUNTIMES:
|
|
370
371
|
return "artifact-only", None
|
|
371
372
|
return "claude-jsonl", None
|
|
372
373
|
|
package/src/cli-registry.mjs
CHANGED
|
@@ -415,18 +415,18 @@ export const COMMANDS = new Map(
|
|
|
415
415
|
]),
|
|
416
416
|
);
|
|
417
417
|
|
|
418
|
-
const PREAMBLE = `okstra — multi-
|
|
418
|
+
const PREAMBLE = `okstra — host-aware multi-provider cross-verification orchestrator
|
|
419
419
|
|
|
420
420
|
This CLI is the installer/admin tool. Day-to-day usage happens inside a
|
|
421
|
-
|
|
421
|
+
supported agent host through installed skills (okstra-setup, okstra-run, ...).
|
|
422
422
|
|
|
423
423
|
Quick start (CLI):
|
|
424
424
|
1. npx -y okstra@latest install # one-time, this machine
|
|
425
425
|
2. cd <your project>
|
|
426
426
|
3. npx -y okstra@latest setup --project-id <id> # one-time, this project
|
|
427
|
-
4. open
|
|
427
|
+
4. open Claude Code, Codex, or Antigravity and run okstra-run # start a task
|
|
428
428
|
|
|
429
|
-
Inside a
|
|
429
|
+
Inside a supported agent host you can replace step 3 with okstra-setup —
|
|
430
430
|
both write the same <PROJECT_ROOT>/.okstra/project.json.
|
|
431
431
|
|
|
432
432
|
Usage:
|
|
@@ -5,18 +5,15 @@ const USAGE = `okstra codex-dispatch — dispatch CLI-backed workers for a prepa
|
|
|
5
5
|
|
|
6
6
|
Usage:
|
|
7
7
|
okstra codex-dispatch --project-root <dir> --run-manifest <path> \\
|
|
8
|
-
[--workers
|
|
9
|
-
[--idle-timeout-seconds <n>]
|
|
10
|
-
[--enable-codex-report-writer --report-writer-codex-model <model>]
|
|
8
|
+
[--workers <cli-assigned-worker-ids>] [--dry-run] \\
|
|
9
|
+
[--idle-timeout-seconds <n>]
|
|
11
10
|
|
|
12
11
|
This command reads an existing run manifest produced by 'okstra codex-run' and
|
|
13
|
-
dispatches only
|
|
14
|
-
|
|
15
|
-
report-writer
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
writer model by default. Explicitly requesting unsupported workers such as
|
|
19
|
-
claude fails.
|
|
12
|
+
dispatches only assignments whose persisted runner is 'cli-wrapper'. When
|
|
13
|
+
--workers is omitted, it selects that subset from the run roster. Each worker,
|
|
14
|
+
including report-writer, uses its persisted provider, model, and registered
|
|
15
|
+
wrapper. Native-session assignments remain owned by the current Codex host;
|
|
16
|
+
explicitly requesting one here fails.
|
|
20
17
|
|
|
21
18
|
Missing worker prompt files are generated automatically from the run manifest
|
|
22
19
|
and active-run-context. Existing prompt files are never overwritten.
|
|
@@ -2,7 +2,7 @@ import { runPythonModule } from "../../lib/python-helper.mjs";
|
|
|
2
2
|
import { resolvePaths } from "../../lib/paths.mjs";
|
|
3
3
|
import { resolveRuntime } from "../../lib/runtime-resolver.mjs";
|
|
4
4
|
|
|
5
|
-
const USAGE = `okstra render-bundle — preview the task bundle without launching
|
|
5
|
+
const USAGE = `okstra render-bundle — preview the task bundle without launching a lead
|
|
6
6
|
|
|
7
7
|
This is a thin shim over \`python3 -m okstra_ctl.run\` that forces
|
|
8
8
|
\`--render-only\`. Use it to preview where prepare_task_bundle() would
|
|
@@ -18,7 +18,7 @@ Usage:
|
|
|
18
18
|
[--analysis-target <text>] [--evidence-inputs <csv>] \\
|
|
19
19
|
[--lead-model <m>] [--claude-model <m>] [--codex-model <m>] \\
|
|
20
20
|
[--antigravity-model <m>] [--report-writer-model <m>] \\
|
|
21
|
-
[--lead-runtime claude-code|codex] \\
|
|
21
|
+
[--lead-runtime claude-code|codex|antigravity|external] \\
|
|
22
22
|
[--related-tasks <list>] [--base-ref <ref>] \\
|
|
23
23
|
[--clarification-response <path>] [--work-category <cat>] \\
|
|
24
24
|
[--stage <auto|N>] [--stages <csv>] [--pr-template-path <path>] \\
|
|
@@ -26,7 +26,7 @@ Usage:
|
|
|
26
26
|
|
|
27
27
|
--stage implementation / final-verification only
|
|
28
28
|
--stages release-handoff only: PR stage bundle csv (empty = whole-task)
|
|
29
|
-
--lead-runtime lead adapter marker
|
|
29
|
+
--lead-runtime host-native lead adapter marker
|
|
30
30
|
--pr-template-path release-handoff only
|
|
31
31
|
--fix-cycle entry phase only: record this re-entry of a done task as a
|
|
32
32
|
bug-fix cycle (yes opens/continues the cycle)
|
|
@@ -7,10 +7,10 @@ const USAGE = `okstra run — host-aware okstra execution front door
|
|
|
7
7
|
|
|
8
8
|
Usage:
|
|
9
9
|
okstra run --project-root <dir> --project-id <id> --task-group <tg> \\
|
|
10
|
-
--task-id <tid> --task-type <type> [--lead-runtime auto|claude-code|codex|external]
|
|
10
|
+
--task-id <tid> --task-type <type> [--lead-runtime auto|claude-code|codex|antigravity|external]
|
|
11
11
|
|
|
12
|
-
Claude Code
|
|
13
|
-
'okstra run' orchestrates
|
|
12
|
+
Host-native Claude Code, Codex, and Antigravity execution use the okstra-run
|
|
13
|
+
skill front door. Bare 'okstra run' orchestrates only external/tmux all-CLI execution.
|
|
14
14
|
`;
|
|
15
15
|
|
|
16
16
|
function flagValue(args, flag) {
|
|
@@ -48,55 +48,22 @@ function projectRootFromArgs(args) {
|
|
|
48
48
|
return flagValue(args, "--project-root");
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
function withoutDispatchOnlyFlags(args) {
|
|
52
|
-
const out = [];
|
|
53
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
54
|
-
const arg = args[i];
|
|
55
|
-
if (arg === "--enable-codex-report-writer") continue;
|
|
56
|
-
if (arg === "--report-writer-codex-model") {
|
|
57
|
-
i += 1;
|
|
58
|
-
continue;
|
|
59
|
-
}
|
|
60
|
-
if (arg.startsWith("--report-writer-codex-model=")) continue;
|
|
61
|
-
out.push(arg);
|
|
62
|
-
}
|
|
63
|
-
return out;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
51
|
function renderArgs(args, resolution) {
|
|
67
52
|
return [
|
|
68
53
|
"--lead-runtime", resolution.resolvedRuntime,
|
|
69
54
|
"--lead-runtime-request", resolution.requestedRuntime,
|
|
70
55
|
"--runtime-resolution-json", JSON.stringify(resolution),
|
|
71
|
-
...
|
|
56
|
+
...withoutLeadRuntimeFlags(args),
|
|
72
57
|
];
|
|
73
58
|
}
|
|
74
59
|
|
|
75
|
-
function codexDispatchArgs(args, projectRoot, runManifestPath) {
|
|
76
|
-
const out = ["--project-root", projectRoot, "--run-manifest", runManifestPath];
|
|
77
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
78
|
-
const arg = args[i];
|
|
79
|
-
if (arg === "--enable-codex-report-writer") {
|
|
80
|
-
out.push(arg);
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
if (arg === "--report-writer-codex-model") {
|
|
84
|
-
out.push(arg, args[i + 1] ?? "");
|
|
85
|
-
i += 1;
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
if (arg.startsWith("--report-writer-codex-model=")) out.push(arg);
|
|
89
|
-
}
|
|
90
|
-
return out;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
60
|
export function buildRunPlan({ args, paths, resolution, runManifestPath = "" }) {
|
|
94
61
|
void paths;
|
|
95
62
|
const projectRoot = projectRootFromArgs(args);
|
|
96
63
|
if (resolution.resolvedRuntime === "claude-code") {
|
|
97
64
|
return {
|
|
98
65
|
ok: false,
|
|
99
|
-
reason: "Bare okstra run cannot invoke Claude
|
|
66
|
+
reason: "Bare okstra run cannot invoke host-native Claude workers. Use the okstra-run skill in Claude Code or pass --lead-runtime external.",
|
|
100
67
|
commands: [],
|
|
101
68
|
};
|
|
102
69
|
}
|
|
@@ -115,12 +82,17 @@ export function buildRunPlan({ args, paths, resolution, runManifestPath = "" })
|
|
|
115
82
|
|
|
116
83
|
if (resolution.resolvedRuntime === "codex") {
|
|
117
84
|
return {
|
|
118
|
-
ok:
|
|
119
|
-
reason:
|
|
120
|
-
commands: [
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
85
|
+
ok: false,
|
|
86
|
+
reason: "Bare okstra run cannot invoke host-native Codex workers. Use the okstra-run skill in Codex or pass --lead-runtime external for an all-CLI run.",
|
|
87
|
+
commands: [],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (resolution.resolvedRuntime === "antigravity") {
|
|
92
|
+
return {
|
|
93
|
+
ok: false,
|
|
94
|
+
reason: "Bare okstra run cannot invoke host-native Antigravity workers. Use the okstra-run skill in Antigravity or pass --lead-runtime external for an all-CLI run.",
|
|
95
|
+
commands: [],
|
|
124
96
|
};
|
|
125
97
|
}
|
|
126
98
|
|
|
@@ -147,11 +119,6 @@ async function runTeam(args) {
|
|
|
147
119
|
return await runTeamCommand(args);
|
|
148
120
|
}
|
|
149
121
|
|
|
150
|
-
async function runCodexDispatch(args) {
|
|
151
|
-
const { run: runCodexDispatchCommand } = await import("./codex-dispatch.mjs");
|
|
152
|
-
return await runCodexDispatchCommand(args);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
122
|
export async function run(args) {
|
|
156
123
|
if (args.includes("--help") || args.includes("-h")) {
|
|
157
124
|
process.stdout.write(USAGE);
|
|
@@ -192,9 +159,7 @@ export async function run(args) {
|
|
|
192
159
|
|
|
193
160
|
const plan = buildRunPlan({ args, paths, resolution, runManifestPath });
|
|
194
161
|
for (const command of plan.commands.slice(1)) {
|
|
195
|
-
const code = command.
|
|
196
|
-
? await runTeam(command.args)
|
|
197
|
-
: await runCodexDispatch(command.args);
|
|
162
|
+
const code = await runTeam(command.args);
|
|
198
163
|
if (code !== 0) return code;
|
|
199
164
|
}
|
|
200
165
|
return 0;
|
|
@@ -20,7 +20,7 @@ Subcommands:
|
|
|
20
20
|
|
|
21
21
|
Usage:
|
|
22
22
|
okstra wizard new-state-file
|
|
23
|
-
okstra wizard init --state-file <path> --project-root <p> --project-id <id>
|
|
23
|
+
okstra wizard init --state-file <path> --project-root <p> --project-id <id> --host-runtime <runtime>
|
|
24
24
|
okstra wizard step --state-file <path> [--answer <value>]
|
|
25
25
|
okstra wizard render-args --state-file <path>
|
|
26
26
|
okstra wizard confirmation --state-file <path>
|
|
@@ -106,6 +106,9 @@ export async function run(args) {
|
|
|
106
106
|
pyArgs.push("--workspace-root", paths.workspace);
|
|
107
107
|
pyArgs.push("--project-root", flags["project-root"]);
|
|
108
108
|
pyArgs.push("--project-id", flags["project-id"]);
|
|
109
|
+
if (flags["host-runtime"]) {
|
|
110
|
+
pyArgs.push("--host-runtime", flags["host-runtime"]);
|
|
111
|
+
}
|
|
109
112
|
} else if (sub === "step" && flags.answer !== undefined) {
|
|
110
113
|
pyArgs.push("--answer", flags.answer);
|
|
111
114
|
}
|
|
@@ -14,7 +14,7 @@ const USAGE = `okstra doctor — diagnose the installed runtime
|
|
|
14
14
|
|
|
15
15
|
Usage:
|
|
16
16
|
okstra doctor Run all checks and print a summary
|
|
17
|
-
okstra doctor --runtime <claude-code|codex|external|all>
|
|
17
|
+
okstra doctor --runtime <claude-code|codex|antigravity|external|all>
|
|
18
18
|
okstra doctor --phase <phase>
|
|
19
19
|
okstra doctor --json Machine-readable result
|
|
20
20
|
Checks:
|
|
@@ -48,7 +48,7 @@ export function parseDoctorArgs(args) {
|
|
|
48
48
|
} else if (arg === "--runtime" || arg.startsWith("--runtime=")) {
|
|
49
49
|
const value = arg.startsWith("--runtime=") ? arg.slice("--runtime=".length) : args[i + 1];
|
|
50
50
|
if (!value || value.startsWith("--")) {
|
|
51
|
-
throw new Error("--runtime requires one of: claude-code, codex, external, all");
|
|
51
|
+
throw new Error("--runtime requires one of: claude-code, codex, antigravity, external, all");
|
|
52
52
|
}
|
|
53
53
|
result.runtime = validateInstallRuntime(value);
|
|
54
54
|
if (arg === "--runtime") i++;
|
|
@@ -226,6 +226,9 @@ export async function run(args) {
|
|
|
226
226
|
await check("bin: okstra.sh", () => checkBashEntry(paths.bin, "okstra.sh")),
|
|
227
227
|
await check("bin: okstra-codex-exec.sh", () => checkBashEntry(paths.bin, "okstra-codex-exec.sh")),
|
|
228
228
|
await check("bin: okstra-antigravity-exec.sh", () => checkBashEntry(paths.bin, "okstra-antigravity-exec.sh")),
|
|
229
|
+
await check("bin: okstra-grok-exec.sh", () => checkBashEntry(paths.bin, "okstra-grok-exec.sh")),
|
|
230
|
+
await check("bin: okstra-kimi-exec.sh", () => checkBashEntry(paths.bin, "okstra-kimi-exec.sh")),
|
|
231
|
+
await check("bin: okstra-provider-exec.py", () => checkBashEntry(paths.bin, "okstra-provider-exec.py")),
|
|
229
232
|
await check("bin: okstra-central.sh", () => checkBashEntry(paths.bin, "okstra-central.sh")),
|
|
230
233
|
...(await Promise.all(
|
|
231
234
|
requiredSkillNamesForRuntime(resolvedRuntime).map((name) =>
|
|
@@ -275,7 +278,7 @@ export async function run(args) {
|
|
|
275
278
|
|
|
276
279
|
if (!runtimeResolution.ok) {
|
|
277
280
|
process.stdout.write(
|
|
278
|
-
` [WARN] runtime host: ${runtimeResolution.reason} skill checks skipped — pass --runtime <claude-code|codex|external> to include them.\n`,
|
|
281
|
+
` [WARN] runtime host: ${runtimeResolution.reason} skill checks skipped — pass --runtime <claude-code|codex|antigravity|external> to include them.\n`,
|
|
279
282
|
);
|
|
280
283
|
}
|
|
281
284
|
if (dockerWarning) {
|