okstra 0.32.1 → 0.33.0
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/kr/architecture.md +1 -1
- package/docs/kr/cli.md +1 -1
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/codex-worker.md +1 -1
- package/runtime/agents/workers/gemini-worker.md +1 -1
- package/runtime/bin/okstra-codex-exec.sh +31 -4
- package/runtime/bin/okstra-gemini-exec.sh +32 -6
- package/runtime/prompts/profiles/_common-contract.md +1 -1
- package/runtime/python/okstra_ctl/render.py +537 -227
- package/runtime/skills/okstra-team-contract/SKILL.md +3 -3
package/docs/kr/architecture.md
CHANGED
|
@@ -880,7 +880,7 @@ Phase 7 step 1.5 가 final-report MD 한 본을 입력으로 두 view 를 결정
|
|
|
880
880
|
### Live-log mirror (codex / gemini wrapper)
|
|
881
881
|
|
|
882
882
|
- `scripts/okstra-codex-exec.sh`, `scripts/okstra-gemini-exec.sh` 는 dispatch 마다 prompt path 옆에 `<prompt>.log` sidecar 를 만들고 stdout 을 거기로 mirror 합니다 (`tee`, `PIPESTATUS[0]` 로 종료코드 보존). stderr 은 같은 파일에 append (subagent stderr 캡처 contract 보존), 매 dispatch 시 truncate. 호출 subagent 의 `BashOutput` 폴링은 60s 간격이라 long-running run (analysis 의 large-codebase scan, implementation 의 cargo / pytest) 동안 사용자가 stalled state 를 탐지할 수 없는 문제를 해소합니다.
|
|
883
|
-
- `$TMUX` 가 셋팅된 lead 환경이면 wrapper 가 sibling pane 을 자동 분할해 `tail -F <log-path>` 를 띄웁니다. pane title 은 `<cli>-<role>-trace` (e.g. `codex-worker-trace`, `gemini-
|
|
883
|
+
- `$TMUX` 가 셋팅된 lead 환경이면 wrapper 가 sibling pane 을 자동 분할해 `tail -F <log-path>` 를 띄웁니다. trace pane title 은 `<cli>-<role>-<pid>-trace` (e.g. `codex-worker-93421-trace`, `gemini-executor-93422-trace`); 동일 시점에 caller (worker) pane title 도 `<cli>-<role>-<pid>` 로 셋팅돼 worker ↔ trace 쌍을 한 눈에 매칭할 수 있습니다. `<pid>` 는 wrapper 자기 자신의 PID 라서 동일 role 의 worker 가 둘 이상 동시에 spawn 돼도 서로 구분됩니다. role 은 wrapper 의 5번째 optional positional 인자이며, 누락 시 기본값 `worker` 로 떨어집니다. caller 가 다른 라벨(예: `executor`)을 원하면 5번째 인자로 명시해야 합니다. wrapper 진입 직전의 caller pane title 은 capture 해두고 EXIT trap 에서 복원하므로, dispatch 사이의 stale title 이 남지 않습니다. focus 는 caller pane 으로 복귀하고, CLI 종료 후 pane 은 유지돼 스크롤백 가능. `$TMUX` 미설정, split 실패, 구버전 tmux 등 모든 경로는 silent degrade.
|
|
884
884
|
- **Claude `/exit` 시 자동 정리**: trace pane 의 `tail -F` 는 tmux 셸의 자식이라 Claude 가 종료돼도 살아남는 문제를 막기 위해, wrapper 는 spawn 한 pane id 를 caller `$TMUX_PANE` 으로 키된 registry (`${TMPDIR:-/tmp}/okstra-trace-panes/<caller-pane>.list`) 에 append 합니다. `templates/reports/settings.template.json` 의 `hooks.SessionEnd` 가 `$HOME/.okstra/bin/okstra-trace-cleanup.sh` 를 호출해 자신의 caller pane registry 만 읽어 `tmux kill-pane` 합니다. caller pane 단위로 scope 가 잡혀 있어 같은 tmux 세션에 Claude 인스턴스가 여러 개 떠 있어도 서로의 trace pane 을 죽이지 않습니다. tmux 가 없거나 stale pane id 인 경우 silent degrade.
|
|
885
885
|
- **Phase 종료 시 사용자 확인**: 매 phase 의 마지막 단계로 lead 가 `okstra-trace-cleanup.sh --list` 로 등록된 pane 목록을 출력한 뒤 사용자에게 "모두 닫기 / 그대로 두기" 양자택일을 묻고 응답대로 처리합니다 (`prompts/profiles/_common-contract.md` 의 *Phase wrap-up* 항목). `$TMUX_PANE` 미설정 환경에서는 단계 자체가 silent-skip. `--list` 모드는 pane 을 죽이지 않고 `<pane_id>\t<pane_title>` 만 출력하므로 사용자가 무엇이 닫힐지 시각적으로 확인할 수 있습니다.
|
|
886
886
|
- 디스크 누적은 `okstra-logs` skill 이 read-only 로 인벤토리 + cleanup 명령을 제안합니다 (실행은 사용자 copy-paste).
|
package/docs/kr/cli.md
CHANGED
|
@@ -546,5 +546,5 @@ chmod +x ~/.local/bin/okstra-ctl
|
|
|
546
546
|
|
|
547
547
|
### Live-log sidecar
|
|
548
548
|
|
|
549
|
-
codex / gemini wrapper 는 매 dispatch 마다 `runs/<task-type>/prompts/<worker>-prompt-<phase>-<seq>.log` sidecar 를 만들고 stdout / stderr 를 mirror 합니다. tmux 안에서 lead 를 띄우면 wrapper 가 자동으로 `tail -F` pane 을 분할합니다 (title: `<cli>-<role>-trace
|
|
549
|
+
codex / gemini wrapper 는 매 dispatch 마다 `runs/<task-type>/prompts/<worker>-prompt-<phase>-<seq>.log` sidecar 를 만들고 stdout / stderr 를 mirror 합니다. tmux 안에서 lead 를 띄우면 wrapper 가 자동으로 `tail -F` pane 을 분할합니다 (trace pane title: `<cli>-<role>-<pid>-trace`, caller (worker) pane title: `<cli>-<role>-<pid>` — wrapper PID 가 동일 role 의 동시 dispatch 를 구분합니다). 분할된 trace pane 은 caller `$TMUX_PANE` 으로 키된 registry 에 등록돼, Claude `/exit` 시 `SessionEnd` 훅이 `okstra-trace-cleanup.sh` 로 자동 정리합니다. 사용량 인벤토리와 `find … -delete` cleanup 명령은 `okstra-logs` skill 이 read-only 로 제안합니다. 자세한 와이어링은 [`docs/kr/architecture.md`](architecture.md) 의 *Live-log mirror* 절 참고.
|
|
550
550
|
|
package/package.json
CHANGED
package/runtime/BUILD.json
CHANGED
|
@@ -30,7 +30,7 @@ You are a Codex worker agent. Your job is to execute the OpenAI Codex CLI and re
|
|
|
30
30
|
$HOME/.okstra/bin/okstra-codex-exec.sh "<absolute-project-root>" "<assigned-model-execution-value>" "<absolute-prompt-history-path>" [<absolute-worktree-path>] [<role>]
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
The fifth argument `<role>` is the trace-pane
|
|
33
|
+
The fifth argument `<role>` is folded into both the caller (worker) pane title `codex-<role>-<pid>` and the sibling trace-pane title `codex-<role>-<pid>-trace` (`<pid>` = the wrapper's PID, present so concurrent dispatches of the same role can be told apart). Pass the literal string `worker` for every dispatch from this subagent. The wrapper defaults to `worker` when the argument is omitted, but pass it explicitly so the dispatch is self-describing.
|
|
34
34
|
|
|
35
35
|
The fourth argument is **mandatory for implementation phase** and optional otherwise. It must be the literal `EXECUTOR_WORKTREE_PATH` recorded in the run context; the wrapper forwards it to codex as `--add-dir`, which grants the codex sandbox write access to the worktree (where all implementation-phase mutations occur). Without it, codex's `workspace-write` sandbox is anchored only at `<project-root>` and rejects every Edit/Write that targets the worktree (EPERM), which is the failure pattern that originally motivated this argument.
|
|
36
36
|
|
|
@@ -30,7 +30,7 @@ You are a Gemini worker agent. Your job is to execute the Google Gemini CLI and
|
|
|
30
30
|
$HOME/.okstra/bin/okstra-gemini-exec.sh "<absolute-project-root>" "<assigned-model-execution-value>" "<absolute-prompt-history-path>" [<absolute-worktree-path>] [<role>]
|
|
31
31
|
```
|
|
32
32
|
|
|
33
|
-
The fifth argument `<role>` is the trace-pane
|
|
33
|
+
The fifth argument `<role>` is folded into both the caller (worker) pane title `gemini-<role>-<pid>` and the sibling trace-pane title `gemini-<role>-<pid>-trace` (`<pid>` = the wrapper's PID, present so concurrent dispatches of the same role can be told apart). Pass the literal string `worker` for every dispatch from this subagent. The wrapper defaults to `worker` when the argument is omitted, but pass it explicitly so the dispatch is self-describing.
|
|
34
34
|
|
|
35
35
|
The fourth argument is **mandatory for implementation phase** and optional otherwise. It must be the literal `EXECUTOR_WORKTREE_PATH` recorded in the run context; the wrapper appends it to gemini's `--include-directories` list so the model can both read and operate on the worktree alongside project-root.
|
|
36
36
|
|
|
@@ -170,6 +170,21 @@ python3 "$script_dir/okstra-wrapper-status.py" \
|
|
|
170
170
|
init "$status_path" "$(basename "$0")" "$role" "$$" "$started_ts" "$log_path" \
|
|
171
171
|
>>"$log_path" 2>&1 || true
|
|
172
172
|
|
|
173
|
+
# Pane titles: worker (caller) pane gets `codex-<role>-<pid>`; the sibling
|
|
174
|
+
# trace pane appends `-trace`. The wrapper PID disambiguates concurrent
|
|
175
|
+
# dispatches of the same role (e.g. two `codex-worker` panes spawned in
|
|
176
|
+
# parallel) so the operator can match worker ↔ trace at a glance.
|
|
177
|
+
pane_label="codex-${role}-$$"
|
|
178
|
+
trace_label="${pane_label}-trace"
|
|
179
|
+
|
|
180
|
+
# Capture the caller pane's current title so the EXIT trap can restore it
|
|
181
|
+
# once the wrapper returns. Empty when not in tmux or capture fails — the
|
|
182
|
+
# restore step degrades to a no-op in that case.
|
|
183
|
+
original_caller_title=""
|
|
184
|
+
if [[ -n "${TMUX_PANE:-}" ]]; then
|
|
185
|
+
original_caller_title=$(tmux display-message -p -t "$TMUX_PANE" '#{pane_title}' 2>/dev/null || true)
|
|
186
|
+
fi
|
|
187
|
+
|
|
173
188
|
_okstra_status_finish() {
|
|
174
189
|
local exit_code=$?
|
|
175
190
|
local ended_ts
|
|
@@ -178,17 +193,29 @@ _okstra_status_finish() {
|
|
|
178
193
|
python3 "$script_dir/okstra-wrapper-status.py" \
|
|
179
194
|
finish "$status_path" "$exit_code" "$ended_ts" "$duration_ms" \
|
|
180
195
|
>>"$log_path" 2>&1 || true
|
|
196
|
+
if [[ -n "${TMUX_PANE:-}" && -n "$original_caller_title" ]]; then
|
|
197
|
+
tmux select-pane -t "$TMUX_PANE" -T "$original_caller_title" 2>/dev/null || true
|
|
198
|
+
fi
|
|
181
199
|
}
|
|
182
200
|
trap _okstra_status_finish EXIT
|
|
183
201
|
|
|
202
|
+
# Label the caller (worker) pane now that the restore trap is armed. Any
|
|
203
|
+
# failure after this point still rewinds the title to its prior value.
|
|
204
|
+
if [[ -n "${TMUX_PANE:-}" ]]; then
|
|
205
|
+
tmux select-pane -t "$TMUX_PANE" -T "$pane_label" 2>/dev/null || true
|
|
206
|
+
fi
|
|
207
|
+
|
|
184
208
|
# When a tmux session is reachable, split a sibling pane that tails the live
|
|
185
209
|
# log so the operator can watch codex's progress in real time without waiting
|
|
186
210
|
# for the wrapper to exit. This fires in every phase the wrapper is invoked
|
|
187
211
|
# from (analysis, error-analysis, implementation-planning, implementation,
|
|
188
212
|
# …) — long-running codex dispatches are not implementation-specific. The
|
|
189
|
-
# new pane carries the title `codex-<role>-trace`
|
|
190
|
-
#
|
|
191
|
-
#
|
|
213
|
+
# new pane carries the title `codex-<role>-<pid>-trace` (matching the
|
|
214
|
+
# caller pane's `codex-<role>-<pid>` label so worker ↔ trace pairs are
|
|
215
|
+
# greppable); `role` is the optional 5th positional arg (defaults to
|
|
216
|
+
# `worker`); callers that dispatch a different role (e.g. `executor`) must
|
|
217
|
+
# pass it explicitly. The `<pid>` suffix is the wrapper's PID and
|
|
218
|
+
# disambiguates concurrent dispatches of the same role. The pane uses
|
|
192
219
|
# `tail -F`
|
|
193
220
|
# (follow-by-name) so it survives any truncation a re-dispatch performs on
|
|
194
221
|
# the same log path. Failures are tolerated silently: missing $TMUX, a tmux
|
|
@@ -201,7 +228,7 @@ if [[ -n "${TMUX:-}" ]]; then
|
|
|
201
228
|
-c "$(dirname "$log_path")" \
|
|
202
229
|
"tail -F $(printf '%q' "$log_path")" 2>/dev/null || true)
|
|
203
230
|
if [[ -n "$trace_pane" ]]; then
|
|
204
|
-
tmux select-pane -t "$trace_pane" -T "
|
|
231
|
+
tmux select-pane -t "$trace_pane" -T "$trace_label" 2>/dev/null || true
|
|
205
232
|
tmux last-pane 2>/dev/null || true
|
|
206
233
|
# Register the spawned pane so the `SessionEnd` hook (see
|
|
207
234
|
# `okstra-trace-cleanup.sh`) can kill it when the caller's Claude
|
|
@@ -121,6 +121,21 @@ python3 "$script_dir/okstra-wrapper-status.py" \
|
|
|
121
121
|
init "$status_path" "$(basename "$0")" "$role" "$$" "$started_ts" "$log_path" \
|
|
122
122
|
>>"$log_path" 2>&1 || true
|
|
123
123
|
|
|
124
|
+
# Pane titles: worker (caller) pane gets `gemini-<role>-<pid>`; the sibling
|
|
125
|
+
# trace pane appends `-trace`. The wrapper PID disambiguates concurrent
|
|
126
|
+
# dispatches of the same role (e.g. two `gemini-worker` panes spawned in
|
|
127
|
+
# parallel) so the operator can match worker ↔ trace at a glance.
|
|
128
|
+
pane_label="gemini-${role}-$$"
|
|
129
|
+
trace_label="${pane_label}-trace"
|
|
130
|
+
|
|
131
|
+
# Capture the caller pane's current title so the EXIT trap can restore it
|
|
132
|
+
# once the wrapper returns. Empty when not in tmux or capture fails — the
|
|
133
|
+
# restore step degrades to a no-op in that case.
|
|
134
|
+
original_caller_title=""
|
|
135
|
+
if [[ -n "${TMUX_PANE:-}" ]]; then
|
|
136
|
+
original_caller_title=$(tmux display-message -p -t "$TMUX_PANE" '#{pane_title}' 2>/dev/null || true)
|
|
137
|
+
fi
|
|
138
|
+
|
|
124
139
|
_okstra_status_finish() {
|
|
125
140
|
local exit_code=$?
|
|
126
141
|
local ended_ts
|
|
@@ -129,23 +144,34 @@ _okstra_status_finish() {
|
|
|
129
144
|
python3 "$script_dir/okstra-wrapper-status.py" \
|
|
130
145
|
finish "$status_path" "$exit_code" "$ended_ts" "$duration_ms" \
|
|
131
146
|
>>"$log_path" 2>&1 || true
|
|
147
|
+
if [[ -n "${TMUX_PANE:-}" && -n "$original_caller_title" ]]; then
|
|
148
|
+
tmux select-pane -t "$TMUX_PANE" -T "$original_caller_title" 2>/dev/null || true
|
|
149
|
+
fi
|
|
132
150
|
}
|
|
133
151
|
trap _okstra_status_finish EXIT
|
|
134
152
|
|
|
153
|
+
# Label the caller (worker) pane now that the restore trap is armed. Any
|
|
154
|
+
# failure after this point still rewinds the title to its prior value.
|
|
155
|
+
if [[ -n "${TMUX_PANE:-}" ]]; then
|
|
156
|
+
tmux select-pane -t "$TMUX_PANE" -T "$pane_label" 2>/dev/null || true
|
|
157
|
+
fi
|
|
158
|
+
|
|
135
159
|
# When a tmux session is reachable, split a sibling pane tailing the log so
|
|
136
160
|
# the operator can watch progress live. This fires in every phase the
|
|
137
161
|
# wrapper is invoked from — long-running gemini dispatches are not
|
|
138
|
-
# implementation-specific. Title `gemini-<role>-trace`
|
|
139
|
-
#
|
|
140
|
-
#
|
|
141
|
-
#
|
|
142
|
-
#
|
|
162
|
+
# implementation-specific. Title `gemini-<role>-<pid>-trace` (matching the
|
|
163
|
+
# caller pane's `gemini-<role>-<pid>` label so worker ↔ trace pairs are
|
|
164
|
+
# greppable). `role` is the optional 5th positional arg (defaults to
|
|
165
|
+
# `worker`); callers that dispatch a different role must pass it
|
|
166
|
+
# explicitly. The `<pid>` suffix is the wrapper's PID and disambiguates
|
|
167
|
+
# concurrent dispatches of the same role. See the codex wrapper for the
|
|
168
|
+
# full design rationale and the silent-degrade failure model.
|
|
143
169
|
if [[ -n "${TMUX:-}" ]]; then
|
|
144
170
|
trace_pane=$(tmux split-window -h -P -F '#{pane_id}' \
|
|
145
171
|
-c "$(dirname "$log_path")" \
|
|
146
172
|
"tail -F $(printf '%q' "$log_path")" 2>/dev/null || true)
|
|
147
173
|
if [[ -n "$trace_pane" ]]; then
|
|
148
|
-
tmux select-pane -t "$trace_pane" -T "
|
|
174
|
+
tmux select-pane -t "$trace_pane" -T "$trace_label" 2>/dev/null || true
|
|
149
175
|
tmux last-pane 2>/dev/null || true
|
|
150
176
|
# See `okstra-codex-exec.sh` for the registry rationale — kept in lock-step.
|
|
151
177
|
if [[ -n "${TMUX_PANE:-}" ]]; then
|
|
@@ -26,7 +26,7 @@ profile document.
|
|
|
26
26
|
- Anti-escalation rule (shared):
|
|
27
27
|
- treating "다음 단계 진행해" or equivalent user phrases as authorisation to start a *different* lifecycle phase is forbidden. The next phase begins only in a separate okstra run launched with the new `--task-type`. Per-profile documents may further restrict this within their own scope.
|
|
28
28
|
- Phase wrap-up — worker trace pane disposition (shared, MUST be the *last* step before returning control to the user):
|
|
29
|
-
- Codex / Gemini worker wrappers spawn `tail -F` trace panes in the lead's tmux session (`codex
|
|
29
|
+
- Codex / Gemini worker wrappers spawn `tail -F` trace panes in the lead's tmux session, titled `<cli>-<role>-<pid>-trace` (e.g. `codex-worker-93421-trace`, `gemini-executor-93422-trace`) — the matching caller (worker) pane is titled `<cli>-<role>-<pid>` so worker ↔ trace pairs can be matched by the shared `<pid>` suffix. They survive every worker invocation by design so the operator can scroll back through the final output, but accumulate across phases and clutter the screen.
|
|
30
30
|
- When `$TMUX_PANE` is set, after the final-report file has been written and the routing recommendation has been issued, the lead MUST run `$HOME/.okstra/bin/okstra-trace-cleanup.sh --list` exactly once. The output is a tab-separated `<pane_id>\t<pane_title>` list of every trace pane registered for this Claude session.
|
|
31
31
|
- If the list is empty, skip the question — there is nothing to ask about.
|
|
32
32
|
- Otherwise the lead MUST present the user with a strict binary choice **before** declaring the phase complete. Use one prompt of this shape (Korean preferred, English acceptable if the rest of the run is in English):
|