okstra 0.125.3 → 0.125.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
package/runtime/BUILD.json
CHANGED
|
@@ -125,12 +125,16 @@ After each worker subagent returns (regardless of role), Lead MUST verify the ca
|
|
|
125
125
|
2. If the SECOND attempt also fails the same check, Lead records the role's terminal status as `error` with `--message "result-missing after 1 retry"` and proceeds to Phase 5.5 / Phase 6 with the remaining workers' results. Lead MUST NOT retry a third time — convergence and the report writer are designed to operate on reduced-confidence single-or-two-analyser mode when one role is absent (`prompts/lead/okstra-lead-contract.md` "If only one worker result is usable: reduced-confidence synthesis").
|
|
126
126
|
3. The retry counter is **per-run, per-role** and is NOT preserved across runs. A subsequent okstra run for the same task-key starts each role's counter fresh.
|
|
127
127
|
4. Convergence reverify rounds (Phase 5.5) inherit the same one-retry budget — a reverify dispatch that triggers result-missing may be re-dispatched once.
|
|
128
|
+
5. **Reading-plan exception for a deterministic failure.** Byte-identical is the rule because it stops a lead from hiding a failure behind a different model, provider, or result path. It does not fit a failure the prompt *causes*: when the first attempt exhausted its whole time budget with no output and the required reading it named is large enough to explain that, an identical retry buys a second timeout at the same cost. In that case only, Lead MAY change the **reading plan** — which files the worker reads and in what order, including skipping a file whose content is already reachable by another named path. Everything the invariant exists to protect stays fixed: same `**Result Path:**`, same `**Prompt History Path:**`, same model, same adapter-native assignment identity, same role-id, same task.
|
|
129
|
+
- The exception applies only when the first attempt produced **no result file** after consuming its full timeout. A worker that failed fast, errored, or returned a partial artifact is a transient/regular failure — retry it byte-identically.
|
|
130
|
+
- Lead MUST log the deviation with the normal `contract-deviation` entry naming the removed or reordered reads and the measured evidence that motivated it (byte counts, elapsed time). An unlogged reading-plan change is a contract violation, not an exception.
|
|
131
|
+
- This exception never licenses changing what the worker is asked to *produce*. Narrowing the deliverable to make it finish is a contract violation.
|
|
128
132
|
|
|
129
133
|
**Logging.** Lead records the first attempt's `cli-failure` (already emitted by the wrapper sub-agent) as-is. The retry, on success, is logged via the normal worker-completion path; on failure (second `*_RESULT_MISSING`), Lead records a single `contract-violation` entry with `--message "result-missing after 1 retry"` referencing both adapter dispatch-attempt ids and prompt-history paths.
|
|
130
134
|
|
|
131
135
|
**Diagnostic sidecar (advisory).** Both codex/antigravity wrappers also write a heartbeat sidecar at `<prompt-path>.status.json` recording `started_ts`, `ended_ts`, `exit_code`, `duration_ms`, and the canonical `log_path` (see `scripts/okstra-wrapper-status.py` for the schema). Lead MAY read this sidecar when deciding whether the first attempt actually launched the CLI (stage=`exited`, `exit_code=0`, non-zero `duration_ms`) versus failed before reaching it (sidecar absent, or stage=`started` with no exit fields). The sidecar is best-effort — its absence is NOT by itself a reason to skip the retry; the canonical trigger remains the missing result file.
|
|
132
136
|
|
|
133
|
-
**Rationale.** Observed failure mode: the CLI (codex/antigravity) streams its full analysis to stdout but hits its token budget or a sandbox EPERM mid-`Write` of the result file, exiting 0 with no artifact. Forwarding the partial stdout silently degrades synthesis; classifying the role as `error` without retrying gives up a recoverable signal. A single retry catches the transient class of this failure (re-dispatch with the same prompt typically succeeds when the underlying cause was an intermittent sandbox lock or a token-budget spike) while bounding the retry cost to a known upper bound (~2× the original wrapper budget per role).
|
|
137
|
+
**Rationale.** Observed failure mode: the CLI (codex/antigravity) streams its full analysis to stdout but hits its token budget or a sandbox EPERM mid-`Write` of the result file, exiting 0 with no artifact. Forwarding the partial stdout silently degrades synthesis; classifying the role as `error` without retrying gives up a recoverable signal. A single retry catches the transient class of this failure (re-dispatch with the same prompt typically succeeds when the underlying cause was an intermittent sandbox lock or a token-budget spike) while bounding the retry cost to a known upper bound (~2× the original wrapper budget per role). The reading-plan exception in item 5 covers the class this rationale does not: a worker that timed out because its prompt asked it to read too much fails identically on an identical retry, so the second attempt must change the reading plan or it is knowingly wasted.
|
|
134
138
|
|
|
135
139
|
## Worker Output Contract
|
|
136
140
|
|
|
@@ -372,13 +372,41 @@ def attached_user_responses_section(source: Path) -> str:
|
|
|
372
372
|
|
|
373
373
|
|
|
374
374
|
def clarification_response_with_sidecars(source: Path) -> str:
|
|
375
|
-
"""clarification-response
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
375
|
+
"""clarification-response 본문에 `user-responses/` 사이드카를 덧붙인 본문.
|
|
376
|
+
|
|
377
|
+
`resume-clarification` 의 설계된 입력은 사용자가 §1 의 `User input` 열을
|
|
378
|
+
채운 **직전 final-report 자체**다. 그 전문을 그대로 복사하면 리포트가
|
|
379
|
+
instruction-set 안에 두 번째로 존재하게 되고, report-writer 는 같은 내용을
|
|
380
|
+
`clarification-response.md` 로 한 번, 직전 리포트 경로로 다시 한 번 읽는다.
|
|
381
|
+
리포트는 run 이 누적될수록 커지므로 이 중복은 스스로 악화된다(실측: 283K
|
|
382
|
+
리포트가 892K 의 중복 읽기를 만들어 report-writer 를 timeout 시켰다).
|
|
383
|
+
|
|
384
|
+
그래서 소스가 final-report 일 때는 답변이 실린 §1 만 잘라내고 원문은 경로로
|
|
385
|
+
가리킨다 — `attached_user_responses_section` 이 implementation carry-in 에서
|
|
386
|
+
이미 쓰는 "원문은 경로로, 답변만 첨부" 규칙과 같다. §1 이 없는 소스(사용자가
|
|
387
|
+
직접 쓴 답변 파일)는 그대로 복사한다.
|
|
379
388
|
"""
|
|
380
389
|
text = source.read_text(encoding="utf-8")
|
|
381
390
|
section = attached_user_responses_section(source)
|
|
391
|
+
body = _clarification_carry_body(source, text)
|
|
382
392
|
if not section:
|
|
393
|
+
return body
|
|
394
|
+
return body.rstrip("\n") + "\n\n---\n\n" + section
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
def _clarification_carry_body(source: Path, text: str) -> str:
|
|
398
|
+
"""final-report 소스는 §1 + 원문 포인터로 좁히고, 그 외는 원문 그대로."""
|
|
399
|
+
slice_ = _section_1_slice(text)
|
|
400
|
+
if slice_ is None:
|
|
383
401
|
return text
|
|
384
|
-
|
|
402
|
+
heading = SECTION_HEADING_PATTERN.search(text)
|
|
403
|
+
assert heading is not None # _section_1_slice returned a slice
|
|
404
|
+
return (
|
|
405
|
+
"# Clarification Response (carry-in)\n\n"
|
|
406
|
+
f"- Source report: `{source}`\n"
|
|
407
|
+
"- This file carries **only** the source report's Clarification Items "
|
|
408
|
+
"section; the report itself is read from the path above when a phase "
|
|
409
|
+
"needs it. Do not re-read the source report to find the answers — they "
|
|
410
|
+
"are in the `User input` column below.\n\n"
|
|
411
|
+
f"{heading.group(0)}\n{slice_.rstrip()}\n"
|
|
412
|
+
)
|