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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.209.2",
3
+ "version": "0.209.4",
4
4
  "description": "Host-aware multi-provider cross-verification orchestrator runtime and agent skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.209.2",
3
- "builtAt": "2026-10-05T05:48:33.643Z",
2
+ "package": "0.209.4",
3
+ "builtAt": "2026-10-05T06:38:59.441Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -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=None, outcome="pending",
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 | None:
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 None
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 | None,
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 DesignPrepError, materialize_design_prep_requests
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: