okstra 0.77.0 → 0.78.1
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.kr.md +1 -1
- package/README.md +1 -1
- package/bin/okstra +1 -125
- package/docs/contributor-change-matrix.md +12 -0
- package/docs/kr/cli.md +5 -4
- package/docs/project-structure-overview.md +1 -1
- package/docs/superpowers/plans/2026-06-13-repo-risk-hardening.md +493 -0
- package/docs/superpowers/specs/2026-06-13-forbidden-actions-ssot-design.md +134 -0
- package/docs/superpowers/specs/2026-06-13-neutral-tmux-lead-adapter-design.md +284 -0
- package/package.json +5 -2
- package/runtime/BUILD.json +2 -2
- package/runtime/DO_NOT_EDIT.md +21 -0
- package/runtime/agents/SKILL.md +3 -0
- package/runtime/bin/okstra-trace-cleanup.sh +16 -12
- package/runtime/prompts/profiles/forbidden-actions.json +69 -0
- package/runtime/prompts/profiles/implementation.md +1 -8
- package/runtime/prompts/profiles/release-handoff.md +1 -15
- package/runtime/python/okstra_ctl/codex_dispatch.py +70 -2
- package/runtime/python/okstra_ctl/context_cost.py +1 -1
- package/runtime/python/okstra_ctl/dispatch_core.py +897 -0
- package/runtime/python/okstra_ctl/doctor.py +292 -0
- package/runtime/python/okstra_ctl/lead_runtime.py +72 -0
- package/runtime/python/okstra_ctl/pr_template.py +4 -3
- package/runtime/python/okstra_ctl/render.py +67 -16
- package/runtime/python/okstra_ctl/run.py +20 -12
- package/runtime/python/okstra_ctl/team.py +267 -0
- package/runtime/python/okstra_ctl/tmux.py +181 -10
- package/runtime/python/okstra_ctl/workflow.py +30 -71
- package/runtime/python/okstra_ctl/wrapper_status.py +55 -0
- package/runtime/schemas/final-report-v1.0.schema.json +3 -1
- package/runtime/skills/okstra-convergence/SKILL.md +3 -0
- package/runtime/skills/okstra-report-writer/SKILL.md +3 -0
- package/runtime/skills/okstra-team-contract/SKILL.md +12 -0
- package/runtime/validators/forbidden_actions.py +135 -0
- package/runtime/validators/validate-run.py +65 -9
- package/runtime/validators/validate_session_conformance.py +15 -10
- package/src/cli-registry.mjs +277 -0
- package/src/doctor.mjs +93 -4
- package/src/install.mjs +6 -5
- package/src/runtime-manifest.mjs +5 -2
- package/src/team.mjs +63 -0
- package/src/uninstall.mjs +1 -0
|
@@ -19,6 +19,7 @@ from pathlib import Path
|
|
|
19
19
|
from typing import Any, Mapping, Sequence
|
|
20
20
|
|
|
21
21
|
from .lead_events import LeadEvent, append_lead_event
|
|
22
|
+
from .wrapper_status import status_path_for_prompt
|
|
22
23
|
from .paths import task_dir
|
|
23
24
|
|
|
24
25
|
|
|
@@ -206,6 +207,7 @@ def dispatch_plan(plan: DispatchPlan) -> int:
|
|
|
206
207
|
|
|
207
208
|
def _dispatch_worker_with_retry(plan: DispatchPlan, worker: WorkerDispatch) -> int:
|
|
208
209
|
for attempt in range(1, MAX_WORKER_ATTEMPTS + 1):
|
|
210
|
+
_record_worker_attempt(plan, worker, attempt, "running")
|
|
209
211
|
attempt_details = _attempt_event_details(worker, attempt)
|
|
210
212
|
_append_event(plan, "worker-dispatched", attempt_details)
|
|
211
213
|
result = _run_worker(plan, worker)
|
|
@@ -214,7 +216,8 @@ def _dispatch_worker_with_retry(plan: DispatchPlan, worker: WorkerDispatch) -> i
|
|
|
214
216
|
post_process = _post_process_report_writer_result(plan, worker)
|
|
215
217
|
if not post_process["ok"]:
|
|
216
218
|
reason = _require_string(post_process, "reason")
|
|
217
|
-
_set_worker_status(plan.team_state_path, worker.worker_id, "
|
|
219
|
+
_set_worker_status(plan.team_state_path, worker.worker_id, "error", reason)
|
|
220
|
+
_record_worker_attempt(plan, worker, attempt, "error", reason)
|
|
218
221
|
details = {
|
|
219
222
|
**attempt_details,
|
|
220
223
|
"exitCode": 1,
|
|
@@ -225,6 +228,7 @@ def _dispatch_worker_with_retry(plan: DispatchPlan, worker: WorkerDispatch) -> i
|
|
|
225
228
|
_append_event(plan, "worker-failed", details)
|
|
226
229
|
return 1
|
|
227
230
|
_set_worker_status(plan.team_state_path, worker.worker_id, "completed", "")
|
|
231
|
+
_record_worker_attempt(plan, worker, attempt, "completed")
|
|
228
232
|
details = {
|
|
229
233
|
**attempt_details,
|
|
230
234
|
"missingCompletionPaths": [],
|
|
@@ -238,11 +242,19 @@ def _dispatch_worker_with_retry(plan: DispatchPlan, worker: WorkerDispatch) -> i
|
|
|
238
242
|
"missingCompletionPaths": [str(path) for path in missing_paths],
|
|
239
243
|
"reason": "required worker artifact was not produced",
|
|
240
244
|
}
|
|
245
|
+
_record_worker_attempt(
|
|
246
|
+
plan,
|
|
247
|
+
worker,
|
|
248
|
+
attempt,
|
|
249
|
+
"error",
|
|
250
|
+
"required worker artifact was not produced",
|
|
251
|
+
)
|
|
241
252
|
_append_event(plan, "worker-retry-scheduled", retry_details)
|
|
242
253
|
continue
|
|
243
254
|
|
|
244
255
|
reason = _failure_reason(result.returncode, missing_paths)
|
|
245
|
-
_set_worker_status(plan.team_state_path, worker.worker_id, "
|
|
256
|
+
_set_worker_status(plan.team_state_path, worker.worker_id, "error", reason)
|
|
257
|
+
_record_worker_attempt(plan, worker, attempt, "error", reason)
|
|
246
258
|
details = {
|
|
247
259
|
**attempt_details,
|
|
248
260
|
"exitCode": result.returncode,
|
|
@@ -252,6 +264,62 @@ def _dispatch_worker_with_retry(plan: DispatchPlan, worker: WorkerDispatch) -> i
|
|
|
252
264
|
_append_event(plan, "worker-failed", details)
|
|
253
265
|
return result.returncode if result.returncode else 1
|
|
254
266
|
return 1
|
|
267
|
+
def _record_worker_attempt(
|
|
268
|
+
plan: DispatchPlan,
|
|
269
|
+
worker: WorkerDispatch,
|
|
270
|
+
attempt: int,
|
|
271
|
+
status: str,
|
|
272
|
+
reason: str = "",
|
|
273
|
+
) -> None:
|
|
274
|
+
payload = _load_json_object(plan.team_state_path, "team-state")
|
|
275
|
+
dispatches = payload.setdefault("workerDispatches", [])
|
|
276
|
+
if not isinstance(dispatches, list):
|
|
277
|
+
raise DispatchError(
|
|
278
|
+
f"team-state workerDispatches must be an array: {plan.team_state_path}"
|
|
279
|
+
)
|
|
280
|
+
for record in dispatches:
|
|
281
|
+
if _matches_worker_attempt(record, worker, attempt):
|
|
282
|
+
record["status"] = status
|
|
283
|
+
record["reason"] = reason
|
|
284
|
+
_write_json(plan.team_state_path, payload)
|
|
285
|
+
return
|
|
286
|
+
dispatches.append(_worker_dispatch_record(worker, attempt, status, reason))
|
|
287
|
+
_write_json(plan.team_state_path, payload)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
def _matches_worker_attempt(
|
|
291
|
+
record: object, worker: WorkerDispatch, attempt: int
|
|
292
|
+
) -> bool:
|
|
293
|
+
return (
|
|
294
|
+
isinstance(record, dict)
|
|
295
|
+
and record.get("workerId") == worker.worker_id
|
|
296
|
+
and record.get("kind") == "initial"
|
|
297
|
+
and record.get("attempt") == attempt
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _worker_dispatch_record(
|
|
302
|
+
worker: WorkerDispatch, attempt: int, status: str, reason: str
|
|
303
|
+
) -> dict[str, Any]:
|
|
304
|
+
provider = "codex" if worker.worker_id == REPORT_WRITER_WORKER_ID else worker.worker_id
|
|
305
|
+
return {
|
|
306
|
+
"workerId": worker.worker_id,
|
|
307
|
+
"role": worker.role,
|
|
308
|
+
"kind": "initial",
|
|
309
|
+
"attempt": attempt,
|
|
310
|
+
"backendType": worker.backend,
|
|
311
|
+
"provider": provider,
|
|
312
|
+
"status": status,
|
|
313
|
+
"paneId": "",
|
|
314
|
+
"promptPath": str(worker.prompt_path),
|
|
315
|
+
"resultPath": str(worker.result_path),
|
|
316
|
+
"workerResultPath": str(worker.worker_result_path),
|
|
317
|
+
"statusSidecarPath": str(status_path_for_prompt(worker.prompt_path)),
|
|
318
|
+
"degradedFrom": "",
|
|
319
|
+
"reason": reason,
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
|
|
255
323
|
|
|
256
324
|
|
|
257
325
|
def main(argv: Sequence[str] | None = None) -> int:
|
|
@@ -188,7 +188,7 @@ def _skill_assets_metric() -> dict:
|
|
|
188
188
|
for fname in WORKER_AGENT_FILES:
|
|
189
189
|
path = _installed_or_dev(
|
|
190
190
|
claude_home / "agents" / fname,
|
|
191
|
-
f"agents/workers/{fname}",
|
|
191
|
+
f"runtime/agents/workers/{fname}",
|
|
192
192
|
)
|
|
193
193
|
entries.append((f"agent:{fname}", path))
|
|
194
194
|
|