okstra 0.209.2 → 0.209.4
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/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/prompts/lead/adapters/cmux.md +1 -1
- package/runtime/prompts/lead/okstra-lead-contract.md +1 -1
- package/runtime/python/okstra_ctl/agent/activity.py +5 -0
- package/runtime/python/okstra_ctl/design_prep.py +18 -4
- package/runtime/python/okstra_ctl/dispatch_checkpoints.py +6 -4
- package/runtime/python/okstra_ctl/report_assembly.py +15 -6
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -33,7 +33,7 @@ Keep the selected host relay's dispatch permission guidance when calling `okstra
|
|
|
33
33
|
| `await_workers` | Run `okstra team await --project-root <root> --run-manifest <path>` through the host's asynchronous shell facility. |
|
|
34
34
|
| `redispatch_worker` | Create the core-specified fresh jobs file and dispatch it with a new `dispatchKind`; never reuse a live worker conversation. |
|
|
35
35
|
| `shutdown_workers` | Run `okstra team teardown --project-root <root> --run-manifest <path>` only after the user-approved cleanup gate. |
|
|
36
|
-
| `record_lead_event` | Append progress and activity records to the manifest-provided `leadEventsPath`. Use `okstra lead-progress append --project-root <dir> --run-manifest <path> --phase <phase-id>` for a checkpoint the command performing that step does not record itself (the lead contract "Progress reporting" lists the ones `team dispatch`, `team await`, `team reclaim`, `worker-dispatch` and `report-finalize` record; emit the `PROGRESS:` lines they print instead) and `okstra agent-activity append --project-root <dir> --run-manifest <path> --kind <kind> ...` for an activity record; both resolve the ledger path from the run manifest. Emit the matching `PROGRESS:` line — the command prints it as `progressLine` — and, when an activity record is required, the immediately following `ACTIVITY:` line from the same structured fields. |
|
|
36
|
+
| `record_lead_event` | Append progress and activity records to the manifest-provided `leadEventsPath`. Use `okstra lead-progress append --project-root <dir> --run-manifest <path> --phase <phase-id>` for a checkpoint the command performing that step does not record itself (the lead contract "Progress reporting" lists the ones `team dispatch`, `team await`, `team reclaim`, `worker-dispatch` and `report-finalize` record; emit the `PROGRESS:` lines they print instead) and `okstra agent-activity append --project-root <dir> --run-manifest <path> --kind <kind> ...` for an activity record — never for the `worker-dispatched` / `worker-completed` rows `team dispatch` and `team await` append themselves; both resolve the ledger path from the run manifest. Emit the matching `PROGRESS:` line — the command prints it as `progressLine` — and, when an activity record is required, the immediately following `ACTIVITY:` line from the same structured fields. |
|
|
37
37
|
| `collect_usage` | Collect artifact/CLI-log-backed usage through the existing Okstra token-usage path; never substitute another runtime's session log. |
|
|
38
38
|
|
|
39
39
|
An `implementation` run calls `dispatch_worker` twice: once for the Executor, then — after `await_workers` settles it — once for the verifiers. That second call's `--workers` list must omit the Executor's worker ID: it is materialized as the Executor on every dispatch, so a batch still carrying it is refused again. A verifier started beside the Executor observes base HEAD instead of the stage diff, so a single batch holding both is refused by `scripts/okstra_ctl/dispatch_core.py` `_validate_implementation_phase_order`, `--dry-run` included.
|
|
@@ -66,7 +66,7 @@ Every `okstra` command the lead documents cite, grouped by phase, each spelled w
|
|
|
66
66
|
| Command | Use when | Procedure |
|
|
67
67
|
|---|---|---|
|
|
68
68
|
| `okstra lead-progress append --project-root <dir> --run-manifest <path> --phase <phase-id>` | Every `PROGRESS` checkpoint the command performing that step does not record itself; print the `progressLine` it returns. `--phase` accepts only the listed phase ids; use `--worker`, `--field NAME=VALUE`, and `--detail <text>` for the rest | this contract "Progress reporting" |
|
|
69
|
-
| `okstra agent-activity append --project-root <dir> --run-manifest <path> --kind <kind> --agent <agent> --outcome <outcome> --summary <text>` | Before each activity boundary when the run manifest declares `activityContractVersion: 1` | launch prompt "Progress reporting" |
|
|
69
|
+
| `okstra agent-activity append --project-root <dir> --run-manifest <path> --kind <kind> --agent <agent> --outcome <outcome> --summary <text>` | Before each activity boundary when the run manifest declares `activityContractVersion: 1`, except a `worker-dispatched` / `worker-completed` row the dispatching command already appended (see "Progress reporting") | launch prompt "Progress reporting" |
|
|
70
70
|
| `okstra error-log append-observed --out <errors-path> --task-key <key> --phase <phase> --agent <provider-worker> --agent-role <role> --model <model> --error-type <type> --command-kind <kind> --command <command> --message <text>` | Record an observed failure. `--agent` takes the provider worker id (`codex-worker`); an execution label goes in `--execution-label` | this contract "Errors log path wiring"; `team-contract` "Worker error-log command" |
|
|
71
71
|
| `okstra approval-decision open --ledger <path> --task-key <key> --task-type <type> --run-seq <seq> --clarification-id <C-NNN> --ticket-id <id> --statement <text> --expected-form <form> --classification <class> --origin <origin> --user-confirmation <text> --unblock-condition <text> --recommended-disposition <disposition> [--supersedes <C-NNN>]` | Open an approval blocker after the user confirmed it | this contract "User confirmation before an approval blocker" |
|
|
72
72
|
| `okstra approval-decision carry --ledger <path> --clarification-id <C-NNN> --from-responses <path>` | Carry a decision answered in an earlier run | `report-writer` "Role-owned inputs" |
|
|
@@ -166,6 +166,9 @@ def _activity_row(event: LeadEvent) -> dict[str, Any]:
|
|
|
166
166
|
from okstra_ctl.execution_identity import stored_identity
|
|
167
167
|
|
|
168
168
|
row = {key: details[key] for key in ACTIVITY_FIELDS}
|
|
169
|
+
if row["resultPath"] is None:
|
|
170
|
+
# 0.209.1 의 team dispatch/await 가 null 로 기록한 행. 원장은 고치지 않고 읽을 때 맞춘다.
|
|
171
|
+
row["resultPath"] = ""
|
|
169
172
|
if "clarificationRefs" in details:
|
|
170
173
|
refs = details["clarificationRefs"]
|
|
171
174
|
if not isinstance(refs, list) or any(
|
|
@@ -201,6 +204,8 @@ def record_activity(
|
|
|
201
204
|
raise ActivityProjectionError(
|
|
202
205
|
"run manifest does not declare activityContractVersion 1"
|
|
203
206
|
)
|
|
207
|
+
if not isinstance(details.get("resultPath"), str):
|
|
208
|
+
raise ActivityProjectionError("activity resultPath must be a string")
|
|
204
209
|
_validate_activity_ownership(project_root, manifest, details)
|
|
205
210
|
event = LeadEvent(
|
|
206
211
|
event_type="activity",
|
|
@@ -313,6 +313,23 @@ def _complete_request(
|
|
|
313
313
|
return "\n".join(lines) + "\n"
|
|
314
314
|
|
|
315
315
|
|
|
316
|
+
def canonical_request_path(planning_seq: str, item_id: str) -> str:
|
|
317
|
+
return f"design-prep-requests/design-prep-request-{planning_seq}-{item_id}.md"
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def canonicalize_request_paths(data: Mapping[str, Any], data_path: Path) -> None:
|
|
321
|
+
"""Name each open item's request by this report's planning seq.
|
|
322
|
+
|
|
323
|
+
The detector snapshot cannot know the report seq and names requests by stage
|
|
324
|
+
number, so a second planning run of the same task pointed its request at the
|
|
325
|
+
first run's file and materialization refused to overwrite it.
|
|
326
|
+
"""
|
|
327
|
+
planning_seq = _planning_seq(Path(data_path))
|
|
328
|
+
for item in _design_preparation_items(data):
|
|
329
|
+
if isinstance(item, dict) and "requestPath" in item and isinstance(item.get("id"), str):
|
|
330
|
+
item["requestPath"] = canonical_request_path(planning_seq, item["id"])
|
|
331
|
+
|
|
332
|
+
|
|
316
333
|
def _request_target(
|
|
317
334
|
*,
|
|
318
335
|
data_path: Path,
|
|
@@ -322,10 +339,7 @@ def _request_target(
|
|
|
322
339
|
item_id = item.get("id")
|
|
323
340
|
if not isinstance(item_id, str) or not item_id:
|
|
324
341
|
raise DesignPrepError("design preparation item id must be a non-empty string")
|
|
325
|
-
expected_relative = (
|
|
326
|
-
"design-prep-requests/"
|
|
327
|
-
f"design-prep-request-{planning_seq}-{item_id}.md"
|
|
328
|
-
)
|
|
342
|
+
expected_relative = canonical_request_path(planning_seq, item_id)
|
|
329
343
|
request_dir = data_path.parent.parent / "design-prep-requests"
|
|
330
344
|
recorded = item.get("requestPath")
|
|
331
345
|
if recorded == expected_relative:
|
|
@@ -74,7 +74,8 @@ def record_dispatch_checkpoints(
|
|
|
74
74
|
project_root, run_manifest, source=source,
|
|
75
75
|
kind="worker-dispatched", agent=job.worker_id,
|
|
76
76
|
summary=f"Dispatched {job.worker_id} as an initial worker",
|
|
77
|
-
result_path=
|
|
77
|
+
result_path=_project_relative(project_root, str(job.worker_result_path)),
|
|
78
|
+
outcome="pending",
|
|
78
79
|
))
|
|
79
80
|
dispatches_writer = any(job.worker_id == REPORT_WRITER_WORKER_ID for job in jobs)
|
|
80
81
|
writer_dispatched_before = bool(
|
|
@@ -165,9 +166,10 @@ def _declares_activity_contract(
|
|
|
165
166
|
return manifest.get("activityContractVersion") == 1
|
|
166
167
|
|
|
167
168
|
|
|
168
|
-
def _project_relative(project_root: Path, value: Any) -> str
|
|
169
|
+
def _project_relative(project_root: Path, value: Any) -> str:
|
|
170
|
+
# 보고서 스키마의 resultPath 는 문자열이다. 리드 CLI 도 경로가 없으면 "" 를 쓴다.
|
|
169
171
|
if not isinstance(value, str) or not value:
|
|
170
|
-
return
|
|
172
|
+
return ""
|
|
171
173
|
path = Path(value)
|
|
172
174
|
root = project_root.resolve()
|
|
173
175
|
return str(path.relative_to(root)) if path.is_relative_to(root) else value
|
|
@@ -181,7 +183,7 @@ def _record_worker_activity(
|
|
|
181
183
|
kind: str,
|
|
182
184
|
agent: str,
|
|
183
185
|
summary: str,
|
|
184
|
-
result_path: str
|
|
186
|
+
result_path: str,
|
|
185
187
|
outcome: str,
|
|
186
188
|
) -> str | None:
|
|
187
189
|
"""실패 처리는 `record_checkpoint` 와 같다: stderr 에만 남기고, 누락은 검증기가 보고한다."""
|
|
@@ -26,7 +26,11 @@ from .report_synthesis_packet import (
|
|
|
26
26
|
)
|
|
27
27
|
from .conformance import strip_stage_declaration_label
|
|
28
28
|
from .coverage_census import census_warning_rows, read_census_state
|
|
29
|
-
from .design_prep import
|
|
29
|
+
from .design_prep import (
|
|
30
|
+
DesignPrepError,
|
|
31
|
+
canonicalize_request_paths,
|
|
32
|
+
materialize_design_prep_requests,
|
|
33
|
+
)
|
|
30
34
|
from .phases.implementation_option_selection.validation import validate_blocked_answer_channel
|
|
31
35
|
from .phases.technical_verification.validation import validate_technical_verification_report
|
|
32
36
|
from .report_routing import technical_verification_routing_errors
|
|
@@ -830,6 +834,16 @@ def assemble_report(
|
|
|
830
834
|
if input_issues:
|
|
831
835
|
raise ReportAssemblyError(tuple(input_issues))
|
|
832
836
|
data = _compose(project_root, manifest_path, manifest, inputs, schema)
|
|
837
|
+
value = manifest.get("expectedReportRecordPath")
|
|
838
|
+
if not isinstance(value, str) or not value:
|
|
839
|
+
_fail("orchestrator", manifest_path, "expectedReportRecordPath", "required")
|
|
840
|
+
target = Path(value)
|
|
841
|
+
target = target if target.is_absolute() else project_root / target
|
|
842
|
+
if manifest.get("taskType") == "implementation-planning":
|
|
843
|
+
try:
|
|
844
|
+
canonicalize_request_paths(data, target)
|
|
845
|
+
except DesignPrepError as exc:
|
|
846
|
+
_fail("orchestrator", target, "designPreparation", str(exc))
|
|
833
847
|
errors = validate(data, schema)
|
|
834
848
|
path_errors = planned_path_declaration_errors(data)
|
|
835
849
|
if path_errors:
|
|
@@ -840,11 +854,6 @@ def assemble_report(
|
|
|
840
854
|
direction_errors = selected_direction_plan_errors(
|
|
841
855
|
data, project_root, manifest
|
|
842
856
|
)
|
|
843
|
-
value = manifest.get("expectedReportRecordPath")
|
|
844
|
-
if not isinstance(value, str) or not value:
|
|
845
|
-
_fail("orchestrator", manifest_path, "expectedReportRecordPath", "required")
|
|
846
|
-
target = Path(value)
|
|
847
|
-
target = target if target.is_absolute() else project_root / target
|
|
848
857
|
verification_errors = validate_technical_verification_report(data, target, project_root)
|
|
849
858
|
verification_errors.extend(technical_verification_routing_errors(data))
|
|
850
859
|
if verification_errors:
|