okstra 0.162.2 → 0.163.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/docs/project-structure-overview.md +2 -1
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/bin/lib/okstra/cli.sh +4 -0
- package/runtime/bin/lib/okstra/globals.sh +3 -0
- package/runtime/bin/okstra.sh +52 -0
- package/runtime/prompts/profiles/implementation-planning.md +4 -1
- package/runtime/prompts/wizard/prompts.ko.json +5 -2
- package/runtime/python/okstra_ctl/models.py +12 -0
- package/runtime/python/okstra_ctl/wizard.py +30 -0
- package/runtime/validators/validate-run.py +30 -7
- package/src/commands/execute/run.mjs +11 -1
|
@@ -294,6 +294,7 @@ Important modules:
|
|
|
294
294
|
| `manager_sync.py` | One-way child project `.okstra` snapshot reader; corrupt child state becomes row-level `error` so other children continue |
|
|
295
295
|
| `manager_launch.py` | Child launch packet and manager child context renderer; records `prepared` launch metadata/events without changing project-local task state |
|
|
296
296
|
| `dispatch_core.py` | Backend-neutral worker dispatch core — worker execution/collection logic shared by any lead runtime (Claude/Codex/Antigravity/external); gates selected initial prompts through the shared cross-task contract before launch |
|
|
297
|
+
| `cmux.py` | cmux-pane worker backend — mirrors the tmux backend's contract (a worker that gets a pane frees the lead process; anything that stops a pane opening degrades quietly to the blocking wrapper, recording a surface UUID rather than a tmux pane id). Detects a usable cmux session before selecting the backend (CLI resolves + ping answers PONG + the lead's workspace is resolvable), derives placement from the workspace geometry each dispatch, relays lead/worker events to the cmux sidebar, and records the run's terminal backend in the manifest so both phases of a run land on one backend. A sandbox that hides cmux (`PermissionError` on the socket) stops dispatch with the remedy instead of degrading into the same broken fallback; a quit app (`FileNotFoundError`) still degrades |
|
|
297
298
|
| `codex_dispatch.py` | Codex lead CLI-worker dispatcher — the `okstra codex-dispatch` backend. Reads the run manifest to run the Codex-side supported worker subset, applies the same cross-task initial-prompt gate, and performs token-usage substitution, view render, follow-up, and validation |
|
|
298
299
|
| `analysis_packet.py` | assembles the compact analysis-worker input packet for a task run from worker-owned profile sections; report/lead procedure stays outside the packet |
|
|
299
300
|
| `analysis_inputs.py` | shared input boundary for `project-analysis`, `feature-analysis`, and `change-impact-analysis` — validates evidence-report identity and review status, enforces the type-to-type relation allowlist, computes `exact`/`stale` freshness, and resolves free-text or `PF-NNN` feature targets for both wizard and prepare paths |
|
|
@@ -431,7 +432,7 @@ Boilerplate shared by several skills (bash invocation rule, outdated-CLI preflig
|
|
|
431
432
|
| `agents/workers/kimi-worker.params.json` | Kimi read-only analyser/critic wrapper params |
|
|
432
433
|
| `agents/workers/report-writer-worker.md` | data.json SSOT author and audit sidecar writer |
|
|
433
434
|
|
|
434
|
-
The neutral lead lifecycle contract lives at `prompts/lead/okstra-lead-contract.md`. Host mappings live under `prompts/lead/adapters/`: `claude-code.md`, `codex.md`, `antigravity.md`, and `
|
|
435
|
+
The neutral lead lifecycle contract lives at `prompts/lead/okstra-lead-contract.md`. Host mappings live under `prompts/lead/adapters/`: `claude-code.md`, `codex.md`, `antigravity.md`, `external.md`, and `cmux.md` (the environment-selected cmux worker backend, read by every task type regardless of lead runtime). All are runtime resources installed under `~/.okstra/prompts/lead/`, not agent skills.
|
|
435
436
|
|
|
436
437
|
### 4.12 `tests/` and `tests-e2e/`
|
|
437
438
|
|
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -94,6 +94,10 @@ while [[ $# -gt 0 ]]; do
|
|
|
94
94
|
REPORT_WRITER_MODEL_OVERRIDE="$(require_option_value --report-writer-model "${2-}")"
|
|
95
95
|
shift 2
|
|
96
96
|
;;
|
|
97
|
+
--launch-only)
|
|
98
|
+
LAUNCH_ONLY="true"
|
|
99
|
+
shift
|
|
100
|
+
;;
|
|
97
101
|
--lead-runtime)
|
|
98
102
|
LEAD_RUNTIME="$(require_option_value --lead-runtime "${2-}")"
|
|
99
103
|
shift 2
|
|
@@ -164,3 +164,6 @@ REPORT_WRITER_MODEL_EXECUTION_VALUE=""
|
|
|
164
164
|
DEFAULT_WORKERS="claude,codex,report-writer"
|
|
165
165
|
DISPLAY_COMMAND_NAME="${OKSTRA_COMMAND_NAME:-$(basename "$0")}"
|
|
166
166
|
DISPLAY_TOOL_NAME="${OKSTRA_TOOL_NAME:-okstra}"
|
|
167
|
+
|
|
168
|
+
# `okstra run <lead>` 전용: 번들을 준비하지 않고 리드 CLI 만 띄운다.
|
|
169
|
+
LAUNCH_ONLY="false"
|
package/runtime/bin/okstra.sh
CHANGED
|
@@ -71,6 +71,58 @@ if [[ "$RESUME_CLARIFICATION_MODE" == "true" ]]; then
|
|
|
71
71
|
exit 0
|
|
72
72
|
fi
|
|
73
73
|
autofill_from_project_json
|
|
74
|
+
|
|
75
|
+
# `okstra run <lead>` — open the host, nothing else. The okstra-run skill inside
|
|
76
|
+
# it collects task inputs, which is how the Claude Code path has always worked.
|
|
77
|
+
# Preparing a bundle here would demand a task id before the user has a session
|
|
78
|
+
# to choose one in.
|
|
79
|
+
if [[ "$LAUNCH_ONLY" == "true" ]]; then
|
|
80
|
+
if [[ -z "$PROJECT_ROOT" ]]; then
|
|
81
|
+
PROJECT_ROOT="$(resolve_project_root_strict "$PROJECT_ROOT_OVERRIDE")" || exit 1
|
|
82
|
+
fi
|
|
83
|
+
read -r LEAD_EXECUTABLE SANDBOX_NOTE < <(
|
|
84
|
+
okstra_py - "$LEAD_RUNTIME" <<'PY'
|
|
85
|
+
import sys
|
|
86
|
+
from okstra_ctl.lead_runtime import lead_runtime_info
|
|
87
|
+
from okstra_ctl.models import bare_lead_launch_argv, lead_launch_spec
|
|
88
|
+
provider = lead_runtime_info(sys.argv[1]).agent
|
|
89
|
+
print(bare_lead_launch_argv(provider)[0], lead_launch_spec(provider).sandbox_waiver_note)
|
|
90
|
+
PY
|
|
91
|
+
)
|
|
92
|
+
if ! command -v "$LEAD_EXECUTABLE" >/dev/null 2>&1; then
|
|
93
|
+
printf '%s command not found\n' "$LEAD_EXECUTABLE" >&2
|
|
94
|
+
exit 1
|
|
95
|
+
fi
|
|
96
|
+
if [[ -n "$SANDBOX_NOTE" ]]; then
|
|
97
|
+
okstra_py - "$LEAD_RUNTIME" <<'PY' >&2
|
|
98
|
+
import sys
|
|
99
|
+
from okstra_ctl.lead_runtime import lead_runtime_info
|
|
100
|
+
from okstra_ctl.models import lead_launch_spec
|
|
101
|
+
print("\n" + lead_launch_spec(lead_runtime_info(sys.argv[1]).agent).sandbox_waiver_note)
|
|
102
|
+
PY
|
|
103
|
+
if [[ "$ASSUME_YES" != "true" ]]; then
|
|
104
|
+
printf 'Start %s without its sandbox? [y/N] ' "$LEAD_EXECUTABLE" >&2
|
|
105
|
+
read -r _sandbox_answer < /dev/tty || _sandbox_answer=""
|
|
106
|
+
if [[ "$_sandbox_answer" != "y" && "$_sandbox_answer" != "Y" ]]; then
|
|
107
|
+
printf 'okstra: cancelled\n' >&2
|
|
108
|
+
exit 1
|
|
109
|
+
fi
|
|
110
|
+
fi
|
|
111
|
+
fi
|
|
112
|
+
LAUNCH_ARGV=()
|
|
113
|
+
while IFS= read -r -d '' _arg; do LAUNCH_ARGV+=("$_arg"); done < <(
|
|
114
|
+
okstra_py - "$LEAD_RUNTIME" <<'PY'
|
|
115
|
+
import sys
|
|
116
|
+
from okstra_ctl.lead_runtime import lead_runtime_info
|
|
117
|
+
from okstra_ctl.models import bare_lead_launch_argv
|
|
118
|
+
for a in bare_lead_launch_argv(lead_runtime_info(sys.argv[1]).agent):
|
|
119
|
+
sys.stdout.write(a + "\0")
|
|
120
|
+
PY
|
|
121
|
+
)
|
|
122
|
+
cd "$PROJECT_ROOT"
|
|
123
|
+
exec "${LAUNCH_ARGV[@]}"
|
|
124
|
+
fi
|
|
125
|
+
|
|
74
126
|
autofill_from_manifest
|
|
75
127
|
collect_required_arguments
|
|
76
128
|
|
|
@@ -200,4 +200,7 @@
|
|
|
200
200
|
9. **Cross-project dependency check** — confirm you have not missed a dependency on another repo / another top-level deployable module / a published package. If `dependencyMigrationRisk` has a `kind: cross-project` row, confirm a matching `direction: upstream-precondition` `XP-NNN` row exists in `crossProjectDependencies`, and re-read as a reviewer whether its `requiredWork` is the concrete work the other side must actually build rather than an abstract phrase ("other side's work done") — validator S only checks existence, so concreteness is the self-review's responsibility. Confirm cross-repo work is split into a separate run + XP row instead of being crammed into one task's stages, and that the cross-project substance is not duplicated in `§3 Recommended Next Steps` but lives only in `§5.4 Cross-Project Dependencies`.
|
|
201
201
|
10. **Decision-draft materialization check** — when `decisionDrafts` is non-empty, confirm as a reviewer which stage's stepwise order contains the matching materialization step (creating `.okstra/decisions/<NNNN>-<slug>.md`) and that the number of drafts corresponds 1:1 with the materialization steps. The validator only checks the *existence* of the step, so the `<NNNN>-<slug>` correctness and count correspondence are the self-review's responsibility.
|
|
202
202
|
11. **Variation-point & seam check** — read `variationPointAnalysis` as a skeptic. Is `hasMultipleImplementations` honest against the brief and the sibling code you inspected during pre-planning, or was `false` chosen because it is the cheaper field to fill? For every point with `extract: true`, confirm the `extractionDecision` names a real interface (a `port` for a hexagonal project, not a shared helper) and a `coveredBy` stage that exists in the Stage Map — an interface no stage builds is a decision nobody executes. Then read the recommended option's `testSeams`: each `injectedAs` must name a construction or wiring point a test can actually substitute at, not a symbol the test would have to re-implement — a seam nothing can be injected into leaves the executor writing self-mocks. An empty `testSeams` array is only acceptable when you can defend it in one sentence; the validator accepts it either way, so this is the check that catches an unfilled field posing as a decision.
|
|
203
|
-
12. **Approval blast-radius check (BLOCKING).** Every `Blocks=approval` clarification row must be reachable *from* the plan, not only *into* it: at least one `planItems[]` entry carrying that id as `clarificationId`, or one `requirementCoverage` row blocked on it in `status` or `approvalDisposition`. Item 7 covers only rows this run promoted from a majority-disagree plan item; a blocker raised any other way can still withhold approval while recording nothing it affects. The cost lands on the re-run: `okstra incremental-scope` resolves impacted stages from exactly these two links and treats an id that traces to no stage as grounds to re-verify every stage, so one unlinked blocker turns an incremental re-run into a full one.
|
|
203
|
+
12. **Approval blast-radius check (BLOCKING).** Every `Blocks=approval` clarification row must be reachable *from* the plan, not only *into* it: at least one `planItems[]` entry carrying that id as `clarificationId`, or one `requirementCoverage` row blocked on it in `status` or `approvalDisposition`. Item 7 covers only rows this run promoted from a majority-disagree plan item; a blocker raised any other way can still withhold approval while recording nothing it affects. The cost lands on the re-run: `okstra incremental-scope` resolves impacted stages from exactly these two links and treats an id that traces to no stage as grounds to re-verify every stage, so one unlinked blocker turns an incremental re-run into a full one.
|
|
204
|
+
- **The link must resolve to a stage, not merely exist.** `incremental-scope` reads the stage number out of a `P-Step-<stage>.<step>` / `P-Prep-S<stage>-<kind>` plan-item id, or out of a `Stage N` citation in the blocked coverage row's `coveredBy`. Every other plan-item prefix (`P-Req-*`, `P-Val-*`, `P-Opt-*`, `P-Dep-*`, `P-Rb-*`) is numbered by position in its own array and carries no stage, so a blocker linked only that way MUST also have its coverage row cite the stage in `coveredBy`. Writing the blocked row's `coveredBy` as prose with no `Stage N` in it — `No stage.`, `Partly covered — …` — satisfies nothing: the row passes the link check and the re-run still re-verifies everything.
|
|
205
|
+
- What to write when no stage covers the requirement yet: name the stage the answer will change, not the stage that satisfies the requirement today. A `Blocks=approval` row is admissible only when, absent an answer, `implementation` would produce wrong or unsafe code (see the admissibility rule above) — so some stage's code is at stake by construction. If you genuinely cannot name one, the row fails the admissibility test and belongs in `## 5. Missing Information and Risks` with `Blocks=none`, not in the approval gate.
|
|
206
|
+
**Enforced:** `validators/validate-run.py` `_validate_approval_clarification_backtrace` — one failure for a missing link, a separate one for a link that resolves to no stage.
|
|
@@ -589,9 +589,12 @@
|
|
|
589
589
|
"worktree_impl_new": " worktree : stage {stage} 새 worktree `{path}` (브랜치 `{branch}`, base 는 run 준비 시 해소)",
|
|
590
590
|
"worktree_impl_reuse": " worktree : 기존 stage {stage} worktree `{path}` (브랜치 `{branch}`)",
|
|
591
591
|
"worktree_impl_auto": " worktree : stage 자동 선택 — `{path}` 아래 stage-<N>/ worktree 생성/재사용",
|
|
592
|
+
"clarification_sidecars_empty": " user-responses: 없음 — final-report 만 첨부됩니다",
|
|
593
|
+
"clarification_sidecars_attached": " user-responses: 사이드카 {files}개 · 답변 {count}개 함께 첨부 — {ids}",
|
|
594
|
+
"clarification_sidecars_none_parsed": "답변으로 셀 항목 없음 (reframe 등)",
|
|
592
595
|
"reverify_scope_incremental": " reverify-scope: incremental 가능 — 답변된 항목이 모두 직전 리포트의 stage 에 연결됨 (최종 확정은 run 시점 base-ref 비교)",
|
|
593
|
-
"reverify_scope_unlinked": " reverify-scope: full 예상 — {ids} 이(가) 직전 리포트의 어느 stage 에도 연결되지 않아 범위를
|
|
594
|
-
"reverify_scope_full": " reverify-scope: full 예상 — {reason}",
|
|
596
|
+
"reverify_scope_unlinked": " reverify-scope: full 예상 — {ids} 이(가) 직전 리포트의 어느 stage 에도 연결되지 않아 범위를 좁히지 못함\n (좁히지 못하는 것은 재검증 범위이지 답변이 아닙니다 — 답변은 모두 carry-in 되어 반영되고, 대신 stage 전체를 다시 검증합니다)",
|
|
597
|
+
"reverify_scope_full": " reverify-scope: full 예상 — {reason}\n (좁히지 못하는 것은 재검증 범위이지 답변이 아닙니다 — 답변은 모두 carry-in 되어 반영되고, 대신 stage 전체를 다시 검증합니다)",
|
|
595
598
|
"reverify_scope_user_full": " reverify-scope: full (사용자 지정 — 전체 재검증)",
|
|
596
599
|
"reverify_scope_user_stages": " reverify-scope: stage {stages} 재검증 지정 (사용자 지정 — 하위 stage 포함, 나머지는 직전 판정 이월)",
|
|
597
600
|
"stage_whole_task": "전체 task",
|
|
@@ -247,6 +247,18 @@ def lead_launch_spec(provider: str) -> LeadLaunchSpec:
|
|
|
247
247
|
return spec.lead_launch
|
|
248
248
|
|
|
249
249
|
|
|
250
|
+
def bare_lead_launch_argv(provider: str) -> list[str]:
|
|
251
|
+
"""Start this provider's CLI with nothing to do yet.
|
|
252
|
+
|
|
253
|
+
`okstra run <lead>` opens the host; the okstra-run skill inside it collects
|
|
254
|
+
task inputs, exactly as the Claude Code path already works. So there is no
|
|
255
|
+
prompt and no model to pin here — only the flags that make the session
|
|
256
|
+
usable to okstra at all.
|
|
257
|
+
"""
|
|
258
|
+
launch = lead_launch_spec(provider)
|
|
259
|
+
return [launch.executable, *launch.sandbox_waiver]
|
|
260
|
+
|
|
261
|
+
|
|
250
262
|
def lead_launch_argv(
|
|
251
263
|
provider: str,
|
|
252
264
|
*,
|
|
@@ -4809,6 +4809,33 @@ def render_args(state: WizardState) -> dict[str, str]:
|
|
|
4809
4809
|
}
|
|
4810
4810
|
|
|
4811
4811
|
|
|
4812
|
+
def _clarification_sidecar_line(state: WizardState) -> Optional[str]:
|
|
4813
|
+
"""확인 블록에 찍는 `user-responses/` 첨부 현황.
|
|
4814
|
+
|
|
4815
|
+
picker 단계의 옵션 라벨에도 같은 사실이 붙지만 그 화면을 지나면 사라지고,
|
|
4816
|
+
확인 블록에는 final-report 경로만 남았다. 그 줄 바로 밑에 "범위를 좁히지
|
|
4817
|
+
못함" 이 오니 두 줄이 겹쳐 "답변이 안 붙었다" 로 읽혔다 — 실제로는 첨부돼
|
|
4818
|
+
반영되고 있었다. 실행 직전 화면에서 답변 id 를 직접 보여 그 오해를 없앤다.
|
|
4819
|
+
"""
|
|
4820
|
+
if not state.clarification_response_path or not state.project_root:
|
|
4821
|
+
return None
|
|
4822
|
+
report = _resolve_path(
|
|
4823
|
+
state.clarification_response_path, Path(state.project_root)
|
|
4824
|
+
)
|
|
4825
|
+
files = user_response_sidecars(report)
|
|
4826
|
+
if not files:
|
|
4827
|
+
return _msg(state.workspace_root, "confirmation",
|
|
4828
|
+
"clarification_sidecars_empty")
|
|
4829
|
+
answers = sorted(sidecar_answers(report))
|
|
4830
|
+
return _msg(
|
|
4831
|
+
state.workspace_root, "confirmation", "clarification_sidecars_attached",
|
|
4832
|
+
files=str(len(files)), count=str(len(answers)),
|
|
4833
|
+
ids=", ".join(answers) or _msg(
|
|
4834
|
+
state.workspace_root, "confirmation",
|
|
4835
|
+
"clarification_sidecars_none_parsed"),
|
|
4836
|
+
)
|
|
4837
|
+
|
|
4838
|
+
|
|
4812
4839
|
def _reverify_scope_line(state: WizardState) -> Optional[str]:
|
|
4813
4840
|
"""이번 clarification 재실행이 좁혀질지 — 확인 단계에서 보여주는 줄.
|
|
4814
4841
|
|
|
@@ -4923,6 +4950,9 @@ def confirmation_block(state: WizardState) -> str:
|
|
|
4923
4950
|
lines.append(f" stage : {stage}")
|
|
4924
4951
|
if state.clarification_response_path:
|
|
4925
4952
|
lines.append(f" clarification : {state.clarification_response_path}")
|
|
4953
|
+
sidecar_line = _clarification_sidecar_line(state)
|
|
4954
|
+
if sidecar_line is not None:
|
|
4955
|
+
lines.append(sidecar_line)
|
|
4926
4956
|
reverify_line = _reverify_scope_line(state)
|
|
4927
4957
|
if reverify_line is not None:
|
|
4928
4958
|
lines.append(reverify_line)
|
|
@@ -66,7 +66,10 @@ from okstra_ctl.report_translation import ( # noqa: E402
|
|
|
66
66
|
hangul_share,
|
|
67
67
|
)
|
|
68
68
|
from okstra_ctl.stage_citations import cited_stage_numbers # noqa: E402
|
|
69
|
-
from okstra_ctl.incremental_scope import
|
|
69
|
+
from okstra_ctl.incremental_scope import ( # noqa: E402
|
|
70
|
+
coverage_row_blocked_on,
|
|
71
|
+
stages_for_clarification,
|
|
72
|
+
)
|
|
70
73
|
from okstra_ctl.workflow import DEFAULT_NEXT_PHASE, PHASE_SEQUENCE # noqa: E402
|
|
71
74
|
from okstra_ctl.md_table import ( # noqa: E402
|
|
72
75
|
is_separator_row as _is_markdown_separator,
|
|
@@ -4155,6 +4158,13 @@ def _validate_approval_clarification_backtrace(
|
|
|
4155
4158
|
`incremental-scope` resolves impacted stages from these links and treats an
|
|
4156
4159
|
id that traces to no stage as grounds to re-verify everything, so one
|
|
4157
4160
|
unlinked blocker turns a narrow re-run into a full one.
|
|
4161
|
+
|
|
4162
|
+
The link must also *resolve to a stage*, which is the thing the re-run
|
|
4163
|
+
actually reads. Checking only that a link exists let a row satisfy this
|
|
4164
|
+
gate and still force full: `P-Req-*` and `P-Val-*` ids are numbered by
|
|
4165
|
+
position in their own array, so they carry no stage, and a blocked
|
|
4166
|
+
coverage row whose `coveredBy` is prose cites none either. Both shapes
|
|
4167
|
+
passed while the re-run they were meant to narrow re-verified everything.
|
|
4158
4168
|
"""
|
|
4159
4169
|
if (data.get("header") or {}).get("taskType") != "implementation-planning":
|
|
4160
4170
|
return
|
|
@@ -4170,15 +4180,28 @@ def _validate_approval_clarification_backtrace(
|
|
|
4170
4180
|
if not isinstance(row, dict) or row.get("blocks") != "approval":
|
|
4171
4181
|
continue
|
|
4172
4182
|
row_id = str(row.get("id") or "<unknown>")
|
|
4173
|
-
if _has_clarification_backtrace(row_id, plan_items, coverage):
|
|
4183
|
+
if not _has_clarification_backtrace(row_id, plan_items, coverage):
|
|
4184
|
+
failures.append(
|
|
4185
|
+
f"final-report data.json: clarification `{row_id}` blocks approval "
|
|
4186
|
+
"but has no back-trace into the plan — no plan item carries it as "
|
|
4187
|
+
"`clarificationId`, and no requirement-coverage row is `blocked "
|
|
4188
|
+
f"{row_id}` in its `status` or `approvalDisposition`. An item that "
|
|
4189
|
+
"withholds approval without recording what it affects forces the "
|
|
4190
|
+
"next re-run to re-verify everything."
|
|
4191
|
+
)
|
|
4192
|
+
continue
|
|
4193
|
+
if stages_for_clarification(data, row_id):
|
|
4174
4194
|
continue
|
|
4175
4195
|
failures.append(
|
|
4176
4196
|
f"final-report data.json: clarification `{row_id}` blocks approval "
|
|
4177
|
-
"
|
|
4178
|
-
"`
|
|
4179
|
-
|
|
4180
|
-
"
|
|
4181
|
-
"
|
|
4197
|
+
"and is linked, but the link resolves to no stage. `incremental-"
|
|
4198
|
+
"scope` reads the stage from a `P-Step-<stage>.<step>` / `P-Prep-"
|
|
4199
|
+
"S<stage>-<kind>` plan-item id, or from a `Stage N` citation in the "
|
|
4200
|
+
f"blocked coverage row's `coveredBy`. A `P-Req-*` / `P-Val-*` id "
|
|
4201
|
+
"carries no stage number, so a row linked only that way must cite "
|
|
4202
|
+
"the stage in `coveredBy`. A blocker whose blast radius resolves to "
|
|
4203
|
+
"no stage costs exactly what an unlinked one does — the next re-run "
|
|
4204
|
+
"re-verifies every stage."
|
|
4182
4205
|
)
|
|
4183
4206
|
|
|
4184
4207
|
|
|
@@ -32,6 +32,8 @@ const RUNTIME_ALIASES = new Map([
|
|
|
32
32
|
["claude-code", "claude-code"],
|
|
33
33
|
["codex", "codex"],
|
|
34
34
|
["antigravity", "antigravity"],
|
|
35
|
+
["avg", "antigravity"],
|
|
36
|
+
["agy", "antigravity"],
|
|
35
37
|
["external", "external"],
|
|
36
38
|
]);
|
|
37
39
|
|
|
@@ -110,10 +112,18 @@ export function buildRunPlan({ args, paths, resolution, runManifestPath = "" })
|
|
|
110
112
|
// Handed to okstra.sh, which owns input collection and the launch. Only
|
|
111
113
|
// the runtime is decided here; everything else is passed through so the
|
|
112
114
|
// launcher can prompt for whatever is still missing.
|
|
115
|
+
// --launch-only: open the host and stop. Task inputs are collected by the
|
|
116
|
+
// okstra-run skill inside the session, which is how the Claude Code path
|
|
117
|
+
// has always worked — demanding a task id out here asks for it before the
|
|
118
|
+
// user has a session to pick one in.
|
|
113
119
|
commands: [
|
|
114
120
|
{
|
|
115
121
|
name: "launcher",
|
|
116
|
-
args: [
|
|
122
|
+
args: [
|
|
123
|
+
"--launch-only",
|
|
124
|
+
"--lead-runtime", resolution.resolvedRuntime,
|
|
125
|
+
...withoutRuntimeSelection(args),
|
|
126
|
+
],
|
|
117
127
|
capture: false,
|
|
118
128
|
},
|
|
119
129
|
],
|